2007-09-02 23:58:35 +02:00
|
|
|
/****************************************************************************
|
2014-06-18 18:18:53 +02:00
|
|
|
* net/tcp/tcp_conn.c
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
2015-01-17 22:17:35 +01:00
|
|
|
* Copyright (C) 2007-2011, 2013-2015 Gregory Nutt. All rights reserved.
|
2012-09-13 20:32:24 +02:00
|
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
2007-09-03 01:11:10 +02:00
|
|
|
* Large parts of this file were leveraged from uIP logic:
|
|
|
|
*
|
|
|
|
* Copyright (c) 2001-2003, Adam Dunkels.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2007-09-02 23:58:35 +02:00
|
|
|
* 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
|
2007-09-03 01:11:10 +02:00
|
|
|
* 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.
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
2007-09-03 01:11:10 +02:00
|
|
|
* 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.
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
2007-11-22 19:36:46 +01:00
|
|
|
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP)
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2009-12-15 15:53:45 +01:00
|
|
|
#include <stdint.h>
|
2007-09-02 23:58:35 +02:00
|
|
|
#include <string.h>
|
2009-12-17 16:01:22 +01:00
|
|
|
#include <assert.h>
|
2007-09-03 01:11:10 +02:00
|
|
|
#include <errno.h>
|
2007-11-06 00:04:16 +01:00
|
|
|
#include <debug.h>
|
|
|
|
|
2015-05-29 23:16:11 +02:00
|
|
|
#include <netinet/in.h>
|
|
|
|
|
2007-09-02 23:58:35 +02:00
|
|
|
#include <arch/irq.h>
|
|
|
|
|
2014-06-24 16:53:28 +02:00
|
|
|
#include <nuttx/net/netconfig.h>
|
2014-07-05 00:38:51 +02:00
|
|
|
#include <nuttx/net/net.h>
|
2014-06-24 17:28:44 +02:00
|
|
|
#include <nuttx/net/netdev.h>
|
2014-07-05 03:13:08 +02:00
|
|
|
#include <nuttx/net/ip.h>
|
2014-06-26 17:32:39 +02:00
|
|
|
#include <nuttx/net/tcp.h>
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2014-06-29 02:07:02 +02:00
|
|
|
#include "devif/devif.h"
|
2017-08-07 19:50:50 +02:00
|
|
|
#include "inet/inet.h"
|
2014-07-05 00:38:51 +02:00
|
|
|
#include "tcp/tcp.h"
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2015-01-15 22:06:46 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* Pre-processor Definitions
|
|
|
|
****************************************************************************/
|
|
|
|
|
2015-01-18 15:56:05 +01:00
|
|
|
#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)])
|
2015-01-15 22:06:46 +01:00
|
|
|
|
2007-09-02 23:58:35 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Private Data
|
|
|
|
****************************************************************************/
|
|
|
|
|
2016-05-30 17:31:44 +02:00
|
|
|
/* The array containing all TCP connections. */
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
static struct tcp_conn_s g_tcp_connections[CONFIG_NET_TCP_CONNS];
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2007-09-03 22:34:44 +02:00
|
|
|
/* A list of all free TCP connections */
|
|
|
|
|
|
|
|
static dq_queue_t g_free_tcp_connections;
|
|
|
|
|
|
|
|
/* A list of all connected TCP connections */
|
|
|
|
|
|
|
|
static dq_queue_t g_active_tcp_connections;
|
|
|
|
|
2007-09-02 23:58:35 +02:00
|
|
|
/* Last port used by a TCP connection connection. */
|
|
|
|
|
2009-12-15 15:53:45 +01:00
|
|
|
static uint16_t g_last_tcp_port;
|
2007-09-02 23:58:35 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-11-22 14:14:17 +01:00
|
|
|
/****************************************************************************
|
2015-01-18 00:07:54 +01:00
|
|
|
* Name: tcp_ipv4_listener
|
2014-11-22 14:14:17 +01:00
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Given a local port number (in network byte order), find the TCP
|
|
|
|
* connection that listens on this this port.
|
|
|
|
*
|
|
|
|
* Primary uses: (1) to determine if a port number is available, (2) to
|
|
|
|
* To identify the socket that will accept new connections on a local port.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2017-08-08 22:24:12 +02:00
|
|
|
#ifdef CONFIG_NET_IPv4
|
2015-02-10 01:15:34 +01:00
|
|
|
static inline FAR struct tcp_conn_s *tcp_ipv4_listener(in_addr_t ipaddr,
|
2015-01-18 00:07:54 +01:00
|
|
|
uint16_t portno)
|
2014-11-22 14:14:17 +01:00
|
|
|
{
|
|
|
|
FAR struct tcp_conn_s *conn;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Check if this port number is in use by any active UIP TCP connection */
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_TCP_CONNS; i++)
|
|
|
|
{
|
|
|
|
conn = &g_tcp_connections[i];
|
2014-11-22 17:46:37 +01:00
|
|
|
|
|
|
|
/* Check if this connection is open and the local port assignment
|
|
|
|
* matches the requested port number.
|
|
|
|
*/
|
|
|
|
|
2014-11-22 14:14:17 +01:00
|
|
|
if (conn->tcpstateflags != TCP_CLOSED && conn->lport == portno)
|
|
|
|
{
|
2014-11-22 17:46:37 +01:00
|
|
|
/* If there are multiple interface devices, then the local IP
|
|
|
|
* address of the connection must also match. INADDR_ANY is a
|
|
|
|
* special case: There can only be instance of a port number
|
|
|
|
* with INADDR_ANY.
|
|
|
|
*/
|
|
|
|
|
2015-01-16 19:30:18 +01:00
|
|
|
if (net_ipv4addr_cmp(conn->u.ipv4.laddr, ipaddr) ||
|
|
|
|
net_ipv4addr_cmp(conn->u.ipv4.laddr, INADDR_ANY))
|
2014-11-22 17:46:37 +01:00
|
|
|
{
|
|
|
|
/* The port number is in use, return the connection */
|
2014-11-22 14:14:17 +01:00
|
|
|
|
2014-11-22 17:46:37 +01:00
|
|
|
return conn;
|
|
|
|
}
|
2014-11-22 14:14:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-08-08 22:24:12 +02:00
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: tcp_ipv6_listener
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Given a local port number (in network byte order), find the TCP
|
|
|
|
* connection that listens on this this port.
|
|
|
|
*
|
|
|
|
* Primary uses: (1) to determine if a port number is available, (2) to
|
|
|
|
* To identify the socket that will accept new connections on a local port.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2017-08-08 22:24:12 +02:00
|
|
|
#ifdef CONFIG_NET_IPv6
|
2015-02-10 13:54:10 +01:00
|
|
|
static inline FAR struct tcp_conn_s *
|
|
|
|
tcp_ipv6_listener(const net_ipv6addr_t ipaddr, uint16_t portno)
|
2015-01-18 00:07:54 +01:00
|
|
|
{
|
|
|
|
FAR struct tcp_conn_s *conn;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Check if this port number is in use by any active UIP TCP connection */
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_TCP_CONNS; i++)
|
|
|
|
{
|
|
|
|
conn = &g_tcp_connections[i];
|
|
|
|
|
|
|
|
/* Check if this connection is open and the local port assignment
|
|
|
|
* matches the requested port number.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (conn->tcpstateflags != TCP_CLOSED && conn->lport == portno)
|
|
|
|
{
|
|
|
|
/* If there are multiple interface devices, then the local IP
|
|
|
|
* address of the connection must also match. INADDR_ANY is a
|
|
|
|
* special case: There can only be instance of a port number
|
|
|
|
* with INADDR_ANY.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (net_ipv6addr_cmp(conn->u.ipv6.laddr, ipaddr) ||
|
|
|
|
net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_allzeroaddr))
|
|
|
|
{
|
|
|
|
/* The port number is in use, return the connection */
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-08-08 22:24:12 +02:00
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: tcp_listener
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Given a local port number (in network byte order), find the TCP
|
|
|
|
* connection that listens on this this port.
|
|
|
|
*
|
|
|
|
* Primary uses: (1) to determine if a port number is available, (2) to
|
|
|
|
* To identify the socket that will accept new connections on a local port.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static FAR struct tcp_conn_s *
|
2015-02-10 01:15:34 +01:00
|
|
|
tcp_listener(uint8_t domain, FAR const union ip_addr_u *ipaddr,
|
2015-01-18 00:07:54 +01:00
|
|
|
uint16_t portno)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
if (domain == PF_INET)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
return tcp_ipv4_listener(ipaddr->ipv4, portno);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
return tcp_ipv6_listener(ipaddr->ipv6, portno);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
|
|
|
}
|
|
|
|
|
2007-09-09 19:20:56 +02:00
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_selectport
|
2007-09-09 19:20:56 +02:00
|
|
|
*
|
|
|
|
* Description:
|
2014-04-12 20:13:01 +02:00
|
|
|
* If the port number is zero; select an unused port for the connection.
|
|
|
|
* If the port number is non-zero, verify that no other connection has
|
2007-09-09 19:20:56 +02:00
|
|
|
* been created with this port number.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* portno -- the selected port number in host order. Zero means no port
|
|
|
|
* selected.
|
|
|
|
*
|
|
|
|
* Return:
|
2016-08-30 15:59:57 +02:00
|
|
|
* Selected or verified port number in host order on success, a negated
|
|
|
|
* errno on failure:
|
2007-11-16 19:48:39 +01:00
|
|
|
*
|
|
|
|
* EADDRINUSE
|
|
|
|
* The given address is already in use.
|
|
|
|
* EADDRNOTAVAIL
|
|
|
|
* Cannot assign requested address (unlikely)
|
2007-09-09 19:20:56 +02:00
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* Interrupts are disabled
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
static int tcp_selectport(uint8_t domain, FAR const union ip_addr_u *ipaddr,
|
|
|
|
uint16_t portno)
|
2007-09-09 19:20:56 +02:00
|
|
|
{
|
|
|
|
if (portno == 0)
|
|
|
|
{
|
|
|
|
/* No local port assigned. Loop until we find a valid listen port number
|
2007-11-16 19:48:39 +01:00
|
|
|
* that is not being used by any other connection. NOTE the following loop
|
|
|
|
* is assumed to terminate but could not if all 32000-4096+1 ports are
|
|
|
|
* in used (unlikely).
|
2007-09-09 19:20:56 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Guess that the next available port number will be the one after
|
|
|
|
* the last port number assigned.
|
|
|
|
*/
|
2016-08-30 15:59:57 +02:00
|
|
|
|
2007-09-09 19:20:56 +02:00
|
|
|
portno = ++g_last_tcp_port;
|
|
|
|
|
|
|
|
/* Make sure that the port number is within range */
|
|
|
|
|
|
|
|
if (g_last_tcp_port >= 32000)
|
|
|
|
{
|
|
|
|
g_last_tcp_port = 4096;
|
|
|
|
}
|
|
|
|
}
|
2015-01-18 00:07:54 +01:00
|
|
|
while (tcp_listener(domain, ipaddr, htons(g_last_tcp_port)));
|
2007-09-09 19:20:56 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* A port number has been supplied. Verify that no other TCP/IP
|
|
|
|
* connection is using this local port.
|
|
|
|
*/
|
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
if (tcp_listener(domain, ipaddr, portno))
|
2007-09-09 19:20:56 +02:00
|
|
|
{
|
|
|
|
/* It is in use... return EADDRINUSE */
|
|
|
|
|
|
|
|
return -EADDRINUSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-30 15:59:57 +02:00
|
|
|
/* Return the selected or verified port number (host byte order) */
|
2007-09-09 19:20:56 +02:00
|
|
|
|
|
|
|
return portno;
|
|
|
|
}
|
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: tcp_ipv4_active
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Find a connection structure that is the appropriate
|
|
|
|
* connection to be used with the provided TCP/IP header
|
|
|
|
*
|
|
|
|
* Assumptions:
|
2017-09-02 18:27:03 +02:00
|
|
|
* This function is called from network logic with the nework locked.
|
2015-01-18 00:07:54 +01:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
static inline FAR struct tcp_conn_s *
|
|
|
|
tcp_ipv4_active(FAR struct net_driver_s *dev, FAR struct tcp_hdr_s *tcp)
|
|
|
|
{
|
2015-01-18 15:56:05 +01:00
|
|
|
FAR struct ipv4_hdr_s *ip = IPv4BUF;
|
2015-01-18 00:07:54 +01:00
|
|
|
FAR struct tcp_conn_s *conn;
|
|
|
|
in_addr_t srcipaddr;
|
|
|
|
in_addr_t destipaddr;
|
|
|
|
|
|
|
|
conn = (FAR struct tcp_conn_s *)g_active_tcp_connections.head;
|
|
|
|
srcipaddr = net_ip4addr_conv32(ip->srcipaddr);
|
|
|
|
destipaddr = net_ip4addr_conv32(ip->destipaddr);
|
|
|
|
|
|
|
|
while (conn)
|
|
|
|
{
|
|
|
|
/* Find an open connection matching the TCP input. The following
|
|
|
|
* checks are performed:
|
|
|
|
*
|
|
|
|
* - The local port number is checked against the destination port
|
|
|
|
* number in the received packet.
|
|
|
|
* - The remote port number is checked if the connection is bound
|
|
|
|
* to a remote port.
|
2017-08-08 22:24:12 +02:00
|
|
|
* - Insist that the destination IP matches the bound address. If
|
|
|
|
* a socket is bound to INADDRY_ANY, then it should receive all
|
|
|
|
* packets directed to the port.
|
2015-01-18 00:07:54 +01:00
|
|
|
* - Finally, if the connection is bound to a remote IP address,
|
|
|
|
* the source IP address of the packet is checked.
|
|
|
|
*
|
|
|
|
* If all of the above are true then the newly received TCP packet
|
|
|
|
* is destined for this TCP connection.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (conn->tcpstateflags != TCP_CLOSED &&
|
|
|
|
tcp->destport == conn->lport &&
|
|
|
|
tcp->srcport == conn->rport &&
|
2015-05-29 23:16:11 +02:00
|
|
|
(net_ipv4addr_cmp(conn->u.ipv4.laddr, INADDR_ANY) ||
|
2015-01-18 00:07:54 +01:00
|
|
|
net_ipv4addr_cmp(destipaddr, conn->u.ipv4.laddr)) &&
|
|
|
|
net_ipv4addr_cmp(srcipaddr, conn->u.ipv4.raddr))
|
|
|
|
{
|
|
|
|
/* Matching connection found.. break out of the loop and return a
|
|
|
|
* reference to it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look at the next active connection */
|
|
|
|
|
|
|
|
conn = (FAR struct tcp_conn_s *)conn->node.flink;
|
|
|
|
}
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: tcp_ipv6_active
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Find a connection structure that is the appropriate
|
|
|
|
* connection to be used with the provided TCP/IP header
|
|
|
|
*
|
|
|
|
* Assumptions:
|
2017-09-02 18:27:03 +02:00
|
|
|
* This function is called from network logic with the nework locked.
|
|
|
|
4 *
|
2015-01-18 00:07:54 +01:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
static inline FAR struct tcp_conn_s *
|
|
|
|
tcp_ipv6_active(FAR struct net_driver_s *dev, FAR struct tcp_hdr_s *tcp)
|
|
|
|
{
|
2015-01-18 15:56:05 +01:00
|
|
|
FAR struct ipv6_hdr_s *ip = IPv6BUF;
|
2015-01-18 00:07:54 +01:00
|
|
|
FAR struct tcp_conn_s *conn;
|
|
|
|
net_ipv6addr_t *srcipaddr;
|
2015-02-10 13:54:10 +01:00
|
|
|
net_ipv6addr_t *destipaddr;
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
conn = (FAR struct tcp_conn_s *)g_active_tcp_connections.head;
|
|
|
|
srcipaddr = (net_ipv6addr_t *)ip->srcipaddr;
|
|
|
|
destipaddr = (net_ipv6addr_t *)ip->destipaddr;
|
|
|
|
|
|
|
|
while (conn)
|
|
|
|
{
|
|
|
|
/* Find an open connection matching the TCP input. The following
|
|
|
|
* checks are performed:
|
|
|
|
*
|
|
|
|
* - The local port number is checked against the destination port
|
|
|
|
* number in the received packet.
|
|
|
|
* - The remote port number is checked if the connection is bound
|
|
|
|
* to a remote port.
|
2017-08-08 22:24:12 +02:00
|
|
|
* - Insist that the destination IP matches the bound address. If
|
|
|
|
* a socket is bound to INADDRY_ANY, then it should receive all
|
|
|
|
* packets directed to the port.
|
2015-01-18 00:07:54 +01:00
|
|
|
* - Finally, if the connection is bound to a remote IP address,
|
|
|
|
* the source IP address of the packet is checked.
|
|
|
|
*
|
|
|
|
* If all of the above are true then the newly received TCP packet
|
|
|
|
* is destined for this TCP connection.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (conn->tcpstateflags != TCP_CLOSED &&
|
|
|
|
tcp->destport == conn->lport &&
|
|
|
|
tcp->srcport == conn->rport &&
|
|
|
|
(net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_allzeroaddr) ||
|
|
|
|
net_ipv6addr_cmp(*destipaddr, conn->u.ipv6.laddr)) &&
|
|
|
|
net_ipv6addr_cmp(*srcipaddr, conn->u.ipv6.raddr))
|
|
|
|
{
|
|
|
|
/* Matching connection found.. break out of the loop and return a
|
|
|
|
* reference to it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look at the next active connection */
|
|
|
|
|
|
|
|
conn = (FAR struct tcp_conn_s *)conn->node.flink;
|
|
|
|
}
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: tcp_ipv4_bind
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* This function implements the lower level parts of the standard TCP
|
|
|
|
* bind() operation.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* 0 on success or -EADDRINUSE on failure
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* This function is called from normal user level code.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
static inline int tcp_ipv4_bind(FAR struct tcp_conn_s *conn,
|
|
|
|
FAR const struct sockaddr_in *addr)
|
|
|
|
{
|
|
|
|
int port;
|
2015-05-30 17:12:27 +02:00
|
|
|
int ret;
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
/* Verify or select a local port and address */
|
|
|
|
|
2016-12-03 23:28:19 +01:00
|
|
|
net_lock();
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2016-08-30 15:59:57 +02:00
|
|
|
/* Verify or select a local port (host byte order) */
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
port = tcp_selectport(PF_INET,
|
2015-02-10 01:15:34 +01:00
|
|
|
(FAR const union ip_addr_u *)&addr->sin_addr.s_addr,
|
2015-01-18 00:07:54 +01:00
|
|
|
ntohs(addr->sin_port));
|
|
|
|
if (port < 0)
|
|
|
|
{
|
2016-06-12 01:37:21 +02:00
|
|
|
nerr("ERROR: tcp_selectport failed: %d\n", port);
|
2015-01-18 00:07:54 +01:00
|
|
|
return port;
|
|
|
|
}
|
|
|
|
|
2016-08-30 15:59:57 +02:00
|
|
|
/* Save the local address in the connection structure (network byte order). */
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2016-08-30 15:59:57 +02:00
|
|
|
conn->lport = htons(port);
|
2015-02-10 01:15:34 +01:00
|
|
|
net_ipv4addr_copy(conn->u.ipv4.laddr, addr->sin_addr.s_addr);
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
/* Find the device that can receive packets on the network associated with
|
|
|
|
* this local address.
|
2015-05-30 17:12:27 +02:00
|
|
|
*/
|
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
ret = tcp_local_ipv4_device(conn);
|
2015-05-30 17:12:27 +02:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
/* If no device is found, then the address is not reachable */
|
|
|
|
|
2016-06-12 01:37:21 +02:00
|
|
|
nerr("ERROR: tcp_local_ipv4_device failed: %d\n", ret);
|
2015-05-30 17:12:27 +02:00
|
|
|
|
|
|
|
/* Back out the local address setting */
|
|
|
|
|
|
|
|
conn->lport = 0;
|
|
|
|
net_ipv4addr_copy(conn->u.ipv4.laddr, INADDR_ANY);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-12-03 23:28:19 +01:00
|
|
|
net_unlock();
|
2015-01-18 00:07:54 +01:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: tcp_ipv6_bind
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* This function implements the lower level parts of the standard TCP
|
|
|
|
* bind() operation.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* 0 on success or -EADDRINUSE on failure
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* This function is called from normal user level code.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
static inline int tcp_ipv6_bind(FAR struct tcp_conn_s *conn,
|
|
|
|
FAR const struct sockaddr_in6 *addr)
|
|
|
|
{
|
|
|
|
int port;
|
2015-05-30 17:12:27 +02:00
|
|
|
int ret;
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
/* Verify or select a local port and address */
|
|
|
|
|
2016-12-03 23:28:19 +01:00
|
|
|
net_lock();
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2016-08-30 15:59:57 +02:00
|
|
|
/* Verify or select a local port (host byte order) */
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2015-02-10 13:54:10 +01:00
|
|
|
/* The port number must be unique for this address binding */
|
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
port = tcp_selectport(PF_INET6,
|
2015-02-10 13:54:10 +01:00
|
|
|
(FAR const union ip_addr_u *)addr->sin6_addr.in6_u.u6_addr16,
|
2015-01-21 01:14:09 +01:00
|
|
|
ntohs(addr->sin6_port));
|
2015-01-18 00:07:54 +01:00
|
|
|
if (port < 0)
|
|
|
|
{
|
2016-06-12 01:37:21 +02:00
|
|
|
nerr("ERROR: tcp_selectport failed: %d\n", port);
|
2015-01-18 00:07:54 +01:00
|
|
|
return port;
|
|
|
|
}
|
|
|
|
|
2016-08-30 15:59:57 +02:00
|
|
|
/* Save the local address in the connection structure (network byte order). */
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2016-08-30 15:59:57 +02:00
|
|
|
conn->lport = htons(port);
|
2015-01-18 00:07:54 +01:00
|
|
|
net_ipv6addr_copy(conn->u.ipv6.laddr, addr->sin6_addr.in6_u.u6_addr16);
|
|
|
|
|
2015-05-30 17:12:27 +02:00
|
|
|
/* Find the device that can receive packets on the network
|
|
|
|
* associated with this local address.
|
|
|
|
*/
|
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
ret = tcp_local_ipv6_device(conn);
|
2015-05-30 17:12:27 +02:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
/* If no device is found, then the address is not reachable */
|
|
|
|
|
2016-06-12 01:37:21 +02:00
|
|
|
nerr("ERROR: tcp_local_ipv6_device failed: %d\n", ret);
|
2015-05-30 17:12:27 +02:00
|
|
|
|
|
|
|
/* Back out the local address setting */
|
|
|
|
|
|
|
|
conn->lport = 0;
|
|
|
|
net_ipv6addr_copy(conn->u.ipv6.laddr, g_ipv6_allzeroaddr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-12-03 23:28:19 +01:00
|
|
|
net_unlock();
|
2015-01-18 00:07:54 +01:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
|
|
|
|
2007-09-02 23:58:35 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_initialize
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Description:
|
2007-09-03 01:11:10 +02:00
|
|
|
* Initialize the TCP/IP connection structures. Called only once and only
|
2014-04-12 20:13:01 +02:00
|
|
|
* from the UIP layer at start-up in normal user mode.
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
void tcp_initialize(void)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
|
|
|
int i;
|
2007-09-03 22:34:44 +02:00
|
|
|
|
|
|
|
/* Initialize the queues */
|
|
|
|
|
|
|
|
dq_init(&g_free_tcp_connections);
|
|
|
|
dq_init(&g_active_tcp_connections);
|
|
|
|
|
|
|
|
/* Now initialize each connection structure */
|
|
|
|
|
2007-11-20 00:09:39 +01:00
|
|
|
for (i = 0; i < CONFIG_NET_TCP_CONNS; i++)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2007-09-03 22:34:44 +02:00
|
|
|
/* Mark the connection closed and move it to the free list */
|
|
|
|
|
2014-07-07 00:10:26 +02:00
|
|
|
g_tcp_connections[i].tcpstateflags = TCP_CLOSED;
|
2007-09-03 22:34:44 +02:00
|
|
|
dq_addlast(&g_tcp_connections[i].node, &g_free_tcp_connections);
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_last_tcp_port = 1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_alloc
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Find a free TCP/IP connection structure and allocate it
|
|
|
|
* for use. This is normally something done by the implementation of the
|
2017-09-02 18:27:03 +02:00
|
|
|
* socket() API but is also called from the even procressing lock when a
|
|
|
|
* TCP packet is received while "listening"
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2015-01-17 20:07:48 +01:00
|
|
|
FAR struct tcp_conn_s *tcp_alloc(uint8_t domain)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2014-06-25 02:12:49 +02:00
|
|
|
FAR struct tcp_conn_s *conn;
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2017-09-02 18:27:03 +02:00
|
|
|
/* Because this routine is called from both event processing (with the
|
|
|
|
* network locked) and and from user level. Make sure that the network
|
|
|
|
* locked in any cased while accessing g_free_tcp_connections[];
|
2007-09-02 23:58:35 +02:00
|
|
|
*/
|
|
|
|
|
2016-12-03 23:28:19 +01:00
|
|
|
net_lock();
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2007-09-03 22:34:44 +02:00
|
|
|
/* Return the entry from the head of the free list */
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
conn = (FAR struct tcp_conn_s *)dq_remfirst(&g_free_tcp_connections);
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2014-06-02 15:48:05 +02:00
|
|
|
#ifndef CONFIG_NET_SOLINGER
|
2007-09-03 22:34:44 +02:00
|
|
|
/* Is the free list empty? */
|
|
|
|
|
|
|
|
if (!conn)
|
|
|
|
{
|
2014-04-12 20:13:01 +02:00
|
|
|
/* As a fall-back, check for connection structures which can be stalled.
|
2013-10-06 17:48:54 +02:00
|
|
|
*
|
|
|
|
* Search the active connection list for the oldest connection
|
2014-06-02 15:48:05 +02:00
|
|
|
* that is about to be closed anyway.
|
2007-09-02 23:58:35 +02:00
|
|
|
*/
|
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
FAR struct tcp_conn_s *tmp =
|
|
|
|
(FAR struct tcp_conn_s *)g_active_tcp_connections.head;
|
2013-11-29 00:47:03 +01:00
|
|
|
|
2007-09-03 22:34:44 +02:00
|
|
|
while (tmp)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2016-06-20 19:59:15 +02:00
|
|
|
ninfo("conn: %p state: %02x\n", tmp, tmp->tcpstateflags);
|
2013-10-06 17:48:54 +02:00
|
|
|
|
2014-06-02 15:48:05 +02:00
|
|
|
/* Is this connection in a state we can sacrifice. */
|
|
|
|
|
|
|
|
/* REVISIT: maybe we could check for SO_LINGER but it's buried
|
|
|
|
* in the socket layer.
|
2013-10-06 17:48:54 +02:00
|
|
|
*/
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2014-07-07 00:10:26 +02:00
|
|
|
if (tmp->tcpstateflags == TCP_CLOSING ||
|
|
|
|
tmp->tcpstateflags == TCP_FIN_WAIT_1 ||
|
|
|
|
tmp->tcpstateflags == TCP_FIN_WAIT_2 ||
|
|
|
|
tmp->tcpstateflags == TCP_TIME_WAIT ||
|
|
|
|
tmp->tcpstateflags == TCP_LAST_ACK)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2013-10-06 17:48:54 +02:00
|
|
|
/* Yes.. Is it the oldest one we have seen so far? */
|
2007-09-03 22:34:44 +02:00
|
|
|
|
|
|
|
if (!conn || tmp->timer > conn->timer)
|
|
|
|
{
|
|
|
|
/* Yes.. remember it */
|
|
|
|
|
|
|
|
conn = tmp;
|
|
|
|
}
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
2007-09-03 22:34:44 +02:00
|
|
|
|
|
|
|
/* Look at the next active connection */
|
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
tmp = (FAR struct tcp_conn_s *)tmp->node.flink;
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2013-10-06 17:48:54 +02:00
|
|
|
/* Did we find a connection that we can re-use? */
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2013-10-06 17:48:54 +02:00
|
|
|
if (conn != NULL)
|
|
|
|
{
|
2016-06-20 17:37:08 +02:00
|
|
|
nwarn("WARNING: Closing unestablished connection: %p\n", conn);
|
2013-10-06 17:48:54 +02:00
|
|
|
|
|
|
|
/* Yes... free it. This will remove the connection from the list
|
|
|
|
* of active connections and release all resources held by the
|
|
|
|
* connection.
|
|
|
|
*
|
|
|
|
* REVISIT: Could there be any higher level, socket interface
|
|
|
|
* that needs to be informed that we did this to them?
|
2014-06-02 15:48:05 +02:00
|
|
|
*
|
|
|
|
* Actually yes. When CONFIG_NET_SOLINGER is enabled there is a
|
|
|
|
* pending callback in netclose_disconnect waiting for getting
|
|
|
|
* woken up. Otherwise there's the callback too, but no one is
|
|
|
|
* waiting for it.
|
2013-10-06 17:48:54 +02:00
|
|
|
*/
|
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
tcp_free(conn);
|
2013-10-06 17:48:54 +02:00
|
|
|
|
|
|
|
/* Now there is guaranteed to be one free connection. Get it! */
|
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
conn = (FAR struct tcp_conn_s *)dq_remfirst(&g_free_tcp_connections);
|
2013-10-06 17:48:54 +02:00
|
|
|
}
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
2014-06-02 15:48:05 +02:00
|
|
|
#endif
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2016-12-03 23:28:19 +01:00
|
|
|
net_unlock();
|
2007-09-03 22:34:44 +02:00
|
|
|
|
|
|
|
/* Mark the connection allocated */
|
|
|
|
|
|
|
|
if (conn)
|
|
|
|
{
|
2014-06-25 02:12:49 +02:00
|
|
|
memset(conn, 0, sizeof(struct tcp_conn_s));
|
2014-07-07 00:10:26 +02:00
|
|
|
conn->tcpstateflags = TCP_ALLOCATED;
|
2015-01-17 21:13:56 +01:00
|
|
|
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
|
2015-01-17 20:07:48 +01:00
|
|
|
conn->domain = domain;
|
2015-01-17 21:13:56 +01:00
|
|
|
#endif
|
2007-09-03 22:34:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return conn;
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_free
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Free a connection structure that is no longer in use. This should be
|
|
|
|
* done by the implementation of close()
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
void tcp_free(FAR struct tcp_conn_s *conn)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2014-06-29 20:59:34 +02:00
|
|
|
FAR struct devif_callback_s *cb;
|
|
|
|
FAR struct devif_callback_s *next;
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
2014-06-21 23:23:39 +02:00
|
|
|
FAR struct tcp_wrbuffer_s *wrbuffer;
|
2007-11-20 00:35:42 +01:00
|
|
|
#endif
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2017-09-02 18:27:03 +02:00
|
|
|
/* Because g_free_tcp_connections is accessed from user level and event
|
|
|
|
* processing logic, it is necessary to keep the newtork locked during this
|
2007-09-03 22:34:44 +02:00
|
|
|
* operation.
|
|
|
|
*/
|
|
|
|
|
2009-06-15 20:58:22 +02:00
|
|
|
DEBUGASSERT(conn->crefs == 0);
|
2016-12-03 23:28:19 +01:00
|
|
|
net_lock();
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2013-10-08 17:14:27 +02:00
|
|
|
/* Free remaining callbacks, actually there should be only the close callback
|
|
|
|
* left.
|
|
|
|
*/
|
2013-10-06 17:48:54 +02:00
|
|
|
|
2013-10-08 17:14:27 +02:00
|
|
|
for (cb = conn->list; cb; cb = next)
|
2013-10-06 17:48:54 +02:00
|
|
|
{
|
2015-05-28 20:01:38 +02:00
|
|
|
next = cb->nxtconn;
|
2014-06-29 02:36:09 +02:00
|
|
|
tcp_callback_free(conn, cb);
|
2013-10-06 17:48:54 +02:00
|
|
|
}
|
|
|
|
|
2014-07-07 00:10:26 +02:00
|
|
|
/* TCP_ALLOCATED means that that the connection is not in the active list
|
2007-09-03 22:34:44 +02:00
|
|
|
* yet.
|
|
|
|
*/
|
|
|
|
|
2014-07-07 00:10:26 +02:00
|
|
|
if (conn->tcpstateflags != TCP_ALLOCATED)
|
2007-09-03 22:34:44 +02:00
|
|
|
{
|
|
|
|
/* Remove the connection from the active list */
|
|
|
|
|
2007-11-20 22:55:06 +01:00
|
|
|
dq_rem(&conn->node, &g_active_tcp_connections);
|
2007-09-03 22:34:44 +02:00
|
|
|
}
|
|
|
|
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_READAHEAD
|
2007-11-20 00:35:42 +01:00
|
|
|
/* Release any read-ahead buffers attached to the connection */
|
|
|
|
|
2014-06-24 23:38:00 +02:00
|
|
|
iob_free_queue(&conn->readahead);
|
2007-11-20 00:35:42 +01:00
|
|
|
#endif
|
|
|
|
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
|
|
|
/* Release any write buffers attached to the connection */
|
|
|
|
|
2014-06-21 23:23:39 +02:00
|
|
|
while ((wrbuffer = (struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q)) != NULL)
|
2014-01-14 00:26:11 +01:00
|
|
|
{
|
2014-06-21 23:23:39 +02:00
|
|
|
tcp_wrbuffer_release(wrbuffer);
|
2014-01-14 00:26:11 +01:00
|
|
|
}
|
|
|
|
|
2014-06-21 23:23:39 +02:00
|
|
|
while ((wrbuffer = (struct tcp_wrbuffer_s *)sq_remfirst(&conn->unacked_q)) != NULL)
|
2014-01-14 00:26:11 +01:00
|
|
|
{
|
2014-06-21 23:23:39 +02:00
|
|
|
tcp_wrbuffer_release(wrbuffer);
|
2014-01-14 00:26:11 +01:00
|
|
|
}
|
|
|
|
#endif
|
2008-11-20 20:24:06 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_NET_TCPBACKLOG
|
2014-01-14 00:26:11 +01:00
|
|
|
/* Remove any backlog attached to this connection */
|
|
|
|
|
2008-11-20 20:24:06 +01:00
|
|
|
if (conn->backlog)
|
|
|
|
{
|
2014-06-25 02:12:49 +02:00
|
|
|
tcp_backlogdestroy(conn);
|
2008-11-20 20:24:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If this connection is, itself, backlogged, then remove it from the
|
|
|
|
* parent connection's backlog list.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (conn->blparent)
|
|
|
|
{
|
2014-06-25 02:12:49 +02:00
|
|
|
tcp_backlogdelete(conn->blparent, conn);
|
2008-11-20 20:24:06 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-09-03 22:34:44 +02:00
|
|
|
/* Mark the connection available and put it into the free list */
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2014-07-07 00:10:26 +02:00
|
|
|
conn->tcpstateflags = TCP_CLOSED;
|
2007-09-03 22:34:44 +02:00
|
|
|
dq_addlast(&conn->node, &g_free_tcp_connections);
|
2016-12-03 23:28:19 +01:00
|
|
|
net_unlock();
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_active
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Find a connection structure that is the appropriate
|
2009-09-11 20:21:57 +02:00
|
|
|
* connection to be used with the provided TCP/IP header
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Assumptions:
|
2017-09-02 18:27:03 +02:00
|
|
|
* This function is called from network logic with the nework locked.
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2015-01-15 22:06:46 +01:00
|
|
|
FAR struct tcp_conn_s *tcp_active(FAR struct net_driver_s *dev,
|
|
|
|
FAR struct tcp_hdr_s *tcp)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2015-01-18 00:07:54 +01:00
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
if (IFF_IS_IPv6(dev->d_flags))
|
2014-11-22 17:46:37 +01:00
|
|
|
#endif
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2015-01-18 00:07:54 +01:00
|
|
|
return tcp_ipv6_active(dev, tcp);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
else
|
2014-11-22 17:46:37 +01:00
|
|
|
#endif
|
2015-01-18 00:07:54 +01:00
|
|
|
{
|
|
|
|
return tcp_ipv4_active(dev, tcp);
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
2015-01-18 00:07:54 +01:00
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
2007-09-03 22:34:44 +02:00
|
|
|
}
|
2007-09-03 01:11:10 +02:00
|
|
|
|
2007-11-06 00:04:16 +01:00
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_nextconn
|
2007-11-06 00:04:16 +01:00
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Traverse the list of active TCP connections
|
|
|
|
*
|
|
|
|
* Assumptions:
|
2017-09-02 18:27:03 +02:00
|
|
|
* This function is called from network logic with the nework locked.
|
2007-11-06 00:04:16 +01:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-07-01 02:03:58 +02:00
|
|
|
FAR struct tcp_conn_s *tcp_nextconn(FAR struct tcp_conn_s *conn)
|
2007-11-06 00:04:16 +01:00
|
|
|
{
|
|
|
|
if (!conn)
|
|
|
|
{
|
2014-06-25 02:12:49 +02:00
|
|
|
return (FAR struct tcp_conn_s *)g_active_tcp_connections.head;
|
2007-11-06 00:04:16 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-25 02:12:49 +02:00
|
|
|
return (FAR struct tcp_conn_s *)conn->node.flink;
|
2007-11-06 00:04:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-23 22:45:30 +02:00
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_alloc_accept
|
2007-09-03 22:34:44 +02:00
|
|
|
*
|
|
|
|
* Description:
|
2017-09-02 18:27:03 +02:00
|
|
|
* Called when driver event processing matches the incoming packet
|
2014-07-03 01:23:25 +02:00
|
|
|
* with a connection in LISTEN. In that case, this function will create
|
|
|
|
* a new connection and initialize it to send a SYNACK in return.
|
2007-09-03 22:34:44 +02:00
|
|
|
*
|
|
|
|
* Assumptions:
|
2017-09-02 18:27:03 +02:00
|
|
|
* This function is called from network logic with the nework locked.
|
2007-09-03 22:34:44 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-11-15 20:13:23 +01:00
|
|
|
FAR struct tcp_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev,
|
2015-01-15 22:06:46 +01:00
|
|
|
FAR struct tcp_hdr_s *tcp)
|
2007-09-03 22:34:44 +02:00
|
|
|
{
|
2015-01-15 22:06:46 +01:00
|
|
|
FAR struct tcp_conn_s *conn;
|
2015-01-18 00:07:54 +01:00
|
|
|
uint8_t domain;
|
2015-05-30 17:12:27 +02:00
|
|
|
int ret;
|
2015-01-15 22:06:46 +01:00
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
/* Get the appropriate IP domain */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv4)
|
|
|
|
bool ipv6 = IFF_IS_IPv6(dev->d_flags);
|
|
|
|
domain = ipv6 ? PF_INET6 : PF_INET;
|
|
|
|
#elif defined(CONFIG_NET_IPv4)
|
|
|
|
domain = PF_INET;
|
|
|
|
#else /* defined(CONFIG_NET_IPv6) */
|
|
|
|
domain = PF_INET6;
|
2015-01-17 20:07:48 +01:00
|
|
|
#endif
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
/* Allocate the connection structure */
|
|
|
|
|
|
|
|
conn = tcp_alloc(domain);
|
2007-09-03 22:34:44 +02:00
|
|
|
if (conn)
|
|
|
|
{
|
2015-05-30 17:12:27 +02:00
|
|
|
/* Set up the local address (laddr) and the remote address (raddr)
|
|
|
|
* that describes the TCP connection.
|
|
|
|
*/
|
2015-01-18 00:07:54 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
if (ipv6)
|
|
|
|
#endif
|
|
|
|
{
|
2015-01-18 15:56:05 +01:00
|
|
|
FAR struct ipv6_hdr_s *ip = IPv6BUF;
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2015-05-30 17:12:27 +02:00
|
|
|
/* Set the IPv6 specific MSS and the IPv6 locally bound address */
|
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
conn->mss = TCP_IPv6_INITIAL_MSS(dev);
|
|
|
|
net_ipv6addr_copy(conn->u.ipv6.raddr, ip->srcipaddr);
|
|
|
|
net_ipv6addr_copy(conn->u.ipv6.laddr, ip->destipaddr);
|
2015-08-27 17:06:46 +02:00
|
|
|
|
|
|
|
/* We now have to filter all outgoing transfers so that they use
|
|
|
|
* only the MSS of this device.
|
|
|
|
*/
|
|
|
|
|
|
|
|
DEBUGASSERT(conn->dev == NULL || conn->dev == dev);
|
|
|
|
conn->dev = dev;
|
2015-05-30 17:12:27 +02:00
|
|
|
|
|
|
|
/* Find the device that can receive packets on the network
|
|
|
|
* associated with this local address.
|
|
|
|
*/
|
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
ret = tcp_remote_ipv6_device(conn);
|
2015-01-18 00:07:54 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
else
|
2014-11-22 17:46:37 +01:00
|
|
|
#endif
|
2015-01-18 00:07:54 +01:00
|
|
|
{
|
2015-01-18 15:56:05 +01:00
|
|
|
FAR struct ipv4_hdr_s *ip = IPv4BUF;
|
2015-01-18 00:07:54 +01:00
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
/* Set the IPv6 specific MSS and the IPv4 bound remote address. */
|
2015-05-30 17:12:27 +02:00
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
conn->mss = TCP_IPv4_INITIAL_MSS(dev);
|
|
|
|
net_ipv4addr_copy(conn->u.ipv4.raddr,
|
|
|
|
net_ip4addr_conv32(ip->srcipaddr));
|
2015-09-03 03:48:31 +02:00
|
|
|
|
|
|
|
/* Set the local address as well */
|
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
net_ipv4addr_copy(conn->u.ipv4.laddr,
|
|
|
|
net_ip4addr_conv32(ip->destipaddr));
|
2015-08-27 17:06:46 +02:00
|
|
|
|
|
|
|
/* We now have to filter all outgoing transfers so that they use
|
|
|
|
* only the MSS of this device.
|
|
|
|
*/
|
|
|
|
|
|
|
|
DEBUGASSERT(conn->dev == NULL || conn->dev == dev);
|
|
|
|
conn->dev = dev;
|
2015-05-30 17:12:27 +02:00
|
|
|
|
|
|
|
/* Find the device that can receive packets on the network
|
|
|
|
* associated with this local address.
|
|
|
|
*/
|
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
ret = tcp_remote_ipv4_device(conn);
|
2015-01-18 00:07:54 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
|
|
|
|
2015-05-30 17:12:27 +02:00
|
|
|
/* Verify that a network device that can provide packets to this
|
|
|
|
* local address was found.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
/* If no device is found, then the address is not reachable.
|
|
|
|
* That should be impossible in this context and we should
|
|
|
|
* probably really just assert here.
|
|
|
|
*/
|
|
|
|
|
2016-06-12 01:37:21 +02:00
|
|
|
nerr("ERROR: Failed to find network device: %d\n", ret);
|
2015-05-30 17:12:27 +02:00
|
|
|
tcp_free(conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill in the necessary fields for the new connection. */
|
|
|
|
|
|
|
|
conn->rto = TCP_RTO;
|
|
|
|
conn->timer = TCP_RTO;
|
|
|
|
conn->sa = 0;
|
|
|
|
conn->sv = 4;
|
|
|
|
conn->nrtx = 0;
|
|
|
|
conn->lport = tcp->destport;
|
|
|
|
conn->rport = tcp->srcport;
|
2014-07-07 00:10:26 +02:00
|
|
|
conn->tcpstateflags = TCP_SYN_RCVD;
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2014-06-25 02:12:49 +02:00
|
|
|
tcp_initsequence(conn->sndseq);
|
While working with version 7.10 I discovered a problem in TCP stack that could be observed on high network load. Generally speaking, the problem is that RST flag is set in unnecessary case, in which between loss of some TCP packet and its proper retransmission, another packets had been successfully sent. The scenario is as follows: NuttX did not receive ACK for some sent packet, so it has been probably lost somewhere. But before its retransmission starts, NuttX is correctly issuing next TCP packets, with sequence numbers increasing properly. When the retransmission of previously lost packet finally succeeds, tcp_input receives the accumulated ACK value, which acknowledges also the packets sent in the meantime (i.e. between unsuccessful sending of lost packet and its proper retransmission). However, variable unackseq is still set to conn->isn + conn->sent, which is truth only if no further packets transmission occurred in the meantime. Because of incorrect (in such specific case) unackseq value, few lines further condition if (ackseq <= unackseq)is not met, and, as a result, we are going to reset label.
2016-06-20 14:55:29 +02:00
|
|
|
conn->unacked = 1;
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
While working with version 7.10 I discovered a problem in TCP stack that could be observed on high network load. Generally speaking, the problem is that RST flag is set in unnecessary case, in which between loss of some TCP packet and its proper retransmission, another packets had been successfully sent. The scenario is as follows: NuttX did not receive ACK for some sent packet, so it has been probably lost somewhere. But before its retransmission starts, NuttX is correctly issuing next TCP packets, with sequence numbers increasing properly. When the retransmission of previously lost packet finally succeeds, tcp_input receives the accumulated ACK value, which acknowledges also the packets sent in the meantime (i.e. between unsuccessful sending of lost packet and its proper retransmission). However, variable unackseq is still set to conn->isn + conn->sent, which is truth only if no further packets transmission occurred in the meantime. Because of incorrect (in such specific case) unackseq value, few lines further condition if (ackseq <= unackseq)is not met, and, as a result, we are going to reset label.
2016-06-20 14:55:29 +02:00
|
|
|
conn->expired = 0;
|
|
|
|
conn->isn = 0;
|
|
|
|
conn->sent = 0;
|
|
|
|
conn->sndseq_max = 0;
|
2014-01-14 00:26:11 +01:00
|
|
|
#endif
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2010-11-27 02:01:26 +01:00
|
|
|
/* rcvseq should be the seqno from the incoming packet + 1. */
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2015-01-15 22:06:46 +01:00
|
|
|
memcpy(conn->rcvseq, tcp->seqno, 4);
|
2007-11-16 19:48:39 +01:00
|
|
|
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_READAHEAD
|
2007-11-20 00:09:39 +01:00
|
|
|
/* Initialize the list of TCP read-ahead buffers */
|
|
|
|
|
2014-06-24 23:38:00 +02:00
|
|
|
IOB_QINIT(&conn->readahead);
|
2007-11-20 00:09:39 +01:00
|
|
|
#endif
|
|
|
|
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
|
|
|
/* Initialize the write buffer lists */
|
|
|
|
|
|
|
|
sq_init(&conn->write_q);
|
|
|
|
sq_init(&conn->unacked_q);
|
|
|
|
#endif
|
|
|
|
|
2007-11-16 19:48:39 +01:00
|
|
|
/* And, finally, put the connection structure into the active list.
|
|
|
|
* Interrupts should already be disabled in this context.
|
|
|
|
*/
|
|
|
|
|
|
|
|
dq_addlast(&conn->node, &g_active_tcp_connections);
|
2007-11-20 00:09:39 +01:00
|
|
|
}
|
2013-10-06 17:48:54 +02:00
|
|
|
|
2007-09-03 22:34:44 +02:00
|
|
|
return conn;
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2015-01-15 22:06:46 +01:00
|
|
|
* Name: tcp_bind
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Description:
|
2014-07-06 20:34:27 +02:00
|
|
|
* This function implements the lower level parts of the standard TCP
|
2007-09-02 23:58:35 +02:00
|
|
|
* bind() operation.
|
|
|
|
*
|
2007-11-16 19:48:39 +01:00
|
|
|
* Return:
|
|
|
|
* 0 on success or -EADDRINUSE on failure
|
|
|
|
*
|
2007-09-02 23:58:35 +02:00
|
|
|
* Assumptions:
|
|
|
|
* This function is called from normal user level code.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
int tcp_bind(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2015-01-18 00:07:54 +01:00
|
|
|
#ifdef CONFIG_NET_IPv4
|
2014-11-22 17:46:37 +01:00
|
|
|
#ifdef CONFIG_NET_IPv6
|
2015-01-18 00:07:54 +01:00
|
|
|
if (conn->domain == PF_INET)
|
2014-11-22 17:46:37 +01:00
|
|
|
#endif
|
2007-09-09 19:20:56 +02:00
|
|
|
{
|
2015-01-18 00:07:54 +01:00
|
|
|
FAR const struct sockaddr_in *inaddr =
|
|
|
|
(FAR const struct sockaddr_in *)addr;
|
2007-11-16 19:48:39 +01:00
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
return tcp_ipv4_bind(conn, inaddr);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
2007-11-22 15:42:52 +01:00
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
else
|
2007-11-16 19:48:39 +01:00
|
|
|
#endif
|
2015-01-18 00:07:54 +01:00
|
|
|
{
|
|
|
|
FAR const struct sockaddr_in6 *inaddr =
|
|
|
|
(FAR const struct sockaddr_in6 *)addr;
|
2007-11-16 19:48:39 +01:00
|
|
|
|
2015-01-18 00:07:54 +01:00
|
|
|
return tcp_ipv6_bind(conn, inaddr);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2014-06-25 02:12:49 +02:00
|
|
|
* Name: tcp_connect
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Description:
|
2014-07-06 20:34:27 +02:00
|
|
|
* This function implements the lower level parts of the standard
|
2007-09-02 23:58:35 +02:00
|
|
|
* TCP connect() operation: It connects to a remote host using TCP.
|
|
|
|
*
|
|
|
|
* This function is used to start a new connection to the specified
|
2014-04-12 20:13:01 +02:00
|
|
|
* port on the specified host. It uses the connection structure that was
|
2007-09-02 23:58:35 +02:00
|
|
|
* allocated by a preceding socket() call. It sets the connection to
|
|
|
|
* the SYN_SENT state and sets the retransmission timer to 0. This will
|
|
|
|
* cause a TCP SYN segment to be sent out the next time this connection
|
|
|
|
* is periodically processed, which usually is done within 0.5 seconds
|
2014-06-25 02:12:49 +02:00
|
|
|
* after the call to tcp_connect().
|
2007-09-02 23:58:35 +02:00
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* This function is called from normal user level code.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2015-01-17 22:17:35 +01:00
|
|
|
int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr)
|
2007-09-02 23:58:35 +02:00
|
|
|
{
|
2007-09-09 19:20:56 +02:00
|
|
|
int port;
|
2015-05-30 17:12:27 +02:00
|
|
|
int ret;
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2014-07-07 00:10:26 +02:00
|
|
|
/* The connection is expected to be in the TCP_ALLOCATED state.. i.e.,
|
2007-09-03 22:34:44 +02:00
|
|
|
* allocated via up_tcpalloc(), but not yet put into the active connections
|
|
|
|
* list.
|
|
|
|
*/
|
|
|
|
|
2014-07-07 00:10:26 +02:00
|
|
|
if (!conn || conn->tcpstateflags != TCP_ALLOCATED)
|
2007-09-03 22:34:44 +02:00
|
|
|
{
|
|
|
|
return -EISCONN;
|
|
|
|
}
|
|
|
|
|
2014-04-12 20:13:01 +02:00
|
|
|
/* If the TCP port has not already been bound to a local port, then select
|
2014-11-22 17:46:37 +01:00
|
|
|
* one now. We assume that the IP address has been bound to a local device,
|
|
|
|
* but the port may still be INPORT_ANY.
|
2007-09-02 23:58:35 +02:00
|
|
|
*/
|
|
|
|
|
2016-12-03 23:28:19 +01:00
|
|
|
net_lock();
|
2015-02-10 13:54:10 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
if (conn->domain == PF_INET)
|
2015-02-10 01:15:34 +01:00
|
|
|
#endif
|
2015-02-10 13:54:10 +01:00
|
|
|
{
|
2016-08-30 15:59:57 +02:00
|
|
|
/* Select a port that is unique for this IPv4 local address (host
|
|
|
|
* order).
|
|
|
|
*/
|
2015-02-10 13:54:10 +01:00
|
|
|
|
|
|
|
port = tcp_selectport(PF_INET,
|
|
|
|
(FAR const union ip_addr_u *)&conn->u.ipv4.laddr,
|
|
|
|
ntohs(conn->lport));
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
else
|
2014-11-22 17:46:37 +01:00
|
|
|
#endif
|
2015-02-10 13:54:10 +01:00
|
|
|
{
|
2016-08-30 15:59:57 +02:00
|
|
|
/* Select a port that is unique for this IPv6 local address (host
|
|
|
|
* order).
|
|
|
|
*/
|
2015-02-10 13:54:10 +01:00
|
|
|
|
|
|
|
port = tcp_selectport(PF_INET6,
|
|
|
|
(FAR const union ip_addr_u *)conn->u.ipv6.laddr,
|
|
|
|
ntohs(conn->lport));
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
|
|
|
|
|
|
|
/* Did we have a port assignment? */
|
|
|
|
|
2007-09-09 19:20:56 +02:00
|
|
|
if (port < 0)
|
|
|
|
{
|
2015-05-30 17:12:27 +02:00
|
|
|
ret = port;
|
|
|
|
goto errout_with_lock;
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
|
|
|
|
2015-08-27 03:18:04 +02:00
|
|
|
/* Set up the local address (laddr) and the remote address (raddr) that
|
|
|
|
* describes the TCP connection.
|
|
|
|
*/
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2015-01-17 22:17:35 +01:00
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
if (conn->domain == PF_INET)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
FAR const struct sockaddr_in *inaddr =
|
|
|
|
(FAR const struct sockaddr_in *)addr;
|
|
|
|
|
|
|
|
/* Save MSS and the port from the sockaddr (already in network order) */
|
|
|
|
|
|
|
|
conn->mss = MIN_IPv4_TCP_INITIAL_MSS;
|
|
|
|
conn->rport = inaddr->sin_port;
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2015-01-17 22:17:35 +01:00
|
|
|
/* The sockaddr address is 32-bits in network order. */
|
|
|
|
|
|
|
|
net_ipv4addr_copy(conn->u.ipv4.raddr, inaddr->sin_addr.s_addr);
|
2015-05-30 17:12:27 +02:00
|
|
|
|
|
|
|
/* Find the device that can receive packets on the network associated
|
2015-09-03 03:48:31 +02:00
|
|
|
* with this remote address.
|
2015-05-30 17:12:27 +02:00
|
|
|
*/
|
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
ret = tcp_remote_ipv4_device(conn);
|
2015-01-17 22:17:35 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv4 */
|
2007-09-02 23:58:35 +02:00
|
|
|
|
2015-01-17 22:17:35 +01:00
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
FAR const struct sockaddr_in6 *inaddr =
|
|
|
|
(FAR const struct sockaddr_in6 *)addr;
|
|
|
|
|
|
|
|
/* Save MSS and the port from the sockaddr (already in network order) */
|
|
|
|
|
|
|
|
conn->mss = MIN_IPv6_TCP_INITIAL_MSS;
|
2015-01-21 01:14:09 +01:00
|
|
|
conn->rport = inaddr->sin6_port;
|
2015-01-17 22:17:35 +01:00
|
|
|
|
2015-05-30 17:12:27 +02:00
|
|
|
/* The sockaddr address is 128-bits in network order. */
|
2015-01-17 22:17:35 +01:00
|
|
|
|
|
|
|
net_ipv6addr_copy(conn->u.ipv6.raddr, inaddr->sin6_addr.s6_addr16);
|
2015-05-30 17:12:27 +02:00
|
|
|
|
|
|
|
/* Find the device that can receive packets on the network associated
|
|
|
|
* with this local address.
|
|
|
|
*/
|
|
|
|
|
2015-09-03 03:48:31 +02:00
|
|
|
ret = tcp_remote_ipv6_device(conn);
|
2015-01-17 22:17:35 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPv6 */
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2015-05-30 17:12:27 +02:00
|
|
|
/* Verify that a network device that can provide packets to this local
|
|
|
|
* address was found.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
/* If no device is found, then the address is not reachable. That
|
|
|
|
* should be impossible in this context and we should probably really
|
|
|
|
* just assert here.
|
|
|
|
*/
|
|
|
|
|
2016-06-12 01:37:21 +02:00
|
|
|
nerr("ERROR: Failed to find network device: %d\n", ret);
|
2015-05-30 17:12:27 +02:00
|
|
|
goto errout_with_lock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize and return the connection structure, bind it to the port
|
|
|
|
* number. At this point, we do not know the size of the initial MSS We
|
|
|
|
* know the total size of the packet buffer, but we don't yet know the
|
|
|
|
* size of link layer header.
|
|
|
|
*/
|
|
|
|
|
|
|
|
conn->tcpstateflags = TCP_SYN_SENT;
|
|
|
|
tcp_initsequence(conn->sndseq);
|
|
|
|
|
|
|
|
conn->unacked = 1; /* TCP length of the SYN is one. */
|
|
|
|
conn->nrtx = 0;
|
|
|
|
conn->timer = 1; /* Send the SYN next time around. */
|
|
|
|
conn->rto = TCP_RTO;
|
|
|
|
conn->sa = 0;
|
|
|
|
conn->sv = 16; /* Initial value of the RTT variance. */
|
|
|
|
conn->lport = htons((uint16_t)port);
|
|
|
|
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
|
|
|
conn->expired = 0;
|
|
|
|
conn->isn = 0;
|
|
|
|
conn->sent = 0;
|
While working with version 7.10 I discovered a problem in TCP stack that could be observed on high network load. Generally speaking, the problem is that RST flag is set in unnecessary case, in which between loss of some TCP packet and its proper retransmission, another packets had been successfully sent. The scenario is as follows: NuttX did not receive ACK for some sent packet, so it has been probably lost somewhere. But before its retransmission starts, NuttX is correctly issuing next TCP packets, with sequence numbers increasing properly. When the retransmission of previously lost packet finally succeeds, tcp_input receives the accumulated ACK value, which acknowledges also the packets sent in the meantime (i.e. between unsuccessful sending of lost packet and its proper retransmission). However, variable unackseq is still set to conn->isn + conn->sent, which is truth only if no further packets transmission occurred in the meantime. Because of incorrect (in such specific case) unackseq value, few lines further condition if (ackseq <= unackseq)is not met, and, as a result, we are going to reset label.
2016-06-20 14:55:29 +02:00
|
|
|
conn->sndseq_max = 0;
|
2015-05-30 17:12:27 +02:00
|
|
|
#endif
|
|
|
|
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_READAHEAD
|
2007-11-20 00:09:39 +01:00
|
|
|
/* Initialize the list of TCP read-ahead buffers */
|
|
|
|
|
2014-06-24 23:38:00 +02:00
|
|
|
IOB_QINIT(&conn->readahead);
|
2007-11-20 00:09:39 +01:00
|
|
|
#endif
|
|
|
|
|
2014-01-14 00:26:11 +01:00
|
|
|
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
|
|
|
/* Initialize the TCP write buffer lists */
|
|
|
|
|
|
|
|
sq_init(&conn->write_q);
|
|
|
|
sq_init(&conn->unacked_q);
|
|
|
|
#endif
|
|
|
|
|
2015-05-30 17:12:27 +02:00
|
|
|
/* And, finally, put the connection structure into the active list. */
|
2007-09-03 22:34:44 +02:00
|
|
|
|
|
|
|
dq_addlast(&conn->node, &g_active_tcp_connections);
|
2015-05-30 17:12:27 +02:00
|
|
|
ret = OK;
|
2007-09-03 22:34:44 +02:00
|
|
|
|
2015-05-30 17:12:27 +02:00
|
|
|
errout_with_lock:
|
2016-12-03 23:28:19 +01:00
|
|
|
net_unlock();
|
2015-05-30 17:12:27 +02:00
|
|
|
return ret;
|
2007-09-02 23:58:35 +02:00
|
|
|
}
|
|
|
|
|
2007-11-22 19:36:46 +01:00
|
|
|
#endif /* CONFIG_NET && CONFIG_NET_TCP */
|