forked from xuos/xiuos
modify nuttx bug of lack of files
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user