modify nuttx bug of lack of files

This commit is contained in:
TangYiwen123
2021-06-11 11:25:07 +08:00
parent 4a7f51a5e9
commit 6d0f2ca39a
6752 changed files with 1528133 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
menu "TCP/IP Networking"
config NET_TCP
bool "TCP/IP Networking"
default n
select NET_READAHEAD if !NET_TCP_NO_STACK
---help---
Enable or disable TCP networking support.
config NET_TCP_NO_STACK
bool "Disable TCP/IP Stack"
default n
select NET_TCP
---help---
Build without TCP/IP stack even if TCP networking support enabled.
if NET_TCP && !NET_TCP_NO_STACK
config NET_TCP_DELAYED_ACK
bool "TCP/IP Delayed ACK"
default n
---help---
RFC 1122: A host that is receiving a stream of TCP data segments
can increase efficiency in both the Internet and the hosts
by sending fewer than one ACK (acknowledgment) segment per data
segment received; this is known as a "delayed ACK".
TCP should implement a delayed ACK, but an ACK should not be
excessively delayed; in particular, the delay MUST be less than
0.5 seconds, and in a stream of full-sized segments there should
be an ACK for at least every second segments.
config NET_TCP_KEEPALIVE
bool "TCP/IP Keep-alive support"
default n
select NET_TCPPROTO_OPTIONS
---help---
Enable support for the SO_KEEPALIVE socket option
config NET_TCPURGDATA
bool "Urgent data"
default n
---help---
Determines if support for TCP urgent data notification should be
compiled in. Urgent data (out-of-band data) is a rarely used TCP feature
that is very seldom would be required.
config NET_TCP_CONNS
int "Number of TCP/IP connections"
default 8
---help---
Maximum number of TCP/IP connections (all tasks)
config NET_TCP_NPOLLWAITERS
int "Number of TCP poll waiters"
default 1
config NET_TCP_RTO
int "RTO of TCP/IP connections"
default 3
---help---
RTO of TCP/IP connections (all tasks)
config NET_TCP_WAIT_TIMEOUT
int "TIME_WAIT Length of TCP/IP connections"
default 120
---help---
TIME_WAIT Length of TCP/IP connections (all tasks). In units
of seconds.
config NET_MAX_LISTENPORTS
int "Number of listening ports"
default 20
---help---
Maximum number of listening TCP/IP ports (all tasks). Default: 20
config NET_TCP_FAST_RETRANSMIT_WATERMARK
int "WaterMark to trigger Fast Retransmission"
default 3
---help---
RFC2001:
3. Fast Retransmit
Modifications to the congestion avoidance algorithm were proposed in
1990 [3]. Before describing the change, realize that TCP may
generate an immediate acknowledgment (a duplicate ACK) when an out-
of-order segment is received (Section 4.2.2.21 of [1], with a note
that one reason for doing so was for the experimental fast-
retransmit algorithm). This duplicate ACK should not be delayed.
The purpose of this duplicate ACK is to let the other end know that a
segment was received out of order, and to tell it what sequence
number is expected.
Since TCP does not know whether a duplicate ACK is caused by a lost
segment or just a reordering of segments, it waits for a small number
of duplicate ACKs to be received. It is assumed that if there is
just a reordering of the segments, there will be only one or two
duplicate ACKs before the reordered segment is processed, which will
then generate a new ACK. If three or more duplicate ACKs are
received in a row, it is a strong indication that a segment has been
lost. TCP then performs a retransmission of what appears to be the
missing segment, without waiting for a retransmission timer to
expire.
config NET_TCP_NOTIFIER
bool "Support TCP notifications"
default n
depends on SCHED_WORKQUEUE
select WQUEUE_NOTIFIER
---help---
Enable building of TCP notifier logic that will execute a worker
function on the low priority work queue when read-ahead data
is available or when a TCP connection is lost. This is is a general
purpose notifier, but was developed specifically to support poll()
logic where the poll must wait for these events.
config NET_TCP_WRITE_BUFFERS
bool "Enable TCP/IP write buffering"
default n
select NET_WRITE_BUFFERS
---help---
Write buffers allows buffering of ongoing TCP/IP packets, providing
for higher performance, streamed output.
You might want to disable TCP/IP write buffering on a highly memory
memory constrained system where there are no performance issues.
if NET_TCP_WRITE_BUFFERS
config NET_TCP_NWRBCHAINS
int "Number of pre-allocated I/O buffer chain heads"
default 8
---help---
These tiny nodes are used as "containers" to support queuing of
TCP write buffers. This setting will limit the number of TCP write
operations that can be "in-flight" at any give time. So a good
choice for this value would be the same as the maximum number of
TCP connections.
config NET_TCP_WRBUFFER_DEBUG
bool "Force write buffer debug"
default n
depends on DEBUG_FEATURES
select IOB_DEBUG
---help---
This option will force debug output from TCP write buffer logic,
even without network debug output. This is not normally something
that would want to do but is convenient if you are debugging the
write buffer logic and do not want to get overloaded with other
network-related debug output.
config NET_TCP_WRBUFFER_DUMP
bool "Force write buffer dump"
default n
depends on DEBUG_NET || NET_TCP_WRBUFFER_DEBUG
select IOB_DEBUG
---help---
Dump the contents of the write buffers. You do not want to do this
unless you really want to analyze the write buffer transfers in
detail.
endif # NET_TCP_WRITE_BUFFERS
config NET_TCPBACKLOG
bool "TCP/IP backlog support"
default n
---help---
Incoming connections pend in a backlog until accept() is called.
The size of the backlog is selected when listen() is called.
if NET_TCPBACKLOG
config NET_TCPBACKLOG_CONNS
int "TCP backlog conns threshold"
default 8
---help---
Maximum number of TCP backlog connections (all tasks).
endif # NET_TCPBACKLOG
config NET_TCP_SPLIT
bool "Enable packet splitting"
default n
depends on !NET_TCP_WRITE_BUFFERS
---help---
send() will not return until the transfer has been ACKed by the
recipient. But under RFC 1122, the host need not ACK each packet
immediately; the host may wait for 500 MS before ACKing. This
combination can cause very slow performance with small transfers are
made to an RFC 1122 client. However, the RFC 1122 must ACK at least
every second (odd) packet.
This option enables logic to trick the RFC 1122 host be exploiting
this last RFC 1122 requirement: If an odd number of packets were to
be sent, then send() will split the last even packet to guarantee
that an even number of packets will be sent and the RFC 1122 host
will ACK the final packet immediately.
if NET_TCP_SPLIT
config NET_TCP_SPLIT_SIZE
int "Split size threshold"
default 40
---help---
Packets of this size or smaller than this will not be split.
endif # NET_TCP_SPLIT
config NET_SENDFILE
bool "Optimized network sendfile()"
default n
---help---
Support larger, higher performance sendfile() for transferring
files out a TCP connection.
endif # NET_TCP && !NET_TCP_NO_STACK
endmenu # TCP/IP Networking
+73
View File
@@ -0,0 +1,73 @@
############################################################################
# net/tcp/Make.defs
#
# 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.
#
############################################################################
# TCP/IP source files
ifeq ($(CONFIG_NET_TCP),y)
ifneq ($(CONFIG_NET_TCP_NO_STACK),y)
# Socket layer
SOCK_CSRCS += tcp_connect.c tcp_accept.c tcp_recvfrom.c
ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y)
SOCK_CSRCS += tcp_send_buffered.c
else
SOCK_CSRCS += tcp_send_unbuffered.c
endif
ifeq ($(CONFIG_NET_SENDFILE),y)
SOCK_CSRCS += tcp_sendfile.c
endif
ifeq ($(CONFIG_NET_TCP_NOTIFIER),y)
SOCK_CSRCS += tcp_notifier.c
ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y)
SOCK_CSRCS += tcp_txdrain.c
endif
endif
ifeq ($(CONFIG_NET_TCPPROTO_OPTIONS),y)
SOCK_CSRCS += tcp_setsockopt.c tcp_getsockopt.c
endif
# Transport layer
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 write buffering
ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y)
NET_CSRCS += tcp_wrbuffer.c
ifeq ($(CONFIG_DEBUG_FEATURES),y)
NET_CSRCS += tcp_wrbuffer_dump.c
endif
endif
# Include TCP build support
DEPPATH += --dep-path tcp
VPATH += :tcp
endif # !CONFIG_NET_TCP_NO_STACK
endif # CONFIG_NET_TCP
File diff suppressed because it is too large Load Diff
+315
View File
@@ -0,0 +1,315 @@
/****************************************************************************
* net/tcp/tcp_accept.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 <nuttx/config.h>
#ifdef CONFIG_NET_TCP
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include "socket/socket.h"
#include "tcp/tcp.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct accept_s
{
FAR struct socket *acpt_sock; /* The accepting socket */
sem_t acpt_sem; /* Wait for driver event */
FAR struct sockaddr *acpt_addr; /* Return connection address */
FAR socklen_t *acpt_addrlen; /* Return length of address */
FAR struct tcp_conn_s *acpt_newconn; /* The accepted connection */
int acpt_result; /* The result of the wait */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: accept_tcpsender
*
* Description:
* Get the sender's address from the UDP packet
*
* Input Parameters:
* psock - The state structure of the accepting socket
* conn - The newly accepted TCP connection
* pstate - the recvfrom state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked
*
****************************************************************************/
#ifdef CONFIG_NET_TCP
static inline void accept_tcpsender(FAR struct socket *psock,
FAR struct tcp_conn_s *conn,
FAR struct sockaddr *addr,
socklen_t *addrlen)
{
if (addr)
{
/* If an address is provided, then the length must also be provided. */
DEBUGASSERT(addrlen);
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
/* If both IPv4 and IPv6 support are enabled, then we will need to
* select which one to use when obtaining the sender's IP address.
*/
if (psock->s_domain == PF_INET)
#endif /* CONFIG_NET_IPv6 */
{
FAR struct sockaddr_in *inaddr = (FAR struct sockaddr_in *)addr;
inaddr->sin_family = AF_INET;
inaddr->sin_port = conn->rport;
net_ipv4addr_copy(inaddr->sin_addr.s_addr, conn->u.ipv4.raddr);
memset(inaddr->sin_zero, 0, sizeof(inaddr->sin_zero));
*addrlen = sizeof(struct sockaddr_in);
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
/* Otherwise, this the IPv6 address is needed */
else
#endif /* CONFIG_NET_IPv4 */
{
FAR struct sockaddr_in6 *inaddr = (FAR struct sockaddr_in6 *)addr;
DEBUGASSERT(psock->s_domain == PF_INET6);
inaddr->sin6_family = AF_INET6;
inaddr->sin6_port = conn->rport;
net_ipv6addr_copy(inaddr->sin6_addr.s6_addr, conn->u.ipv6.raddr);
*addrlen = sizeof(struct sockaddr_in6);
}
#endif /* CONFIG_NET_IPv6 */
}
}
#endif /* CONFIG_NET_TCP */
/****************************************************************************
* Name: accept_eventhandler
*
* Description:
* Receive event callbacks when connections occur
*
* Input Parameters:
* listener The connection structure of the listener
* conn The connection structure that was just accepted
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked
*
****************************************************************************/
static int accept_eventhandler(FAR struct tcp_conn_s *listener,
FAR struct tcp_conn_s *conn)
{
struct accept_s *pstate = (struct accept_s *)listener->accept_private;
int ret = -EINVAL;
if (pstate)
{
/* Get the connection address */
accept_tcpsender(pstate->acpt_sock, conn, pstate->acpt_addr,
pstate->acpt_addrlen);
/* Save the connection structure */
pstate->acpt_newconn = conn;
pstate->acpt_result = OK;
/* There should be a reference of one on the new connection */
DEBUGASSERT(conn->crefs == 1);
/* Wake-up the waiting caller thread */
nxsem_post(&pstate->acpt_sem);
/* Stop any further callbacks */
listener->accept_private = NULL;
listener->accept = NULL;
ret = OK;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_accept
*
* Description:
* This function implements accept() for TCP/IP sockets. See the
* description of accept() for further information.
*
* Input Parameters:
* psock The listening TCP socket structure
* addr Receives the address of the connecting client
* addrlen Input: allocated size of 'addr', Return: returned size of
* 'addr'
* newconn The new, accepted TCP connection structure
*
* Returned Value:
* Returns zero (OK) on success or a negated errno value on failure.
* See the description of accept of the possible errno values in the
* description of accept().
*
* Assumptions:
* Network is locked.
*
****************************************************************************/
int psock_tcp_accept(FAR struct socket *psock, FAR struct sockaddr *addr,
FAR socklen_t *addrlen, FAR void **newconn)
{
FAR struct tcp_conn_s *conn;
struct accept_s state;
int ret;
DEBUGASSERT(psock && newconn);
/* Check the backlog to see if there is a connection already pending for
* this listener.
*/
conn = (FAR struct tcp_conn_s *)psock->s_conn;
#ifdef CONFIG_NET_TCPBACKLOG
state.acpt_newconn = tcp_backlogremove(conn);
if (state.acpt_newconn)
{
/* Yes... get the address of the connected client */
ninfo("Pending conn=%p\n", state.acpt_newconn);
accept_tcpsender(psock, state.acpt_newconn, addr, addrlen);
}
/* In general, this implementation will not support non-blocking socket
* operations... except in a few cases: Here for TCP accept with
* backlog enabled. If this socket is configured as non-blocking then
* return EAGAIN if there is no pending connection in the backlog.
*/
else if (_SS_ISNONBLOCK(psock->s_flags))
{
return -EAGAIN;
}
else
#endif
{
/* Perform the TCP accept operation */
/* Initialize the state structure. This is done with the network
* locked because we don't want anything to happen until we are
* ready.
*/
state.acpt_sock = psock;
state.acpt_addr = addr;
state.acpt_addrlen = addrlen;
state.acpt_newconn = NULL;
state.acpt_result = OK;
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&state.acpt_sem, 0, 0);
nxsem_set_protocol(&state.acpt_sem, SEM_PRIO_NONE);
/* Set up the callback in the connection */
conn->accept_private = (FAR void *)&state;
conn->accept = accept_eventhandler;
/* Wait for the send to complete or an error to occur: NOTES:
* net_lockedwait will also terminate if a signal is received.
*/
ret = net_lockedwait(&state.acpt_sem);
/* Make sure that no further events are processed */
conn->accept_private = NULL;
conn->accept = NULL;
nxsem_destroy(&state.acpt_sem);
/* Check for a errors. Errors are signalled by negative errno values
* for the send length.
*/
if (state.acpt_result != 0)
{
DEBUGASSERT(state.acpt_result > 0);
return -state.acpt_result;
}
/* If net_lockedwait failed, then we were probably reawakened by a
* signal. In this case, net_lockedwait will have returned negated
* errno appropriately.
*/
if (ret < 0)
{
return ret;
}
}
*newconn = (FAR void *)state.acpt_newconn;
return OK;
}
#endif /* CONFIG_NET_TCP */
@@ -0,0 +1,335 @@
/****************************************************************************
* net/tcp/tcp_appsend.c
*
* Copyright (C) 2007-2010, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Adapted for NuttX from logic in uIP which also has a BSD-like license:
*
* Original author Adam Dunkels <adam@dunkels.com>
* Copyright () 2001-2003, Adam Dunkels.
* All rights reserved.
*
* 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. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP)
#include <inttypes.h>
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/tcp.h>
#include "devif/devif.h"
#include "tcp/tcp.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_appsend
*
* Description:
* Handle application or TCP protocol response. If this function is called
* with dev->d_sndlen > 0, then this is an application attempting to send
* packet.
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP connection structure holding connection information
* result - App result event sent
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint16_t result)
{
uint8_t hdrlen;
ninfo("result: %04x d_sndlen: %d conn->tx_unacked: %" PRId32 "\n",
result, dev->d_sndlen, (uint32_t)conn->tx_unacked);
#ifdef CONFIG_NET_TCP_DELAYED_ACK
/* Did the caller request that an ACK be sent? */
if ((result & TCP_SNDACK) != 0)
{
/* Yes.. Handle delayed acknowledgments */
/* Reset the ACK timer in any event. */
conn->rx_acktimer = 0;
/* Per RFC 1122: "...in a stream of full-sized segments there
* SHOULD be an ACK for at least every second segment."
*
* NOTES:
* 1. If there is a data payload or other flags to be sent with the
* outgoing packet, then we may as well include the ACK too.
* 2. The RFC refers to full-size segments. It is not clear what
* "full-size" means. Does that mean that the payload is the size
* of the MSS? Payload size is not considered other there being
* a payload or or not. Should there be some special action for
* small payloads of size < MSS?
* 3. Experimentation shows that Windows and Linux behave somewhat
* differently; they delay the ACKs for many more segments (6 or
* more). Delaying for more segments would provide less network
* traffic and better performance but seems non-compliant.
*/
if (conn->rx_unackseg > 0 || dev->d_sndlen > 0 ||
result != TCP_SNDACK)
{
/* Reset the delayed ACK state and send the ACK with this packet. */
conn->rx_unackseg = 0;
}
else
{
/* This is only an ACK and there is no pending delayed ACK and
* no TX data is being sent. Indicate that there is one un-ACKed
* segment and don't send anything now.
*/
conn->rx_unackseg = 1;
return;
}
}
/* If there are data to be sent in the same direction as the ACK before
* the second data packet is received and the delay timer expires, the ACK
* is piggybacked with the data segment and sent immediately.
*/
else if (dev->d_sndlen > 0 && conn->rx_unackseg > 0)
{
result |= TCP_SNDACK;
conn->rx_unackseg = 0;
}
#endif
/* Get the IP header length associated with the IP domain configured for
* this TCP connection.
*/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
DEBUGASSERT(IFF_IS_IPv4(dev->d_flags));
hdrlen = IPv4TCP_HDRLEN;
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
DEBUGASSERT(IFF_IS_IPv6(dev->d_flags));
hdrlen = IPv6TCP_HDRLEN;
}
#endif /* CONFIG_NET_IPv6 */
/* Check If the device went down */
if ((result & NETDEV_DOWN) != 0)
{
/* If so, make sure that the connection is marked closed
* and do not try to send anything.
*/
dev->d_sndlen = 0;
conn->tcpstateflags = TCP_CLOSED;
ninfo("TCP state: NETDEV_DOWN\n");
}
/* Check for connection aborted */
else if ((result & TCP_ABORT) != 0)
{
dev->d_sndlen = 0;
conn->tcpstateflags = TCP_CLOSED;
ninfo("TCP state: TCP_CLOSED\n");
tcp_send(dev, conn, TCP_RST | TCP_ACK, hdrlen);
}
/* Check for connection closed */
else if ((result & TCP_CLOSE) != 0)
{
conn->tcpstateflags = TCP_FIN_WAIT_1;
conn->tx_unacked = 1;
conn->nrtx = 0;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
conn->sndseq_max = tcp_getsequence(conn->sndseq) + 1;
#endif
ninfo("TCP state: TCP_FIN_WAIT_1\n");
dev->d_sndlen = 0;
tcp_send(dev, conn, TCP_FIN | TCP_ACK, hdrlen);
}
/* None of the above */
else
{
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
DEBUGASSERT(dev->d_sndlen <= conn->mss);
#else
/* If d_sndlen > 0, the application has data to be sent. */
if (dev->d_sndlen > 0)
{
/* Remember how much data we send out now so that we know
* when everything has been acknowledged. Just increment the
* amount of data sent. This will be needed in sequence number
* calculations and we know that this is not a re-transmission.
* Retransmissions do not go through this path.
*/
conn->tx_unacked += dev->d_sndlen;
/* The application cannot send more than what is allowed by the
* MSS (the minimum of the MSS and the available window).
*/
DEBUGASSERT(dev->d_sndlen <= conn->mss);
}
conn->nrtx = 0;
#endif
/* Then handle the rest of the operation just as for the rexmit case */
tcp_rexmit(dev, conn, result);
}
}
/****************************************************************************
* Name: tcp_rexmit
*
* Description:
* Handle application retransmission
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP connection structure holding connection information
* result - App result event sent
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
void tcp_rexmit(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint16_t result)
{
uint8_t hdrlen;
ninfo("result: %04x d_sndlen: %d conn->tx_unacked: %" PRId32 "\n",
result, dev->d_sndlen, (uint32_t)conn->tx_unacked);
/* Get the IP header length associated with the IP domain configured for
* this TCP connection.
*/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
DEBUGASSERT(IFF_IS_IPv4(dev->d_flags));
hdrlen = IPv4TCP_HDRLEN;
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
DEBUGASSERT(IFF_IS_IPv6(dev->d_flags));
hdrlen = IPv6TCP_HDRLEN;
}
#endif /* CONFIG_NET_IPv6 */
/* If the application has data to be sent, or if the incoming packet had
* new data in it, we must send out a packet.
*/
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
if (dev->d_sndlen > 0)
#else
if (dev->d_sndlen > 0 && conn->tx_unacked > 0)
#endif
{
/* We always set the ACK flag in response packets adding the length of
* the IP and TCP headers.
*/
tcp_send(dev, conn, TCP_ACK | TCP_PSH, dev->d_sndlen + hdrlen);
}
/* If there is no data to send, just send out a pure ACK if one is
* requested.
*/
else if ((result & TCP_SNDACK) != 0)
{
tcp_send(dev, conn, TCP_ACK, hdrlen);
}
/* There is nothing to do -- drop the packet */
else
{
dev->d_len = 0;
}
}
#endif /* CONFIG_NET && CONFIG_NET_TCP */
@@ -0,0 +1,394 @@
/****************************************************************************
* net/tcp/tcp_backlog.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 <nuttx/net/netconfig.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCPBACKLOG)
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <queue.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/net/net.h>
#include "devif/devif.h"
#include "tcp/tcp.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_backlogcreate
*
* Description:
* Called from the listen() logic to setup the backlog as specified in the
* the listen arguments.
*
* Assumptions:
* Called from normal task logic. The network may or may not be locked.
*
****************************************************************************/
int tcp_backlogcreate(FAR struct tcp_conn_s *conn, int nblg)
{
FAR struct tcp_backlog_s *bls = NULL;
FAR struct tcp_blcontainer_s *blc;
int size;
int offset;
int i;
ninfo("conn=%p nblg=%d\n", conn, nblg);
#ifdef CONFIG_DEBUG_FEATURES
if (!conn)
{
return -EINVAL;
}
#endif
/* Then allocate the backlog as requested */
if (nblg > 0)
{
/* nblog value must less than SOMAXCONN */
if (nblg > SOMAXCONN)
{
nblg = SOMAXCONN;
}
/* Align the list of backlog structures to 32-bit boundaries. This
* may be excessive on 24-16-bit address machines; and insufficient
* on 64-bit address machines -- REVISIT
*/
offset = (sizeof(struct tcp_backlog_s) + 3) & ~3;
/* Then determine the full size of the allocation include the
* tcp_backlog_s, a pre-allocated array of struct tcp_blcontainer_s
* and alignment padding
*/
size = offset + nblg * sizeof(struct tcp_blcontainer_s);
/* Then allocate that much */
bls = (FAR struct tcp_backlog_s *)kmm_zalloc(size);
if (!bls)
{
nerr("ERROR: Failed to allocate backlog\n");
return -ENOMEM;
}
/* Then add all of the pre-allocated containers to the free list */
blc = (FAR struct tcp_blcontainer_s *)(((FAR uint8_t *)bls) + offset);
for (i = 0; i < nblg; i++)
{
sq_addfirst(&blc->bc_node, &bls->bl_free);
blc++;
}
}
/* Destroy any existing backlog (shouldn't be any) */
net_lock();
tcp_backlogdestroy(conn);
/* Now install the backlog tear-off in the connection. NOTE that bls may
* actually be NULL if nblg is <= 0; In that case, we are disabling
* backlog support. Since the network is locked, destroying the old
* backlog and replace it with the new is an atomic operation
*/
conn->backlog = bls;
net_unlock();
return OK;
}
/****************************************************************************
* Name: tcp_backlogdestroy
*
* Description:
* (1) Called from tcp_free() whenever a connection is freed.
* (2) Called from tcp_backlogcreate() to destroy any old backlog
*
* NOTE: This function may re-enter tcp_free when a connection that
* is freed that has pending connections.
*
* Assumptions:
* Called from network socket logic with the network locked
*
****************************************************************************/
int tcp_backlogdestroy(FAR struct tcp_conn_s *conn)
{
FAR struct tcp_backlog_s *blg;
FAR struct tcp_blcontainer_s *blc;
FAR struct tcp_conn_s *blconn;
ninfo("conn=%p\n", conn);
#ifdef CONFIG_DEBUG_FEATURES
if (!conn)
{
return -EINVAL;
}
#endif
/* Make sure that the connection has a backlog to be destroyed */
if (conn->backlog)
{
/* Remove the backlog structure reference from the connection */
blg = conn->backlog;
conn->backlog = NULL;
/* Handle any pending connections in the backlog */
while ((blc = (FAR struct tcp_blcontainer_s *)
sq_remfirst(&blg->bl_pending)) != NULL)
{
blconn = blc->bc_conn;
if (blconn)
{
/* REVISIT
* -- such connections really need to be gracefully closed
*/
blconn->blparent = NULL;
blconn->backlog = NULL;
blconn->crefs = 0;
tcp_free(blconn);
}
}
/* Then free the entire backlog structure */
kmm_free(blg);
}
return OK;
}
/****************************************************************************
* Name: tcp_backlogadd
*
* Description:
* Called tcp_listen when a new connection is made with a listener socket
* but when there is no accept() in place to receive the connection. This
* function adds the new connection to the backlog.
*
* Assumptions:
* Called from network socket logic with the network locked
*
****************************************************************************/
int tcp_backlogadd(FAR struct tcp_conn_s *conn,
FAR struct tcp_conn_s *blconn)
{
FAR struct tcp_backlog_s *bls;
FAR struct tcp_blcontainer_s *blc;
int ret = -EINVAL;
ninfo("conn=%p blconn=%p\n", conn, blconn);
#ifdef CONFIG_DEBUG_FEATURES
if (!conn)
{
return -EINVAL;
}
#endif
bls = conn->backlog;
if (bls && blconn)
{
/* Get a container for the connection from the free list */
blc = (FAR struct tcp_blcontainer_s *)sq_remfirst(&bls->bl_free);
if (!blc)
{
nerr("ERROR: There are no free containers for TCP BACKLOG!\n");
ret = -ENOMEM;
}
else
{
/* Save the connection reference in the container and put the
* container at the end of the pending connection list (FIFO).
*/
blc->bc_conn = blconn;
sq_addlast(&blc->bc_node, &bls->bl_pending);
ret = OK;
}
}
return ret;
}
/****************************************************************************
* Name: tcp_backlogremove
*
* Description:
* Called from poll(). Before waiting for a new connection, poll will
* call this API to see if there are pending connections in the backlog.
*
* Assumptions:
* Called from network socket logic with the network locked
*
****************************************************************************/
bool tcp_backlogavailable(FAR struct tcp_conn_s *conn)
{
return (conn && conn->backlog && !sq_empty(&conn->backlog->bl_pending));
}
/****************************************************************************
* Name: tcp_backlogremove
*
* Description:
* Called from accept(). Before waiting for a new connection, accept will
* call this API to see if there are pending connections in the backlog.
*
* Assumptions:
* Called from network socket logic with the network locked
*
****************************************************************************/
FAR struct tcp_conn_s *tcp_backlogremove(FAR struct tcp_conn_s *conn)
{
FAR struct tcp_backlog_s *bls;
FAR struct tcp_blcontainer_s *blc;
FAR struct tcp_conn_s *blconn = NULL;
#ifdef CONFIG_DEBUG_FEATURES
if (!conn)
{
return NULL;
}
#endif
bls = conn->backlog;
if (bls)
{
/* Remove the a container at the head of the pending connection list
* (FIFO)
*/
blc = (FAR struct tcp_blcontainer_s *)sq_remfirst(&bls->bl_pending);
if (blc)
{
/* Extract the connection reference from the container and put
* container in the free list
*/
blconn = blc->bc_conn;
blc->bc_conn = NULL;
sq_addlast(&blc->bc_node, &bls->bl_free);
}
}
ninfo("conn=%p, returning %p\n", conn, blconn);
return blconn;
}
/****************************************************************************
* Name: tcp_backlogdelete
*
* Description:
* Called from tcp_free() when a connection is freed that this also
* retained in the pending connection list of a listener. We simply need
* to remove the defunct connection from the list.
*
* Assumptions:
* Called from network socket logic with the network locked
*
****************************************************************************/
int tcp_backlogdelete(FAR struct tcp_conn_s *conn,
FAR struct tcp_conn_s *blconn)
{
FAR struct tcp_backlog_s *bls;
FAR struct tcp_blcontainer_s *blc;
FAR struct tcp_blcontainer_s *prev;
ninfo("conn=%p blconn=%p\n", conn, blconn);
#ifdef CONFIG_DEBUG_FEATURES
if (!conn)
{
return -EINVAL;
}
#endif
bls = conn->backlog;
if (bls)
{
/* Find the container hold the connection */
for (blc = (FAR struct tcp_blcontainer_s *)sq_peek(&bls->bl_pending),
prev = NULL;
blc;
prev = blc,
blc = (FAR struct tcp_blcontainer_s *)sq_next(&blc->bc_node))
{
if (blc->bc_conn == blconn)
{
if (prev)
{
/* Remove the a container from the middle of the list of
* pending connections
*/
sq_remafter(&prev->bc_node, &bls->bl_pending);
}
else
{
/* Remove the a container from the head of the list of
* pending connections
*/
sq_remfirst(&bls->bl_pending);
}
/* Put container in the free list */
blc->bc_conn = NULL;
sq_addlast(&blc->bc_node, &bls->bl_free);
return OK;
}
}
nerr("ERROR: Failed to find pending connection\n");
return -EINVAL;
}
return OK;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP && CONFIG_NET_TCPBACKLOG */
@@ -0,0 +1,296 @@
/****************************************************************************
* net/tcp/tcp_callback.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 <nuttx/config.h>
#include <stdint.h>
#include <string.h>
#include <debug.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#include "devif/devif.h"
#include "tcp/tcp.h"
#ifdef NET_TCP_HAVE_STACK
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_data_event
*
* Description:
* Handle data that is not accepted by the application because there is no
* listener in place ready to receive the data.
*
* Assumptions:
* - The caller has checked that TCP_NEWDATA is set in flags and that is no
* other handler available to process the incoming data.
* - This function must be called with the network locked.
*
****************************************************************************/
static inline uint16_t
tcp_data_event(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint16_t flags)
{
uint16_t ret;
/* Assume that we will ACK the data. The data will be ACKed if it is
* placed in the read-ahead buffer -OR- if it zero length
*/
ret = (flags & ~TCP_NEWDATA) | TCP_SNDACK;
/* Is there new data? With non-zero length? (Certain connection events
* can have zero-length with TCP_NEWDATA set just to cause an ACK).
*/
if (dev->d_len > 0)
{
uint8_t *buffer = dev->d_appdata;
int buflen = dev->d_len;
uint16_t recvlen;
ninfo("No listener on connection\n");
/* Save as the packet data as in the read-ahead buffer. NOTE that
* partial packets will not be buffered.
*/
recvlen = tcp_datahandler(conn, buffer, buflen);
if (recvlen < buflen)
{
/* There is no handler to receive new data and there are no free
* read-ahead buffers to retain the data -- drop the packet.
*/
ninfo("Dropped %d bytes\n", dev->d_len);
#ifdef CONFIG_NET_STATISTICS
g_netstats.tcp.drop++;
#endif
/* Clear the TCP_SNDACK bit so that no ACK will be sent */
ret &= ~TCP_SNDACK;
}
}
/* In any event, the new data has now been handled */
dev->d_len = 0;
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_callback
*
* Description:
* Inform the application holding the TCP socket of a change in state.
*
* Assumptions:
* This function must be called with the network locked.
*
****************************************************************************/
uint16_t tcp_callback(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn, uint16_t flags)
{
#ifdef CONFIG_NET_TCP_NOTIFIER
uint16_t orig = flags;
#endif
/* Preserve the TCP_ACKDATA, TCP_CLOSE, and TCP_ABORT in the response.
* These is needed by the network to handle responses and buffer state.
* The TCP_NEWDATA indication will trigger the ACK response, but must be
* explicitly set in the callback.
*/
ninfo("flags: %04x\n", flags);
/* Perform the data callback. When a data callback is executed from
* 'list', the input flags are normally returned, however, the
* implementation may set one of the following:
*
* TCP_CLOSE - Gracefully close the current connection
* TCP_ABORT - Abort (reset) the current connection on an error that
* prevents TCP_CLOSE from working.
*
* And/Or set/clear the following:
*
* TCP_NEWDATA - May be cleared to indicate that the data was consumed
* and that no further process of the new data should be
* attempted.
* TCP_SNDACK - If TCP_NEWDATA is cleared, then TCP_SNDACK may be set
* to indicate that an ACK should be included in the
* response. (In TCP_NEWDATA is cleared but TCP_SNDACK is
* not set, then dev->d_len should also be cleared).
*/
flags = devif_conn_event(dev, conn, flags, conn->list);
/* There may be no new data handler in place at them moment that the new
* incoming data is received. If the new incoming data was not handled,
* then either (1) put the unhandled incoming data in the read-ahead
* buffer (if enabled) or (2) suppress the ACK to the data in the hope
* that it will be re-transmitted at a better time.
*/
if ((flags & TCP_NEWDATA) != 0)
{
/* Data was not handled.. dispose of it appropriately */
flags = tcp_data_event(dev, conn, flags);
}
/* Check if there is a connection-related event and a connection
* callback.
*/
if ((flags & TCP_CONN_EVENTS) != 0)
{
/* Perform the callback disconnect callbacks */
flags = devif_conn_event(dev, conn, flags, conn->connevents);
}
#ifdef CONFIG_NET_TCP_NOTIFIER
/* Provide notification(s) if the TCP connection has been lost. */
if ((orig & TCP_DISCONN_EVENTS) != 0)
{
tcp_disconnect_signal(conn);
}
#endif
return flags;
}
/****************************************************************************
* Name: tcp_datahandler
*
* Description:
* Handle data that is not accepted by the application. This may be called
* either (1) from the data receive logic if it cannot buffer the data, or
* (2) from the TCP event logic is there is no listener in place ready to
* receive the data.
*
* Input Parameters:
* conn - A pointer to the TCP connection structure
* buffer - A pointer to the buffer to be copied to the read-ahead
* buffers
* buflen - The number of bytes to copy to the read-ahead buffer.
*
* Returned Value:
* The number of bytes actually buffered is returned. This will be either
* zero or equal to buflen; partial packets are not buffered.
*
* Assumptions:
* - The caller has checked that TCP_NEWDATA is set in flags and that is no
* other handler available to process the incoming data.
* - This function must be called with the network locked.
*
****************************************************************************/
uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
uint16_t buflen)
{
FAR struct iob_s *iob;
bool throttled = true;
int ret;
/* Try to allocate on I/O buffer to start the chain without waiting (and
* throttling as necessary). If we would have to wait, then drop the
* packet.
*/
iob = iob_tryalloc(throttled, IOBUSER_NET_TCP_READAHEAD);
if (iob == NULL)
{
#if CONFIG_IOB_THROTTLE > 0
if (IOB_QEMPTY(&conn->readahead))
{
/* Fallback out of the throttled entry */
throttled = false;
iob = iob_tryalloc(throttled, IOBUSER_NET_TCP_READAHEAD);
}
#endif
if (iob == NULL)
{
nerr("ERROR: Failed to create new I/O buffer chain\n");
return 0;
}
}
/* Copy the new appdata into the I/O buffer chain (without waiting) */
ret = iob_trycopyin(iob, buffer, buflen, 0, throttled,
IOBUSER_NET_TCP_READAHEAD);
if (ret < 0)
{
/* On a failure, iob_copyin return a negated error value but does
* not free any I/O buffers.
*/
nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
iob_free_chain(iob, IOBUSER_NET_TCP_READAHEAD);
return 0;
}
/* Add the new I/O buffer chain to the tail of the read-ahead queue (again
* without waiting).
*/
ret = iob_tryadd_queue(iob, &conn->readahead);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
iob_free_chain(iob, IOBUSER_NET_TCP_READAHEAD);
return 0;
}
#ifdef CONFIG_NET_TCP_NOTIFIER
/* Provide notification(s) that additional TCP read-ahead data is
* available.
*/
tcp_readahead_signal(conn);
#endif
ninfo("Buffered %d bytes\n", buflen);
return buflen;
}
#endif /* NET_TCP_HAVE_STACK */
+394
View File
@@ -0,0 +1,394 @@
/****************************************************************************
* net/tcp/tcp_close.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 <nuttx/config.h>
#ifdef CONFIG_NET_TCP
#include <errno.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/tcp.h>
#include "netdev/netdev.h"
#include "devif/devif.h"
#include "tcp/tcp.h"
#include "socket/socket.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct tcp_close_s
{
FAR struct devif_callback_s *cl_cb; /* Reference to TCP callback instance */
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 */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_close_eventhandler
****************************************************************************/
static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv,
uint16_t flags)
{
FAR struct tcp_close_s *pstate = (FAR struct tcp_close_s *)pvpriv;
FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn;
DEBUGASSERT(pstate != NULL);
ninfo("flags: %04x\n", 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. Wake up the waiting thread with an
* appropriate result. Success is returned in these cases:
*
* * TCP_CLOSE indicates normal successful closure. The TCP_CLOSE
* event is sent when the remote ACKs the outgoing FIN in the
* FIN_WAIT_1 state. That is the appropriate time for the
* application to close the socket.
*
* NOTE: The underlying connection, however, will persist, waiting
* for the FIN to be returned by the remote in the TIME_WAIT state.
*
* * TCP_ABORT is less likely but still means that the socket was
* closed, albeit abnormally due to a RST from the remote.
*
* * TCP_TIMEDOUT would be reported in this context if there is no
* ACK response to the FIN in the FIN_WAIT_2 state. The socket will
* again be closed abnormally.
*
* This is the only true error case.
*
* * NETDEV_DOWN would indicate that the network went down before the
* close completed. A non-standard ENODEV error will be returned
* in this case. The socket will be left in a limbo state if the
* network is taken down but should recover later when the
* NETWORK_DOWN event is processed further.
*/
if ((flags & NETDEV_DOWN) != 0)
{
pstate->cl_result = -ENODEV;
}
else
{
pstate->cl_result = OK;
}
goto end_wait;
}
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Check if all outstanding bytes have been ACKed */
else if (conn->tx_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 &= ~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;
}
UNUSED(conn); /* May not be used */
return flags;
end_wait:
pstate->cl_cb->flags = 0;
pstate->cl_cb->priv = NULL;
pstate->cl_cb->event = NULL;
nxsem_post(&pstate->cl_sem);
ninfo("Resuming\n");
return flags;
}
/****************************************************************************
* Name: tcp_close_txnotify
*
* Description:
* Notify the appropriate device driver that we have data ready to
* be sent (TCP)
*
* Input Parameters:
* psock - Socket state structure
* conn - The TCP connection structure
*
* Returned Value:
* None
*
****************************************************************************/
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 */
netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr);
}
#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);
netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr);
}
#endif /* CONFIG_NET_IPv6 */
}
/****************************************************************************
* Name: tcp_close_disconnect
*
* Description:
* Break any current TCP connection
*
* Input Parameters:
* conn - TCP connection structure
*
* Returned Value:
* None
*
* Assumptions:
* Called from normal user-level logic
*
****************************************************************************/
static inline int tcp_close_disconnect(FAR struct socket *psock)
{
struct tcp_close_s state;
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);
#ifdef CONFIG_NET_SOLINGER
/* SO_LINGER
* Lingers on a close() if data is present. This option controls the
* action taken when unsent messages queue on a socket and close() is
* performed. If SO_LINGER is set, the system shall block the calling
* thread during close() until it can transmit the data or until the
* time expires. If SO_LINGER is not specified, and close() is issued,
* the system handles the call in a way that allows the calling thread
* to continue as quickly as possible. This option takes a linger
* structure, as defined in the <sys/socket.h> header, to specify the
* state of the option and linger interval.
*/
if (_SO_GETOPT(psock->s_options, SO_LINGER))
{
/* Wait until for the buffered TX data to be sent. */
ret = tcp_txdrain(psock, _SO_TIMEOUT(psock->s_linger));
if (ret < 0)
{
/* tcp_txdrain may fail, but that won't stop us from closing
* the socket.
*/
nerr("ERROR: tcp_txdrain() failed: %d\n", ret);
}
}
#endif
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* If we have a semi-permanent write buffer callback in place, then
* is needs to be be nullified.
*
* Commit f1ef2c6cdeb032eaa1833cc534a63b50c5058270:
* "When a socket is closed, it should make sure that any pending write
* data is sent before the FIN is sent. It already would wait for all
* sent data to be acked, however it would discard any pending write
* data that had not been sent at least once.
*
* "This change adds a check for pending write data in addition to unacked
* data. However, to be able to actually send any new data, the send
* callback must be left. The callback should be freed later when the
* socket is actually destroyed."
*
* REVISIT: Where and how exactly is s_sndcb ever freed? Is there a
* memory leak here?
*/
psock->s_sndcb = NULL;
#endif
/* 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_eventhandler;
/* 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.
*/
nxsem_init(&state.cl_sem, 0, 0);
nxsem_set_protocol(&state.cl_sem, SEM_PRIO_NONE);
/* Notify the device driver of the availability of TX data */
tcp_close_txnotify(psock, conn);
/* Wait for the disconnect event */
net_lockedwait(&state.cl_sem);
/* We are now disconnected */
nxsem_destroy(&state.cl_sem);
tcp_callback_free(conn, state.cl_cb);
/* Free the connection
* No more references on the connection
*/
conn->crefs = 0;
/* Get the result of the close */
ret = state.cl_result;
}
/* Free network resources */
tcp_free(conn);
net_unlock();
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_close
*
* Description:
* Break any current TCP connection
*
* Input Parameters:
* psock - An instance of the internal socket structure.
*
* Assumptions:
* Called from normal user-level logic
*
****************************************************************************/
int tcp_close(FAR struct socket *psock)
{
FAR struct tcp_conn_s *conn = psock->s_conn;
int ret;
/* Perform the disconnection now */
tcp_unlisten(conn); /* No longer accepting connections */
conn->crefs = 0; /* Discard our reference to the connection */
/* Break any current connections and close the socket */
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 for all sockets */
tcp_stop_monitor(conn, TCP_CLOSE);
return OK;
}
#endif /* CONFIG_NET_TCP */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,383 @@
/****************************************************************************
* net/tcp/tcp_connect.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 <nuttx/config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arch/irq.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/udp.h>
#include <nuttx/net/tcp.h>
#include "devif/devif.h"
#include "netdev/netdev.h"
#include "socket/socket.h"
#include "tcp/tcp.h"
#ifdef NET_TCP_HAVE_STACK
/****************************************************************************
* Private Types
****************************************************************************/
struct tcp_connect_s
{
FAR struct tcp_conn_s *tc_conn; /* Reference to TCP connection structure */
FAR struct devif_callback_s *tc_cb; /* Reference to callback instance */
FAR struct socket *tc_psock; /* The socket being connected */
sem_t tc_sem; /* Semaphore signals recv completion */
int tc_result; /* OK on success, otherwise a negated errno. */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static inline int psock_setup_callbacks(FAR struct socket *psock,
FAR struct tcp_connect_s *pstate);
static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate,
int status);
static uint16_t psock_connect_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvconn,
FAR void *pvpriv, uint16_t flags);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: psock_setup_callbacks
****************************************************************************/
static inline int psock_setup_callbacks(FAR struct socket *psock,
FAR struct tcp_connect_s *pstate)
{
FAR struct tcp_conn_s *conn = psock->s_conn;
int ret = -EBUSY;
/* Initialize the TCP state structure */
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&pstate->tc_sem, 0, 0); /* Doesn't really fail */
nxsem_set_protocol(&pstate->tc_sem, SEM_PRIO_NONE);
pstate->tc_conn = conn;
pstate->tc_psock = psock;
pstate->tc_result = -EAGAIN;
/* Set up the callbacks in the connection */
pstate->tc_cb = tcp_callback_alloc(conn);
if (pstate->tc_cb)
{
/* Set up the connection event handler */
pstate->tc_cb->flags = (TCP_NEWDATA | TCP_CLOSE | TCP_ABORT |
TCP_TIMEDOUT | TCP_CONNECTED | NETDEV_DOWN);
pstate->tc_cb->priv = (FAR void *)pstate;
pstate->tc_cb->event = psock_connect_eventhandler;
ret = OK;
}
return ret;
}
/****************************************************************************
* Name: psock_teardown_callbacks
****************************************************************************/
static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate,
int status)
{
FAR struct tcp_conn_s *conn = pstate->tc_conn;
/* Make sure that no further events are processed */
tcp_callback_free(conn, pstate->tc_cb);
pstate->tc_cb = NULL;
/* If we successfully connected, we will continue to monitor the connection
* state via callbacks.
*/
if (status < 0)
{
/* Failed to connect. Stop the connection event monitor */
tcp_stop_monitor(conn, TCP_CLOSE);
}
}
/****************************************************************************
* Name: psock_connect_eventhandler
*
* Description:
* This function is called to perform the actual connection operation via
* by the lower, device interfacing layer.
*
* Input Parameters:
* dev The structure of the network driver that reported the event
* pvconn The connection structure associated with the socket
* flags Set of events describing why the callback was invoked
*
* Returned Value:
* The new flags setting
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static uint16_t psock_connect_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvconn,
FAR void *pvpriv, uint16_t flags)
{
struct tcp_connect_s *pstate = (struct tcp_connect_s *)pvpriv;
ninfo("flags: %04x\n", flags);
/* 'priv' might be null in some race conditions (?) */
if (pstate)
{
/* The following errors should be detected here (someday)
*
* ECONNREFUSED
* No one listening on the remote address.
* ENETUNREACH
* Network is unreachable.
* ETIMEDOUT
* Timeout while attempting connection. The server may be too
* busy to accept new connections.
*/
/* TCP_CLOSE: The remote host has closed the connection
* TCP_ABORT: The remote host has aborted the connection
*/
if ((flags & (TCP_CLOSE | TCP_ABORT)) != 0)
{
/* Indicate that remote host refused the connection */
pstate->tc_result = -ECONNREFUSED;
}
/* TCP_TIMEDOUT: Connection aborted due to too many retransmissions. */
else if ((flags & TCP_TIMEDOUT) != 0)
{
/* Indicate that the connection timedout?) */
pstate->tc_result = -ETIMEDOUT;
}
else if ((flags & NETDEV_DOWN) != 0)
{
/* The network device went down. Indicate that the remote host
* is unreachable.
*/
pstate->tc_result = -ENETUNREACH;
}
/* TCP_CONNECTED: The socket is successfully connected */
else if ((flags & TCP_CONNECTED) != 0)
{
FAR struct socket *psock = pstate->tc_psock;
DEBUGASSERT(psock);
/* Mark the connection bound and connected. NOTE this is
* is done here (vs. later) in order to avoid any race condition
* in the socket state. It is known to connected here and now,
* but not necessarily at any time later.
*/
psock->s_flags |= (_SF_BOUND | _SF_CONNECTED);
/* Indicate that the socket is no longer connected */
pstate->tc_result = OK;
}
/* Otherwise, it is not an event of importance to us at the moment */
else
{
/* Drop data received in this state */
dev->d_len = 0;
return flags & ~TCP_NEWDATA;
}
ninfo("Resuming: %d\n", pstate->tc_result);
/* Stop further callbacks */
psock_teardown_callbacks(pstate, pstate->tc_result);
/* We now have to filter all outgoing transfers so that they use only
* the MSS of this device.
*/
DEBUGASSERT(pstate->tc_conn != NULL);
DEBUGASSERT(pstate->tc_conn->dev == NULL ||
pstate->tc_conn->dev == dev);
pstate->tc_conn->dev = dev;
/* Wake up the waiting thread */
nxsem_post(&pstate->tc_sem);
}
return flags;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: psock_tcp_connect
*
* Description:
* Perform a TCP connection
*
* Input Parameters:
* psock - A reference to the socket structure of the socket to be
* connected
* addr - The address of the remote server to connect to
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked
*
****************************************************************************/
int psock_tcp_connect(FAR struct socket *psock,
FAR const struct sockaddr *addr)
{
struct tcp_connect_s state;
int ret = OK;
/* Interrupts must be disabled through all of the following because
* we cannot allow the network callback to occur until we are completely
* setup.
*/
net_lock();
/* Get the connection reference from the socket */
if (!psock->s_conn) /* Should always be non-NULL */
{
ret = -EINVAL;
}
else
{
/* Perform the TCP connection operation */
ret = tcp_connect(psock->s_conn, addr);
}
if (ret >= 0)
{
/* Set up the callbacks in the connection */
ret = psock_setup_callbacks(psock, &state);
if (ret >= 0)
{
/* Notify the device driver that new connection is available. */
netdev_txnotify_dev(((FAR struct tcp_conn_s *)psock->s_conn)->dev);
/* Wait for either the connect to complete or for an error/timeout
* to occur. NOTES: net_lockedwait will also terminate if a signal
* is received.
*/
ret = net_lockedwait(&state.tc_sem);
/* Uninitialize the state structure */
nxsem_destroy(&state.tc_sem);
/* If net_lockedwait failed, negated errno was returned. */
if (ret >= 0)
{
/* If the wait succeeded, then get the new error value from
* the state structure
*/
ret = state.tc_result;
}
/* Make sure that no further events are processed */
psock_teardown_callbacks(&state, ret);
}
/* Check if the socket was successfully connected. */
if (ret >= 0)
{
/* Yes... Now that we are connected, we need to set up to monitor
* the state of the connection up the connection event monitor.
*/
ret = tcp_start_monitor(psock);
if (ret < 0)
{
/* tcp_start_monitor() can only fail on certain race
* conditions where the connection was lost just before
* this function was called. That is not expected to
* happen in this context, but just in case...
*/
tcp_stop_monitor(psock->s_conn, TCP_ABORT);
}
}
}
net_unlock();
return ret;
}
#endif /* NET_TCP_HAVE_STACK */
@@ -0,0 +1,134 @@
/****************************************************************************
* net/tcp/tcp_devpoll.c
* Driver poll for the availability of TCP TX data
*
* Copyright (C) 2007-2009, 2016-2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Adapted for NuttX from logic in uIP which also has a BSD-like license:
*
* Original author Adam Dunkels <adam@dunkels.com>
* Copyright () 2001-2003, Adam Dunkels.
* All rights reserved.
*
* 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. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP)
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/tcp.h>
#include "devif/devif.h"
#include "tcp/tcp.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_poll
*
* Description:
* Poll a TCP connection structure for availability of TX data
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP "connection" to poll for TX data
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
void tcp_poll(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
{
uint16_t result;
/* Discard any currently buffered data */
dev->d_len = 0;
dev->d_sndlen = 0;
/* Verify that the connection is established. */
if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
{
/* The TCP connection is established and, hence, should be bound
* to a device. Make sure that the polling device is the one that
* we are bound to.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev == conn->dev)
{
/* Set up for the callback. We can't know in advance if the
* application is going to send a IPv4 or an IPv6 packet, so this
* setup may not actually be used.
*/
#if defined(CONFIG_NET_IPv6) && defined(CONFIG_NET_IPv4)
if (conn->domain == PF_INET)
{
tcp_ipv4_select(dev);
}
else
{
tcp_ipv6_select(dev);
}
#elif defined(CONFIG_NET_IPv4)
tcp_ipv4_select(dev);
#else /* if defined(CONFIG_NET_IPv6) */
tcp_ipv6_select(dev);
#endif
/* Perform the callback */
result = tcp_callback(dev, conn, TCP_POLL);
/* Handle the callback response */
tcp_appsend(dev, conn, result);
}
}
}
#endif /* CONFIG_NET && CONFIG_NET_TCP */
@@ -0,0 +1,241 @@
/****************************************************************************
* net/tcp/tcp_finddev.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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP)
#include <string.h>
#include <errno.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/ip.h>
#include "netdev/netdev.h"
#include "inet/inet.h"
#include "tcp/tcp.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_find_ipv4_device
*
* Description:
* Select the network driver to use with the IPv4 TCP connection.
*
* Input Parameters:
* conn - TCP connection structure.
* addr - The IPv4 address to use
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned
* on failure. -ENETUNREACH is the only expected error value.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
static int tcp_find_ipv4_device(FAR struct tcp_conn_s *conn,
in_addr_t addr, bool local)
{
/* Do nothing if a device is already bound to the connection */
if (conn->dev != NULL)
{
return OK;
}
/* Return success without using device notification if the locally bound
* address is INADDR_ANY. In this case, there may be multiple devices
* that can provide data so the exceptional events from any particular
* device are not important.
*/
if (net_ipv4addr_cmp(addr, INADDR_ANY))
{
return local ? OK : -EINVAL;
}
/* We need to select the device that is going to route the TCP packet
* based on the provided IP address.
*/
conn->dev = netdev_findby_ripv4addr(addr, addr);
/* Return success if we found the device */
return conn->dev != NULL ? OK : -ENETUNREACH;
}
#endif /* CONFIG_NET_IPv4 */
/****************************************************************************
* Name: tcp_find_ipv6_device
*
* Description:
* Select the network driver to use with the IPv6 TCP transaction.
*
* Input Parameters:
* conn - TCP connection structure.
* addr - The IPv6 address to use
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned
* on failure. -ENETUNREACH is the only expected error value.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv6
static int tcp_find_ipv6_device(FAR struct tcp_conn_s *conn,
const net_ipv6addr_t addr, bool local)
{
/* Do nothing if a device is already bound to the connection */
if (conn->dev != NULL)
{
return OK;
}
/* Return success without using device notification if the locally bound
* address is the IPv6 unspecified address. In this case, there may be
* multiple devices* that can provide data so the exceptional events from
* any particular device are not important.
*/
if (net_ipv6addr_cmp(addr, g_ipv6_unspecaddr))
{
return local ? OK : -EINVAL;
}
/* We need to select the device that is going to route the TCP packet
* based on the provided IP address.
*/
conn->dev = netdev_findby_ripv6addr(addr, addr);
/* Return success if we found the device */
return conn->dev != NULL ? OK : -ENETUNREACH;
}
#endif /* CONFIG_NET_IPv6 */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_local_ipv4_device
*
* Description:
* Select the network driver to use with the IPv4 TCP transaction based
* on the locally bound IPv4 address.
*
* Input Parameters:
* conn - TCP connection structure. The locally bound address, laddr,
* should be set to a non-zero value in this structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned
* on failure. -ENETUNREACH is the only expected error value.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
int tcp_local_ipv4_device(FAR struct tcp_conn_s *conn)
{
return tcp_find_ipv4_device(conn, conn->u.ipv4.laddr, true);
}
#endif /* CONFIG_NET_IPv4 */
/****************************************************************************
* Name: tcp_remote_ipv4_device
*
* Description:
* Select the network driver to use with the IPv4 TCP transaction based
* on the remotely connected IPv4 address.
*
* Input Parameters:
* conn - TCP connection structure. The remotely connected address, raddr,
* should be set to a non-zero value in this structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned
* on failure. -ENETUNREACH is the only expected error value.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
int tcp_remote_ipv4_device(FAR struct tcp_conn_s *conn)
{
return tcp_find_ipv4_device(conn, conn->u.ipv4.raddr, false);
}
#endif
/****************************************************************************
* Name: tcp_local_ipv6_device
*
* Description:
* Select the network driver to use with the IPv6 TCP transaction.
*
* Input Parameters:
* conn - TCP connection structure. The locally bound address, laddr,
* should be set to a non-zero value in this structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned
* on failure. -ENETUNREACH is the only expected error value.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv6
int tcp_local_ipv6_device(FAR struct tcp_conn_s *conn)
{
return tcp_find_ipv6_device(conn, conn->u.ipv6.laddr, true);
}
#endif /* CONFIG_NET_IPv6 */
/****************************************************************************
* Name: tcp_remote_ipv6_device
*
* Description:
* Select the network driver to use with the IPv6 TCP transaction based
* on the remotely connected IPv6 address.
*
* Input Parameters:
* conn - TCP connection structure. The remotely connected address, raddr,
* should be set to a non-zero value in this structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned
* on failure. -EHOSTUNREACH is the only expected error value.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv6
int tcp_remote_ipv6_device(FAR struct tcp_conn_s *conn)
{
return tcp_find_ipv6_device(conn, conn->u.ipv6.raddr, false);
}
#endif /* CONFIG_NET_IPv6 */
#endif /* CONFIG_NET && CONFIG_NET_TCP */
@@ -0,0 +1,239 @@
/****************************************************************************
* net/tcp/tcp_getsockopt.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 <nuttx/config.h>
#include <sys/time.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <netinet/tcp.h>
#include <nuttx/net/net.h>
#include <nuttx/net/tcp.h>
#include "socket/socket.h"
#include "utils/utils.h"
#include "tcp/tcp.h"
#ifdef CONFIG_NET_TCPPROTO_OPTIONS
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_getsockopt
*
* Description:
* tcp_getsockopt() retrieves 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 <sys/socket.h> 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.
*
****************************************************************************/
int tcp_getsockopt(FAR struct socket *psock, int option,
FAR void *value, FAR socklen_t *value_len)
{
#ifdef CONFIG_NET_TCP_KEEPALIVE
/* Keep alive options are the only TCP protocol socket option currently
* supported.
*/
FAR struct tcp_conn_s *conn;
int ret;
DEBUGASSERT(psock != NULL && value != NULL && value_len != NULL &&
psock->s_conn != NULL);
conn = (FAR struct tcp_conn_s *)psock->s_conn;
/* All of the TCP protocol options apply only TCP sockets. The sockets
* do not have to be connected.. that might occur later with the KeepAlive
* already configured.
*/
if (psock->s_type != SOCK_STREAM)
{
nerr("ERROR: Not a TCP socket\n");
return -ENOTCONN;
}
/* Handle the Keep-Alive option */
switch (option)
{
/* Handle the SO_KEEPALIVE socket-level option.
*
* 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 */
if (*value_len < sizeof(int))
{
/* REVISIT: POSIX says that we should truncate the value if it
* is larger than value_len. That just doesn't make sense
* to me in this case.
*/
ret = -EINVAL;
}
else
{
FAR int *keepalive = (FAR int *)value;
*keepalive = (int)conn->keepalive;
*value_len = sizeof(int);
ret = OK;
}
break;
case TCP_NODELAY: /* Avoid coalescing of small segments. */
nerr("ERROR: TCP_NODELAY not supported\n");
ret = -ENOSYS;
break;
case TCP_KEEPIDLE: /* Start keepalives after this IDLE period */
if (*value_len < sizeof(struct timeval))
{
/* REVISIT: POSIX says that we should truncate the value if it
* is larger than value_len. That just doesn't make sense
* to me in this case.
*/
ret = -EINVAL;
}
else
{
FAR struct timeval *tv = (FAR struct timeval *)value;
if (tv == NULL)
{
ret = -EINVAL;
}
else
{
/* Convert the KeepIdle time from deciseconds to struct
* timeval.
*/
net_dsec2timeval(conn->keepidle, tv);
*value_len = sizeof(struct timeval);
ret = OK;
}
}
break;
case TCP_KEEPINTVL: /* Interval between keepalives */
if (*value_len < sizeof(struct timeval))
{
/* REVISIT: POSIX says that we should truncate the value if it
* is larger than value_len. That just doesn't make sense
* to me in this case.
*/
ret = -EINVAL;
}
else
{
FAR struct timeval *tv = (FAR struct timeval *)value;
if (tv == NULL)
{
ret = -EINVAL;
}
else
{
/* Convert the KeepIdle time from deciseconds to struct
* timeval.
*/
net_dsec2timeval(conn->keepintvl, tv);
*value_len = sizeof(struct timeval);
ret = OK;
}
}
break;
case TCP_KEEPCNT: /* Number of keepalives before death */
if (*value_len < sizeof(int))
{
/* REVISIT: POSIX says that we should truncate the value if it
* is larger than value_len. That just doesn't make sense
* to me in this case.
*/
ret = -EINVAL;
}
else
{
FAR int *keepcnt = (FAR int *)value;
*keepcnt = (int)conn->keepcnt;
*value_len = sizeof(int);
ret = OK;
}
break;
default:
nerr("ERROR: Unrecognized TCP option: %d\n", option);
ret = -ENOPROTOOPT;
break;
}
return ret;
#else
return -ENOPROTOOPT;
#endif /* CONFIG_NET_TCP_KEEPALIVE */
}
#endif /* CONFIG_NET_TCPPROTO_OPTIONS */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,83 @@
/****************************************************************************
* net/tcp/tcp_ipselect.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 <nuttx/config.h>
#ifdef CONFIG_NET
#include <stdint.h>
#include <debug.h>
#include <net/if.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/tcp.h>
#include "tcp/tcp.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_ipv4_select
*
* Description:
* Configure to send or receive an TCP IPv4 packet
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
void tcp_ipv4_select(FAR struct net_driver_s *dev)
{
/* Clear a bit in the d_flags to distinguish this from an IPv6 packet */
IFF_SET_IPv4(dev->d_flags);
/* Set the offset to the beginning of the TCP data payload */
dev->d_appdata = &dev->d_buf[IPv4TCP_HDRLEN + NET_LL_HDRLEN(dev)];
}
#endif /* CONFIG_NET_IPv4 */
/****************************************************************************
* Name: tcp_ipv6_select
*
* Description:
* Configure to send or receive an TCP IPv6 packet
*
****************************************************************************/
#ifdef CONFIG_NET_IPv6
void tcp_ipv6_select(FAR struct net_driver_s *dev)
{
/* Set a bit in the d_flags to distinguish this from an IPv6 packet */
IFF_SET_IPv6(dev->d_flags);
/* Set the offset to the beginning of the TCP data payload */
dev->d_appdata = &dev->d_buf[IPv6TCP_HDRLEN + NET_LL_HDRLEN(dev)];
}
#endif /* CONFIG_NET_IPv6 */
#endif /* CONFIG_NET */
+313
View File
@@ -0,0 +1,313 @@
/****************************************************************************
* net/tcp/tcp_listen.c
*
* Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* A direct leverage of logic from uIP which also has b BSD style license
*
* Author: Adam Dunkels <adam@dunkels.com>
* Copyright (c) 2001-2003, Adam Dunkels.
* All rights reserved.
*
* 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. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <nuttx/config.h>
#ifdef CONFIG_NET
#include <stdint.h>
#include <stdbool.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/net.h>
#include "devif/devif.h"
#include "tcp/tcp.h"
/****************************************************************************
* Private Data
****************************************************************************/
/* The tcp_listenports list all currently listening ports. */
static FAR struct tcp_conn_s *tcp_listenports[CONFIG_NET_MAX_LISTENPORTS];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_findlistener
*
* Description:
* Return the connection listener for connections on this port (if any)
*
* Assumptions:
* This function is called from network logic with the network locked.
*
****************************************************************************/
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
FAR struct tcp_conn_s *tcp_findlistener(uint16_t portno, uint8_t domain)
#else
FAR struct tcp_conn_s *tcp_findlistener(uint16_t portno)
#endif
{
int ndx;
/* Examine each connection structure in each slot of the listener list */
for (ndx = 0; ndx < CONFIG_NET_MAX_LISTENPORTS; ndx++)
{
/* Is this slot assigned? If so, does the connection have the same
* local port number?
*/
FAR struct tcp_conn_s *conn = tcp_listenports[ndx];
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
if (conn && conn->lport == portno && conn->domain == domain)
#else
if (conn && conn->lport == portno)
#endif
{
/* Yes.. we found a listener on this port */
return conn;
}
}
/* No listener for this port */
return NULL;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_listen_initialize
*
* Description:
* Setup the listening data structures
*
* Assumptions:
* Called early in the initialization phase while the system is still
* single-threaded.
*
****************************************************************************/
void tcp_listen_initialize(void)
{
int ndx;
for (ndx = 0; ndx < CONFIG_NET_MAX_LISTENPORTS; ndx++)
{
tcp_listenports[ndx] = NULL;
}
}
/****************************************************************************
* Name: tcp_unlisten
*
* Description:
* Stop listening to the port bound to the specified TCP connection
*
* Assumptions:
* Called from normal user code.
*
****************************************************************************/
int tcp_unlisten(FAR struct tcp_conn_s *conn)
{
int ndx;
int ret = -EINVAL;
net_lock();
for (ndx = 0; ndx < CONFIG_NET_MAX_LISTENPORTS; ndx++)
{
if (tcp_listenports[ndx] == conn)
{
tcp_listenports[ndx] = NULL;
ret = OK;
break;
}
}
net_unlock();
return ret;
}
/****************************************************************************
* Name: tcp_listen
*
* Description:
* Start listening to the port bound to the specified TCP connection
*
* Assumptions:
* Called from normal user code.
*
****************************************************************************/
int tcp_listen(FAR struct tcp_conn_s *conn)
{
int ndx;
int ret;
/* This must be done with network locked because the listener table
* is accessed from event processing logic as well.
*/
net_lock();
/* First, check if there is already a socket listening on this port */
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
if (tcp_islistener(conn->lport, conn->domain))
#else
if (tcp_islistener(conn->lport))
#endif
{
/* Yes, then we must refuse this request */
ret = -EADDRINUSE;
}
else
{
/* Otherwise, save a reference to the connection structure in the
* "listener" list.
*/
ret = -ENOBUFS; /* Assume failure */
/* Search all slots until an available slot is found */
for (ndx = 0; ndx < CONFIG_NET_MAX_LISTENPORTS; ndx++)
{
/* Is the next slot available? */
if (!tcp_listenports[ndx])
{
/* Yes.. we found it */
tcp_listenports[ndx] = conn;
ret = OK;
break;
}
}
}
net_unlock();
return ret;
}
/****************************************************************************
* Name: tcp_islistener
*
* Description:
* Return true is there is a listener for the specified port
*
* Assumptions:
* This function is called from network logic with the network locked.
*
****************************************************************************/
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
bool tcp_islistener(uint16_t portno, uint8_t domain)
{
return tcp_findlistener(portno, domain) != NULL;
}
#else
bool tcp_islistener(uint16_t portno)
{
return tcp_findlistener(portno) != NULL;
}
#endif
/****************************************************************************
* Name: tcp_accept_connection
*
* Description:
* Accept the new connection for the specified listening port.
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
int tcp_accept_connection(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn, uint16_t portno)
{
FAR struct tcp_conn_s *listener;
int ret = -EINVAL;
/* The event processing logic has already allocated and initialized a TCP
* connection -- now check there if is an application in place to accept
* the connection.
*/
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
listener = tcp_findlistener(portno, conn->domain);
#else
listener = tcp_findlistener(portno);
#endif
if (listener != NULL)
{
/* Yes, there is a listener. Is it accepting connections now? */
if (listener->accept)
{
/* Yes.. accept the connection */
ret = listener->accept(listener, conn);
}
#ifdef CONFIG_NET_TCPBACKLOG
else
{
/* Add the connection to the backlog and notify any threads that
* may be waiting on poll()/select() that the connection is
* available.
*/
ret = tcp_backlogadd(listener, conn);
if (ret == OK)
{
tcp_callback(dev, listener, TCP_BACKLOG);
}
}
#endif
}
return ret;
}
#endif /* CONFIG_NET */
@@ -0,0 +1,443 @@
/****************************************************************************
* net/tcp/tcp_monitor.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 <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/net/tcp.h>
#include "devif/devif.h"
#include "socket/socket.h"
#include "tcp/tcp.h"
#ifdef NET_TCP_HAVE_STACK
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void tcp_close_connection(FAR struct socket *psock, uint16_t flags);
static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv,
uint16_t flags);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_close_connection
*
* Description:
* Called when a loss-of-connection event has occurred.
*
* Input Parameters:
* psock The TCP socket structure associated.
* flags Set of connection events events
*
* Returned Value:
* None
*
* Assumptions:
* The caller holds the network lock.
*
****************************************************************************/
static void tcp_close_connection(FAR struct socket *psock, uint16_t flags)
{
/* These loss-of-connection events may be reported:
*
* TCP_CLOSE: The remote host has closed the connection
* TCP_ABORT: The remote host has aborted the connection
* TCP_TIMEDOUT: Connection aborted due to too many retransmissions.
* NETDEV_DOWN: The network device went down
*
* And we need to set these two socket status bits appropriately:
*
* _SF_CONNECTED==1 && _SF_CLOSED==0 - the socket is connected
* _SF_CONNECTED==0 && _SF_CLOSED==1 - the socket was gracefully
* disconnected
* _SF_CONNECTED==0 && _SF_CLOSED==0 - the socket was rudely disconnected
*/
if ((flags & TCP_CLOSE) != 0)
{
/* The peer gracefully closed the connection. Marking the
* connection as disconnected will suppress some subsequent
* ENOTCONN errors from receive. A graceful disconnection is
* not handle as an error but as an "end-of-file"
*/
psock->s_flags &= ~_SF_CONNECTED;
psock->s_flags |= _SF_CLOSED;
}
else if ((flags & (TCP_ABORT | TCP_TIMEDOUT | NETDEV_DOWN)) != 0)
{
/* The loss of connection was less than graceful. This will
* (eventually) be reported as an ENOTCONN error.
*/
psock->s_flags &= ~(_SF_CONNECTED | _SF_CLOSED);
}
}
/****************************************************************************
* Name: tcp_disconnect_event
*
* Description:
* Some connection related event has occurred
*
* Input Parameters:
* dev The device which as active when the event was detected.
* conn The connection structure associated with the socket
* flags Set of events describing why the callback was invoked
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv,
uint16_t flags)
{
FAR struct socket *psock = (FAR struct socket *)pvpriv;
if (psock != NULL)
{
ninfo("flags: %04x s_flags: %02x\n", flags, psock->s_flags);
/* TCP_DISCONN_EVENTS: TCP_CLOSE, TCP_ABORT, TCP_TIMEDOUT, or
* NETDEV_DOWN. All loss-of-connection events.
*/
if ((flags & TCP_DISCONN_EVENTS) != 0)
{
tcp_close_connection(psock, flags);
}
/* TCP_CONNECTED: The socket is successfully connected */
else if ((flags & TCP_CONNECTED) != 0)
{
#if 0 /* REVISIT: Assertion fires. Why? */
FAR struct tcp_conn_s *conn =
(FAR struct tcp_conn_s *)psock->s_conn;
/* Make sure that this is the device bound to the connection */
DEBUGASSERT(conn->dev == NULL || conn->dev == dev);
conn->dev = dev;
#endif
/* If there is no local address assigned to the socket (perhaps
* because it was INADDR_ANY), then assign it the address of the
* connecting device.
*
* TODO: Implement this.
*/
/* Indicate that the socket is now connected */
psock->s_flags |= _SF_CONNECTED;
psock->s_flags &= ~_SF_CLOSED;
}
}
return flags;
}
/****************************************************************************
* Name: tcp_shutdown_monitor
*
* Description:
* Stop monitoring TCP connection changes for a given socket.
*
* Input Parameters:
* conn - The TCP connection of interest
* flags - Indicates the type of shutdown. TCP_CLOSE or TCP_ABORT
*
* Returned Value:
* None
*
* Assumptions:
* The caller holds the network lock (if not, it will be locked momentarily
* by this function).
*
****************************************************************************/
static void tcp_shutdown_monitor(FAR struct tcp_conn_s *conn, uint16_t flags)
{
DEBUGASSERT(conn);
/* Perform callbacks to assure that all sockets, including dup'ed copies,
* are informed of the loss of connection event.
*/
net_lock();
tcp_callback(conn->dev, conn, flags);
/* Free all allocated connection event callback structures */
while (conn->connevents != NULL)
{
devif_conn_callback_free(conn->dev, conn->connevents,
&conn->connevents);
}
net_unlock();
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_start_monitor
*
* Description:
* Set up to receive TCP connection state changes for a given socket
*
* Input Parameters:
* psock - The socket of interest
*
* Returned Value:
* On success, tcp_start_monitor returns OK; On any failure,
* tcp_start_monitor will return a negated errno value. The only failure
* that can occur is if the socket has already been closed and, in this
* case, -ENOTCONN is returned.
*
* Assumptions:
* The caller holds the network lock (if not, it will be locked momentarily
* by this function).
*
****************************************************************************/
int tcp_start_monitor(FAR struct socket *psock)
{
FAR struct tcp_conn_s *conn;
FAR struct devif_callback_s *cb;
DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
conn = (FAR struct tcp_conn_s *)psock->s_conn;
/* Check if the connection has already been closed before any callbacks
* have been registered. (Maybe the connection is lost before accept has
* registered the monitoring callback.)
*/
net_lock();
if (!(conn->tcpstateflags == TCP_ESTABLISHED ||
conn->tcpstateflags == TCP_SYN_RCVD))
{
/* Invoke the TCP_CLOSE connection event now */
tcp_shutdown_monitor(conn, TCP_CLOSE);
/* And return -ENOTCONN to indicate the monitor was not started
* because the socket was already disconnected.
*/
net_unlock();
return -ENOTCONN;
}
/* Allocate a callback structure that we will use to get callbacks if
* the network goes down.
*/
cb = devif_callback_alloc(conn->dev, &conn->connevents);
if (cb != NULL)
{
cb->event = tcp_disconnect_event;
cb->priv = (FAR void *)psock;
cb->flags = TCP_DISCONN_EVENTS;
}
net_unlock();
return OK;
}
/****************************************************************************
* Name: tcp_stop_monitor
*
* Description:
* Stop monitoring TCP connection changes for a sockets associated with
* a given TCP connection structure.
*
* Input Parameters:
* conn - The TCP connection of interest
* flags Set of disconnection events
*
* Returned Value:
* None
*
* Assumptions:
* The caller holds the network lock (if not, it will be locked momentarily
* by this function).
*
****************************************************************************/
void tcp_stop_monitor(FAR struct tcp_conn_s *conn, uint16_t flags)
{
DEBUGASSERT(conn != NULL);
/* Stop the network monitor */
tcp_shutdown_monitor(conn, flags);
}
/****************************************************************************
* Name: tcp_close_monitor
*
* Description:
* One socket in a group of dup'ed sockets has been closed. We need to
* selectively terminate just those things that are waiting of events
* from this specific socket. And also recover any resources that are
* committed to monitoring this socket.
*
* Input Parameters:
* psock - The TCP socket structure that is closed
*
* Returned Value:
* None
*
* Assumptions:
* The caller holds the network lock (if not, it will be locked momentarily
* by this function).
*
****************************************************************************/
void tcp_close_monitor(FAR struct socket *psock)
{
FAR struct tcp_conn_s *conn;
FAR struct devif_callback_s *cb;
DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
conn = (FAR struct tcp_conn_s *)psock->s_conn;
/* Find and free the the connection event callback */
net_lock();
for (cb = conn->connevents;
cb != NULL && cb->priv != (FAR void *)psock;
cb = cb->nxtconn)
{
}
if (cb != NULL)
{
devif_conn_callback_free(conn->dev, cb, &conn->connevents);
}
/* Make sure that this socket is explicitly marked as closed */
tcp_close_connection(psock, TCP_CLOSE);
/* Now notify any sockets waiting for events from this particular sockets.
* Other dup'ed sockets sharing the same connection must not be effected.
*/
/* REVISIT: The following logic won't work: There is no way to compare
* psocks to check for a match. This missing logic could only be an issue
* if the same socket were being used on one thread, but then closed on
* another. Some redesign would be required to find only those event
* handlers that are waiting specifically for this socket (vs. a dup of
* this socket)
*/
#if 0
for (cb = conn->list; cb != NULL; cb = cb->nxtconn)
{
if (cb->event != NULL && (cb->flags & TCP_CLOSE) != 0)
{
cb->event(conn->dev, conn, cb->priv, TCP_CLOSE);
}
}
#endif
net_unlock();
}
/****************************************************************************
* Name: tcp_lost_connection
*
* Description:
* Called when a loss-of-connection event has been detected by network
* event handling logic. Perform operations like tcp_stop_monitor but (1)
* explicitly mark this socket and (2) disable further callbacks the to the
* event handler.
*
* Input Parameters:
* psock - The TCP socket structure whose connection was lost.
* cb - devif callback structure
* flags - Set of connection events events
*
* Returned Value:
* None
*
* Assumptions:
* The caller holds the network lock (if not, it will be locked momentarily
* by this function).
*
****************************************************************************/
void tcp_lost_connection(FAR struct socket *psock,
FAR struct devif_callback_s *cb, uint16_t flags)
{
DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
/* Nullify the callback structure so that recursive callbacks are not
* received by the event handler due to disconnection processing.
*
* NOTE: In a configuration with CONFIG_NET_TCP_WRITE_BUFFERS=y,
* the "semi-permanent" callback structure may have already been
* nullified.
*/
if (cb != NULL)
{
cb->flags = 0;
cb->priv = NULL;
cb->event = NULL;
}
/* Make sure that this socket is explicitly marked. It may not get a
* callback due to the above nullification.
*/
tcp_close_connection(psock, flags);
/* Then stop the network monitor for all sockets. */
tcp_shutdown_monitor((FAR struct tcp_conn_s *)psock->s_conn, flags);
}
#endif /* NET_TCP_HAVE_STACK */
@@ -0,0 +1,357 @@
/****************************************************************************
* net/tcp/tcp_netpoll.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 <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <poll.h>
#include <debug.h>
#include <nuttx/net/net.h>
#include <nuttx/semaphore.h>
#include "devif/devif.h"
#include "netdev/netdev.h"
#include "socket/socket.h"
#include "inet/inet.h"
#include "tcp/tcp.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_poll_eventhandler
*
* Description:
* This function is called to perform the actual TCP receive operation via
* the device interface layer.
*
* Input Parameters:
* dev The structure of the network driver that caused the event
* conn The connection structure associated with the socket
* flags Set of events describing why the callback was invoked
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked
*
****************************************************************************/
static uint16_t tcp_poll_eventhandler(FAR struct net_driver_s *dev,
FAR void *conn,
FAR void *pvpriv, uint16_t flags)
{
FAR struct tcp_poll_s *info = (FAR struct tcp_poll_s *)pvpriv;
ninfo("flags: %04x\n", flags);
DEBUGASSERT(info == NULL || (info->psock != NULL && info->fds != NULL));
/* 'priv' might be null in some race conditions (?) */
if (info != NULL)
{
pollevent_t eventset = 0;
/* Check for data or connection availability events. */
if ((flags & (TCP_NEWDATA | TCP_BACKLOG)) != 0)
{
eventset |= POLLIN & info->fds->events;
}
/* Check for a loss of connection events. */
if ((flags & TCP_DISCONN_EVENTS) != 0)
{
/* Mark that the connection has been lost */
tcp_lost_connection(info->psock, info->cb, flags);
eventset |= (POLLERR | POLLHUP);
}
/* A poll is a sign that we are free to send data. */
/* Wake up poll() speculatively on TCP_ACKDATA.
* Note: our event handler is usually executed before
* psock_send_eventhandler, which might free IOBs/WRBs on TCP_ACKDATA.
* Revisit: consider some kind of priority for devif callback to allow
* this callback to be inserted after psock_send_eventhandler.
*/
else if (psock_tcp_cansend(info->psock) >= 0
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS)
|| (flags & TCP_ACKDATA) != 0
#endif
)
{
eventset |= (POLLOUT & info->fds->events);
}
/* Awaken the caller of poll() if requested event occurred. */
if (eventset != 0)
{
/* Stop further callbacks */
info->cb->flags = 0;
info->cb->priv = NULL;
info->cb->event = NULL;
info->fds->revents |= eventset;
nxsem_post(info->fds->sem);
}
}
return flags;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_pollsetup
*
* Description:
* Setup to monitor events on one TCP/IP socket
*
* Input Parameters:
* psock - The TCP/IP socket of interest
* fds - The structure describing the events to be monitored, OR NULL if
* this is a request to stop monitoring events.
*
* Returned Value:
* 0: Success; Negated errno on failure
*
****************************************************************************/
int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
{
FAR struct tcp_conn_s *conn = psock->s_conn;
FAR struct tcp_poll_s *info;
FAR struct devif_callback_s *cb;
int ret = OK;
/* Sanity check */
#ifdef CONFIG_DEBUG_FEATURES
if (!conn || !fds)
{
return -EINVAL;
}
#endif
/* Some of the following must be atomic */
net_lock();
/* Find a container to hold the poll information */
info = conn->pollinfo;
while (info->psock != NULL)
{
if (++info >= &conn->pollinfo[CONFIG_NET_TCP_NPOLLWAITERS])
{
ret = -ENOMEM;
goto errout_with_lock;
}
}
/* Allocate a TCP/IP callback structure */
cb = tcp_callback_alloc(conn);
if (!cb)
{
ret = -EBUSY;
goto errout_with_lock;
}
/* Initialize the poll info container */
info->psock = psock;
info->fds = fds;
info->cb = cb;
/* Initialize the callback structure. Save the reference to the info
* structure as callback private data so that it will be available during
* callback processing.
*/
cb->flags = TCP_DISCONN_EVENTS;
cb->priv = (FAR void *)info;
cb->event = tcp_poll_eventhandler;
if ((fds->events & POLLOUT) != 0)
{
cb->flags |= TCP_POLL
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS)
| TCP_ACKDATA
#endif
;
}
if ((fds->events & POLLIN) != 0)
{
cb->flags |= TCP_NEWDATA | TCP_BACKLOG;
}
/* Save the reference in the poll info structure as fds private as well
* for use during poll teardown as well.
*/
fds->priv = (FAR void *)info;
/* Check for read data or backlogged connection availability now */
if (!IOB_QEMPTY(&conn->readahead) || tcp_backlogavailable(conn))
{
/* Normal data may be read without blocking. */
fds->revents |= (POLLRDNORM & fds->events);
}
/* Check for a loss of connection events. We need to be careful here.
* There are four possibilities:
*
* 1) The socket is connected and we are waiting for data availability
* events.
*
* __SS_ISCONNECTED(f) == true
* __SS_ISLISTENING(f) == false
* __SS_ISCLOSED(f) == false
*
* Action: Wait for data availability events
*
* 2) This is a listener socket that was never connected and we are
* waiting for connection events.
*
* __SS_ISCONNECTED(f) == false
* __SS_ISLISTENING(f) == true
* __SS_ISCLOSED(f) == false
*
* Action: Wait for connection events
*
* 3) This socket was previously connected, but the peer has gracefully
* closed the connection.
*
* __SS_ISCONNECTED(f) == false
* __SS_ISLISTENING(f) == false
* __SS_ISCLOSED(f) == true
*
* Action: Return with POLLHUP|POLLERR events
*
* 4) This socket was previously connected, but we lost the connection
* due to some exceptional event.
*
* __SS_ISCONNECTED(f) == false
* __SS_ISLISTENING(f) == false
* __SS_ISCLOSED(f) == false
*
* Action: Return with POLLHUP|POLLERR events
*/
if (!_SS_ISCONNECTED(psock->s_flags) && !_SS_ISLISTENING(psock->s_flags))
{
/* We were previously connected but lost the connection either due
* to a graceful shutdown by the remote peer or because of some
* exceptional event.
*/
fds->revents |= (POLLERR | POLLHUP);
}
else if (_SS_ISCONNECTED(psock->s_flags) && psock_tcp_cansend(psock) >= 0)
{
fds->revents |= (POLLWRNORM & fds->events);
}
/* Check if any requested events are already in effect */
if (fds->revents != 0)
{
/* Yes.. then signal the poll logic */
nxsem_post(fds->sem);
}
errout_with_lock:
net_unlock();
return ret;
}
/****************************************************************************
* Name: tcp_pollteardown
*
* Description:
* Teardown monitoring of events on an TCP/IP socket
*
* Input Parameters:
* psock - The TCP/IP socket of interest
* fds - The structure describing the events to be monitored, OR NULL if
* this is a request to stop monitoring events.
*
* Returned Value:
* 0: Success; Negated errno on failure
*
****************************************************************************/
int tcp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds)
{
FAR struct tcp_conn_s *conn = psock->s_conn;
FAR struct tcp_poll_s *info;
/* Sanity check */
#ifdef CONFIG_DEBUG_FEATURES
if (!conn || !fds->priv)
{
return -EINVAL;
}
#endif
/* Recover the socket descriptor poll state info from the poll structure */
info = (FAR struct tcp_poll_s *)fds->priv;
DEBUGASSERT(info != NULL && info->fds != NULL && info->cb != NULL);
if (info != NULL)
{
/* Release the callback */
tcp_callback_free(conn, info->cb);
/* Release the poll/select data slot */
info->fds->priv = NULL;
/* Then free the poll info container */
info->psock = NULL;
}
return OK;
}
@@ -0,0 +1,312 @@
/****************************************************************************
* net/tcp/tcp_notifier.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 <nuttx/config.h>
#include <sys/types.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/wqueue.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/tcp.h>
#include "tcp/tcp.h"
#ifdef CONFIG_NET_TCP_NOTIFIER
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_readahead_notifier_setup
*
* Description:
* Set up to perform a callback to the worker function when an TCP data
* is added to the read-ahead buffer. The worker function will execute
* on the low priority worker thread.
*
* Input Parameters:
* worker - The worker function to execute on the low priority work
* queue when data is available in the TCP read-ahead buffer.
* conn - The TCP connection where read-ahead data is needed.
* arg - A user-defined argument that will be available to the worker
* function when it runs.
*
* Returned Value:
* > 0 - The notification is in place. The returned value is a key that
* may be used later in a call to tcp_notifier_teardown().
* == 0 - There is already buffered read-ahead data. No notification
* will be provided.
* < 0 - An unexpected error occurred and notification will occur. The
* returned value is a negated errno value that indicates the
* nature of the failure.
*
****************************************************************************/
int tcp_readahead_notifier_setup(worker_t worker,
FAR struct tcp_conn_s *conn,
FAR void *arg)
{
struct work_notifier_s info;
DEBUGASSERT(worker != NULL);
/* If there is already buffered read-ahead data, then return zero without
* setting up the notification.
*/
if (conn->readahead.qh_head != NULL)
{
return 0;
}
/* Otherwise, this is just a simple wrapper around work_notifer_setup(). */
info.evtype = WORK_TCP_READAHEAD;
info.qid = LPWORK;
info.qualifier = conn;
info.arg = arg;
info.worker = worker;
return work_notifier_setup(&info);
}
/****************************************************************************
* Name: tcp_writebuffer_notifier_setup
*
* Description:
* Set up to perform a callback to the worker function when an TCP write
* buffer is emptied. The worker function will execute on the high
* priority worker thread.
*
* Input Parameters:
* worker - The worker function to execute on the high priority work
* queue when all buffer TX data has been sent.
* conn - The TCP connection where buffer write data is pending.
* arg - A user-defined argument that will be available to the worker
* function when it runs.
*
* Returned Value:
* > 0 - The signal notification is in place. The returned value is a
* key that may be used later in a call to
* tcp_notifier_teardown().
* == 0 - There is already buffered read-ahead data. No signal
* notification will be provided.
* < 0 - An unexpected error occurred and no signal will be sent. The
* returned value is a negated errno value that indicates the
* nature of the failure.
*
****************************************************************************/
int tcp_writebuffer_notifier_setup(worker_t worker,
FAR struct tcp_conn_s *conn,
FAR void *arg)
{
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
struct work_notifier_s info;
DEBUGASSERT(worker != NULL);
/* If the write buffers are already empty, then return zero without
* setting up the notification.
*/
if (sq_empty(&conn->write_q) && sq_empty(&conn->unacked_q))
{
return 0;
}
/* Otherwise, this is just a simple wrapper around work_notifer_setup(). */
info.evtype = WORK_TCP_WRITEBUFFER;
info.qid = LPWORK;
info.qualifier = conn;
info.arg = arg;
info.worker = worker;
return work_notifier_setup(&info);
#else
return 0;
#endif
}
/****************************************************************************
* Name: tcp_disconnect_notifier_setup
*
* Description:
* Set up to perform a callback to the worker function if the TCP
* connection is lost.
*
* Input Parameters:
* worker - The worker function to execute on the low priority work
* queue when data is available in the TCP read-ahead buffer.
* conn - The TCP connection where read-ahead data is needed.
* arg - A user-defined argument that will be available to the worker
* function when it runs.
*
* Returned Value:
* > 0 - The notification is in place. The returned value is a key that
* may be used later in a call to tcp_notifier_teardown().
* == 0 - No connection has been established.
* < 0 - An unexpected error occurred and notification will occur. The
* returned value is a negated errno value that indicates the
* nature of the failure.
*
****************************************************************************/
int tcp_disconnect_notifier_setup(worker_t worker,
FAR struct tcp_conn_s *conn,
FAR void *arg)
{
struct work_notifier_s info;
DEBUGASSERT(worker != NULL);
/* If connection has not been established, then return 0. */
if (conn->tcpstateflags != TCP_ESTABLISHED)
{
return 0;
}
/* Otherwise, this is just a simple wrapper around work_notifer_setup(). */
info.evtype = WORK_TCP_DISCONNECT;
info.qid = LPWORK;
info.qualifier = conn;
info.arg = arg;
info.worker = worker;
return work_notifier_setup(&info);
}
/****************************************************************************
* Name: tcp_notifier_teardown
*
* Description:
* Eliminate a TCP read-ahead notification previously setup by
* tcp_readahead_notifier_setup(). This function should only be called
* if the notification should be aborted prior to the notification. The
* notification will automatically be torn down after the notification.
*
* Input Parameters:
* key - The key value returned from a previous call to
* tcp_readahead_notifier_setup().
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int tcp_notifier_teardown(int key)
{
/* This is just a simple wrapper around work_notifier_teardown(). */
return work_notifier_teardown(key);
}
/****************************************************************************
* Name: tcp_readahead_signal
*
* Description:
* Read-ahead data has been buffered. Signal all threads waiting for
* read-ahead data to become available.
*
* When read-ahead data becomes available, *all* of the workers waiting
* for read-ahead data will be executed. If there are multiple workers
* waiting for read-ahead data then only the first to execute will get the
* data. Others will need to call tcp_readahead_notifier_setup() once
* again.
*
* Input Parameters:
* conn - The TCP connection where read-ahead data was just buffered.
*
* Returned Value:
* None.
*
****************************************************************************/
void tcp_readahead_signal(FAR struct tcp_conn_s *conn)
{
/* This is just a simple wrapper around work_notifier_signal(). */
work_notifier_signal(WORK_TCP_READAHEAD, conn);
}
/****************************************************************************
* Name: tcp_writebuffer_signal
*
* Description:
* All buffer Tx data has been sent. Signal all threads waiting for the
* write buffers to become empty.
*
* When write buffer becomes empty, *all* of the workers waiting
* for that event data will be executed. If there are multiple workers
* waiting for read-ahead data then only the first to execute will get the
* data. Others will need to call tcp_writebuffer_notifier_setup() once
* again.
*
* Input Parameters:
* conn - The TCP connection where read-ahead data was just buffered.
*
* Returned Value:
* None.
*
****************************************************************************/
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
void tcp_writebuffer_signal(FAR struct tcp_conn_s *conn)
{
/* This is just a simple wrapper around work_notifier_signal(). */
work_notifier_signal(WORK_TCP_WRITEBUFFER, conn);
}
#endif
/****************************************************************************
* Name: tcp_disconnect_signal
*
* Description:
* The TCP connection has been lost. Signal all threads monitoring TCP
* state events.
*
* Input Parameters:
* conn - The TCP connection where read-ahead data was just buffered.
*
* Returned Value:
* None.
*
****************************************************************************/
void tcp_disconnect_signal(FAR struct tcp_conn_s *conn)
{
/* This is just a simple wrapper around work_notifier_signal(). */
work_notifier_signal(WORK_TCP_DISCONNECT, conn);
}
#endif /* CONFIG_NET_TCP_NOTIFIER */
@@ -0,0 +1,830 @@
/****************************************************************************
* net/tcp/tcp_recvfrom.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 <nuttx/config.h>
#ifdef CONFIG_NET_TCP
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/tcp.h>
#include "netdev/netdev.h"
#include "devif/devif.h"
#include "tcp/tcp.h"
#include "socket/socket.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
#define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
#define TCPIPv4BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN])
#define TCPIPv6BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN])
/****************************************************************************
* Private Types
****************************************************************************/
struct tcp_recvfrom_s
{
FAR struct socket *ir_sock; /* The parent socket structure */
FAR struct devif_callback_s *ir_cb; /* Reference to callback instance */
sem_t ir_sem; /* Semaphore signals recv completion */
size_t ir_buflen; /* Length of receive buffer */
uint8_t *ir_buffer; /* Pointer to receive buffer */
FAR struct sockaddr *ir_from; /* Address of sender */
FAR socklen_t *ir_fromlen; /* Number of bytes allocated for address of sender */
ssize_t ir_recvlen; /* The received length */
int ir_result; /* Success:OK, failure:negated errno */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_update_recvlen
*
* Description:
* Update information about space available for new data and update size
* of data in buffer.
*
* Input Parameters:
* pstate recvfrom state structure
* recvlen size of new data appended to buffer
*
* Returned Value:
* None
*
****************************************************************************/
static inline void tcp_update_recvlen(FAR struct tcp_recvfrom_s *pstate,
size_t recvlen)
{
if (pstate->ir_recvlen < 0)
{
pstate->ir_recvlen = 0;
}
pstate->ir_recvlen += recvlen;
pstate->ir_buffer += recvlen;
pstate->ir_buflen -= recvlen;
}
/****************************************************************************
* Name: tcp_recvfrom_newdata
*
* Description:
* Copy the read data from the packet
*
* Input Parameters:
* dev The structure of the network driver that generated the event.
* pstate recvfrom state structure
*
* Returned Value:
* The number of bytes taken from the packet.
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static size_t tcp_recvfrom_newdata(FAR struct net_driver_s *dev,
FAR struct tcp_recvfrom_s *pstate)
{
size_t recvlen;
/* Get the length of the data to return */
if (dev->d_len > pstate->ir_buflen)
{
recvlen = pstate->ir_buflen;
}
else
{
recvlen = dev->d_len;
}
/* Copy the new appdata into the user buffer */
memcpy(pstate->ir_buffer, dev->d_appdata, recvlen);
ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len);
/* Update the accumulated size of the data read */
tcp_update_recvlen(pstate, recvlen);
return recvlen;
}
/****************************************************************************
* Name: tcp_newdata
*
* Description:
* Copy the read data from the packet
*
* Input Parameters:
* dev The structure of the network driver that generated the event
* pstate recvfrom state structure
*
* Returned Value:
* None.
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static inline void tcp_newdata(FAR struct net_driver_s *dev,
FAR struct tcp_recvfrom_s *pstate)
{
/* Take as much data from the packet as we can */
size_t recvlen = tcp_recvfrom_newdata(dev, pstate);
/* If there is more data left in the packet that we could not buffer, then
* add it to the read-ahead buffers.
*/
if (recvlen < dev->d_len)
{
FAR struct tcp_conn_s *conn =
(FAR struct tcp_conn_s *)pstate->ir_sock->s_conn;
FAR uint8_t *buffer = (FAR uint8_t *)dev->d_appdata + recvlen;
uint16_t buflen = dev->d_len - recvlen;
#ifdef CONFIG_DEBUG_NET
uint16_t nsaved;
nsaved = tcp_datahandler(conn, buffer, buflen);
#else
tcp_datahandler(conn, buffer, buflen);
#endif
/* There are complicated buffering issues that are not addressed fully
* here. For example, what if up_datahandler() cannot buffer the
* remainder of the packet? In that case, the data will be dropped but
* still ACKed. Therefore it would not be resent.
*
* This is probably not an issue here because we only get here if the
* read-ahead buffers are empty and there would have to be something
* serioulsy wrong with the configuration not to be able to buffer a
* partial packet in this context.
*/
#ifdef CONFIG_DEBUG_NET
if (nsaved < buflen)
{
nerr("ERROR: packet data not saved (%d bytes)\n", buflen - nsaved);
}
#endif
}
/* Indicate no data in the buffer */
dev->d_len = 0;
}
/****************************************************************************
* Name: tcp_readahead
*
* Description:
* Copy the read-ahead data from the packet
*
* Input Parameters:
* pstate recvfrom state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static inline void tcp_readahead(struct tcp_recvfrom_s *pstate)
{
FAR struct tcp_conn_s *conn =
(FAR struct tcp_conn_s *)pstate->ir_sock->s_conn;
FAR struct iob_s *iob;
int recvlen;
/* Check there is any TCP data already buffered in a read-ahead
* buffer.
*/
while ((iob = iob_peek_queue(&conn->readahead)) != NULL &&
pstate->ir_buflen > 0)
{
DEBUGASSERT(iob->io_pktlen > 0);
/* Transfer that buffered data from the I/O buffer chain into
* the user buffer.
*/
recvlen = iob_copyout(pstate->ir_buffer, iob, pstate->ir_buflen, 0);
ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen);
/* Update the accumulated size of the data read */
tcp_update_recvlen(pstate, recvlen);
/* If we took all of the data from the I/O buffer chain is empty, then
* release it. If there is still data available in the I/O buffer
* chain, then just trim the data that we have taken from the
* beginning of the I/O buffer chain.
*/
if (recvlen >= iob->io_pktlen)
{
FAR struct iob_s *tmp;
/* Remove the I/O buffer chain from the head of the read-ahead
* buffer queue.
*/
tmp = iob_remove_queue(&conn->readahead);
DEBUGASSERT(tmp == iob);
UNUSED(tmp);
/* And free the I/O buffer chain */
iob_free_chain(iob, IOBUSER_NET_TCP_READAHEAD);
}
else
{
/* The bytes that we have received from the head of the I/O
* buffer chain (probably changing the head of the I/O
* buffer queue).
*/
iob_trimhead_queue(&conn->readahead, recvlen,
IOBUSER_NET_TCP_READAHEAD);
}
}
}
/****************************************************************************
* Name: tcp_sender
*
* Description:
* Getting the sender's address from the UDP packet
*
* Input Parameters:
* dev - The device driver data structure
* pstate - the recvfrom state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked
*
****************************************************************************/
static inline void tcp_sender(FAR struct net_driver_s *dev,
FAR struct tcp_recvfrom_s *pstate)
{
/* Get the family from the packet type, IP address from the IP header, and
* the port number from the TCP header.
*/
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
FAR struct sockaddr_in6 *infrom =
(FAR struct sockaddr_in6 *)pstate->ir_from;
if (infrom)
{
FAR struct tcp_hdr_s *tcp = TCPIPv6BUF;
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
infrom->sin6_family = AF_INET6;
infrom->sin6_port = tcp->srcport;
net_ipv6addr_copy(infrom->sin6_addr.s6_addr, ipv6->srcipaddr);
}
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
FAR struct sockaddr_in *infrom =
(FAR struct sockaddr_in *)pstate->ir_from;
if (infrom)
{
FAR struct tcp_hdr_s *tcp = TCPIPv4BUF;
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
infrom->sin_family = AF_INET;
infrom->sin_port = tcp->srcport;
net_ipv4addr_copy(infrom->sin_addr.s_addr,
net_ip4addr_conv32(ipv4->srcipaddr));
memset(infrom->sin_zero, 0, sizeof(infrom->sin_zero));
}
}
#endif /* CONFIG_NET_IPv4 */
}
/****************************************************************************
* Name: tcp_recvhandler
*
* Description:
* This function is called with the network locked to perform the actual
* TCP receive operation via by the lower, device interfacing layer.
*
* Input Parameters:
* dev The structure of the network driver that generated the event.
* pvconn The connection structure associated with the socket
* flags Set of events describing why the callback was invoked
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static uint16_t tcp_recvhandler(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv,
uint16_t flags)
{
FAR struct tcp_recvfrom_s *pstate = (struct tcp_recvfrom_s *)pvpriv;
#if 0 /* REVISIT: The assertion fires. Why? */
FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn;
/* The TCP socket is connected and, hence, should be bound to a device.
* Make sure that the polling device is the own that we are bound to.
*/
DEBUGASSERT(conn->dev == NULL || conn->dev == dev);
if (conn->dev != NULL && conn->dev != dev)
{
return flags;
}
#endif
ninfo("flags: %04x\n", flags);
/* 'priv' might be null in some race conditions (?) */
if (pstate)
{
/* If new data is available, then complete the read action. */
if ((flags & TCP_NEWDATA) != 0)
{
/* Copy the data from the packet (saving any unused bytes from the
* packet in the read-ahead buffer).
*/
tcp_newdata(dev, pstate);
/* Save the sender's address in the caller's 'from' location */
tcp_sender(dev, pstate);
/* Indicate that the data has been consumed and that an ACK
* should be sent.
*/
flags = (flags & ~TCP_NEWDATA) | TCP_SNDACK;
/* Check for transfer complete. We will consider the
* TCP/IP transfer complete as soon as any data has been received.
* This is safe because if any additional data is received, it
* will be retained in the TCP/IP read-ahead buffer until the
* next receive is performed.
*/
if (pstate->ir_recvlen > 0)
{
ninfo("TCP resume\n");
/* The TCP receive buffer is non-empty. Return now and don't
* allow any further TCP call backs.
*/
pstate->ir_cb->flags = 0;
pstate->ir_cb->priv = NULL;
pstate->ir_cb->event = NULL;
/* Wake up the waiting thread, returning the number of bytes
* actually read.
*/
nxsem_post(&pstate->ir_sem);
}
}
/* Check for a loss of connection.
*
* TCP_DISCONN_EVENTS:
* TCP_CLOSE: The remote host has closed the connection
* TCP_ABORT: The remote host has aborted the connection
* TCP_TIMEDOUT: Connection aborted due to too many retransmissions.
* NETDEV_DOWN: The network device went down
*/
else if ((flags & TCP_DISCONN_EVENTS) != 0)
{
FAR struct socket *psock = pstate->ir_sock;
nwarn("WARNING: Lost connection\n");
/* We could get here recursively through the callback actions of
* tcp_lost_connection(). So don't repeat that action if we have
* already been disconnected.
*/
DEBUGASSERT(psock != NULL);
if (_SS_ISCONNECTED(psock->s_flags))
{
/* Handle loss-of-connection event */
tcp_lost_connection(psock, pstate->ir_cb, flags);
}
/* Check if the peer gracefully closed the connection. */
if ((flags & TCP_CLOSE) != 0)
{
/* This case should always return success (zero)! The value of
* ir_recvlen, if zero, will indicate that the connection was
* gracefully closed.
*/
pstate->ir_result = 0;
}
else
{
pstate->ir_result = -ENOTCONN;
}
/* Wake up the waiting thread */
nxsem_post(&pstate->ir_sem);
}
}
return flags;
}
/****************************************************************************
* Name: tcp_ackhandler
*
* Description:
* This function is called with the network locked to send the ACK in
* response by the lower, device interfacing layer.
*
* Input Parameters:
* dev The structure of the network driver that generated the event.
* pvconn The connection structure associated with the socket
* flags Set of events describing why the callback was invoked
*
* Returned Value:
* ACK should be send in the response.
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static uint16_t tcp_ackhandler(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv,
uint16_t flags)
{
FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn;
ninfo("flags: %04x\n", flags);
if (conn != NULL && (flags & TCP_POLL) != 0)
{
/* Indicate that the data has been consumed and that an ACK
* should be send.
*/
if (tcp_get_recvwindow(dev, conn) != 0 &&
conn->rcv_wnd == 0)
{
flags |= TCP_SNDACK;
}
tcp_callback_free(conn, conn->rcv_ackcb);
conn->rcv_ackcb = NULL;
}
return flags;
}
/****************************************************************************
* Name: tcp_recvfrom_initialize
*
* Description:
* Initialize the state structure
*
* Input Parameters:
* psock Pointer to the socket structure for the socket
* buf Buffer to receive data
* len Length of buffer
* pstate A pointer to the state structure to be initialized
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void tcp_recvfrom_initialize(FAR struct socket *psock, FAR void *buf,
size_t len, FAR struct sockaddr *infrom,
FAR socklen_t *fromlen,
FAR struct tcp_recvfrom_s *pstate)
{
/* Initialize the state structure. */
memset(pstate, 0, sizeof(struct tcp_recvfrom_s));
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&pstate->ir_sem, 0, 0); /* Doesn't really fail */
nxsem_set_protocol(&pstate->ir_sem, SEM_PRIO_NONE);
pstate->ir_buflen = len;
pstate->ir_buffer = buf;
pstate->ir_from = infrom;
pstate->ir_fromlen = fromlen;
/* Set up the start time for the timeout */
pstate->ir_sock = psock;
}
/* The only un-initialization that has to be performed is destroying the
* semaphore.
*/
#define tcp_recvfrom_uninitialize(s) nxsem_destroy(&(s)->ir_sem)
/****************************************************************************
* Name: tcp_recvfrom_result
*
* Description:
* Evaluate the result of the recv operations
*
* Input Parameters:
* result The result of the net_timedwait operation (may indicate EINTR)
* pstate A pointer to the state structure to be initialized
*
* Returned Value:
* The result of the recv operation with errno set appropriately
*
* Assumptions:
*
****************************************************************************/
static ssize_t tcp_recvfrom_result(int result, struct tcp_recvfrom_s *pstate)
{
/* Check for a error/timeout detected by the event handler. Errors are
* signaled by negative errno values for the rcv length
*/
if (pstate->ir_result < 0)
{
/* This might return EAGAIN on a timeout or ENOTCONN on loss of
* connection (TCP only)
*/
return pstate->ir_result;
}
/* If net_timedwait failed, then we were probably reawakened by a signal.
* In this case, net_timedwait will have returned negated errno
* appropriately.
*/
if (result < 0)
{
return result;
}
return pstate->ir_recvlen;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: psock_tcp_recvfrom
*
* Description:
* Perform the recvfrom operation for a TCP/IP SOCK_STREAM
*
* Input Parameters:
* psock Pointer to the socket structure for the SOCK_DRAM socket
* buf Buffer to receive data
* len Length of buffer
* flags Receive flags
* from INET address of source (may be NULL)
* fromlen The length of the address structure
*
* Returned Value:
* On success, returns the number of characters received. On error,
* -errno is returned (see recvfrom for list of errnos).
*
* Assumptions:
*
****************************************************************************/
ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR void *buf,
size_t len, int flags, FAR struct sockaddr *from,
FAR socklen_t *fromlen)
{
struct tcp_recvfrom_s state;
FAR struct tcp_conn_s *conn;
int ret;
net_lock();
conn = (FAR struct tcp_conn_s *)psock->s_conn;
/* Initialize the state structure. This is done with the network locked
* because we don't want anything to happen until we are ready.
*/
tcp_recvfrom_initialize(psock, buf, len, from, fromlen, &state);
/* Handle any any TCP data already buffered in a read-ahead buffer. NOTE
* that there may be read-ahead data to be retrieved even after the
* socket has been disconnected.
*/
tcp_readahead(&state);
/* The default return value is the number of bytes that we just copied
* into the user buffer. We will return this if the socket has become
* disconnected or if the user request was completely satisfied with
* data from the readahead buffers.
*/
ret = state.ir_recvlen;
/* Verify that the SOCK_STREAM has been and still is connected */
if (!_SS_ISCONNECTED(psock->s_flags))
{
/* Was any data transferred from the readahead buffer after we were
* disconnected? If so, then return the number of bytes received. We
* will wait to return end disconnection indications the next time that
* recvfrom() is called.
*
* If no data was received (i.e., ret == 0 -- it will not be
* negative) and the connection was gracefully closed by the remote
* peer, then return success. If ir_recvlen is zero, the caller of
* recvfrom() will get an end-of-file indication.
*/
if (ret <= 0 && !_SS_ISCLOSED(psock->s_flags))
{
/* Nothing was previously received from the read-ahead buffers.
* The SOCK_STREAM must be (re-)connected in order to receive any
* additional data.
*/
ret = -ENOTCONN;
}
}
/* In general, this implementation will not support non-blocking socket
* operations... except in a few cases: Here for TCP receive with read-
* ahead enabled. If this socket is configured as non-blocking then
* return EAGAIN if no data was obtained from the read-ahead buffers.
*/
else if (_SS_ISNONBLOCK(psock->s_flags) || (flags & MSG_DONTWAIT) != 0)
{
/* Return the number of bytes read from the read-ahead buffer if
* something was received (already in 'ret'); EAGAIN if not.
*/
if (ret <= 0)
{
/* Nothing was received */
ret = -EAGAIN;
}
}
/* It is okay to block if we need to. If there is space to receive
* anything more, then we will wait to receive the data. Otherwise return
* the number of bytes read from the read-ahead buffer (already in 'ret').
*/
else
/* We get here when we we decide that we need to setup the wait for
* incoming TCP/IP data. Just a few more conditions to check:
*
* 1) Make sure thet there is buffer space to receive additional data
* (state.ir_buflen > 0). This could be zero, for example, we filled
* the user buffer with data from the read-ahead buffers. And
* 2) then we not want to wait if we already obtained some data from the
* read-ahead buffer. In that case, return now with what we have (don't
* want for more because there may be no timeout).
*/
if (state.ir_recvlen == 0 && state.ir_buflen > 0)
{
/* Set up the callback in the connection */
state.ir_cb = tcp_callback_alloc(conn);
if (state.ir_cb)
{
state.ir_cb->flags = (TCP_NEWDATA | TCP_DISCONN_EVENTS);
state.ir_cb->priv = (FAR void *)&state;
state.ir_cb->event = tcp_recvhandler;
/* Wait for either the receive to complete or for an error/timeout
* to occur. net_timedwait will also terminate if a signal isi
* received.
*/
ret = net_timedwait(&state.ir_sem, _SO_TIMEOUT(psock->s_rcvtimeo));
if (ret == -ETIMEDOUT)
{
ret = -EAGAIN;
}
/* Make sure that no further events are processed */
tcp_callback_free(conn, state.ir_cb);
ret = tcp_recvfrom_result(ret, &state);
}
else
{
ret = -EBUSY;
}
}
/* Receive additional data from read-ahead buffer, send the ACK timely. */
if (conn->rcv_wnd == 0 && conn->rcv_ackcb == NULL)
{
conn->rcv_ackcb = tcp_callback_alloc(conn);
if (conn->rcv_ackcb)
{
conn->rcv_ackcb->flags = TCP_POLL;
conn->rcv_ackcb->event = tcp_ackhandler;
netdev_txnotify_dev(conn->dev);
}
}
net_unlock();
tcp_recvfrom_uninitialize(&state);
return (ssize_t)ret;
}
#endif /* CONFIG_NET_TCP */
@@ -0,0 +1,156 @@
/****************************************************************************
* net/tcp/tcp_recvwindow.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 <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <debug.h>
#include <net/if.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/tcp.h>
#include "tcp/tcp.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_get_recvwindow
*
* Description:
* Calculate the TCP receive window for the specified device.
*
* Input Parameters:
* dev - The device whose TCP receive window will be updated.
*
* Returned Value:
* The value of the TCP receive window to use.
*
****************************************************************************/
uint16_t tcp_get_recvwindow(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn)
{
uint16_t iplen;
uint16_t mss;
uint16_t recvwndo;
int niob_avail;
int nqentry_avail;
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
iplen = IPv6_HDRLEN;
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
iplen = IPv4_HDRLEN;
}
#endif /* CONFIG_NET_IPv4 */
/* Calculate the packet MSS.
*
* REVISIT: The actual TCP header length is variable. TCP_HDRLEN
* is the minimum size.
*/
mss = dev->d_pktsize - (NET_LL_HDRLEN(dev) + iplen + TCP_HDRLEN);
/* Update the TCP received window based on read-ahead I/O buffer
* and IOB chain availability. At least one queue entry is required.
* If one queue entry is available, then the amount of read-ahead
* data that can be buffered is given by the number of IOBs available
* (ignoring competition with other IOB consumers).
*/
niob_avail = iob_navail(true);
nqentry_avail = iob_qentry_navail();
/* Is there a a queue entry and IOBs available for read-ahead buffering? */
if (nqentry_avail > 0 && niob_avail > 0)
{
uint32_t rwnd;
/* The optimal TCP window size is the amount of TCP data that we can
* currently buffer via TCP read-ahead buffering for the device packet
* buffer. This logic here assumes that all IOBs are available for
* TCP buffering.
*
* Assume that all of the available IOBs are can be used for buffering
* on this connection. Also assume that at least one chain is
* available concatenate the IOBs.
*
* REVISIT: In an environment with multiple, active read-ahead TCP
* sockets (and perhaps multiple network devices) or if there are
* other consumers of IOBs (such as for TCP write buffering) then the
* total number of IOBs will all not be available for read-ahead
* buffering for this connection.
*/
rwnd = (niob_avail * CONFIG_IOB_BUFSIZE);
if (rwnd > UINT16_MAX)
{
rwnd = UINT16_MAX;
}
/* Save the new receive window size */
recvwndo = (uint16_t)rwnd;
}
else if (IOB_QEMPTY(&conn->readahead))
{
/* Advertise maximum segment size for window edge if here is no
* available iobs on current "free" connection.
*/
recvwndo = mss;
}
else /* nqentry_avail == 0 || niob_avail == 0 */
{
/* No IOB chains or noIOBs are available.
* Advertise the edge of window to zero.
*
* NOTE: If no IOBs are available, then the next packet will be
* lost if there is no listener on the connection.
*/
recvwndo = 0;
}
return recvwndo;
}
+633
View File
@@ -0,0 +1,633 @@
/****************************************************************************
* net/tcp/tcp_send.c
*
* Copyright (C) 2007-2010, 2012, 2015, 2018-2019 Gregory Nutt. All rights
* reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Adapted for NuttX from logic in uIP which also has a BSD-like license:
*
* Original author Adam Dunkels <adam@dunkels.com>
* Copyright () 2001-2003, Adam Dunkels.
* All rights reserved.
*
* 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. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP)
#include <stdint.h>
#include <string.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/tcp.h>
#include "devif/devif.h"
#include "inet/inet.h"
#include "tcp/tcp.h"
#include "utils/utils.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
#define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
#define TCPIPv4BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN])
#define TCPIPv6BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN])
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_header
*
* Description:
* Get the length of the IP header
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* The length of the IP header (IPv4_HDRLEN or IPv6_HDRLEN)
*
****************************************************************************/
static inline FAR struct tcp_hdr_s *tcp_header(FAR struct net_driver_s *dev)
{
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
return TCPIPv6BUF;
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
return TCPIPv4BUF;
}
#endif /* CONFIG_NET_IPv4 */
}
/****************************************************************************
* Name: tcp_sendcomplete, tcp_ipv4_sendcomplete, and tcp_ipv6_sendcomplete
*
* Description:
* Complete the final portions of the send operation. This function sets
* up IP header and computes the TCP checksum
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
static inline void tcp_ipv4_sendcomplete(FAR struct net_driver_s *dev,
FAR struct tcp_hdr_s *tcp,
FAR struct ipv4_hdr_s *ipv4)
{
/* Set up some IP header fields that are needed for TCP checksum
* calculation.
*/
ipv4->proto = IP_PROTO_TCP;
ipv4->ttl = IP_TTL;
ipv4->vhl = 0x45;
/* At this point the TCP header holds the size of the payload, the
* TCP header, and the IP header.
*/
ipv4->len[0] = (dev->d_len >> 8);
ipv4->len[1] = (dev->d_len & 0xff);
/* Calculate TCP checksum. */
tcp->urgp[0] = 0;
tcp->urgp[1] = 0;
tcp->tcpchksum = 0;
tcp->tcpchksum = ~tcp_ipv4_chksum(dev);
/* Finish initializing the IP header and calculate the IP checksum */
ipv4->vhl = 0x45;
ipv4->tos = 0;
ipv4->ipoffset[0] = 0;
ipv4->ipoffset[1] = 0;
++g_ipid;
ipv4->ipid[0] = g_ipid >> 8;
ipv4->ipid[1] = g_ipid & 0xff;
/* Calculate IP checksum. */
ipv4->ipchksum = 0;
ipv4->ipchksum = ~ipv4_chksum(dev);
ninfo("IPv4 length: %d\n", ((int)ipv4->len[0] << 8) + ipv4->len[1]);
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.sent++;
#endif
}
#endif /* CONFIG_NET_IPv4 */
/****************************************************************************
* Name: tcp_ipv6_sendcomplete
*
* Description:
* Complete the final portions of the send operation. This function sets
* up IP header and computes the TCP checksum
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv6
static inline void tcp_ipv6_sendcomplete(FAR struct net_driver_s *dev,
FAR struct tcp_hdr_s *tcp,
FAR struct ipv6_hdr_s *ipv6)
{
uint16_t iplen;
/* Set up some IP header fields that are needed for TCP checksum
* calculation.
*/
ipv6->proto = IP_PROTO_TCP;
ipv6->ttl = IP_TTL;
/* At this point the TCP header holds the size of the payload, the
* TCP header, and the IP header. For IPv6, the IP length field does
* not include the size of IPv6 IP header length.
*/
iplen = dev->d_len - IPv6_HDRLEN;
ipv6->len[0] = (iplen >> 8);
ipv6->len[1] = (iplen & 0xff);
/* Calculate TCP checksum. */
tcp->urgp[0] = 0;
tcp->urgp[1] = 0;
tcp->tcpchksum = 0;
tcp->tcpchksum = ~tcp_ipv6_chksum(dev);
/* Finish initializing the IP header (no IPv6 checksum) */
ipv6->vtc = 0x60;
ipv6->tcf = 0x00;
ipv6->flow = 0x00;
ninfo("IPv6 length: %d\n", ((int)ipv6->len[0] << 8) + ipv6->len[1]);
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv6.sent++;
#endif
}
#endif /* CONFIG_NET_IPv6 */
/****************************************************************************
* Name: tcp_sendcomplete
*
* Description:
* Complete the final portions of the send operation. This function sets
* up IP header and computes the TCP checksum
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
static void tcp_sendcomplete(FAR struct net_driver_s *dev,
FAR struct tcp_hdr_s *tcp)
{
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
tcp_ipv6_sendcomplete(dev, tcp, IPv6BUF);
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
tcp_ipv4_sendcomplete(dev, tcp, IPv4BUF);
}
#endif /* CONFIG_NET_IPv4 */
ninfo("Outgoing TCP packet length: %d bytes\n", dev->d_len);
#ifdef CONFIG_NET_STATISTICS
g_netstats.tcp.sent++;
#endif
}
/****************************************************************************
* Name: tcp_sendcommon
*
* Description:
* We're done with the input processing. We are now ready to send a reply
* Our job is to fill in all the fields of the TCP and IP headers before
* calculating the checksum and finally send the packet.
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP connection structure holding connection information
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
static void tcp_sendcommon(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn,
FAR struct tcp_hdr_s *tcp)
{
/* Copy the IP address into the IPv6 header */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
net_ipv6addr_hdrcopy(ipv6->srcipaddr, dev->d_ipv6addr);
net_ipv6addr_hdrcopy(ipv6->destipaddr, conn->u.ipv6.raddr);
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr);
net_ipv4addr_hdrcopy(ipv4->destipaddr, &conn->u.ipv4.raddr);
}
#endif /* CONFIG_NET_IPv4 */
/* Set TCP sequence numbers and port numbers */
memcpy(tcp->ackno, conn->rcvseq, 4);
memcpy(tcp->seqno, conn->sndseq, 4);
tcp->srcport = conn->lport;
tcp->destport = conn->rport;
/* Set the TCP window */
if (conn->tcpstateflags & TCP_STOPPED)
{
/* If the connection has issued TCP_STOPPED, we advertise a zero
* window so that the remote host will stop sending data.
*/
tcp->wnd[0] = 0;
tcp->wnd[1] = 0;
}
else
{
/* Update the TCP received window based on I/O buffer availability */
uint16_t recvwndo = tcp_get_recvwindow(dev, conn);
/* Set the TCP Window */
tcp->wnd[0] = recvwndo >> 8;
tcp->wnd[1] = recvwndo & 0xff;
/* Update the Receiver Window */
conn->rcv_wnd = recvwndo;
}
/* Finish the IP portion of the message and calculate checksums */
tcp_sendcomplete(dev, tcp);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_send
*
* Description:
* Setup to send a TCP packet
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP connection structure holding connection information
* flags - flags to apply to the TCP header
* len - length of the message (includes the length of the IP and TCP
* headers)
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
void tcp_send(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint16_t flags, uint16_t len)
{
FAR struct tcp_hdr_s *tcp = tcp_header(dev);
tcp->flags = flags;
dev->d_len = len;
tcp->tcpoffset = (TCP_HDRLEN / 4) << 4;
tcp_sendcommon(dev, conn, tcp);
}
/****************************************************************************
* Name: tcp_reset
*
* Description:
* Send a TCP reset (no-data) message
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
void tcp_reset(FAR struct net_driver_s *dev)
{
FAR struct tcp_hdr_s *tcp = tcp_header(dev);
uint32_t ackno;
uint16_t tmp16;
uint16_t acklen = 0;
uint8_t seqbyte;
#ifdef CONFIG_NET_STATISTICS
g_netstats.tcp.rst++;
#endif
/* TCP setup */
if ((tcp->flags & TCP_SYN) != 0 || (tcp->flags & TCP_FIN) != 0)
{
acklen++;
}
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
FAR struct ipv6_hdr_s *ip = IPv6BUF;
acklen += (ip->len[0] << 8 | ip->len[1]);
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
FAR struct ipv4_hdr_s *ip = IPv4BUF;
acklen += (ip->len[0] << 8) + ip->len[1] - (ip->vhl & 0x0f) * 4;
}
#endif /* CONFIG_NET_IPv4 */
acklen -= (tcp->tcpoffset >> 4) << 2;
tcp->flags = TCP_RST | TCP_ACK;
tcp->tcpoffset = 5 << 4;
/* Flip the seqno and ackno fields in the TCP header. */
seqbyte = tcp->seqno[3];
tcp->seqno[3] = tcp->ackno[3];
tcp->ackno[3] = seqbyte;
seqbyte = tcp->seqno[2];
tcp->seqno[2] = tcp->ackno[2];
tcp->ackno[2] = seqbyte;
seqbyte = tcp->seqno[1];
tcp->seqno[1] = tcp->ackno[1];
tcp->ackno[1] = seqbyte;
seqbyte = tcp->seqno[0];
tcp->seqno[0] = tcp->ackno[0];
tcp->ackno[0] = seqbyte;
/* We also have to increase the sequence number we are
* acknowledging. If the least significant byte overflowed, we need
* to propagate the carry to the other bytes as well.
*/
ackno = tcp_addsequence(tcp->ackno, acklen);
tcp_setsequence(tcp->ackno, ackno);
/* Swap port numbers. */
tmp16 = tcp->srcport;
tcp->srcport = tcp->destport;
tcp->destport = tmp16;
/* Set the packet length and swap IP addresses. */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
/* Set the packet length to the size of the IPv6 + TCP headers */
dev->d_len = IPv6TCP_HDRLEN;
/* Swap IPv6 addresses */
net_ipv6addr_hdrcopy(ipv6->destipaddr, ipv6->srcipaddr);
net_ipv6addr_hdrcopy(ipv6->srcipaddr, dev->d_ipv6addr);
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
/* Set the packet length to the size of the IPv4 + TCP headers */
dev->d_len = IPv4TCP_HDRLEN;
/* Swap IPv4 addresses */
net_ipv4addr_hdrcopy(ipv4->destipaddr, ipv4->srcipaddr);
net_ipv4addr_hdrcopy(ipv4->srcipaddr, &dev->d_ipaddr);
}
#endif /* CONFIG_NET_IPv4 */
/* And send out the RST packet */
tcp_sendcomplete(dev, tcp);
}
/****************************************************************************
* Name: tcp_synack
*
* Description:
* Send the SYN, ACK, or SYNACK response.
*
* - SYN and SYNACK are sent only from the TCP state machine.
* - ACK may be sent alone only if delayed ACKs are enabled and the ACK
* delay timeout occurs.
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP connection structure holding connection information
* ack - The ACK response to send
*
* Returned Value:
* None
*
* Assumptions:
* Called with the network locked.
*
****************************************************************************/
void tcp_synack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint8_t ack)
{
struct tcp_hdr_s *tcp;
uint16_t tcp_mss;
/* Get values that vary with the underlying IP domain */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
/* Get the MSS value and offset TCP header address for this packet */
tcp = TCPIPv6BUF;
tcp_mss = TCP_IPv6_MSS(dev);
/* Set the packet length for the TCP Maximum Segment Size */
dev->d_len = IPv6TCP_HDRLEN + TCP_OPT_MSS_LEN;
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
/* Get the MSS value and offset TCP header address for this packet */
tcp = TCPIPv4BUF;
tcp_mss = TCP_IPv4_MSS(dev);
/* Set the packet length for the TCP Maximum Segment Size */
dev->d_len = IPv4TCP_HDRLEN + TCP_OPT_MSS_LEN;
}
#endif /* CONFIG_NET_IPv4 */
/* Save the ACK bits */
tcp->flags = ack;
/* We send out the TCP Maximum Segment Size option with our ACK. */
tcp->optdata[0] = TCP_OPT_MSS;
tcp->optdata[1] = TCP_OPT_MSS_LEN;
tcp->optdata[2] = tcp_mss >> 8;
tcp->optdata[3] = tcp_mss & 0xff;
tcp->tcpoffset = ((TCP_HDRLEN + TCP_OPT_MSS_LEN) / 4) << 4;
/* Complete the common portions of the TCP message */
tcp_sendcommon(dev, conn, tcp);
}
#endif /* CONFIG_NET && CONFIG_NET_TCP */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,761 @@
/****************************************************************************
* net/tcp/tcp_send_unbuffered.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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP) && \
!defined(CONFIG_NET_TCP_WRITE_BUFFERS)
#include <sys/types.h>
#include <sys/socket.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arch/irq.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/arp.h>
#include <nuttx/net/tcp.h>
#include "netdev/netdev.h"
#include "devif/devif.h"
#include "socket/socket.h"
#include "inet/inet.h"
#include "arp/arp.h"
#include "icmpv6/icmpv6.h"
#include "neighbor/neighbor.h"
#include "route/route.h"
#include "tcp/tcp.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* If both IPv4 and IPv6 support are both enabled, then we will need to build
* in some additional domain selection support.
*/
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
# define NEED_IPDOMAIN_SUPPORT 1
#endif
#if defined(CONFIG_NET_TCP_SPLIT) && !defined(CONFIG_NET_TCP_SPLIT_SIZE)
# define CONFIG_NET_TCP_SPLIT_SIZE 40
#endif
#define TCPIPv4BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN])
#define TCPIPv6BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN])
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure holds the state of the send operation until it can be
* operated upon when the TX poll event occurs.
*/
struct send_s
{
FAR struct socket *snd_sock; /* Points to the parent socket structure */
FAR struct devif_callback_s *snd_cb; /* Reference to callback instance */
sem_t snd_sem; /* Used to wake up the waiting thread */
FAR const uint8_t *snd_buffer; /* Points to the buffer of data to send */
size_t snd_buflen; /* Number of bytes in the buffer to send */
ssize_t snd_sent; /* The number of bytes sent */
uint32_t snd_isn; /* Initial sequence number */
uint32_t snd_acked; /* The number of bytes acked */
#if defined(CONFIG_NET_TCP_SPLIT)
bool snd_odd; /* True: Odd packet in pair transaction */
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcpsend_ipselect
*
* Description:
* If both IPv4 and IPv6 support are enabled, then we will need to select
* which one to use when generating the outgoing packet. If only one
* domain is selected, then the setup is already in place and we need do
* nothing.
*
* Input Parameters:
* dev - The structure of the network driver that caused the event
* pstate - sendto state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
#ifdef NEED_IPDOMAIN_SUPPORT
static inline void tcpsend_ipselect(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn)
{
/* Which domain does the socket support */
if (conn->domain == PF_INET)
{
/* Select the IPv4 domain */
tcp_ipv4_select(dev);
}
else /* if (conn->domain == PF_INET6) */
{
/* Select the IPv6 domain */
DEBUGASSERT(conn->domain == PF_INET6);
tcp_ipv6_select(dev);
}
}
#endif
/****************************************************************************
* Name: tcpsend_eventhandler
*
* Description:
* This function is called to perform the actual send operation when
* polled by the lower, device interfacing layer.
*
* Input Parameters:
* dev The structure of the network driver that caused the event
* conn The connection structure associated with the socket
* flags Set of events describing why the callback was invoked
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvconn,
FAR void *pvpriv, uint16_t flags)
{
FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn;
FAR struct send_s *pstate = (FAR struct send_s *)pvpriv;
/* The TCP socket is connected and, hence, should be bound to a device.
* Make sure that the polling device is the one that we are bound to.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev != conn->dev)
{
return flags;
}
ninfo("flags: %04x acked: %" PRId32 " sent: %zd\n",
flags, pstate->snd_acked, pstate->snd_sent);
/* If this packet contains an acknowledgement, then update the count of
* acknowledged bytes.
*/
if ((flags & TCP_ACKDATA) != 0)
{
FAR struct tcp_hdr_s *tcp;
/* Get the offset address of the TCP header */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
DEBUGASSERT(IFF_IS_IPv4(dev->d_flags));
tcp = TCPIPv4BUF;
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
DEBUGASSERT(IFF_IS_IPv6(dev->d_flags));
tcp = TCPIPv6BUF;
}
#endif /* CONFIG_NET_IPv6 */
/* The current acknowledgement number is the (relative) offset of the
* next byte needed by the receiver. The snd_isn is the offset of the
* first byte to send to the receiver. The difference is the number
* of bytes to be acknowledged.
*/
pstate->snd_acked = tcp_getsequence(tcp->ackno) - pstate->snd_isn;
ninfo("ACK: acked=%" PRId32 " sent=%zd buflen=%zd\n",
pstate->snd_acked, pstate->snd_sent, pstate->snd_buflen);
/* Have all of the bytes in the buffer been sent and acknowledged? */
if (pstate->snd_acked >= pstate->snd_buflen)
{
/* Yes. Then pstate->snd_buflen should hold the number of bytes
* actually sent.
*/
goto end_wait;
}
/* No.. fall through to send more data if necessary */
}
/* Check if we are being asked to retransmit data */
else if ((flags & TCP_REXMIT) != 0)
{
/* Yes.. in this case, reset the number of bytes that have been sent
* to the number of bytes that have been ACKed.
*/
pstate->snd_sent = pstate->snd_acked;
#if defined(CONFIG_NET_TCP_SPLIT)
/* Reset the even/odd indicator to even since we need to
* retransmit.
*/
pstate->snd_odd = false;
#endif
/* Fall through to re-send data from the last that was ACKed */
}
/* Check for a loss of connection */
else if ((flags & TCP_DISCONN_EVENTS) != 0)
{
FAR struct socket *psock = pstate->snd_sock;
ninfo("Lost connection\n");
/* We could get here recursively through the callback actions of
* tcp_lost_connection(). So don't repeat that action if we have
* already been disconnected.
*/
DEBUGASSERT(psock != NULL);
if (_SS_ISCONNECTED(psock->s_flags))
{
/* Report not connected */
tcp_lost_connection(psock, pstate->snd_cb, flags);
}
pstate->snd_sent = -ENOTCONN;
goto end_wait;
}
/* Check if the outgoing packet is available (it may have been claimed
* by a sendto event serving a different thread).
*/
#if 0 /* We can't really support multiple senders on the same TCP socket */
else if (dev->d_sndlen > 0)
{
/* Another thread has beat us sending data, wait for the next poll */
return flags;
}
#endif
/* We get here if (1) not all of the data has been ACKed, (2) we have been
* asked to retransmit data, (3) the connection is still healthy, and (4)
* the outgoing packet is available for our use. In this case, we are
* now free to send more data to receiver -- UNLESS the buffer contains
* unprocessed incoming data. In that event, we will have to wait for the
* next polling cycle.
*/
if ((flags & TCP_NEWDATA) == 0 && pstate->snd_sent < pstate->snd_buflen)
{
uint32_t seqno;
/* Get the amount of data that we can send in the next packet */
uint32_t sndlen = pstate->snd_buflen - pstate->snd_sent;
#if defined(CONFIG_NET_TCP_SPLIT)
/* RFC 1122 states that a host may delay ACKing for up to 500ms but
* must respond to every second segment). This logic here will trick
* the RFC 1122 recipient into responding sooner. This logic will be
* activated if:
*
* 1. An even number of packets has been send (where zero is an even
* number),
* 2. There is more data be sent (more than or equal to
* CONFIG_NET_TCP_SPLIT_SIZE), but
* 3. Not enough data for two packets.
*
* Then we will split the remaining, single packet into two partial
* packets. This will stimulate the RFC 1122 peer to ACK sooner.
*
* Don't try to split very small packets (less than
* CONFIG_NET_TCP_SPLIT_SIZE). Only the first even packet and the
* last odd packets could have sndlen less than
* CONFIG_NET_TCP_SPLIT_SIZE. The value of sndlen on the last even
* packet is guaranteed to be at least MSS / 2 by the logic below.
*/
if (sndlen >= CONFIG_NET_TCP_SPLIT_SIZE)
{
/* sndlen is the number of bytes remaining to be sent.
* conn->mss will provide the number of bytes that can sent
* in one packet. The difference, then, is the number of bytes
* that would be sent in the next packet after this one.
*/
int32_t next_sndlen = sndlen - conn->mss;
/* Is this the even packet in the packet pair transaction? */
if (!pstate->snd_odd)
{
/* next_sndlen <= 0 means that the entire remaining data
* could fit into this single packet. This is condition
* in which we must do the split.
*/
if (next_sndlen <= 0)
{
/* Split so that there will be an odd packet. Here
* we know that 0 < sndlen <= MSS
*/
sndlen = (sndlen / 2) + 1;
}
}
/* No... this is the odd packet in the packet pair transaction */
else
{
/* Will there be another (even) packet after this one?
* (next_sndlen > 0) Will the split condition occur on that
* next, even packet? ((next_sndlen - conn->mss) < 0) If
* so, then perform the split now to avoid the case where the
* byte count is less than CONFIG_NET_TCP_SPLIT_SIZE on the
* next pair.
*/
if (next_sndlen > 0 && (next_sndlen - conn->mss) < 0)
{
/* Here, we know that sndlen must be MSS < sndlen <= 2*MSS
* and so (sndlen / 2) is <= MSS.
*/
sndlen /= 2;
}
}
}
/* Toggle the even/odd indicator */
pstate->snd_odd ^= true;
#endif /* CONFIG_NET_TCP_SPLIT */
if (sndlen > conn->mss)
{
sndlen = conn->mss;
}
/* Check if we have "space" in the window */
if ((pstate->snd_sent - pstate->snd_acked + sndlen) < conn->snd_wnd)
{
/* Set the sequence number for this packet. NOTE: The network
* updates sndseq on receipt of ACK *before* this function is
* called. In that case sndseq will point to the next
* unacknowledged byte (which might have already been sent). We
* will overwrite the value of sndseq here before the packet is
* sent.
*/
seqno = pstate->snd_sent + pstate->snd_isn;
ninfo("SEND: sndseq %08" PRIx32 "->%08" PRIx32 "\n",
tcp_getsequence(conn->sndseq), seqno);
tcp_setsequence(conn->sndseq, seqno);
#ifdef NEED_IPDOMAIN_SUPPORT
/* If both IPv4 and IPv6 support are enabled, then we will need to
* select which one to use when generating the outgoing packet.
* If only one domain is selected, then the setup is already in
* place and we need do nothing.
*/
tcpsend_ipselect(dev, conn);
#endif
/* Then set-up to send that amount of data. (this won't actually
* happen until the polling cycle completes).
*/
devif_send(dev, &pstate->snd_buffer[pstate->snd_sent], sndlen);
/* Update the amount of data sent (but not necessarily ACKed) */
pstate->snd_sent += sndlen;
ninfo("SEND: acked=%" PRId32 " sent=%zd buflen=%zd\n",
pstate->snd_acked, pstate->snd_sent, pstate->snd_buflen);
}
}
/* Continue waiting */
return flags;
end_wait:
/* Do not allow any further callbacks */
pstate->snd_cb->flags = 0;
pstate->snd_cb->priv = NULL;
pstate->snd_cb->event = NULL;
/* There are no outstanding, unacknowledged bytes */
conn->tx_unacked = 0;
/* Wake up the waiting thread */
nxsem_post(&pstate->snd_sem);
return flags;
}
/****************************************************************************
* Name: send_txnotify
*
* Description:
* Notify the appropriate device driver that we are have data ready to
* be send (TCP)
*
* Input Parameters:
* psock - Socket state structure
* conn - The TCP connection structure
*
* Returned Value:
* None
*
****************************************************************************/
static inline void send_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 */
netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr);
}
#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);
netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr);
}
#endif /* CONFIG_NET_IPv6 */
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: psock_tcp_send
*
* Description:
* psock_tcp_send() call may be used only when the TCP socket is in a
* connected state (so that the intended recipient is known).
*
* Input Parameters:
* psock An instance of the internal socket structure.
* buf Data to send
* len Length of data to send
* flags Send flags
*
* Returned Value:
* On success, returns the number of characters sent. On error,
* a negated errno value is returned.
*
* EAGAIN or EWOULDBLOCK
* The socket is marked non-blocking and the requested operation
* would block.
* EBADF
* An invalid descriptor was specified.
* ECONNRESET
* Connection reset by peer.
* EDESTADDRREQ
* The socket is not connection-mode, and no peer address is set.
* EFAULT
* An invalid user space address was specified for a parameter.
* EINTR
* A signal occurred before any data was transmitted.
* EINVAL
* Invalid argument passed.
* EISCONN
* The connection-mode socket was connected already but a recipient
* was specified. (Now either this error is returned, or the recipient
* specification is ignored.)
* EMSGSIZE
* The socket type requires that message be sent atomically, and the
* size of the message to be sent made this impossible.
* ENOBUFS
* The output queue for a network interface was full. This generally
* indicates that the interface has stopped sending, but may be
* caused by transient congestion.
* ENOMEM
* No memory available.
* ENOTCONN
* The socket is not connected, and no target has been given.
* ENOTSOCK
* The argument s is not a socket.
* EPIPE
* The local end has been shut down on a connection oriented socket.
* In this case the process will also receive a SIGPIPE unless
* MSG_NOSIGNAL is set.
*
****************************************************************************/
ssize_t psock_tcp_send(FAR struct socket *psock,
FAR const void *buf, size_t len, int flags)
{
FAR struct tcp_conn_s *conn;
struct send_s state;
int ret = OK;
/* Verify that the sockfd corresponds to valid, allocated socket */
if (psock == NULL || psock->s_conn == NULL)
{
nerr("ERROR: Invalid socket\n");
ret = -EBADF;
goto errout;
}
/* If this is an un-connected socket, then return ENOTCONN */
if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags))
{
nerr("ERROR: Not connected\n");
ret = -ENOTCONN;
goto errout;
}
/* Make sure that we have the IP address mapping */
conn = (FAR struct tcp_conn_s *)psock->s_conn;
DEBUGASSERT(conn);
#if defined(CONFIG_NET_ARP_SEND) || defined(CONFIG_NET_ICMPv6_NEIGHBOR)
#ifdef CONFIG_NET_ARP_SEND
#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
if (psock->s_domain == PF_INET)
#endif
{
/* Make sure that the IP address mapping is in the ARP table */
ret = arp_send(conn->u.ipv4.raddr);
}
#endif /* CONFIG_NET_ARP_SEND */
#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
#ifdef CONFIG_NET_ARP_SEND
else
#endif
{
/* Make sure that the IP address mapping is in the Neighbor Table */
ret = icmpv6_neighbor(conn->u.ipv6.raddr);
}
#endif /* CONFIG_NET_ICMPv6_NEIGHBOR */
/* Did we successfully get the address mapping? */
if (ret < 0)
{
nerr("ERROR: Not reachable\n");
ret = -ENETUNREACH;
goto errout;
}
#endif /* CONFIG_NET_ARP_SEND || CONFIG_NET_ICMPv6_NEIGHBOR */
/* Perform the TCP send operation */
/* Initialize the state structure. This is done with the network
* locked because we don't want anything to happen until we are
* ready.
*/
net_lock();
memset(&state, 0, sizeof(struct send_s));
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */
nxsem_set_protocol(&state.snd_sem, SEM_PRIO_NONE);
state.snd_sock = psock; /* Socket descriptor to use */
state.snd_buflen = len; /* Number of bytes to send */
state.snd_buffer = buf; /* Buffer to send from */
if (len > 0)
{
/* Allocate resources to receive a callback */
ret = -ENOMEM; /* Assume allocation failure */
state.snd_cb = tcp_callback_alloc(conn);
if (state.snd_cb)
{
/* Get the initial sequence number that will be used */
state.snd_isn = tcp_getsequence(conn->sndseq);
/* There is no outstanding, unacknowledged data after this
* initial sequence number.
*/
conn->tx_unacked = 0;
/* Set up the callback in the connection */
state.snd_cb->flags = (TCP_ACKDATA | TCP_REXMIT | TCP_POLL |
TCP_DISCONN_EVENTS);
state.snd_cb->priv = (FAR void *)&state;
state.snd_cb->event = tcpsend_eventhandler;
/* Notify the device driver of the availability of TX data */
send_txnotify(psock, conn);
/* Wait for the send to complete or an error to occur: NOTES:
* net_lockedwait will also terminate if a signal is received.
*/
for (; ; )
{
uint32_t acked = state.snd_acked;
ret = net_timedwait(&state.snd_sem,
_SO_TIMEOUT(psock->s_sndtimeo));
if (ret != -ETIMEDOUT || acked == state.snd_acked)
{
break; /* Timeout without any progress */
}
}
/* Make sure that no further events are processed */
tcp_callback_free(conn, state.snd_cb);
}
}
nxsem_destroy(&state.snd_sem);
net_unlock();
/* Check for a errors. Errors are signalled by negative errno values
* for the send length
*/
if (state.snd_sent < 0)
{
ret = state.snd_sent;
goto errout;
}
/* If net_timedwait failed, then we were probably reawakened by a signal.
* In this case, net_timedwait will have returned negated errno
* appropriately.
*/
if (ret < 0)
{
goto errout;
}
/* Return the number of bytes actually sent */
ret = state.snd_sent;
errout:
return ret;
}
/****************************************************************************
* Name: psock_tcp_cansend
*
* Description:
* psock_tcp_cansend() returns a value indicating if a write to the socket
* would block. It is still possible that the write may block if another
* write occurs first.
*
* Input Parameters:
* psock An instance of the internal socket structure.
*
* Returned Value:
* OK (Always can send).
*
* Assumptions:
* None
*
****************************************************************************/
int psock_tcp_cansend(FAR struct socket *psock)
{
return OK;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP && !CONFIG_NET_TCP_WRITE_BUFFERS */
@@ -0,0 +1,597 @@
/****************************************************************************
* net/tcp/tcp_sendfile.c
*
* Copyright (C) 2013 UVC Ingenieure. All rights reserved.
* Copyright (C) 2007-2017 Gregory Nutt. All rights reserved.
* Authors: Gregory Nutt <gnutt@nuttx.org>
* Max Holtzberg <mh@uvc.de>
*
* 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 <nuttx/config.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <debug.h>
#include <arch/irq.h>
#include <nuttx/semaphore.h>
#include <nuttx/fs/fs.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/arp.h>
#include <nuttx/net/tcp.h>
#include "netdev/netdev.h"
#include "devif/devif.h"
#include "arp/arp.h"
#include "icmpv6/icmpv6.h"
#include "neighbor/neighbor.h"
#include "socket/socket.h"
#include "tcp/tcp.h"
#if defined(CONFIG_NET_SENDFILE) && defined(CONFIG_NET_TCP) && \
defined(NET_TCP_HAVE_STACK)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define TCPIPv4BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN])
#define TCPIPv6BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN])
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure holds the state of the send operation until it can be
* operated upon from the driver poll event.
*/
struct sendfile_s
{
FAR struct socket *snd_sock; /* Points to the parent socket structure */
FAR struct devif_callback_s *snd_datacb; /* Data callback */
FAR struct devif_callback_s *snd_ackcb; /* ACK callback */
FAR struct file *snd_file; /* File structure of the input file */
sem_t snd_sem; /* Used to wake up the waiting thread */
off_t snd_foffset; /* Input file offset */
size_t snd_flen; /* File length */
ssize_t snd_sent; /* The number of bytes sent */
uint32_t snd_isn; /* Initial sequence number */
uint32_t snd_acked; /* The number of bytes acked */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static uint16_t ack_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvconn,
FAR void *pvpriv, uint16_t flags)
{
FAR struct sendfile_s *pstate = (FAR struct sendfile_s *)pvpriv;
ninfo("flags: %04x\n", flags);
if ((flags & TCP_ACKDATA) != 0)
{
FAR struct tcp_hdr_s *tcp;
/* Get the offset address of the TCP header */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv6(dev->d_flags))
#endif
{
DEBUGASSERT(pstate->snd_sock->s_domain == PF_INET6);
tcp = TCPIPv6BUF;
}
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
else
#endif
{
DEBUGASSERT(pstate->snd_sock->s_domain == PF_INET);
tcp = TCPIPv4BUF;
}
#endif /* CONFIG_NET_IPv4 */
/* The current acknowledgement number number is the (relative) offset
* of the of the next byte needed by the receiver. The snd_isn is the
* offset of the first byte to send to the receiver. The difference
* is the number of bytes to be acknowledged.
*/
pstate->snd_acked = tcp_getsequence(tcp->ackno) - pstate->snd_isn;
ninfo("ACK: acked=%" PRId32 " sent=%zd flen=%zu\n",
pstate->snd_acked, pstate->snd_sent, pstate->snd_flen);
dev->d_sndlen = 0;
flags &= ~TCP_ACKDATA;
}
else if ((flags & TCP_REXMIT) != 0)
{
nwarn("WARNING: TCP_REXMIT\n");
/* Yes.. in this case, reset the number of bytes that have been sent
* to the number of bytes that have been ACKed.
*/
pstate->snd_sent = pstate->snd_acked;
}
/* Check for a loss of connection */
else if ((flags & TCP_DISCONN_EVENTS) != 0)
{
FAR struct socket *psock = pstate->snd_sock;
nwarn("WARNING: Lost connection\n");
/* We could get here recursively through the callback actions of
* tcp_lost_connection(). So don't repeat that action if we have
* already been disconnected.
*/
DEBUGASSERT(psock != NULL);
if (_SS_ISCONNECTED(psock->s_flags))
{
/* Report not connected */
tcp_lost_connection(psock, pstate->snd_ackcb, flags);
}
/* Report not connected */
pstate->snd_sent = -ENOTCONN;
}
/* Prohibit further callbacks */
pstate->snd_ackcb->flags = 0;
pstate->snd_ackcb->priv = NULL;
pstate->snd_ackcb->event = NULL;
/* Wake up the waiting thread */
nxsem_post(&pstate->snd_sem);
return flags;
}
/****************************************************************************
* Name: sendfile_eventhandler
*
* Description:
* This function is called to perform the actual send operation when
* polled by the lower, device interfacing layer.
*
* Input Parameters:
* dev The structure of the network driver that caused the event
* conn The connection structure associated with the socket
* flags Set of events describing why the callback was invoked
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked
*
****************************************************************************/
static uint16_t sendfile_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv,
uint16_t flags)
{
FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn;
FAR struct sendfile_s *pstate = (FAR struct sendfile_s *)pvpriv;
int ret;
/* Check for a loss of connection */
if ((flags & TCP_DISCONN_EVENTS) != 0)
{
FAR struct socket *psock = pstate->snd_sock;
nwarn("WARNING: Lost connection\n");
/* We could get here recursively through the callback actions of
* tcp_lost_connection(). So don't repeat that action if we have
* already been disconnected.
*/
DEBUGASSERT(psock != NULL);
if (_SS_ISCONNECTED(psock->s_flags))
{
/* Report not connected */
tcp_lost_connection(psock, pstate->snd_datacb, flags);
}
/* Report not connected */
pstate->snd_sent = -ENOTCONN;
goto end_wait;
}
/* The TCP socket is connected and, hence, should be bound to a device.
* Make sure that the polling device is the own that we are bound to.
*/
DEBUGASSERT(conn);
DEBUGASSERT(conn->dev != NULL);
if (dev != conn->dev)
{
return flags;
}
ninfo("flags: %04x acked: %" PRId32 " sent: %zd\n",
flags, pstate->snd_acked, pstate->snd_sent);
/* We get here if (1) not all of the data has been ACKed, (2) we have been
* asked to retransmit data, (3) the connection is still healthy, and (4)
* the outgoing packet is available for our use. In this case, we are
* now free to send more data to receiver -- UNLESS the buffer contains
* unprocessing incoming data. In that event, we will have to wait for the
* next polling cycle.
*/
if ((flags & TCP_NEWDATA) == 0 && pstate->snd_sent < pstate->snd_flen)
{
/* Get the amount of data that we can send in the next packet */
uint32_t sndlen = pstate->snd_flen - pstate->snd_sent;
if (sndlen > conn->mss)
{
sndlen = conn->mss;
}
/* Check if we have "space" in the window */
if ((pstate->snd_sent - pstate->snd_acked + sndlen) < conn->snd_wnd)
{
uint32_t seqno;
/* Then set-up to send that amount of data. (this won't actually
* happen until the polling cycle completes).
*/
ret = file_seek(pstate->snd_file,
pstate->snd_foffset + pstate->snd_sent, SEEK_SET);
if (ret < 0)
{
nerr("ERROR: Failed to lseek: %d\n", ret);
pstate->snd_sent = ret;
goto end_wait;
}
ret = file_read(pstate->snd_file, dev->d_appdata, sndlen);
if (ret < 0)
{
nerr("ERROR: Failed to read from input file: %d\n", (int)ret);
pstate->snd_sent = ret;
goto end_wait;
}
dev->d_sndlen = sndlen;
/* Set the sequence number for this packet. NOTE: The network
* updates sndseq on recept of ACK *before* this function is
* called. In that case sndseq will point to the next
* unacknowledge byte (which might have already been sent). We
* will overwrite the value of sndseq here before the packet is
* sent.
*/
seqno = pstate->snd_sent + pstate->snd_isn;
ninfo("SEND: sndseq %08" PRIx32 "->%08" PRIx32 " len: %d\n",
tcp_getsequence(conn->sndseq), seqno, ret);
tcp_setsequence(conn->sndseq, seqno);
/* Update the amount of data sent (but not necessarily ACKed) */
pstate->snd_sent += sndlen;
ninfo("pid: %d SEND: acked=%" PRId32 " sent=%zd flen=%zu\n",
getpid(),
pstate->snd_acked, pstate->snd_sent, pstate->snd_flen);
}
else
{
nwarn("WARNING: Window full, wait for ack\n");
goto wait;
}
}
if (pstate->snd_sent >= pstate->snd_flen
&& pstate->snd_acked < pstate->snd_flen)
{
/* All data has been sent, but there are outstanding ACK's */
goto wait;
}
end_wait:
/* Do not allow any further callbacks */
pstate->snd_datacb->flags = 0;
pstate->snd_datacb->priv = NULL;
pstate->snd_datacb->event = NULL;
/* Wake up the waiting thread */
nxsem_post(&pstate->snd_sem);
wait:
return flags;
}
/****************************************************************************
* Name: sendfile_txnotify
*
* Description:
* Notify the appropriate device driver that we are have data ready to
* be send (TCP)
*
* Input Parameters:
* psock - Socket state structure
* conn - The TCP connection structure
*
* Returned Value:
* None
*
****************************************************************************/
static inline void sendfile_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 */
netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr);
}
#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);
netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr);
}
#endif /* CONFIG_NET_IPv6 */
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_sendfile
*
* Description:
* The tcp_sendfile() call may be used only when the INET socket is in a
* connected state (so that the intended recipient is known).
*
* Input Parameters:
* psock An instance of the internal socket structure.
* buf Data to send
* len Length of data to send
* flags Send flags
*
* Returned Value:
* On success, returns the number of characters sent. On error,
* a negated errno value is returned. See sendfile() for a list
* appropriate error return values.
*
****************************************************************************/
ssize_t tcp_sendfile(FAR struct socket *psock, FAR struct file *infile,
FAR off_t *offset, size_t count)
{
FAR struct tcp_conn_s *conn;
struct sendfile_s state;
int ret;
/* If this is an un-connected socket, then return ENOTCONN */
if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags))
{
nerr("ERROR: Not connected\n");
return -ENOTCONN;
}
/* Make sure that we have the IP address mapping */
conn = (FAR struct tcp_conn_s *)psock->s_conn;
DEBUGASSERT(conn != NULL);
#if defined(CONFIG_NET_ARP_SEND) || defined(CONFIG_NET_ICMPv6_NEIGHBOR)
#ifdef CONFIG_NET_ARP_SEND
#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
if (psock->s_domain == PF_INET)
#endif
{
/* Make sure that the IP address mapping is in the ARP table */
ret = arp_send(conn->u.ipv4.raddr);
}
#endif /* CONFIG_NET_ARP_SEND */
#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
#ifdef CONFIG_NET_ARP_SEND
else
#endif
{
/* Make sure that the IP address mapping is in the Neighbor Table */
ret = icmpv6_neighbor(conn->u.ipv6.raddr);
}
#endif /* CONFIG_NET_ICMPv6_NEIGHBOR */
/* Did we successfully get the address mapping? */
if (ret < 0)
{
nerr("ERROR: Not reachable\n");
return -ENETUNREACH;
}
#endif /* CONFIG_NET_ARP_SEND || CONFIG_NET_ICMPv6_NEIGHBOR */
/* Initialize the state structure. This is done with the network
* locked because we don't want anything to happen until we are
* ready.
*/
net_lock();
memset(&state, 0, sizeof(struct sendfile_s));
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */
nxsem_set_protocol(&state.snd_sem, SEM_PRIO_NONE);
state.snd_sock = psock; /* Socket descriptor to use */
state.snd_foffset = offset ? *offset : 0; /* Input file offset */
state.snd_flen = count; /* Number of bytes to send */
state.snd_file = infile; /* File to read from */
/* Allocate resources to receive a callback */
state.snd_datacb = tcp_callback_alloc(conn);
if (state.snd_datacb == NULL)
{
nerr("ERROR: Failed to allocate data callback\n");
ret = -ENOMEM;
goto errout_locked;
}
state.snd_ackcb = tcp_callback_alloc(conn);
if (state.snd_ackcb == NULL)
{
nerr("ERROR: Failed to allocate ack callback\n");
ret = -ENOMEM;
goto errout_datacb;
}
/* Get the initial sequence number that will be used */
state.snd_isn = tcp_getsequence(conn->sndseq);
/* There is no outstanding, unacknowledged data after this
* initial sequence number.
*/
conn->tx_unacked = 0;
/* Set up the ACK callback in the connection */
state.snd_ackcb->flags = (TCP_ACKDATA | TCP_REXMIT | TCP_DISCONN_EVENTS);
state.snd_ackcb->priv = (FAR void *)&state;
state.snd_ackcb->event = ack_eventhandler;
/* Perform the TCP send operation */
state.snd_datacb->flags = TCP_POLL;
state.snd_datacb->priv = (FAR void *)&state;
state.snd_datacb->event = sendfile_eventhandler;
/* Notify the device driver of the availability of TX data */
sendfile_txnotify(psock, conn);
for (; ; )
{
uint32_t acked = state.snd_acked;
ret = net_timedwait_uninterruptible(&state.snd_sem,
_SO_TIMEOUT(psock->s_sndtimeo));
if (ret != -ETIMEDOUT || acked == state.snd_acked)
{
break; /* Timeout without any progress */
}
}
tcp_callback_free(conn, state.snd_ackcb);
errout_datacb:
tcp_callback_free(conn, state.snd_datacb);
errout_locked:
nxsem_destroy(&state.snd_sem);
net_unlock();
if (ret < 0)
{
return ret;
}
else
{
return state.snd_sent;
}
}
#endif /* CONFIG_NET_SENDFILE && CONFIG_NET_TCP && NET_TCP_HAVE_STACK */
+164
View File
@@ -0,0 +1,164 @@
/****************************************************************************
* net/tcp/tcp_seqno.c
*
* Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Large parts of this file were leveraged from uIP logic:
*
* Copyright (c) 2001-2003, Adam Dunkels.
* All rights reserved.
*
* 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. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP)
#include <stdint.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include "devif/devif.h"
/****************************************************************************
* Private Data
****************************************************************************/
/* g_tcpsequence is used to generate initial TCP sequence numbers */
static uint32_t g_tcpsequence;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_setsequence
*
* Description:
* Set the TCP/IP sequence number
*
* Assumptions:
* This function must be called with the network locked if seqno refers
* to a shared, global resource.
*
****************************************************************************/
void tcp_setsequence(FAR uint8_t *seqno, uint32_t value)
{
/* Copy the sequence number in network (big-endian) order */
*seqno++ = value >> 24;
*seqno++ = (value >> 16) & 0xff;
*seqno++ = (value >> 8) & 0xff;
*seqno = value & 0xff;
}
/****************************************************************************
* Name: tcp_getsequence
*
* Description:
* Get the TCP/IP sequence number
*
* Assumptions:
* This function must be called with the network locked if seqno refers
* to a shared, global resource.
*
****************************************************************************/
uint32_t tcp_getsequence(FAR uint8_t *seqno)
{
uint32_t value;
/* Combine the sequence number from network (big-endian) order */
value = (uint32_t)seqno[0] << 24 |
(uint32_t)seqno[1] << 16 |
(uint32_t)seqno[2] << 8 |
(uint32_t)seqno[3];
return value;
}
/****************************************************************************
* Name: tcp_addsequence
*
* Description:
* Add the length to get the next TCP sequence number.
*
* Assumptions:
* This function must be called with the network locked if seqno refers
* to a shared, global resource.
*
****************************************************************************/
uint32_t tcp_addsequence(FAR uint8_t *seqno, uint16_t len)
{
return tcp_getsequence(seqno) + (uint32_t)len;
}
/****************************************************************************
* Name: tcp_initsequence
*
* Description:
* Set the (initial) the TCP/IP sequence number when a TCP connection is
* established.
*
* Assumptions:
* This function must be called with the network locked if seqno refers
* to a shared, global resource.
*
****************************************************************************/
void tcp_initsequence(FAR uint8_t *seqno)
{
tcp_setsequence(seqno, g_tcpsequence);
}
/****************************************************************************
* Name: tcp_nextsequence
*
* Description:
* Increment the TCP/IP sequence number
*
* Assumptions:
* This function must be called with the network locked.
*
****************************************************************************/
void tcp_nextsequence(void)
{
g_tcpsequence++;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP */
@@ -0,0 +1,251 @@
/****************************************************************************
* net/tcp/tcp_setsockopt.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 <nuttx/config.h>
#include <sys/time.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <netinet/tcp.h>
#include <nuttx/net/net.h>
#include <nuttx/net/tcp.h>
#include "socket/socket.h"
#include "utils/utils.h"
#include "tcp/tcp.h"
#ifdef CONFIG_NET_TCPPROTO_OPTIONS
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_setsockopt
*
* Description:
* tcp_setsockopt() sets the TCP-protocol option specified by the
* 'option' argument to the value pointed to by the 'value' argument for
* the socket specified by the 'psock' argument.
*
* See <netinet/tcp.h> for the a complete list of values of TCP protocol
* options.
*
* Input Parameters:
* psock Socket structure of socket to operate on
* option identifies the option to set
* value Points to the argument value
* value_len The length of the argument value
*
* Returned Value:
* Returns zero (OK) on success. On failure, it returns a negated errno
* value to indicate the nature of the error. See psock_setcockopt() for
* the list of possible error values.
*
****************************************************************************/
int tcp_setsockopt(FAR struct socket *psock, int option,
FAR const void *value, socklen_t value_len)
{
#ifdef CONFIG_NET_TCP_KEEPALIVE
/* Keep alive options are the only TCP protocol socket option currently
* supported.
*/
FAR struct tcp_conn_s *conn;
int ret;
DEBUGASSERT(psock != NULL && value != NULL && psock->s_conn != NULL);
conn = (FAR struct tcp_conn_s *)psock->s_conn;
/* All of the TCP protocol options apply only TCP sockets. The sockets
* do not have to be connected.. that might occur later with the KeepAlive
* already configured.
*/
if (psock->s_type != SOCK_STREAM)
{
nerr("ERROR: Not a TCP socket\n");
return -ENOTCONN;
}
/* Handle the Keep-Alive option */
switch (option)
{
/* Handle the SO_KEEPALIVE socket-level option.
*
* 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 */
if (value_len != sizeof(int))
{
ret = -EDOM;
}
else
{
int keepalive = *(FAR int *)value;
if (keepalive != 0 && keepalive != 1)
{
nerr("ERROR: SO_KEEPALIVE value out of range: %d\n",
keepalive);
return -EDOM;
}
else
{
conn->keepalive = (bool)keepalive;
conn->keeptime = clock_systime_ticks(); /* Reset start time */
ret = OK;
}
}
break;
case TCP_NODELAY: /* Avoid coalescing of small segments. */
nerr("ERROR: TCP_NODELAY not supported\n");
ret = -ENOSYS;
break;
case TCP_KEEPIDLE: /* Start keepalives after this IDLE period */
if (value_len != sizeof(struct timeval))
{
ret = -EDOM;
}
else
{
FAR struct timeval *tv = (FAR struct timeval *)value;
if (tv == NULL)
{
ret = -EINVAL;
}
else
{
unsigned int dsecs;
/* Get the IDLE time value. Any microsecond remainder will
* be forced to the next larger, whole decisecond value.
*/
dsecs = (socktimeo_t)net_timeval2dsec(tv, TV2DS_CEIL);
if (dsecs > UINT16_MAX)
{
nwarn("WARNING: TCP_KEEPIDLE value out of range: %u\n",
dsecs);
ret = -EDOM;
}
else
{
conn->keepidle = (uint16_t)dsecs;
conn->keeptime = clock_systime_ticks(); /* Reset start time */
ret = OK;
}
}
}
break;
case TCP_KEEPINTVL: /* Interval between keepalives */
if (value_len != sizeof(struct timeval))
{
ret = -EDOM;
}
else
{
FAR struct timeval *tv = (FAR struct timeval *)value;
if (tv == NULL)
{
ret = -EINVAL;
}
else
{
unsigned int dsecs;
/* Get the IDLE time value. Any microsecond remainder will
* be forced to the next larger, whole decisecond value.
*/
dsecs = (socktimeo_t)net_timeval2dsec(tv, TV2DS_CEIL);
if (dsecs > UINT16_MAX)
{
nwarn("WARNING: TCP_KEEPINTVL value out of range: %u\n",
dsecs);
ret = -EDOM;
}
else
{
conn->keepintvl = (uint16_t)dsecs;
conn->keeptime = clock_systime_ticks(); /* Reset start time */
ret = OK;
}
}
}
break;
case TCP_KEEPCNT: /* Number of keepalives before death */
if (value_len != sizeof(int))
{
ret = -EDOM;
}
else
{
int keepcnt = *(FAR int *)value;
if (keepcnt < 0 || keepcnt > UINT8_MAX)
{
nerr("ERROR: TCP_KEEPCNT value out of range: %d\n", keepcnt);
return -EDOM;
}
else
{
conn->keepcnt = (uint8_t)keepcnt;
conn->keeptime = clock_systime_ticks(); /* Reset start time */
ret = OK;
}
}
break;
default:
nerr("ERROR: Unrecognized TCP option: %d\n", option);
ret = -ENOPROTOOPT;
break;
}
return ret;
#else
return -ENOPROTOOPT;
#endif /* CONFIG_NET_TCP_KEEPALIVE */
}
#endif /* CONFIG_NET_TCPPROTO_OPTIONS */
+534
View File
@@ -0,0 +1,534 @@
/****************************************************************************
* net/tcp/tcp_timer.c
* Poll for the availability of TCP TX data
*
* Copyright (C) 2007-2010, 2015-2016, 2018, 2020 Gregory Nutt. All rights
* reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Adapted for NuttX from logic in uIP which also has a BSD-like license:
*
* Original author Adam Dunkels <adam@dunkels.com>
* Copyright () 2001-2003, Adam Dunkels.
* All rights reserved.
*
* 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. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP)
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/tcp.h>
#include "devif/devif.h"
#include "socket/socket.h"
#include "tcp/tcp.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Per RFC 1122: "... an ACK should not be excessively delayed; in
* particular, the delay MUST be less than 0.5 seconds ..."
*
* NOTE: We only have 0.5 timing resolution here so the delay will be
* between 0.5 and 1.0 seconds, and may be delayed further, depending on the
* polling rate of the the driver (often 1 second).
*/
#define ACK_DELAY (1)
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_timer
*
* Description:
* Handle a TCP timer expiration for the provided TCP connection
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP "connection" to poll for TX data
* hsec - The polling interval in units of halves of a second
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
int hsec)
{
uint16_t result;
uint8_t hdrlen;
/* Set up for the callback. We can't know in advance if the application
* is going to send a IPv4 or an IPv6 packet, so this setup may not
* actually be used. Furthermore, the TCP logic is required to call
* tcp_ipv4_select() or tcp_ipv6_select() prior to sending any packets.
* We will try to set the correct value here basic on the binding of
* the connection.
*/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
hdrlen = IPv4TCP_HDRLEN;
tcp_ipv4_select(dev);
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
hdrlen = IPv6TCP_HDRLEN;
tcp_ipv6_select(dev);
}
#endif /* CONFIG_NET_IPv6 */
/* Increase the TCP sequence number */
tcp_nextsequence();
/* Reset the length variables. */
dev->d_len = 0;
dev->d_sndlen = 0;
/* Check if the connection is in a state in which we simply wait
* for the connection to time out. If so, we increase the
* connection's timer and remove the connection if it times
* out.
*/
if (conn->tcpstateflags == TCP_TIME_WAIT ||
conn->tcpstateflags == TCP_FIN_WAIT_2)
{
unsigned int newtimer;
/* Increment the connection timer */
newtimer = (unsigned int)conn->timer + hsec;
/* Check if the timer exceeds the timeout value */
if (newtimer >= (TCP_TIME_WAIT_TIMEOUT * HSEC_PER_SEC))
{
/* Set the timer to the maximum value */
conn->timer = TCP_TIME_WAIT_TIMEOUT * HSEC_PER_SEC;
/* The TCP connection was established and, hence, should be bound
* to a device. Make sure that the polling device is the one that
* we are bound to.
*
* If not, then we will catch the timeout on the next poll from
* the correct device.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev != conn->dev)
{
ninfo("TCP: TCP_CLOSED pending\n");
}
else
{
conn->tcpstateflags = TCP_CLOSED;
/* Notify upper layers about the timeout */
tcp_callback(dev, conn, TCP_TIMEDOUT);
ninfo("TCP state: TCP_CLOSED\n");
}
}
else
{
/* No timeout. Just update the incremented timer */
conn->timer = newtimer;
}
}
else if (conn->tcpstateflags != TCP_CLOSED)
{
/* If the connection has outstanding data, we increase the connection's
* timer and see if it has reached the RTO value in which case we
* retransmit.
*/
if (conn->tx_unacked > 0)
{
/* The connection has outstanding data */
if (conn->timer > hsec)
{
/* Will not yet decrement to zero */
conn->timer -= hsec;
}
else
{
/* Will decrement to zero */
conn->timer = 0;
/* The TCP is connected and, hence, should be bound to a
* device. Make sure that the polling device is the one that
* we are bound to.
*
* If not, then we will catch the timeout on the next poll
* from the correct device.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev != conn->dev)
{
ninfo("TCP: TCP_CLOSED pending\n");
goto done;
}
/* Check for a timeout on connection in the TCP_SYN_RCVD state.
* On such timeouts, we would normally resend the SYNACK until
* the ACK is received, completing the 3-way handshake. But if
* the retry count elapsed, then we must assume that no ACK is
* forthcoming and terminate the attempted connection.
*/
if (conn->tcpstateflags == TCP_SYN_RCVD &&
conn->nrtx >= TCP_MAXSYNRTX)
{
FAR struct tcp_conn_s *listener;
conn->tcpstateflags = TCP_CLOSED;
ninfo("TCP state: TCP_SYN_RCVD->TCP_CLOSED\n");
/* Find the listener for this connection. */
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
listener = tcp_findlistener(conn->lport, conn->domain);
#else
listener = tcp_findlistener(conn->lport);
#endif
if (listener != NULL)
{
/* We call tcp_callback() for the connection with
* TCP_TIMEDOUT to inform the listener that the
* connection has timed out.
*/
tcp_callback(dev, listener, TCP_TIMEDOUT);
}
/* We also send a reset packet to the remote host. */
tcp_send(dev, conn, TCP_RST | TCP_ACK, hdrlen);
/* Finally, we must free this TCP connection structure */
conn->crefs = 0;
tcp_free(conn);
goto done;
}
/* Otherwise, check for a timeout on an established connection.
* If the retry count is exceeded in this case, we should
* close the connection.
*/
else if (
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
conn->expired > 0 ||
#else
conn->nrtx >= TCP_MAXRTX ||
#endif
(conn->tcpstateflags == TCP_SYN_SENT &&
conn->nrtx >= TCP_MAXSYNRTX)
)
{
conn->tcpstateflags = TCP_CLOSED;
ninfo("TCP state: TCP_CLOSED\n");
/* We call tcp_callback() with TCP_TIMEDOUT to
* inform the application that the connection has
* timed out.
*/
tcp_callback(dev, conn, TCP_TIMEDOUT);
/* We also send a reset packet to the remote host. */
tcp_send(dev, conn, TCP_RST | TCP_ACK, hdrlen);
goto done;
}
/* Exponential backoff. */
conn->timer = TCP_RTO << (conn->nrtx > 4 ? 4: conn->nrtx);
(conn->nrtx)++;
/* Ok, so we need to retransmit. We do this differently
* depending on which state we are in. In ESTABLISHED, we
* call upon the application so that it may prepare the
* data for the retransmit. In SYN_RCVD, we resend the
* SYNACK that we sent earlier and in LAST_ACK we have to
* retransmit our FINACK.
*/
#ifdef CONFIG_NET_STATISTICS
g_netstats.tcp.rexmit++;
#endif
switch (conn->tcpstateflags & TCP_STATE_MASK)
{
case TCP_SYN_RCVD:
/* In the SYN_RCVD state, we should retransmit our
* SYNACK.
*/
tcp_synack(dev, conn, TCP_ACK | TCP_SYN);
goto done;
case TCP_SYN_SENT:
/* In the SYN_SENT state, we retransmit out SYN. */
tcp_synack(dev, conn, TCP_SYN);
goto done;
case TCP_ESTABLISHED:
/* In the ESTABLISHED state, we call upon the application
* to do the actual retransmit after which we jump into
* the code for sending out the packet.
*/
result = tcp_callback(dev, conn, TCP_REXMIT);
tcp_rexmit(dev, conn, result);
goto done;
case TCP_FIN_WAIT_1:
case TCP_CLOSING:
case TCP_LAST_ACK:
/* In all these states we should retransmit a FINACK. */
tcp_send(dev, conn, TCP_FIN | TCP_ACK, hdrlen);
goto done;
}
}
}
/* The connection does not have outstanding data. Check if the TCP
* connection has been established.
*/
else if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
{
/* The TCP connection is established and, hence, should be bound
* to a device. Make sure that the polling device is the one that
* we are bound to.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev == conn->dev)
{
#ifdef CONFIG_NET_TCP_KEEPALIVE
/* Is this an established connected with KeepAlive enabled? */
if (conn->keepalive)
{
socktimeo_t timeo;
uint32_t saveseq;
/* If this is the first probe, then the keepstart time is
* the time that the last ACK or data was received from the
* remote.
*
* On subsequent retries, keepstart is the time that the
* last probe was sent.
*/
if (conn->keepretries > 0)
{
timeo = (socktimeo_t)conn->keepintvl;
}
else
{
timeo = (socktimeo_t)conn->keepidle;
}
/* Yes... has the idle period elapsed with no data or ACK
* received from the remote peer?
*/
if (net_timeo(conn->keeptime, timeo))
{
/* Yes.. Has the retry count expired? */
if (conn->keepretries >= conn->keepcnt)
{
/* Yes... stop the network monitor, closing the
* connection and all sockets associated with the
* connection.
*/
tcp_stop_monitor(conn, TCP_ABORT);
}
else
{
unsigned int tcpiplen;
/* No.. we need to send another probe.
* Get the size of the IP and TCP header.
*/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
tcpiplen = IPv4_HDRLEN + TCP_HDRLEN;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
tcpiplen = IPv6_HDRLEN + TCP_HDRLEN;
}
#endif
/* And send the probe.
* The packet we send must have these properties:
*
* - TCP_ACK flag (only) is set.
* - Sequence number is the sequence number of
* previously ACKed data, i.e., the expected
* sequence number minus one.
*
* tcp_send() will send the TCP sequence number as
* conn->sndseq. Rather than creating a new
* interface, we spoof tcp_end() here:
*/
saveseq = tcp_getsequence(conn->sndseq);
tcp_setsequence(conn->sndseq, saveseq - 1);
tcp_send(dev, conn, TCP_ACK, tcpiplen);
tcp_setsequence(conn->sndseq, saveseq);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Increment the un-ACKed sequence number */
conn->sndseq_max++;
#endif
/* Update for the next probe */
conn->keeptime = clock_systime_ticks();
conn->keepretries++;
}
goto done;
}
}
#endif
#ifdef CONFIG_NET_TCP_DELAYED_ACK
/* Handle delayed acknowledgments. Is there a segment with a
* delayed acknowledgment?
*/
if (conn->rx_unackseg > 0)
{
/* Increment the ACK delay. */
conn->rx_acktimer += hsec;
/* Per RFC 1122: "...an ACK should not be excessively
* delayed; in particular, the delay must be less than
* 0.5 seconds..."
*/
if (conn->rx_acktimer >= ACK_DELAY)
{
/* Reset the delayed ACK state and send the ACK
* packet.
*/
conn->rx_unackseg = 0;
conn->rx_acktimer = 0;
tcp_synack(dev, conn, TCP_ACK);
goto done;
}
}
#endif
/* There was no need for a retransmission and there was no
* need to probe the remote peer and there was no need to
* send a delayed ACK. We poll the application for new
* outgoing data.
*/
result = tcp_callback(dev, conn, TCP_POLL);
tcp_appsend(dev, conn, result);
goto done;
}
}
}
/* Nothing to be done */
dev->d_len = 0;
done:
return;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP */
@@ -0,0 +1,163 @@
/****************************************************************************
* net/tcp/tcp_txdrain.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 <nuttx/config.h>
#include <sys/types.h>
#include <sched.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/semaphore.h>
#include "tcp/tcp.h"
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_NET_TCP_NOTIFIER)
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: txdrain_worker
*
* Description:
* Called with the write buffers have all been sent.
*
* Input Parameters:
* arg - The notifier entry.
*
* Returned Value:
* None.
*
****************************************************************************/
static void txdrain_worker(FAR void *arg)
{
FAR sem_t *waitsem = (FAR sem_t *)arg;
DEBUGASSERT(waitsem != NULL);
/* Then just post the semaphore, waking up tcp_txdrain() */
nxsem_post(waitsem);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_txdrain
*
* Description:
* Wait for all write buffers to be sent (or for a timeout to occur).
*
* Input Parameters:
* psock - An instance of the internal socket structure.
* timeout - The relative time when the timeout will occur
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on any failure.
*
****************************************************************************/
int tcp_txdrain(FAR struct socket *psock, unsigned int timeout)
{
FAR struct tcp_conn_s *conn;
sem_t waitsem;
int ret;
DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
DEBUGASSERT(psock->s_type == SOCK_STREAM);
conn = (FAR struct tcp_conn_s *)psock->s_conn;
/* Initialize the wait semaphore */
nxsem_init(&waitsem, 0, 0);
nxsem_set_protocol(&waitsem, SEM_PRIO_NONE);
/* The following needs to be done with the network stable */
net_lock();
/* Get a notification when the write buffers are drained */
ret = tcp_writebuffer_notifier_setup(txdrain_worker, conn, &waitsem);
/* The special return value of 0 means that there is no Tx data to be
* drained. Otherwise it is a special 'key' that can be used to teardown
* the notification later
*/
if (ret > 0)
{
/* Save the drain key */
int drain_key = ret;
/* Also get a notification if we lose the connection.
* REVISIT: This really should not be necessary. tcp_send() should
* detect the disconnection event and signal the txdrain but I do not
* see that TCP disconnection event. This assures that the txdrain
* operation will not wait for the full timeout in that case.
*/
ret = tcp_disconnect_notifier_setup(txdrain_worker, conn, &waitsem);
/* Zero is a special value that means that no connection has been
* established. Otherwise it is a special 'key' that can be used
* to teardown the notification later
*/
if (ret > 0)
{
/* Save the disconnect key */
int disconn_key = ret;
/* There is pending write data and the socket is connected..
* wait for it to drain or be be disconnected.
*/
ret = net_timedwait_uninterruptible(&waitsem, timeout);
/* Tear down the disconnect notifier */
tcp_notifier_teardown(disconn_key);
}
/* Tear down the disconnect notifier */
tcp_notifier_teardown(drain_key);
}
net_unlock();
nxsem_destroy(&waitsem);
return ret;
}
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS && CONFIG_NET_TCP_NOTIFIER */
@@ -0,0 +1,298 @@
/****************************************************************************
* net/tcp/tcp_wrbuffer.c
*
* Copyright (C) 2007-2009, 2013-2014, 2018 Gregory Nutt. All rights
* reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
* Jason Jiang <jasonj@live.cn>
*
* 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 <nuttx/net/netconfig.h>
#if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_NET_TCP_WRBUFFER_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET
# define CONFIG_DEBUG_NET 1
#endif
#include <queue.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include <nuttx/mm/iob.h>
#include "utils/utils.h"
#include "tcp/tcp.h"
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_WRITE_BUFFERS)
/****************************************************************************
* Private Types
****************************************************************************/
/* Package all globals used by this logic into a structure */
struct wrbuffer_s
{
/* The semaphore to protect the buffers */
sem_t sem;
/* This is the list of available write buffers */
sq_queue_t freebuffers;
/* These are the pre-allocated write buffers */
struct tcp_wrbuffer_s buffers[CONFIG_NET_TCP_NWRBCHAINS];
};
/****************************************************************************
* Private Data
****************************************************************************/
/* This is the state of the global write buffer resource */
static struct wrbuffer_s g_wrbuffer;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_wrbuffer_initialize
*
* Description:
* Initialize the list of free write buffers
*
* Assumptions:
* Called once early initialization.
*
****************************************************************************/
void tcp_wrbuffer_initialize(void)
{
int i;
sq_init(&g_wrbuffer.freebuffers);
for (i = 0; i < CONFIG_NET_TCP_NWRBCHAINS; i++)
{
sq_addfirst(&g_wrbuffer.buffers[i].wb_node, &g_wrbuffer.freebuffers);
}
nxsem_init(&g_wrbuffer.sem, 0, CONFIG_NET_TCP_NWRBCHAINS);
nxsem_set_protocol(&g_wrbuffer.sem, SEM_PRIO_NONE);
}
/****************************************************************************
* Name: tcp_wrbuffer_alloc
*
* Description:
* Allocate a TCP write buffer by taking a pre-allocated buffer from
* the free list. This function is called from TCP logic when a buffer
* of TCP data is about to sent
*
* Input Parameters:
* None
*
* Assumptions:
* Called from user logic with the network locked.
*
****************************************************************************/
FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void)
{
FAR struct tcp_wrbuffer_s *wrb;
/* We need to allocate two things: (1) A write buffer structure and (2)
* at least one I/O buffer to start the chain.
*
* Allocate the write buffer structure first then the IOB. In order to
* avoid deadlocks, we will need to free the IOB first, then the write
* buffer
*/
net_lockedwait_uninterruptible(&g_wrbuffer.sem);
/* Now, we are guaranteed to have a write buffer structure reserved
* for us in the free list.
*/
wrb = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers);
DEBUGASSERT(wrb);
memset(wrb, 0, sizeof(struct tcp_wrbuffer_s));
/* Now get the first I/O buffer for the write buffer structure */
wrb->wb_iob = net_ioballoc(false, IOBUSER_NET_TCP_WRITEBUFFER);
/* Did we get an IOB? We should always get one except under some really
* weird error conditions.
*/
if (wrb->wb_iob == NULL)
{
nerr("ERROR: Failed to allocate I/O buffer\n");
tcp_wrbuffer_release(wrb);
return NULL;
}
return wrb;
}
/****************************************************************************
* Name: tcp_wrbuffer_tryalloc
*
* Description:
* Try to allocate a TCP write buffer by taking a pre-allocated buffer from
* the free list. This function is called from TCP logic when a buffer
* of TCP data is about to be sent on a non-blocking socket. Returns
* immediately if the allocation failed.
*
* Input parameters:
* None
*
* Assumptions:
* Called from user logic with the network locked. Will return if no buffer
* is available.
*
****************************************************************************/
FAR struct tcp_wrbuffer_s *tcp_wrbuffer_tryalloc(void)
{
FAR struct tcp_wrbuffer_s *wrb;
/* We need to allocate two things: (1) A write buffer structure and (2)
* at least one I/O buffer to start the chain.
*
* Allocate the write buffer structure first then the IOBG. In order to
* avoid deadlocks, we will need to free the IOB first, then the write
* buffer
*/
if (nxsem_trywait(&g_wrbuffer.sem) != OK)
{
return NULL;
}
/* Now, we are guaranteed to have a write buffer structure reserved
* for us in the free list.
*/
wrb = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers);
DEBUGASSERT(wrb);
memset(wrb, 0, sizeof(struct tcp_wrbuffer_s));
/* Now get the first I/O buffer for the write buffer structure */
wrb->wb_iob = iob_tryalloc(false, IOBUSER_NET_TCP_WRITEBUFFER);
if (!wrb->wb_iob)
{
nerr("ERROR: Failed to allocate I/O buffer\n");
tcp_wrbuffer_release(wrb);
return NULL;
}
return wrb;
}
/****************************************************************************
* Name: tcp_wrbuffer_release
*
* Description:
* Release a TCP write buffer by returning the buffer to the free list.
* This function is called from user logic after it is consumed the
* buffered data.
*
* Assumptions:
* This function must be called with the network locked.
*
****************************************************************************/
void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb)
{
DEBUGASSERT(wrb != NULL);
/* To avoid deadlocks, we must following this ordering: Release the I/O
* buffer chain first, then the write buffer structure.
*/
if (wrb->wb_iob != NULL)
{
iob_free_chain(wrb->wb_iob, IOBUSER_NET_TCP_WRITEBUFFER);
}
/* Reset the ack counter */
TCP_WBNACK(wrb) = 0;
/* Then free the write buffer structure */
sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers);
nxsem_post(&g_wrbuffer.sem);
}
/****************************************************************************
* Name: tcp_wrbuffer_test
*
* Description:
* Check if there is room in the write buffer. Does not reserve any space.
*
* Assumptions:
* None.
*
****************************************************************************/
int tcp_wrbuffer_test(void)
{
int val = 0;
int ret;
ret = nxsem_get_value(&g_wrbuffer.sem, &val);
if (ret >= 0)
{
ret = val > 0 ? OK : -ENOSPC;
}
return ret;
}
#endif /* CONFIG_NET_TCP && CONFIG_NET_TCP_WRITE_BUFFERS */
@@ -0,0 +1,57 @@
/****************************************************************************
* net/tcp/tcp_wrbuffer_dump.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 <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <debug.h>
#include <nuttx/mm/iob.h>
#include "tcp/tcp.h"
#ifdef CONFIG_DEBUG_FEATURES
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_wrbuffer_dump
*
* Description:
* Dump the contents of a write buffer
*
****************************************************************************/
void tcp_wrbuffer_dump(FAR const char *msg, FAR struct tcp_wrbuffer_s *wrb,
unsigned int len, unsigned int offset)
{
syslog(LOG_DEBUG, "%s: wrb=%p segno=%" PRIu32 " sent=%d nrtx=%d\n",
msg, wrb, TCP_WBSEQNO(wrb), TCP_WBSENT(wrb), TCP_WBNRTX(wrb));
iob_dump("I/O Buffer Chain", TCP_WBIOB(wrb), len, offset);
}
#endif /* CONFIG_DEBUG_FEATURES */