#include "lwip/opt.h"
#if LWIP_TCP
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/tcp.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
#include "lwip/nd6.h"
#include <string.h>
#ifdef LWIP_HOOK_FILENAME
#include LWIP_HOOK_FILENAME
#endif
#ifndef TCP_LOCAL_PORT_RANGE_START
#define TCP_LOCAL_PORT_RANGE_START 0xc000
#define TCP_LOCAL_PORT_RANGE_END 0xffff
#define TCP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & (u16_t)~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START))
#endif
#if LWIP_TCP_KEEPALIVE
#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl)
#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl)
#else
#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE
#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT
#endif
#if TCP_MSS > 536
#define INITIAL_MSS 536
#else
#define INITIAL_MSS TCP_MSS
#endif
static const char *const tcp_state_str[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RCVD",
"ESTABLISHED",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START;
u32_t tcp_ticks;
static const u8_t tcp_backoff[13] =
{ 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
static const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
struct tcp_pcb *tcp_bound_pcbs;
union tcp_listen_pcbs_t tcp_listen_pcbs;
struct tcp_pcb *tcp_active_pcbs;
struct tcp_pcb *tcp_tw_pcbs;
struct tcp_pcb **const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
&tcp_active_pcbs, &tcp_tw_pcbs
};
u8_t tcp_active_pcbs_changed;
static u8_t tcp_timer;
static u8_t tcp_timer_ctr;
static u16_t tcp_new_port(void);
static err_t tcp_close_shutdown_fin(struct tcp_pcb *pcb);
#if LWIP_TCP_PCB_NUM_EXT_ARGS
static void tcp_ext_arg_invoke_callbacks_destroyed(struct tcp_pcb_ext_args *ext_args);
#endif
void
tcp_init(void)
{
#ifdef LWIP_RAND
tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
#endif
}
void
tcp_free(struct tcp_pcb *pcb)
{
LWIP_ASSERT("tcp_free: LISTEN", pcb->state != LISTEN);
#if LWIP_TCP_PCB_NUM_EXT_ARGS
tcp_ext_arg_invoke_callbacks_destroyed(pcb->ext_args);
#endif
memp_free(MEMP_TCP_PCB, pcb);
}
static void
tcp_free_listen(struct tcp_pcb *pcb)
{
LWIP_ASSERT("tcp_free_listen: !LISTEN", pcb->state != LISTEN);
#if LWIP_TCP_PCB_NUM_EXT_ARGS
tcp_ext_arg_invoke_callbacks_destroyed(pcb->ext_args);
#endif
memp_free(MEMP_TCP_PCB_LISTEN, pcb);
}
void
tcp_tmr(void)
{
tcp_fasttmr();
if (++tcp_timer & 1) {
tcp_slowtmr();
}
}
#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
static void
tcp_remove_listener(struct tcp_pcb *list, struct tcp_pcb_listen *lpcb)
{
struct tcp_pcb *pcb;
LWIP_ASSERT("tcp_remove_listener: invalid listener", lpcb != NULL);
for (pcb = list; pcb != NULL; pcb = pcb->next) {
if (pcb->listener == lpcb) {
pcb->listener = NULL;
}
}
}
#endif
static void
tcp_listen_closed(struct tcp_pcb *pcb)
{
#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
size_t i;
LWIP_ASSERT("pcb != NULL", pcb != NULL);
LWIP_ASSERT("pcb->state == LISTEN", pcb->state == LISTEN);
for (i = 1; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
tcp_remove_listener(*tcp_pcb_lists[i], (struct tcp_pcb_listen *)pcb);
}
#endif
LWIP_UNUSED_ARG(pcb);
}
#if TCP_LISTEN_BACKLOG
void
tcp_backlog_delayed(struct tcp_pcb *pcb)
{
LWIP_ASSERT("pcb != NULL", pcb != NULL);
LWIP_ASSERT_CORE_LOCKED();
if ((pcb->flags & TF_BACKLOGPEND) == 0) {
if (pcb->listener != NULL) {
pcb->listener->accepts_pending++;
LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
tcp_set_flags(pcb, TF_BACKLOGPEND);
}
}
}
void
tcp_backlog_accepted(struct tcp_pcb *pcb)
{
LWIP_ASSERT("pcb != NULL", pcb != NULL);
LWIP_ASSERT_CORE_LOCKED();
if ((pcb->flags & TF_BACKLOGPEND) != 0) {
if (pcb->listener != NULL) {
LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
pcb->listener->accepts_pending--;
tcp_clear_flags(pcb, TF_BACKLOGPEND);
}
}
}
#endif
static err_t
tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
{
LWIP_ASSERT("tcp_close_shutdown: invalid pcb", pcb != NULL);
if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND_MAX(pcb))) {
LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
pcb->local_port, pcb->remote_port);
tcp_pcb_purge(pcb);
TCP_RMV_ACTIVE(pcb);
if (tcp_input_pcb == pcb) {
tcp_trigger_input_pcb_close();
} else {
tcp_free(pcb);
}
return ERR_OK;
}
}
switch (pcb->state) {
case CLOSED:
if (pcb->local_port != 0) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
tcp_free(pcb);
break;
case LISTEN:
tcp_listen_closed(pcb);
tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
tcp_free_listen(pcb);
break;
case SYN_SENT:
TCP_PCB_REMOVE_ACTIVE(pcb);
tcp_free(pcb);
MIB2_STATS_INC(mib2.tcpattemptfails);
break;
default:
return tcp_close_shutdown_fin(pcb);
}
return ERR_OK;
}
static err_t
tcp_close_shutdown_fin(struct tcp_pcb *pcb)
{
err_t err;
LWIP_ASSERT("pcb != NULL", pcb != NULL);
switch (pcb->state) {
case SYN_RCVD:
err = tcp_send_fin(pcb);
if (err == ERR_OK) {
tcp_backlog_accepted(pcb);
MIB2_STATS_INC(mib2.tcpattemptfails);
pcb->state = FIN_WAIT_1;
}
break;
case ESTABLISHED:
err = tcp_send_fin(pcb);
if (err == ERR_OK) {
MIB2_STATS_INC(mib2.tcpestabresets);
pcb->state = FIN_WAIT_1;
}
break;
case CLOSE_WAIT:
err = tcp_send_fin(pcb);
if (err == ERR_OK) {
MIB2_STATS_INC(mib2.tcpestabresets);
pcb->state = LAST_ACK;
}
break;
default:
return ERR_OK;
}
if (err == ERR_OK) {
tcp_output(pcb);
} else if (err == ERR_MEM) {
tcp_set_flags(pcb, TF_CLOSEPEND);
return ERR_OK;
}
return err;
}
err_t
tcp_close(struct tcp_pcb *pcb)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_close: invalid pcb", pcb != NULL, return ERR_ARG);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
tcp_debug_print_state(pcb->state);
if (pcb->state != LISTEN) {
tcp_set_flags(pcb, TF_RXCLOSED);
}
return tcp_close_shutdown(pcb, 1);
}
err_t
tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_shutdown: invalid pcb", pcb != NULL, return ERR_ARG);
if (pcb->state == LISTEN) {
return ERR_CONN;
}
if (shut_rx) {
tcp_set_flags(pcb, TF_RXCLOSED);
if (shut_tx) {
return tcp_close_shutdown(pcb, 1);
}
if (pcb->refused_data != NULL) {
pbuf_free(pcb->refused_data);
pcb->refused_data = NULL;
}
}
if (shut_tx) {
switch (pcb->state) {
case SYN_RCVD:
case ESTABLISHED:
case CLOSE_WAIT:
return tcp_close_shutdown(pcb, (u8_t)shut_rx);
default:
return ERR_CONN;
}
}
return ERR_OK;
}
void
tcp_abandon(struct tcp_pcb *pcb, int reset)
{
u32_t seqno, ackno;
#if LWIP_CALLBACK_API
tcp_err_fn errf;
#endif
void *errf_arg;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_abandon: invalid pcb", pcb != NULL, return);
LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
pcb->state != LISTEN);
if (pcb->state == TIME_WAIT) {
tcp_pcb_remove(&tcp_tw_pcbs, pcb);
tcp_free(pcb);
} else {
int send_rst = 0;
u16_t local_port = 0;
enum tcp_state last_state;
seqno = pcb->snd_nxt;
ackno = pcb->rcv_nxt;
#if LWIP_CALLBACK_API
errf = pcb->errf;
#endif
errf_arg = pcb->callback_arg;
if (pcb->state == CLOSED) {
if (pcb->local_port != 0) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
} else {
send_rst = reset;
local_port = pcb->local_port;
TCP_PCB_REMOVE_ACTIVE(pcb);
}
if (pcb->unacked != NULL) {
tcp_segs_free(pcb->unacked);
}
if (pcb->unsent != NULL) {
tcp_segs_free(pcb->unsent);
}
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL) {
tcp_segs_free(pcb->ooseq);
}
#endif
tcp_backlog_accepted(pcb);
if (send_rst) {
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
tcp_rst(pcb, seqno, ackno, &pcb->local_ip, &pcb->remote_ip, local_port, pcb->remote_port);
}
last_state = pcb->state;
tcp_free(pcb);
TCP_EVENT_ERR(last_state, errf, errf_arg, ERR_ABRT);
}
}
void
tcp_abort(struct tcp_pcb *pcb)
{
tcp_abandon(pcb, 1);
}
err_t
tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
int i;
int max_pcb_list = NUM_TCP_PCB_LISTS;
struct tcp_pcb *cpcb;
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
ip_addr_t zoned_ipaddr;
#endif
LWIP_ASSERT_CORE_LOCKED();
#if LWIP_IPV4
if (ipaddr == NULL) {
ipaddr = IP4_ADDR_ANY;
}
#else
LWIP_ERROR("tcp_bind: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
#endif
LWIP_ERROR("tcp_bind: invalid pcb", pcb != NULL, return ERR_ARG);
LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
#if SO_REUSE
if (ip_get_option(pcb, SOF_REUSEADDR)) {
max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
}
#endif
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) {
ip_addr_copy(zoned_ipaddr, *ipaddr);
ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
ipaddr = &zoned_ipaddr;
}
#endif
if (port == 0) {
port = tcp_new_port();
if (port == 0) {
return ERR_BUF;
}
} else {
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
if (!ip_get_option(pcb, SOF_REUSEADDR) ||
!ip_get_option(cpcb, SOF_REUSEADDR))
#endif
{
if ((IP_IS_V6(ipaddr) == IP_IS_V6_VAL(cpcb->local_ip)) &&
(ip_addr_isany(&cpcb->local_ip) ||
ip_addr_isany(ipaddr) ||
ip_addr_cmp(&cpcb->local_ip, ipaddr))) {
return ERR_USE;
}
}
}
}
}
}
if (!ip_addr_isany(ipaddr)
#if LWIP_IPV4 && LWIP_IPV6
|| (IP_GET_TYPE(ipaddr) != IP_GET_TYPE(&pcb->local_ip))
#endif
) {
ip_addr_set(&pcb->local_ip, ipaddr);
}
pcb->local_port = port;
TCP_REG(&tcp_bound_pcbs, pcb);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
return ERR_OK;
}
void
tcp_bind_netif(struct tcp_pcb *pcb, const struct netif *netif)
{
LWIP_ASSERT_CORE_LOCKED();
if (netif != NULL) {
pcb->netif_idx = netif_get_index(netif);
} else {
pcb->netif_idx = NETIF_NO_INDEX;
}
}
#if LWIP_CALLBACK_API
static err_t
tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
LWIP_ASSERT("tcp_accept_null: invalid pcb", pcb != NULL);
tcp_abort(pcb);
return ERR_ABRT;
}
#endif
struct tcp_pcb *
tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
{
LWIP_ASSERT_CORE_LOCKED();
return tcp_listen_with_backlog_and_err(pcb, backlog, NULL);
}
struct tcp_pcb *
tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
{
struct tcp_pcb_listen *lpcb = NULL;
err_t res;
LWIP_UNUSED_ARG(backlog);
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_listen_with_backlog_and_err: invalid pcb", pcb != NULL, res = ERR_ARG; goto done);
LWIP_ERROR("tcp_listen_with_backlog_and_err: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done);
if (pcb->state == LISTEN) {
lpcb = (struct tcp_pcb_listen *)pcb;
res = ERR_ALREADY;
goto done;
}
#if SO_REUSE
if (ip_get_option(pcb, SOF_REUSEADDR)) {
for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if ((lpcb->local_port == pcb->local_port) &&
ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
lpcb = NULL;
res = ERR_USE;
goto done;
}
}
}
#endif
lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
if (lpcb == NULL) {
res = ERR_MEM;
goto done;
}
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->netif_idx = NETIF_NO_INDEX;
lpcb->ttl = pcb->ttl;
lpcb->tos = pcb->tos;
#if LWIP_IPV4 && LWIP_IPV6
IP_SET_TYPE_VAL(lpcb->remote_ip, pcb->local_ip.type);
#endif
ip_addr_copy(lpcb->local_ip, pcb->local_ip);
if (pcb->local_port != 0) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
#if LWIP_TCP_PCB_NUM_EXT_ARGS
memcpy(&lpcb->ext_args, &pcb->ext_args, sizeof(pcb->ext_args));
#endif
tcp_free(pcb);
#if LWIP_CALLBACK_API
lpcb->accept = tcp_accept_null;
#endif
#if TCP_LISTEN_BACKLOG
lpcb->accepts_pending = 0;
tcp_backlog_set(lpcb, backlog);
#endif
TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
res = ERR_OK;
done:
if (err != NULL) {
*err = res;
}
return (struct tcp_pcb *)lpcb;
}
u32_t
tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
{
u32_t new_right_edge;
LWIP_ASSERT("tcp_update_rcv_ann_wnd: invalid pcb", pcb != NULL);
new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
pcb->rcv_ann_wnd = pcb->rcv_wnd;
return new_right_edge - pcb->rcv_ann_right_edge;
} else {
if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
pcb->rcv_ann_wnd = 0;
} else {
u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
#if !LWIP_WND_SCALE
LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
#endif
pcb->rcv_ann_wnd = (tcpwnd_size_t)new_rcv_ann_wnd;
}
return 0;
}
}
void
tcp_recved(struct tcp_pcb *pcb, u16_t len)
{
u32_t wnd_inflation;
tcpwnd_size_t rcv_wnd;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_recved: invalid pcb", pcb != NULL, return);
LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
pcb->state != LISTEN);
rcv_wnd = (tcpwnd_size_t)(pcb->rcv_wnd + len);
if ((rcv_wnd > TCP_WND_MAX(pcb)) || (rcv_wnd < pcb->rcv_wnd)) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: window got too big or tcpwnd_size_t overflow\n"));
pcb->rcv_wnd = TCP_WND_MAX(pcb);
} else {
pcb->rcv_wnd = rcv_wnd;
}
wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
tcp_ack_now(pcb);
tcp_output(pcb);
}
LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: received %"U16_F" bytes, wnd %"TCPWNDSIZE_F" (%"TCPWNDSIZE_F").\n",
len, pcb->rcv_wnd, (u16_t)(TCP_WND_MAX(pcb) - pcb->rcv_wnd)));
}
static u16_t
tcp_new_port(void)
{
u8_t i;
u16_t n = 0;
struct tcp_pcb *pcb;
again:
tcp_port++;
if (tcp_port == TCP_LOCAL_PORT_RANGE_END) {
tcp_port = TCP_LOCAL_PORT_RANGE_START;
}
for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
for (pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
if (pcb->local_port == tcp_port) {
n++;
if (n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
return 0;
}
goto again;
}
}
}
return tcp_port;
}
err_t
tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
tcp_connected_fn connected)
{
struct netif *netif = NULL;
err_t ret;
u32_t iss;
u16_t old_local_port;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_connect: invalid pcb", pcb != NULL, return ERR_ARG);
LWIP_ERROR("tcp_connect: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
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));
ip_addr_set(&pcb->remote_ip, ipaddr);
pcb->remote_port = port;
if (pcb->netif_idx != NETIF_NO_INDEX) {
netif = netif_get_by_index(pcb->netif_idx);
} else {
netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
}
if (netif == NULL) {
return ERR_RTE;
}
if (ip_addr_isany(&pcb->local_ip)) {
const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, ipaddr);
if (local_ip == NULL) {
return ERR_RTE;
}
ip_addr_copy(pcb->local_ip, *local_ip);
}
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
if (IP_IS_V6(&pcb->remote_ip) &&
ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST)) {
ip6_addr_assign_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST, netif);
}
#endif
old_local_port = pcb->local_port;
if (pcb->local_port == 0) {
pcb->local_port = tcp_new_port();
if (pcb->local_port == 0) {
return ERR_BUF;
}
} else {
#if SO_REUSE
if (ip_get_option(pcb, SOF_REUSEADDR)) {
struct tcp_pcb *cpcb;
int i;
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)) {
return ERR_USE;
}
}
}
}
#endif
}
iss = tcp_next_iss(pcb);
pcb->rcv_nxt = 0;
pcb->snd_nxt = iss;
pcb->lastack = iss - 1;
pcb->snd_wl2 = iss - 1;
pcb->snd_lbb = iss - 1;
pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->snd_wnd = TCP_WND;
pcb->mss = INITIAL_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss_netif(pcb->mss, netif, &pcb->remote_ip);
#endif
pcb->cwnd = 1;
#if LWIP_CALLBACK_API
pcb->connected = connected;
#else
LWIP_UNUSED_ARG(connected);
#endif
ret = tcp_enqueue_flags(pcb, TCP_SYN);
if (ret == ERR_OK) {
pcb->state = SYN_SENT;
if (old_local_port != 0) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
TCP_REG_ACTIVE(pcb);
MIB2_STATS_INC(mib2.tcpactiveopens);
tcp_output(pcb);
}
return ret;
}
void
tcp_slowtmr(void)
{
struct tcp_pcb *pcb, *prev;
tcpwnd_size_t eff_wnd;
u8_t pcb_remove;
u8_t pcb_reset;
err_t err;
err = ERR_OK;
++tcp_ticks;
++tcp_timer_ctr;
tcp_slowtmr_start:
prev = NULL;
pcb = tcp_active_pcbs;
if (pcb == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
}
while (pcb != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
if (pcb->last_timer == tcp_timer_ctr) {
prev = pcb;
pcb = pcb->next;
continue;
}
pcb->last_timer = tcp_timer_ctr;
pcb_remove = 0;
pcb_reset = 0;
if (pcb->state == SYN_SENT && pcb->nrtx >= TCP_SYNMAXRTX) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
} else if (pcb->nrtx >= TCP_MAXRTX) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
} else {
if (pcb->persist_backoff > 0) {
LWIP_ASSERT("tcp_slowtimr: persist ticking with in-flight data", pcb->unacked == NULL);
LWIP_ASSERT("tcp_slowtimr: persist ticking with empty send buffer", pcb->unsent != NULL);
if (pcb->persist_probe >= TCP_MAXRTX) {
++pcb_remove;
} else {
u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff - 1];
if (pcb->persist_cnt < backoff_cnt) {
pcb->persist_cnt++;
}
if (pcb->persist_cnt >= backoff_cnt) {
int next_slot = 1;
if (pcb->snd_wnd == 0) {
if (tcp_zero_window_probe(pcb) != ERR_OK) {
next_slot = 0;
}
} else {
if (tcp_split_unsent_seg(pcb, (u16_t)pcb->snd_wnd) == ERR_OK) {
if (tcp_output(pcb) == ERR_OK) {
next_slot = 0;
}
}
}
if (next_slot) {
pcb->persist_cnt = 0;
if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
pcb->persist_backoff++;
}
}
}
}
} else {
if ((pcb->rtime >= 0) && (pcb->rtime < 0x7FFF)) {
++pcb->rtime;
}
if (pcb->rtime >= pcb->rto) {
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
" pcb->rto %"S16_F"\n",
pcb->rtime, pcb->rto));
if ((tcp_rexmit_rto_prepare(pcb) == ERR_OK) || ((pcb->unacked == NULL) && (pcb->unsent != NULL))) {
if (pcb->state != SYN_SENT) {
u8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff) - 1);
int calc_rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx];
pcb->rto = (s16_t)LWIP_MIN(calc_rto, 0x7FFF);
}
pcb->rtime = 0;
eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
pcb->ssthresh = eff_wnd >> 1;
if (pcb->ssthresh < (tcpwnd_size_t)(pcb->mss << 1)) {
pcb->ssthresh = (tcpwnd_size_t)(pcb->mss << 1);
}
pcb->cwnd = pcb->mss;
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F
" ssthresh %"TCPWNDSIZE_F"\n",
pcb->cwnd, pcb->ssthresh));
pcb->bytes_acked = 0;
tcp_rexmit_rto_commit(pcb);
}
}
}
}
if (pcb->state == FIN_WAIT_2) {
if (pcb->flags & TF_RXCLOSED) {
if ((u32_t)(tcp_ticks - pcb->tmr) >
TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
}
}
}
if (ip_get_option(pcb, SOF_KEEPALIVE) &&
((pcb->state == ESTABLISHED) ||
(pcb->state == CLOSE_WAIT))) {
if ((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to "));
ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
++pcb_remove;
++pcb_reset;
} else if ((u32_t)(tcp_ticks - pcb->tmr) >
(pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
/ TCP_SLOW_INTERVAL) {
err = tcp_keepalive(pcb);
if (err == ERR_OK) {
pcb->keep_cnt_sent++;
}
}
}
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL &&
(tcp_ticks - pcb->tmr >= (u32_t)pcb->rto * TCP_OOSEQ_TIMEOUT)) {
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
tcp_free_ooseq(pcb);
}
#endif
if (pcb->state == SYN_RCVD) {
if ((u32_t)(tcp_ticks - pcb->tmr) >
TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
}
}
if (pcb->state == LAST_ACK) {
if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n"));
}
}
if (pcb_remove) {
struct tcp_pcb *pcb2;
#if LWIP_CALLBACK_API
tcp_err_fn err_fn = pcb->errf;
#endif
void *err_arg;
enum tcp_state last_state;
tcp_pcb_purge(pcb);
if (prev != NULL) {
LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
prev->next = pcb->next;
} else {
LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
tcp_active_pcbs = pcb->next;
}
if (pcb_reset) {
tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
pcb->local_port, pcb->remote_port);
}
err_arg = pcb->callback_arg;
last_state = pcb->state;
pcb2 = pcb;
pcb = pcb->next;
tcp_free(pcb2);
tcp_active_pcbs_changed = 0;
TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT);
if (tcp_active_pcbs_changed) {
goto tcp_slowtmr_start;
}
} else {
prev = pcb;
pcb = pcb->next;
++prev->polltmr;
if (prev->polltmr >= prev->pollinterval) {
prev->polltmr = 0;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
tcp_active_pcbs_changed = 0;
TCP_EVENT_POLL(prev, err);
if (tcp_active_pcbs_changed) {
goto tcp_slowtmr_start;
}
if (err == ERR_OK) {
tcp_output(prev);
}
}
}
}
prev = NULL;
pcb = tcp_tw_pcbs;
while (pcb != NULL) {
LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
pcb_remove = 0;
if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
++pcb_remove;
}
if (pcb_remove) {
struct tcp_pcb *pcb2;
tcp_pcb_purge(pcb);
if (prev != NULL) {
LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
prev->next = pcb->next;
} else {
LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
tcp_tw_pcbs = pcb->next;
}
pcb2 = pcb;
pcb = pcb->next;
tcp_free(pcb2);
} else {
prev = pcb;
pcb = pcb->next;
}
}
}
void
tcp_fasttmr(void)
{
struct tcp_pcb *pcb;
++tcp_timer_ctr;
tcp_fasttmr_start:
pcb = tcp_active_pcbs;
while (pcb != NULL) {
if (pcb->last_timer != tcp_timer_ctr) {
struct tcp_pcb *next;
pcb->last_timer = tcp_timer_ctr;
if (pcb->flags & TF_ACK_DELAY) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
tcp_ack_now(pcb);
tcp_output(pcb);
tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
}
if (pcb->flags & TF_CLOSEPEND) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n"));
tcp_clear_flags(pcb, TF_CLOSEPEND);
tcp_close_shutdown_fin(pcb);
}
next = pcb->next;
if (pcb->refused_data != NULL) {
tcp_active_pcbs_changed = 0;
tcp_process_refused_data(pcb);
if (tcp_active_pcbs_changed) {
goto tcp_fasttmr_start;
}
}
pcb = next;
} else {
pcb = pcb->next;
}
}
}
void
tcp_txnow(void)
{
struct tcp_pcb *pcb;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->flags & TF_NAGLEMEMERR) {
tcp_output(pcb);
}
}
}
err_t
tcp_process_refused_data(struct tcp_pcb *pcb)
{
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
struct pbuf *rest;
#endif
LWIP_ERROR("tcp_process_refused_data: invalid pcb", pcb != NULL, return ERR_ARG);
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
while (pcb->refused_data != NULL)
#endif
{
err_t err;
u8_t refused_flags = pcb->refused_data->flags;
struct pbuf *refused_data = pcb->refused_data;
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
pbuf_split_64k(refused_data, &rest);
pcb->refused_data = rest;
#else
pcb->refused_data = NULL;
#endif
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
if (err == ERR_OK) {
if ((refused_flags & PBUF_FLAG_TCP_FIN)
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
&& (rest == NULL)
#endif
) {
if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
pcb->rcv_wnd++;
}
TCP_EVENT_CLOSED(pcb, err);
if (err == ERR_ABRT) {
return ERR_ABRT;
}
}
} else if (err == ERR_ABRT) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
return ERR_ABRT;
} else {
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
if (rest != NULL) {
pbuf_cat(refused_data, rest);
}
#endif
pcb->refused_data = refused_data;
return ERR_INPROGRESS;
}
}
return ERR_OK;
}
void
tcp_segs_free(struct tcp_seg *seg)
{
while (seg != NULL) {
struct tcp_seg *next = seg->next;
tcp_seg_free(seg);
seg = next;
}
}
void
tcp_seg_free(struct tcp_seg *seg)
{
if (seg != NULL) {
if (seg->p != NULL) {
pbuf_free(seg->p);
#if TCP_DEBUG
seg->p = NULL;
#endif
}
memp_free(MEMP_TCP_SEG, seg);
}
}
void
tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_setprio: invalid pcb", pcb != NULL, return);
pcb->prio = prio;
}
#if TCP_QUEUE_OOSEQ
struct tcp_seg *
tcp_seg_copy(struct tcp_seg *seg)
{
struct tcp_seg *cseg;
LWIP_ASSERT("tcp_seg_copy: invalid seg", seg != NULL);
cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
if (cseg == NULL) {
return NULL;
}
SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
pbuf_ref(cseg->p);
return cseg;
}
#endif
#if LWIP_CALLBACK_API
err_t
tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_ERROR("tcp_recv_null: invalid pcb", pcb != NULL, return ERR_ARG);
if (p != NULL) {
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
} else if (err == ERR_OK) {
return tcp_close(pcb);
}
return ERR_OK;
}
#endif
static void
tcp_kill_prio(u8_t prio)
{
struct tcp_pcb *pcb, *inactive;
u32_t inactivity;
u8_t mprio;
mprio = LWIP_MIN(TCP_PRIO_MAX, prio);
if (mprio == 0) {
return;
}
mprio--;
inactivity = 0;
inactive = NULL;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if ((pcb->prio < mprio) ||
((pcb->prio == mprio) && ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity))) {
inactivity = tcp_ticks - pcb->tmr;
inactive = pcb;
mprio = pcb->prio;
}
}
if (inactive != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
(void *)inactive, inactivity));
tcp_abort(inactive);
}
}
static void
tcp_kill_state(enum tcp_state state)
{
struct tcp_pcb *pcb, *inactive;
u32_t inactivity;
LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK));
inactivity = 0;
inactive = NULL;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->state == state) {
if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
inactivity = tcp_ticks - pcb->tmr;
inactive = pcb;
}
}
}
if (inactive != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_closing: killing oldest %s PCB %p (%"S32_F")\n",
tcp_state_str[state], (void *)inactive, inactivity));
tcp_abandon(inactive, 0);
}
}
static void
tcp_kill_timewait(void)
{
struct tcp_pcb *pcb, *inactive;
u32_t inactivity;
inactivity = 0;
inactive = NULL;
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
inactivity = tcp_ticks - pcb->tmr;
inactive = pcb;
}
}
if (inactive != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
(void *)inactive, inactivity));
tcp_abort(inactive);
}
}
static void
tcp_handle_closepend(void)
{
struct tcp_pcb *pcb = tcp_active_pcbs;
while (pcb != NULL) {
struct tcp_pcb *next = pcb->next;
if (pcb->flags & TF_CLOSEPEND) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_handle_closepend: pending FIN\n"));
tcp_clear_flags(pcb, TF_CLOSEPEND);
tcp_close_shutdown_fin(pcb);
}
pcb = next;
}
}
struct tcp_pcb *
tcp_alloc(u8_t prio)
{
struct tcp_pcb *pcb;
LWIP_ASSERT_CORE_LOCKED();
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
tcp_handle_closepend();
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
tcp_kill_timewait();
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest LAST-ACK connection\n"));
tcp_kill_state(LAST_ACK);
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest CLOSING connection\n"));
tcp_kill_state(CLOSING);
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing oldest connection with prio lower than %d\n", prio));
tcp_kill_prio(prio);
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb != NULL) {
MEMP_STATS_DEC(err, MEMP_TCP_PCB);
}
}
if (pcb != NULL) {
MEMP_STATS_DEC(err, MEMP_TCP_PCB);
}
}
if (pcb != NULL) {
MEMP_STATS_DEC(err, MEMP_TCP_PCB);
}
}
if (pcb != NULL) {
MEMP_STATS_DEC(err, MEMP_TCP_PCB);
}
}
if (pcb != NULL) {
memset(pcb, 0, sizeof(struct tcp_pcb));
pcb->prio = prio;
pcb->snd_buf = TCP_SND_BUF;
pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
pcb->ttl = TCP_TTL;
pcb->mss = INITIAL_MSS;
pcb->rto = 3000 / TCP_SLOW_INTERVAL;
pcb->sv = 3000 / TCP_SLOW_INTERVAL;
pcb->rtime = -1;
pcb->cwnd = 1;
pcb->tmr = tcp_ticks;
pcb->last_timer = tcp_timer_ctr;
pcb->ssthresh = TCP_SND_BUF;
#if LWIP_CALLBACK_API
pcb->recv = tcp_recv_null;
#endif
pcb->keep_idle = TCP_KEEPIDLE_DEFAULT;
#if LWIP_TCP_KEEPALIVE
pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
pcb->keep_cnt = TCP_KEEPCNT_DEFAULT;
#endif
}
return pcb;
}
struct tcp_pcb *
tcp_new(void)
{
return tcp_alloc(TCP_PRIO_NORMAL);
}
struct tcp_pcb *
tcp_new_ip_type(u8_t type)
{
struct tcp_pcb *pcb;
pcb = tcp_alloc(TCP_PRIO_NORMAL);
#if LWIP_IPV4 && LWIP_IPV6
if (pcb != NULL) {
IP_SET_TYPE_VAL(pcb->local_ip, type);
IP_SET_TYPE_VAL(pcb->remote_ip, type);
}
#else
LWIP_UNUSED_ARG(type);
#endif
return pcb;
}
void
tcp_arg(struct tcp_pcb *pcb, void *arg)
{
LWIP_ASSERT_CORE_LOCKED();
if (pcb != NULL) {
pcb->callback_arg = arg;
}
}
#if LWIP_CALLBACK_API
void
tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
{
LWIP_ASSERT_CORE_LOCKED();
if (pcb != NULL) {
LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
pcb->recv = recv;
}
}
void
tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
{
LWIP_ASSERT_CORE_LOCKED();
if (pcb != NULL) {
LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
pcb->sent = sent;
}
}
void
tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
{
LWIP_ASSERT_CORE_LOCKED();
if (pcb != NULL) {
LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
pcb->errf = err;
}
}
void
tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
{
LWIP_ASSERT_CORE_LOCKED();
if ((pcb != NULL) && (pcb->state == LISTEN)) {
struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen *)pcb;
lpcb->accept = accept;
}
}
#endif
void
tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("tcp_poll: invalid pcb", pcb != NULL, return);
LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN);
#if LWIP_CALLBACK_API
pcb->poll = poll;
#else
LWIP_UNUSED_ARG(poll);
#endif
pcb->pollinterval = interval;
}
void
tcp_pcb_purge(struct tcp_pcb *pcb)
{
LWIP_ERROR("tcp_pcb_purge: invalid pcb", pcb != NULL, return);
if (pcb->state != CLOSED &&
pcb->state != TIME_WAIT &&
pcb->state != LISTEN) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
tcp_backlog_accepted(pcb);
if (pcb->refused_data != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
pbuf_free(pcb->refused_data);
pcb->refused_data = NULL;
}
if (pcb->unsent != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
}
if (pcb->unacked != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
}
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
tcp_free_ooseq(pcb);
}
#endif
pcb->rtime = -1;
tcp_segs_free(pcb->unsent);
tcp_segs_free(pcb->unacked);
pcb->unacked = pcb->unsent = NULL;
#if TCP_OVERSIZE
pcb->unsent_oversize = 0;
#endif
}
}
void
tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
{
LWIP_ASSERT("tcp_pcb_remove: invalid pcb", pcb != NULL);
LWIP_ASSERT("tcp_pcb_remove: invalid pcblist", pcblist != NULL);
TCP_RMV(pcblist, pcb);
tcp_pcb_purge(pcb);
if ((pcb->state != TIME_WAIT) &&
(pcb->state != LISTEN) &&
(pcb->flags & TF_ACK_DELAY)) {
tcp_ack_now(pcb);
tcp_output(pcb);
}
if (pcb->state != LISTEN) {
LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
#if TCP_QUEUE_OOSEQ
LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
#endif
}
pcb->state = CLOSED;
pcb->local_port = 0;
LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
}
u32_t
tcp_next_iss(struct tcp_pcb *pcb)
{
#ifdef LWIP_HOOK_TCP_ISN
LWIP_ASSERT("tcp_next_iss: invalid pcb", pcb != NULL);
return LWIP_HOOK_TCP_ISN(&pcb->local_ip, pcb->local_port, &pcb->remote_ip, pcb->remote_port);
#else
static u32_t iss = 6510;
LWIP_ASSERT("tcp_next_iss: invalid pcb", pcb != NULL);
LWIP_UNUSED_ARG(pcb);
iss += tcp_ticks;
return iss;
#endif
}
#if TCP_CALCULATE_EFF_SEND_MSS
u16_t
tcp_eff_send_mss_netif(u16_t sendmss, struct netif *outif, const ip_addr_t *dest)
{
u16_t mss_s;
u16_t mtu;
LWIP_UNUSED_ARG(dest);
LWIP_ASSERT("tcp_eff_send_mss_netif: invalid dst_ip", dest != NULL);
#if LWIP_IPV6
#if LWIP_IPV4
if (IP_IS_V6(dest))
#endif
{
mtu = nd6_get_destination_mtu(ip_2_ip6(dest), outif);
}
#if LWIP_IPV4
else
#endif
#endif
#if LWIP_IPV4
{
if (outif == NULL) {
return sendmss;
}
mtu = outif->mtu;
}
#endif
if (mtu != 0) {
u16_t offset;
#if LWIP_IPV6
#if LWIP_IPV4
if (IP_IS_V6(dest))
#endif
{
offset = IP6_HLEN + TCP_HLEN;
}
#if LWIP_IPV4
else
#endif
#endif
#if LWIP_IPV4
{
offset = IP_HLEN + TCP_HLEN;
}
#endif
mss_s = (mtu > offset) ? (u16_t)(mtu - offset) : 0;
sendmss = LWIP_MIN(sendmss, mss_s);
}
return sendmss;
}
#endif
static void
tcp_netif_ip_addr_changed_pcblist(const ip_addr_t *old_addr, struct tcp_pcb *pcb_list)
{
struct tcp_pcb *pcb;
pcb = pcb_list;
LWIP_ASSERT("tcp_netif_ip_addr_changed_pcblist: invalid old_addr", old_addr != NULL);
while (pcb != NULL) {
if (ip_addr_cmp(&pcb->local_ip, old_addr)
#if LWIP_AUTOIP
&& (!IP_IS_V4_VAL(pcb->local_ip) || !ip4_addr_islinklocal(ip_2_ip4(&pcb->local_ip)))
#endif
) {
struct tcp_pcb *next = pcb->next;
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
tcp_abort(pcb);
pcb = next;
} else {
pcb = pcb->next;
}
}
}
void
tcp_netif_ip_addr_changed(const ip_addr_t *old_addr, const ip_addr_t *new_addr)
{
struct tcp_pcb_listen *lpcb;
if (!ip_addr_isany(old_addr)) {
tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_active_pcbs);
tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_bound_pcbs);
if (!ip_addr_isany(new_addr)) {
for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if (ip_addr_cmp(&lpcb->local_ip, old_addr)) {
ip_addr_copy(lpcb->local_ip, *new_addr);
}
}
}
}
}
const char *
tcp_debug_state_str(enum tcp_state s)
{
return tcp_state_str[s];
}
err_t
tcp_tcp_get_tcp_addrinfo(struct tcp_pcb *pcb, int local, ip_addr_t *addr, u16_t *port)
{
if (pcb) {
if (local) {
if (addr) {
*addr = pcb->local_ip;
}
if (port) {
*port = pcb->local_port;
}
} else {
if (addr) {
*addr = pcb->remote_ip;
}
if (port) {
*port = pcb->remote_port;
}
}
return ERR_OK;
}
return ERR_VAL;
}
#if TCP_QUEUE_OOSEQ
void
tcp_free_ooseq(struct tcp_pcb *pcb)
{
if (pcb->ooseq) {
tcp_segs_free(pcb->ooseq);
pcb->ooseq = NULL;
#if LWIP_TCP_SACK_OUT
memset(pcb->rcv_sacks, 0, sizeof(pcb->rcv_sacks));
#endif
}
}
#endif
#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
void
tcp_debug_print(struct tcp_hdr *tcphdr)
{
LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
lwip_ntohs(tcphdr->src), lwip_ntohs(tcphdr->dest)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n",
lwip_ntohl(tcphdr->seqno)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n",
lwip_ntohl(tcphdr->ackno)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (",
TCPH_HDRLEN(tcphdr),
(u16_t)(TCPH_FLAGS(tcphdr) >> 5 & 1),
(u16_t)(TCPH_FLAGS(tcphdr) >> 4 & 1),
(u16_t)(TCPH_FLAGS(tcphdr) >> 3 & 1),
(u16_t)(TCPH_FLAGS(tcphdr) >> 2 & 1),
(u16_t)(TCPH_FLAGS(tcphdr) >> 1 & 1),
(u16_t)(TCPH_FLAGS(tcphdr) & 1),
lwip_ntohs(tcphdr->wnd)));
tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n",
lwip_ntohs(tcphdr->chksum), lwip_ntohs(tcphdr->urgp)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
}
void
tcp_debug_print_state(enum tcp_state s)
{
LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
}
void
tcp_debug_print_flags(u8_t flags)
{
if (flags & TCP_FIN) {
LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
}
if (flags & TCP_SYN) {
LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
}
if (flags & TCP_RST) {
LWIP_DEBUGF(TCP_DEBUG, ("RST "));
}
if (flags & TCP_PSH) {
LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
}
if (flags & TCP_ACK) {
LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
}
if (flags & TCP_URG) {
LWIP_DEBUGF(TCP_DEBUG, ("URG "));
}
if (flags & TCP_ECE) {
LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
}
if (flags & TCP_CWR) {
LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
}
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
}
void
tcp_debug_print_pcbs(void)
{
struct tcp_pcb *pcb;
struct tcp_pcb_listen *pcbl;
LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt));
tcp_debug_print_state(pcb->state);
}
LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
for (pcbl = tcp_listen_pcbs.listen_pcbs; pcbl != NULL; pcbl = pcbl->next) {
LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F" ", pcbl->local_port));
tcp_debug_print_state(pcbl->state);
}
LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt));
tcp_debug_print_state(pcb->state);
}
}
s16_t
tcp_pcbs_sane(void)
{
struct tcp_pcb *pcb;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
}
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
}
return 1;
}
#endif
#if LWIP_TCP_PCB_NUM_EXT_ARGS
static u8_t tcp_ext_arg_id;
u8_t
tcp_ext_arg_alloc_id(void)
{
u8_t result = tcp_ext_arg_id;
tcp_ext_arg_id++;
LWIP_ASSERT_CORE_LOCKED();
#if LWIP_TCP_PCB_NUM_EXT_ARGS >= 255
#error LWIP_TCP_PCB_NUM_EXT_ARGS
#endif
LWIP_ASSERT("Increase LWIP_TCP_PCB_NUM_EXT_ARGS in lwipopts.h", result < LWIP_TCP_PCB_NUM_EXT_ARGS);
return result;
}
void
tcp_ext_arg_set_callbacks(struct tcp_pcb *pcb, uint8_t id, const struct tcp_ext_arg_callbacks * const callbacks)
{
LWIP_ASSERT("pcb != NULL", pcb != NULL);
LWIP_ASSERT("id < LWIP_TCP_PCB_NUM_EXT_ARGS", id < LWIP_TCP_PCB_NUM_EXT_ARGS);
LWIP_ASSERT("callbacks != NULL", callbacks != NULL);
LWIP_ASSERT_CORE_LOCKED();
pcb->ext_args[id].callbacks = callbacks;
}
void tcp_ext_arg_set(struct tcp_pcb *pcb, uint8_t id, void *arg)
{
LWIP_ASSERT("pcb != NULL", pcb != NULL);
LWIP_ASSERT("id < LWIP_TCP_PCB_NUM_EXT_ARGS", id < LWIP_TCP_PCB_NUM_EXT_ARGS);
LWIP_ASSERT_CORE_LOCKED();
pcb->ext_args[id].data = arg;
}
void *tcp_ext_arg_get(const struct tcp_pcb *pcb, uint8_t id)
{
LWIP_ASSERT("pcb != NULL", pcb != NULL);
LWIP_ASSERT("id < LWIP_TCP_PCB_NUM_EXT_ARGS", id < LWIP_TCP_PCB_NUM_EXT_ARGS);
LWIP_ASSERT_CORE_LOCKED();
return pcb->ext_args[id].data;
}
static void
tcp_ext_arg_invoke_callbacks_destroyed(struct tcp_pcb_ext_args *ext_args)
{
int i;
LWIP_ASSERT("ext_args != NULL", ext_args != NULL);
for (i = 0; i < LWIP_TCP_PCB_NUM_EXT_ARGS; i++) {
if (ext_args[i].callbacks != NULL) {
if (ext_args[i].callbacks->destroy != NULL) {
ext_args[i].callbacks->destroy((u8_t)i, ext_args[i].data);
}
}
}
}
err_t
tcp_ext_arg_invoke_callbacks_passive_open(struct tcp_pcb_listen *lpcb, struct tcp_pcb *cpcb)
{
int i;
LWIP_ASSERT("lpcb != NULL", lpcb != NULL);
LWIP_ASSERT("cpcb != NULL", cpcb != NULL);
for (i = 0; i < LWIP_TCP_PCB_NUM_EXT_ARGS; i++) {
if (lpcb->ext_args[i].callbacks != NULL) {
if (lpcb->ext_args[i].callbacks->passive_open != NULL) {
err_t err = lpcb->ext_args[i].callbacks->passive_open((u8_t)i, lpcb, cpcb);
if (err != ERR_OK) {
return err;
}
}
}
}
return ERR_OK;
}
#endif
#endif