summaryrefslogtreecommitdiff
path: root/core/lwip/src/core/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/lwip/src/core/tcp.c')
-rw-r--r--core/lwip/src/core/tcp.c524
1 files changed, 349 insertions, 175 deletions
diff --git a/core/lwip/src/core/tcp.c b/core/lwip/src/core/tcp.c
index 29cdf185..c629bc4e 100644
--- a/core/lwip/src/core/tcp.c
+++ b/core/lwip/src/core/tcp.c
@@ -49,12 +49,13 @@
#include "lwip/memp.h"
#include "lwip/snmp.h"
#include "lwip/tcp.h"
+#include "lwip/tcp_impl.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include <string.h>
-const char *tcp_state_str[] = {
+const char * const tcp_state_str[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
@@ -78,17 +79,25 @@ const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
/* The TCP PCB lists. */
/** List of all TCP PCBs bound but not yet (connected || listening) */
-struct tcp_pcb *tcp_bound_pcbs;
+struct tcp_pcb *tcp_bound_pcbs;
/** List of all TCP PCBs in LISTEN state */
union tcp_listen_pcbs_t tcp_listen_pcbs;
/** List of all TCP PCBs that are in a state in which
* they accept or send data. */
-struct tcp_pcb *tcp_active_pcbs;
+struct tcp_pcb *tcp_active_pcbs;
/** List of all TCP PCBs in TIME-WAIT state */
struct tcp_pcb *tcp_tw_pcbs;
+#define NUM_TCP_PCB_LISTS 4
+#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+ &tcp_active_pcbs, &tcp_tw_pcbs};
+
+/** Only used for temporary storage. */
struct tcp_pcb *tcp_tmp_pcb;
+/** Timer counter to handle calling slow-timer from tcp_tmr() */
static u8_t tcp_timer;
static u16_t tcp_new_port(void);
@@ -110,7 +119,9 @@ tcp_tmr(void)
}
/**
- * Closes the connection held by the PCB.
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
*
* Listening pcbs are freed and may not be referenced any more.
* Connection pcbs are freed if not yet connected and may not be referenced
@@ -123,15 +134,34 @@ tcp_tmr(void)
* @return ERR_OK if connection has been closed
* another err_t if closing failed and pcb is not freed
*/
-err_t
-tcp_close(struct tcp_pcb *pcb)
+static err_t
+tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
{
err_t err;
-#if TCP_DEBUG
- LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
- tcp_debug_print_state(pcb->state);
-#endif /* TCP_DEBUG */
+ if (rst_on_unacked_data && (pcb->state != LISTEN)) {
+ if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
+ /* Not all data received by application, send RST to tell the remote
+ side about this. */
+ LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+ /* don't call tcp_abort here: we must not deallocate the pcb since
+ that might not be expected when calling tcp_close */
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+
+ tcp_pcb_purge(pcb);
+
+ /* TODO: to which state do we move now? */
+
+ /* move to TIME_WAIT since we close actively */
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+
+ return ERR_OK;
+ }
+ }
switch (pcb->state) {
case CLOSED:
@@ -143,13 +173,15 @@ tcp_close(struct tcp_pcb *pcb)
* is erroneous, but this should never happen as the pcb has in those cases
* been freed, and so any remaining handles are bogus. */
err = ERR_OK;
- TCP_RMV(&tcp_bound_pcbs, pcb);
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
memp_free(MEMP_TCP_PCB, pcb);
pcb = NULL;
break;
case LISTEN:
err = ERR_OK;
- tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb);
+ tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
memp_free(MEMP_TCP_PCB_LISTEN, pcb);
pcb = NULL;
break;
@@ -161,21 +193,21 @@ tcp_close(struct tcp_pcb *pcb)
snmp_inc_tcpattemptfails();
break;
case SYN_RCVD:
- err = tcp_send_ctrl(pcb, TCP_FIN);
+ err = tcp_send_fin(pcb);
if (err == ERR_OK) {
snmp_inc_tcpattemptfails();
pcb->state = FIN_WAIT_1;
}
break;
case ESTABLISHED:
- err = tcp_send_ctrl(pcb, TCP_FIN);
+ err = tcp_send_fin(pcb);
if (err == ERR_OK) {
snmp_inc_tcpestabresets();
pcb->state = FIN_WAIT_1;
}
break;
case CLOSE_WAIT:
- err = tcp_send_ctrl(pcb, TCP_FIN);
+ err = tcp_send_fin(pcb);
if (err == ERR_OK) {
snmp_inc_tcpestabresets();
pcb->state = LAST_ACK;
@@ -195,13 +227,86 @@ tcp_close(struct tcp_pcb *pcb)
returns (unsent data is sent from tcp timer functions, also), we don't care
for the return value of tcp_output for now. */
/* @todo: When implementing SO_LINGER, this must be changed somehow:
- If SOF_LINGER is set, the data should be sent when tcp_close returns. */
+ If SOF_LINGER is set, the data should be sent and acked before close returns.
+ This can only be valid for sequential APIs, not for the raw API. */
tcp_output(pcb);
}
return err;
}
/**
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+#if TCP_DEBUG
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+
+ if (pcb->state != LISTEN) {
+ /* Set a flag not to receive any more data... */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ /* ... and close */
+ return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB!
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
+ * another err_t on error.
+ */
+err_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+ if (pcb->state == LISTEN) {
+ return ERR_CONN;
+ }
+ if (shut_rx) {
+ /* shut down the receive side: free buffered data... */
+ if (pcb->refused_data != NULL) {
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ /* ... and set a flag not to receive any more data */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ if (shut_tx) {
+ /* This can't happen twice since if it succeeds, the pcb's state is changed.
+ Only close in these states as the others directly deallocate the PCB */
+ switch (pcb->state) {
+ case SYN_RCVD:
+ case ESTABLISHED:
+ case CLOSE_WAIT:
+ return tcp_close_shutdown(pcb, 0);
+ default:
+ /* don't shut down other states */
+ break;
+ }
+ }
+ /* @todo: return another err_t if not in correct state or already shut? */
+ return ERR_OK;
+}
+
+/**
* Abandons a connection and optionally sends a RST to the remote
* host. Deletes the local protocol control block. This is done when
* a connection is killed because of shortage of memory.
@@ -214,13 +319,15 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
{
u32_t seqno, ackno;
u16_t remote_port, local_port;
- struct ip_addr remote_ip, local_ip;
+ ip_addr_t remote_ip, local_ip;
#if LWIP_CALLBACK_API
- void (* errf)(void *arg, err_t err);
+ tcp_err_fn errf;
#endif /* LWIP_CALLBACK_API */
void *errf_arg;
-
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+ pcb->state != LISTEN);
/* Figure out on which TCP PCB list we are, and remove us. If we
are in an active state, call the receive function associated with
the PCB with a NULL argument, and send an RST to the remote end. */
@@ -230,8 +337,8 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
} else {
seqno = pcb->snd_nxt;
ackno = pcb->rcv_nxt;
- ip_addr_set(&local_ip, &(pcb->local_ip));
- ip_addr_set(&remote_ip, &(pcb->remote_ip));
+ ip_addr_copy(local_ip, pcb->local_ip);
+ ip_addr_copy(remote_ip, pcb->remote_ip);
local_port = pcb->local_port;
remote_port = pcb->remote_port;
#if LWIP_CALLBACK_API
@@ -260,6 +367,22 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
}
/**
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+ tcp_abandon(pcb, 1);
+}
+
+/**
* Binds the connection to a local portnumber and IP address. If the
* IP address is not given (i.e., ipaddr == NULL), the IP address of
* the outgoing network interface is used instead.
@@ -270,57 +393,51 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
* to any local address
* @param port the local port to bind to
* @return ERR_USE if the port is already in use
+ * ERR_VAL if bind failed because the PCB is not in a valid state
* ERR_OK if bound
*/
err_t
-tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
+tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
{
+ int i;
+ int max_pcb_list = NUM_TCP_PCB_LISTS;
struct tcp_pcb *cpcb;
- LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+ LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
+
+#if SO_REUSE
+ /* Unless the REUSEADDR flag is set,
+ we have to check the pcbs in TIME-WAIT state, also.
+ We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+ packets using both local and remote IP addresses and ports to distinguish.
+ */
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+ }
+#endif /* SO_REUSE */
if (port == 0) {
port = tcp_new_port();
}
- /* Check if the address already is in use. */
- /* Check the listen pcbs. */
- for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs;
- cpcb != NULL; cpcb = cpcb->next) {
- if (cpcb->local_port == port) {
- if (ip_addr_isany(&(cpcb->local_ip)) ||
- ip_addr_isany(ipaddr) ||
- ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
- return ERR_USE;
- }
- }
- }
- /* Check the connected pcbs. */
- for(cpcb = tcp_active_pcbs;
- cpcb != NULL; cpcb = cpcb->next) {
- if (cpcb->local_port == port) {
- if (ip_addr_isany(&(cpcb->local_ip)) ||
- ip_addr_isany(ipaddr) ||
- ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
- return ERR_USE;
- }
- }
- }
- /* Check the bound, not yet connected pcbs. */
- for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) {
- if (cpcb->local_port == port) {
- if (ip_addr_isany(&(cpcb->local_ip)) ||
- ip_addr_isany(ipaddr) ||
- ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
- return ERR_USE;
- }
- }
- }
- /* @todo: until SO_REUSEADDR is implemented (see task #6995 on savannah),
- * we have to check the pcbs in TIME-WAIT state, also: */
- for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) {
- if (cpcb->local_port == port) {
- if (ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
- return ERR_USE;
+
+ /* Check if the address already is in use (on all lists) */
+ for (i = 0; i < max_pcb_list; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if (cpcb->local_port == port) {
+#if SO_REUSE
+ /* Omit checking for the same port if both pcbs have REUSEADDR set.
+ For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+ tcp_connect. */
+ if (((pcb->so_options & SOF_REUSEADDR) == 0) ||
+ ((cpcb->so_options & SOF_REUSEADDR) == 0))
+#endif /* SO_REUSE */
+ {
+ if (ip_addr_isany(&(cpcb->local_ip)) ||
+ ip_addr_isany(ipaddr) ||
+ ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
+ return ERR_USE;
+ }
+ }
}
}
}
@@ -374,19 +491,37 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
if (pcb->state == LISTEN) {
return pcb;
}
- lpcb = memp_malloc(MEMP_TCP_PCB_LISTEN);
+#if SO_REUSE
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
+ is declared (listen-/connection-pcb), we have to make sure now that
+ this port is only used once for every local IP. */
+ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if (lpcb->local_port == pcb->local_port) {
+ if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
+ /* this address/port is already used */
+ return NULL;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
if (lpcb == NULL) {
return NULL;
}
lpcb->callback_arg = pcb->callback_arg;
lpcb->local_port = pcb->local_port;
lpcb->state = LISTEN;
+ lpcb->prio = pcb->prio;
lpcb->so_options = pcb->so_options;
lpcb->so_options |= SOF_ACCEPTCONN;
lpcb->ttl = pcb->ttl;
lpcb->tos = pcb->tos;
- ip_addr_set(&lpcb->local_ip, &pcb->local_ip);
- TCP_RMV(&tcp_bound_pcbs, pcb);
+ ip_addr_copy(lpcb->local_ip, pcb->local_ip);
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
memp_free(MEMP_TCP_PCB, pcb);
#if LWIP_CALLBACK_API
lpcb->accept = tcp_accept_null;
@@ -395,7 +530,7 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
lpcb->accepts_pending = 0;
lpcb->backlog = (backlog ? backlog : 1);
#endif /* TCP_LISTEN_BACKLOG */
- TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb);
+ TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
return (struct tcp_pcb *)lpcb;
}
@@ -420,7 +555,9 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
pcb->rcv_ann_wnd = 0;
} else {
/* keep the right edge of window constant */
- pcb->rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+ u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+ LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
+ pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd;
}
return 0;
}
@@ -443,17 +580,20 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len)
len <= 0xffff - pcb->rcv_wnd );
pcb->rcv_wnd += len;
- if (pcb->rcv_wnd > TCP_WND)
+ if (pcb->rcv_wnd > TCP_WND) {
pcb->rcv_wnd = TCP_WND;
+ }
wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
/* If the change in the right edge of window is significant (default
- * watermark is TCP_WND/2), then send an explicit update now.
+ * watermark is TCP_WND/4), then send an explicit update now.
* Otherwise wait for a packet to be sent in the normal course of
* events (or more window to be available later) */
- if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD)
+ if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
tcp_ack_now(pcb);
+ tcp_output(pcb);
+ }
LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
@@ -468,31 +608,26 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len)
static u16_t
tcp_new_port(void)
{
+ int i;
struct tcp_pcb *pcb;
#ifndef TCP_LOCAL_PORT_RANGE_START
-#define TCP_LOCAL_PORT_RANGE_START 4096
-#define TCP_LOCAL_PORT_RANGE_END 0x7fff
+/* From http://www.iana.org/assignments/port-numbers:
+ "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define TCP_LOCAL_PORT_RANGE_START 0xc000
+#define TCP_LOCAL_PORT_RANGE_END 0xffff
#endif
static u16_t port = TCP_LOCAL_PORT_RANGE_START;
again:
- if (++port > TCP_LOCAL_PORT_RANGE_END) {
+ if (port++ >= TCP_LOCAL_PORT_RANGE_END) {
port = TCP_LOCAL_PORT_RANGE_START;
}
-
- for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
- if (pcb->local_port == port) {
- goto again;
- }
- }
- for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
- if (pcb->local_port == port) {
- goto again;
- }
- }
- for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
- if (pcb->local_port == port) {
- goto again;
+ /* Check all PCB lists. */
+ for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
+ for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+ if (pcb->local_port == port) {
+ goto again;
+ }
}
}
return port;
@@ -511,13 +646,14 @@ tcp_new_port(void)
* other err_t values if connect request couldn't be sent
*/
err_t
-tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
- err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
+tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
+ tcp_connected_fn connected)
{
err_t ret;
u32_t iss;
+ u16_t old_local_port;
- LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+ LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
if (ipaddr != NULL) {
@@ -526,9 +662,44 @@ tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
return ERR_VAL;
}
pcb->remote_port = port;
+
+ /* check if we have a route to the remote host */
+ if (ip_addr_isany(&(pcb->local_ip))) {
+ /* no local IP address set, yet. */
+ struct netif *netif = ip_route(&(pcb->remote_ip));
+ if (netif == NULL) {
+ /* Don't even try to send a SYN packet if we have no route
+ since that will fail. */
+ return ERR_RTE;
+ }
+ /* Use the netif's IP address as local address. */
+ ip_addr_copy(pcb->local_ip, netif->ip_addr);
+ }
+
+ old_local_port = pcb->local_port;
if (pcb->local_port == 0) {
pcb->local_port = tcp_new_port();
}
+#if SO_REUSE
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
+ now that the 5-tuple is unique. */
+ struct tcp_pcb *cpcb;
+ int i;
+ /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+ for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if ((cpcb->local_port == pcb->local_port) &&
+ (cpcb->remote_port == port) &&
+ ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
+ ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
+ /* linux returns EISCONN here, but ERR_USE should be OK for us */
+ return ERR_USE;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
iss = tcp_next_iss();
pcb->rcv_nxt = 0;
pcb->snd_nxt = iss;
@@ -546,25 +717,27 @@ tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
pcb->cwnd = 1;
pcb->ssthresh = pcb->mss * 10;
- pcb->state = SYN_SENT;
-#if LWIP_CALLBACK_API
+#if LWIP_CALLBACK_API
pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(connected);
#endif /* LWIP_CALLBACK_API */
- TCP_RMV(&tcp_bound_pcbs, pcb);
- TCP_REG(&tcp_active_pcbs, pcb);
- snmp_inc_tcpactiveopens();
-
- ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS
-#if LWIP_TCP_TIMESTAMPS
- | TF_SEG_OPTS_TS
-#endif
- );
- if (ret == ERR_OK) {
+ /* Send a SYN together with the MSS option. */
+ ret = tcp_enqueue_flags(pcb, TCP_SYN);
+ if (ret == ERR_OK) {
+ /* SYN segment was enqueued, changed the pcbs state now */
+ pcb->state = SYN_SENT;
+ if (old_local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ TCP_REG(&tcp_active_pcbs, pcb);
+ snmp_inc_tcpactiveopens();
+
tcp_output(pcb);
}
return ret;
-}
+}
/**
* Called every 500 ms and implements the retransmission timer and the timer that
@@ -576,7 +749,7 @@ tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
void
tcp_slowtmr(void)
{
- struct tcp_pcb *pcb, *pcb2, *prev;
+ struct tcp_pcb *pcb, *prev;
u16_t eff_wnd;
u8_t pcb_remove; /* flag if a PCB should be removed */
u8_t pcb_reset; /* flag if a RST should be sent when removing */
@@ -643,8 +816,8 @@ tcp_slowtmr(void)
/* Reduce congestion window and ssthresh. */
eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
pcb->ssthresh = eff_wnd >> 1;
- if (pcb->ssthresh < pcb->mss) {
- pcb->ssthresh = pcb->mss * 2;
+ if (pcb->ssthresh < (pcb->mss << 1)) {
+ pcb->ssthresh = (pcb->mss << 1);
}
pcb->cwnd = pcb->mss;
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F
@@ -667,21 +840,21 @@ tcp_slowtmr(void)
}
/* Check if KEEPALIVE should be sent */
- if((pcb->so_options & SOF_KEEPALIVE) &&
- ((pcb->state == ESTABLISHED) ||
+ if((pcb->so_options & SOF_KEEPALIVE) &&
+ ((pcb->state == ESTABLISHED) ||
(pcb->state == CLOSE_WAIT))) {
#if LWIP_TCP_KEEPALIVE
- if((u32_t)(tcp_ticks - pcb->tmr) >
+ if((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl))
/ TCP_SLOW_INTERVAL)
#else
- if((u32_t)(tcp_ticks - pcb->tmr) >
+ if((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)
#endif /* LWIP_TCP_KEEPALIVE */
{
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
- ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
- ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
++pcb_remove;
++pcb_reset;
@@ -704,7 +877,7 @@ tcp_slowtmr(void)
/* If this PCB has queued out of sequence data, but has been
inactive for too long, will drop the data (it will eventually
be retransmitted). */
-#if TCP_QUEUE_OOSEQ
+#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL &&
(u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {
tcp_segs_free(pcb->ooseq);
@@ -732,7 +905,8 @@ tcp_slowtmr(void)
/* If the PCB should be removed, do it. */
if (pcb_remove) {
- tcp_pcb_purge(pcb);
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
/* Remove PCB from tcp_active_pcbs list. */
if (prev != NULL) {
LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
@@ -749,30 +923,31 @@ tcp_slowtmr(void)
pcb->local_port, pcb->remote_port);
}
- pcb2 = pcb->next;
- memp_free(MEMP_TCP_PCB, pcb);
- pcb = pcb2;
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
} else {
+ /* get the 'next' element now and work with 'prev' below (in case of abort) */
+ prev = pcb;
+ pcb = pcb->next;
/* We check if we should poll the connection. */
- ++pcb->polltmr;
- if (pcb->polltmr >= pcb->pollinterval) {
- pcb->polltmr = 0;
+ ++prev->polltmr;
+ if (prev->polltmr >= prev->pollinterval) {
+ prev->polltmr = 0;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
- TCP_EVENT_POLL(pcb, err);
+ TCP_EVENT_POLL(prev, err);
+ /* if err == ERR_ABRT, 'prev' is already deallocated */
if (err == ERR_OK) {
- tcp_output(pcb);
+ tcp_output(prev);
}
}
-
- prev = pcb;
- pcb = pcb->next;
}
}
/* Steps through all of the TIME-WAIT PCBs. */
- prev = NULL;
+ prev = NULL;
pcb = tcp_tw_pcbs;
while (pcb != NULL) {
LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
@@ -787,7 +962,8 @@ tcp_slowtmr(void)
/* If the PCB should be removed, do it. */
if (pcb_remove) {
- tcp_pcb_purge(pcb);
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
/* Remove PCB from tcp_tw_pcbs list. */
if (prev != NULL) {
LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
@@ -797,9 +973,9 @@ tcp_slowtmr(void)
LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
tcp_tw_pcbs = pcb->next;
}
- pcb2 = pcb->next;
- memp_free(MEMP_TCP_PCB, pcb);
- pcb = pcb2;
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
} else {
prev = pcb;
pcb = pcb->next;
@@ -816,9 +992,10 @@ tcp_slowtmr(void)
void
tcp_fasttmr(void)
{
- struct tcp_pcb *pcb;
+ struct tcp_pcb *pcb = tcp_active_pcbs;
- for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ while(pcb != NULL) {
+ struct tcp_pcb *next = pcb->next;
/* If there is data which was previously "refused" by upper layer */
if (pcb->refused_data != NULL) {
/* Notify again application with data previously received. */
@@ -827,15 +1004,21 @@ tcp_fasttmr(void)
TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
if (err == ERR_OK) {
pcb->refused_data = NULL;
+ } else if (err == ERR_ABRT) {
+ /* if err == ERR_ABRT, 'pcb' is already deallocated */
+ pcb = NULL;
}
}
- /* send delayed ACKs */
- if (pcb->flags & TF_ACK_DELAY) {
+ /* send delayed ACKs */
+ if (pcb && (pcb->flags & TF_ACK_DELAY)) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
tcp_ack_now(pcb);
+ tcp_output(pcb);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
+
+ pcb = next;
}
}
@@ -843,42 +1026,34 @@ tcp_fasttmr(void)
* Deallocates a list of TCP segments (tcp_seg structures).
*
* @param seg tcp_seg list of TCP segments to free
- * @return the number of pbufs that were deallocated
*/
-u8_t
+void
tcp_segs_free(struct tcp_seg *seg)
{
- u8_t count = 0;
- struct tcp_seg *next;
while (seg != NULL) {
- next = seg->next;
- count += tcp_seg_free(seg);
+ struct tcp_seg *next = seg->next;
+ tcp_seg_free(seg);
seg = next;
}
- return count;
}
/**
* Frees a TCP segment (tcp_seg structure).
*
* @param seg single tcp_seg to free
- * @return the number of pbufs that were deallocated
*/
-u8_t
+void
tcp_seg_free(struct tcp_seg *seg)
{
- u8_t count = 0;
-
if (seg != NULL) {
if (seg->p != NULL) {
- count = pbuf_free(seg->p);
+ pbuf_free(seg->p);
#if TCP_DEBUG
seg->p = NULL;
#endif /* TCP_DEBUG */
}
memp_free(MEMP_TCP_SEG, seg);
}
- return count;
}
/**
@@ -892,8 +1067,8 @@ tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
{
pcb->prio = prio;
}
-#if TCP_QUEUE_OOSEQ
+#if TCP_QUEUE_OOSEQ
/**
* Returns a copy of the given TCP segment.
* The pbuf and data are not copied, only the pointers
@@ -906,7 +1081,7 @@ tcp_seg_copy(struct tcp_seg *seg)
{
struct tcp_seg *cseg;
- cseg = memp_malloc(MEMP_TCP_SEG);
+ cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
if (cseg == NULL) {
return NULL;
}
@@ -914,7 +1089,7 @@ tcp_seg_copy(struct tcp_seg *seg)
pbuf_ref(cseg->p);
return cseg;
}
-#endif
+#endif /* TCP_QUEUE_OOSEQ */
#if LWIP_CALLBACK_API
/**
@@ -966,7 +1141,7 @@ tcp_kill_prio(u8_t prio)
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
(void *)inactive, inactivity));
tcp_abort(inactive);
- }
+ }
}
/**
@@ -992,7 +1167,7 @@ tcp_kill_timewait(void)
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
(void *)inactive, inactivity));
tcp_abort(inactive);
- }
+ }
}
/**
@@ -1007,19 +1182,19 @@ tcp_alloc(u8_t prio)
struct tcp_pcb *pcb;
u32_t iss;
- pcb = memp_malloc(MEMP_TCP_PCB);
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
/* Try killing oldest connection in TIME-WAIT. */
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
tcp_kill_timewait();
/* Try to allocate a tcp_pcb again. */
- pcb = memp_malloc(MEMP_TCP_PCB);
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
/* Try killing active connections with lower priority than the new one. */
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio));
tcp_kill_prio(prio);
/* Try to allocate a tcp_pcb again. */
- pcb = memp_malloc(MEMP_TCP_PCB);
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb != NULL) {
/* adjust err stats: memp_malloc failed twice before */
MEMP_STATS_DEC(err, MEMP_TCP_PCB);
@@ -1032,7 +1207,7 @@ tcp_alloc(u8_t prio)
}
if (pcb != NULL) {
memset(pcb, 0, sizeof(struct tcp_pcb));
- pcb->prio = TCP_PRIO_NORMAL;
+ pcb->prio = prio;
pcb->snd_buf = TCP_SND_BUF;
pcb->snd_queuelen = 0;
pcb->rcv_wnd = TCP_WND;
@@ -1113,8 +1288,7 @@ tcp_arg(struct tcp_pcb *pcb, void *arg)
* @param recv callback function to call for this pcb when data is received
*/
void
-tcp_recv(struct tcp_pcb *pcb,
- err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err))
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
{
pcb->recv = recv;
}
@@ -1127,8 +1301,7 @@ tcp_recv(struct tcp_pcb *pcb,
* @param sent callback function to call for this pcb when data is successfully sent
*/
void
-tcp_sent(struct tcp_pcb *pcb,
- err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len))
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
{
pcb->sent = sent;
}
@@ -1138,14 +1311,13 @@ tcp_sent(struct tcp_pcb *pcb,
* has occured on the connection.
*
* @param pcb tcp_pcb to set the err callback
- * @param errf callback function to call for this pcb when a fatal error
+ * @param err callback function to call for this pcb when a fatal error
* has occured on the connection
*/
void
-tcp_err(struct tcp_pcb *pcb,
- void (* errf)(void *arg, err_t err))
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
{
- pcb->errf = errf;
+ pcb->errf = err;
}
/**
@@ -1157,8 +1329,7 @@ tcp_err(struct tcp_pcb *pcb,
* connection has been connected to another host
*/
void
-tcp_accept(struct tcp_pcb *pcb,
- err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err))
+tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
{
pcb->accept = accept;
}
@@ -1172,11 +1343,12 @@ tcp_accept(struct tcp_pcb *pcb,
*
*/
void
-tcp_poll(struct tcp_pcb *pcb,
- err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval)
+tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
{
#if LWIP_CALLBACK_API
pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(poll);
#endif /* LWIP_CALLBACK_API */
pcb->pollinterval = interval;
}
@@ -1228,21 +1400,24 @@ tcp_pcb_purge(struct tcp_pcb *pcb)
if (pcb->unacked != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
}
-#if TCP_QUEUE_OOSEQ /* LW */
+#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
}
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
/* Stop the retransmission timer as it will expect data on unacked
queue if it fires */
pcb->rtime = -1;
- tcp_segs_free(pcb->ooseq);
- pcb->ooseq = NULL;
-#endif /* TCP_QUEUE_OOSEQ */
tcp_segs_free(pcb->unsent);
tcp_segs_free(pcb->unacked);
pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
}
}
@@ -1301,7 +1476,7 @@ tcp_next_iss(void)
* calculating the minimum of TCP_MSS and that netif's mtu (if set).
*/
u16_t
-tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr)
+tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr)
{
u16_t mss_s;
struct netif *outif;
@@ -1311,8 +1486,7 @@ tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr)
mss_s = outif->mtu - IP_HLEN - TCP_HLEN;
/* RFC 1122, chap 4.2.2.6:
* Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
- * We correct for TCP options in tcp_enqueue(), and don't support
- * IP options
+ * We correct for TCP options in tcp_write(), and don't support IP options.
*/
sendmss = LWIP_MIN(sendmss, mss_s);
}