#include "lwip/opt.h"
#if LWIP_TCP
#include "lwip/priv/tcp_priv.h"
#include "lwip/def.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/inet_chksum.h"
#include "lwip/stats.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
#if LWIP_ND6_TCP_REACHABILITY_HINTS
#include "lwip/nd6.h"
#endif
#include <string.h>
#ifdef LWIP_HOOK_FILENAME
#include LWIP_HOOK_FILENAME
#endif
#define LWIP_TCP_CALC_INITIAL_CWND(mss) ((tcpwnd_size_t)LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U)))
static struct tcp_seg inseg;
static struct tcp_hdr *tcphdr;
static u16_t tcphdr_optlen;
static u16_t tcphdr_opt1len;
static u8_t *tcphdr_opt2;
static u16_t tcp_optidx;
static u32_t seqno, ackno;
static tcpwnd_size_t recv_acked;
static u16_t tcplen;
static u8_t flags;
static u8_t recv_flags;
static struct pbuf *recv_data;
struct tcp_pcb *tcp_input_pcb;
static err_t tcp_process(struct tcp_pcb *pcb);
static void tcp_receive(struct tcp_pcb *pcb);
static void tcp_parseopt(struct tcp_pcb *pcb);
static void tcp_listen_input(struct tcp_pcb_listen *pcb);
static void tcp_timewait_input(struct tcp_pcb *pcb);
static int tcp_input_delayed_close(struct tcp_pcb *pcb);
#if LWIP_TCP_SACK_OUT
static void tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right);
static void tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq);
#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
static void tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq);
#endif
#endif
void
tcp_input(struct pbuf *p, struct netif *inp)
{
struct tcp_pcb *pcb, *prev;
struct tcp_pcb_listen *lpcb;
#if SO_REUSE
struct tcp_pcb *lpcb_prev = NULL;
struct tcp_pcb_listen *lpcb_any = NULL;
#endif
u8_t hdrlen_bytes;
err_t err;
LWIP_UNUSED_ARG(inp);
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("tcp_input: invalid pbuf", p != NULL);
PERF_START;
TCP_STATS_INC(tcp.recv);
MIB2_STATS_INC(mib2.tcpinsegs);
tcphdr = (struct tcp_hdr *)p->payload;
#if TCP_INPUT_DEBUG
tcp_debug_print(tcphdr);
#endif
if (p->len < TCP_HLEN) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
TCP_STATS_INC(tcp.lenerr);
goto dropped;
}
if (ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()) ||
ip_addr_ismulticast(ip_current_dest_addr())) {
TCP_STATS_INC(tcp.proterr);
goto dropped;
}
#if CHECKSUM_CHECK_TCP
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_TCP) {
u16_t chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
ip_current_src_addr(), ip_current_dest_addr());
if (chksum != 0) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
chksum));
tcp_debug_print(tcphdr);
TCP_STATS_INC(tcp.chkerr);
goto dropped;
}
}
#endif
hdrlen_bytes = TCPH_HDRLEN_BYTES(tcphdr);
if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: invalid header length (%"U16_F")\n", (u16_t)hdrlen_bytes));
TCP_STATS_INC(tcp.lenerr);
goto dropped;
}
tcphdr_optlen = (u16_t)(hdrlen_bytes - TCP_HLEN);
tcphdr_opt2 = NULL;
if (p->len >= hdrlen_bytes) {
tcphdr_opt1len = tcphdr_optlen;
pbuf_remove_header(p, hdrlen_bytes);
} else {
u16_t opt2len;
LWIP_ASSERT("p->next != NULL", p->next != NULL);
pbuf_remove_header(p, TCP_HLEN);
tcphdr_opt1len = p->len;
opt2len = (u16_t)(tcphdr_optlen - tcphdr_opt1len);
pbuf_remove_header(p, tcphdr_opt1len);
if (opt2len > p->next->len) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: options overflow second pbuf (%"U16_F" bytes)\n", p->next->len));
TCP_STATS_INC(tcp.lenerr);
goto dropped;
}
tcphdr_opt2 = (u8_t *)p->next->payload;
pbuf_remove_header(p->next, opt2len);
p->tot_len = (u16_t)(p->tot_len - opt2len);
LWIP_ASSERT("p->len == 0", p->len == 0);
LWIP_ASSERT("p->tot_len == p->next->tot_len", p->tot_len == p->next->tot_len);
}
tcphdr->src = lwip_ntohs(tcphdr->src);
tcphdr->dest = lwip_ntohs(tcphdr->dest);
seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno);
ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno);
tcphdr->wnd = lwip_ntohs(tcphdr->wnd);
flags = TCPH_FLAGS(tcphdr);
tcplen = p->tot_len;
if (flags & (TCP_FIN | TCP_SYN)) {
tcplen++;
if (tcplen < p->tot_len) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: length u16_t overflow, cannot handle this\n"));
TCP_STATS_INC(tcp.lenerr);
goto dropped;
}
}
prev = NULL;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
if ((pcb->netif_idx != NETIF_NO_INDEX) &&
(pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
prev = pcb;
continue;
}
if (pcb->remote_port == tcphdr->src &&
pcb->local_port == tcphdr->dest &&
ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()) &&
ip_addr_eq(&pcb->local_ip, ip_current_dest_addr())) {
LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
if (prev != NULL) {
prev->next = pcb->next;
pcb->next = tcp_active_pcbs;
tcp_active_pcbs = pcb;
} else {
TCP_STATS_INC(tcp.cachehit);
}
LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
break;
}
prev = pcb;
}
if (pcb == NULL) {
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
if ((pcb->netif_idx != NETIF_NO_INDEX) &&
(pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
continue;
}
if (pcb->remote_port == tcphdr->src &&
pcb->local_port == tcphdr->dest &&
ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()) &&
ip_addr_eq(&pcb->local_ip, ip_current_dest_addr())) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
#ifdef LWIP_HOOK_TCP_INPACKET_PCB
if (LWIP_HOOK_TCP_INPACKET_PCB(pcb, tcphdr, tcphdr_optlen, tcphdr_opt1len,
tcphdr_opt2, p) == ERR_OK)
#endif
{
tcp_timewait_input(pcb);
}
pbuf_free(p);
return;
}
}
prev = NULL;
for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if ((lpcb->netif_idx != NETIF_NO_INDEX) &&
(lpcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
prev = (struct tcp_pcb *)lpcb;
continue;
}
if ((lpcb->pretend_netif_idx != NETIF_NO_INDEX) &&
(lpcb->pretend_netif_idx == netif_get_index(inp))) {
if (IP_ADDR_PCB_VERSION_MATCH(lpcb, ip_current_dest_addr())) {
break;
}
} else if (lpcb->local_port == tcphdr->dest) {
if (IP_IS_ANY_TYPE_VAL(lpcb->local_ip)) {
#if SO_REUSE
lpcb_any = lpcb;
lpcb_prev = prev;
#else
break;
#endif
} else if (IP_ADDR_PCB_VERSION_MATCH_EXACT(lpcb, ip_current_dest_addr())) {
if (ip_addr_eq(&lpcb->local_ip, ip_current_dest_addr())) {
break;
} else if (ip_addr_isany(&lpcb->local_ip)) {
#if SO_REUSE
lpcb_any = lpcb;
lpcb_prev = prev;
#else
break;
#endif
}
}
}
prev = (struct tcp_pcb *)lpcb;
}
#if SO_REUSE
if (lpcb == NULL) {
lpcb = lpcb_any;
prev = lpcb_prev;
}
#endif
if (lpcb != NULL) {
if (prev != NULL) {
((struct tcp_pcb_listen *)prev)->next = lpcb->next;
lpcb->next = tcp_listen_pcbs.listen_pcbs;
tcp_listen_pcbs.listen_pcbs = lpcb;
} else {
TCP_STATS_INC(tcp.cachehit);
}
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
#ifdef LWIP_HOOK_TCP_INPACKET_PCB
if (LWIP_HOOK_TCP_INPACKET_PCB((struct tcp_pcb *)lpcb, tcphdr, tcphdr_optlen,
tcphdr_opt1len, tcphdr_opt2, p) == ERR_OK)
#endif
{
tcp_listen_input(lpcb);
}
pbuf_free(p);
return;
}
}
#if TCP_INPUT_DEBUG
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
#endif
#ifdef LWIP_HOOK_TCP_INPACKET_PCB
if ((pcb != NULL) && LWIP_HOOK_TCP_INPACKET_PCB(pcb, tcphdr, tcphdr_optlen,
tcphdr_opt1len, tcphdr_opt2, p) != ERR_OK) {
pbuf_free(p);
return;
}
#endif
if (pcb != NULL) {
#if TCP_INPUT_DEBUG
tcp_debug_print_state(pcb->state);
#endif
inseg.next = NULL;
inseg.len = p->tot_len;
inseg.p = p;
inseg.tcphdr = tcphdr;
recv_data = NULL;
recv_flags = 0;
recv_acked = 0;
if (flags & TCP_PSH) {
p->flags |= PBUF_FLAG_PUSH;
}
if (pcb->refused_data != NULL) {
if ((tcp_process_refused_data(pcb) == ERR_ABRT) ||
((pcb->refused_data != NULL) && (tcplen > 0))) {
if (pcb->rcv_ann_wnd == 0) {
tcp_send_empty_ack(pcb);
}
TCP_STATS_INC(tcp.drop);
MIB2_STATS_INC(mib2.tcpinerrs);
goto aborted;
}
}
tcp_input_pcb = pcb;
err = tcp_process(pcb);
if (err != ERR_ABRT) {
if (recv_flags & TF_RESET) {
TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST);
tcp_pcb_remove(&tcp_active_pcbs, pcb);
tcp_free(pcb);
} else {
err = ERR_OK;
if (recv_acked > 0) {
u16_t acked16;
#if LWIP_WND_SCALE
u32_t acked = recv_acked;
while (acked > 0) {
acked16 = (u16_t)LWIP_MIN(acked, 0xffffu);
acked -= acked16;
#else
{
acked16 = recv_acked;
#endif
TCP_EVENT_SENT(pcb, (u16_t)acked16, err);
if (err == ERR_ABRT) {
goto aborted;
}
}
recv_acked = 0;
}
if (tcp_input_delayed_close(pcb)) {
goto aborted;
}
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
while (recv_data != NULL) {
struct pbuf *rest = NULL;
pbuf_split_64k(recv_data, &rest);
#else
if (recv_data != NULL) {
#endif
LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
if (pcb->flags & TF_RXCLOSED) {
pbuf_free(recv_data);
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
if (rest != NULL) {
pbuf_free(rest);
}
#endif
tcp_abort(pcb);
goto aborted;
}
TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
if (err == ERR_ABRT) {
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
if (rest != NULL) {
pbuf_free(rest);
}
#endif
goto aborted;
}
if (err != ERR_OK) {
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
if (rest != NULL) {
pbuf_cat(recv_data, rest);
}
#endif
pcb->refused_data = recv_data;
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
break;
} else {
recv_data = rest;
#endif
}
}
if (recv_flags & TF_GOT_FIN) {
if (pcb->refused_data != NULL) {
pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
} else {
if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
pcb->rcv_wnd++;
}
TCP_EVENT_CLOSED(pcb, err);
if (err == ERR_ABRT) {
goto aborted;
}
}
}
tcp_input_pcb = NULL;
if (tcp_input_delayed_close(pcb)) {
goto aborted;
}
tcp_output(pcb);
#if TCP_INPUT_DEBUG
#if TCP_DEBUG
tcp_debug_print_state(pcb->state);
#endif
#endif
}
}
aborted:
tcp_input_pcb = NULL;
recv_data = NULL;
if (inseg.p != NULL) {
pbuf_free(inseg.p);
inseg.p = NULL;
}
} else {
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
TCP_STATS_INC(tcp.proterr);
TCP_STATS_INC(tcp.drop);
tcp_rst_netif(ip_data.current_input_netif, ackno, seqno + tcplen, ip_current_dest_addr(),
ip_current_src_addr(), tcphdr->dest, tcphdr->src);
}
pbuf_free(p);
}
LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
PERF_STOP("tcp_input");
return;
dropped:
TCP_STATS_INC(tcp.drop);
MIB2_STATS_INC(mib2.tcpinerrs);
pbuf_free(p);
}
static int
tcp_input_delayed_close(struct tcp_pcb *pcb)
{
LWIP_ASSERT("tcp_input_delayed_close: invalid pcb", pcb != NULL);
if (recv_flags & TF_CLOSED) {
if (!(pcb->flags & TF_RXCLOSED)) {
TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_CLSD);
}
tcp_pcb_remove(&tcp_active_pcbs, pcb);
tcp_free(pcb);
return 1;
}
return 0;
}
static void
tcp_listen_input(struct tcp_pcb_listen *pcb)
{
struct tcp_pcb *npcb;
u32_t iss;
err_t rc;
if (flags & TCP_RST) {
return;
}
LWIP_ASSERT("tcp_listen_input: invalid pcb", pcb != NULL);
if (flags & TCP_ACK) {
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
tcp_rst_netif(ip_data.current_input_netif, ackno, seqno + tcplen, ip_current_dest_addr(),
ip_current_src_addr(), tcphdr->dest, tcphdr->src);
} else if (flags & TCP_SYN) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
#if TCP_LISTEN_BACKLOG
if (pcb->accepts_pending >= pcb->backlog) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
return;
}
#endif
npcb = tcp_alloc(pcb->prio);
if (npcb == NULL) {
err_t err;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
TCP_STATS_INC(tcp.memerr);
TCP_EVENT_ACCEPT(pcb, NULL, pcb->callback_arg, ERR_MEM, err);
LWIP_UNUSED_ARG(err);
return;
}
#if TCP_LISTEN_BACKLOG
pcb->accepts_pending++;
tcp_set_flags(npcb, TF_BACKLOGPEND);
#endif
ip_addr_copy(npcb->local_ip, *ip_current_dest_addr());
ip_addr_copy(npcb->remote_ip, *ip_current_src_addr());
npcb->local_port = tcphdr->dest;
npcb->remote_port = tcphdr->src;
npcb->state = SYN_RCVD;
npcb->rcv_nxt = seqno + 1;
npcb->rcv_ann_right_edge = npcb->rcv_nxt;
iss = tcp_next_iss(npcb);
npcb->snd_wl2 = iss;
npcb->snd_nxt = iss;
npcb->lastack = iss;
npcb->snd_lbb = iss;
npcb->snd_wl1 = seqno - 1;
npcb->callback_arg = pcb->callback_arg;
#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
npcb->listener = pcb;
#endif
#if LWIP_VLAN_PCP
npcb->netif_hints.tci = pcb->netif_hints.tci;
#endif
npcb->so_options = pcb->so_options & SOF_INHERITED;
npcb->netif_idx = pcb->netif_idx;
TCP_REG_ACTIVE(npcb);
tcp_parseopt(npcb);
npcb->snd_wnd = tcphdr->wnd;
npcb->snd_wnd_max = npcb->snd_wnd;
#if TCP_CALCULATE_EFF_SEND_MSS
npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, &npcb->remote_ip);
#endif
MIB2_STATS_INC(mib2.tcppassiveopens);
#if LWIP_TCP_PCB_NUM_EXT_ARGS
if (tcp_ext_arg_invoke_callbacks_passive_open(pcb, npcb) != ERR_OK) {
tcp_abandon(npcb, 0);
return;
}
#endif
rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
if (rc != ERR_OK) {
tcp_abandon(npcb, 0);
return;
}
tcp_output(npcb);
}
return;
}
static void
tcp_timewait_input(struct tcp_pcb *pcb)
{
if (flags & TCP_RST) {
return;
}
LWIP_ASSERT("tcp_timewait_input: invalid pcb", pcb != NULL);
if (flags & TCP_SYN) {
if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd)) {
tcp_rst(pcb, ackno, seqno + tcplen, ip_current_dest_addr(),
ip_current_src_addr(), tcphdr->dest, tcphdr->src);
return;
}
} else if (flags & TCP_FIN) {
pcb->tmr = tcp_ticks;
}
if ((tcplen > 0)) {
tcp_ack_now(pcb);
tcp_output(pcb);
}
return;
}
static err_t
tcp_process(struct tcp_pcb *pcb)
{
struct tcp_seg *rseg;
u8_t acceptable = 0;
err_t err;
err = ERR_OK;
LWIP_ASSERT("tcp_process: invalid pcb", pcb != NULL);
if (flags & TCP_RST) {
if (pcb->state == SYN_SENT) {
if (ackno == pcb->snd_nxt) {
acceptable = 1;
}
} else {
if (seqno == pcb->rcv_nxt) {
acceptable = 1;
} else if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
pcb->rcv_nxt + pcb->rcv_wnd)) {
tcp_ack_now(pcb);
}
}
if (acceptable) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
recv_flags |= TF_RESET;
tcp_clear_flags(pcb, TF_ACK_DELAY);
return ERR_RST;
} else {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
seqno, pcb->rcv_nxt));
LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
seqno, pcb->rcv_nxt));
return ERR_OK;
}
}
if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
tcp_ack_now(pcb);
return ERR_OK;
}
if ((pcb->flags & TF_RXCLOSED) == 0) {
pcb->tmr = tcp_ticks;
}
pcb->keep_cnt_sent = 0;
pcb->persist_probe = 0;
tcp_parseopt(pcb);
if (flags & TCP_SYN) {
if ((pcb->state != SYN_SENT) && (pcb->state != SYN_RCVD)) {
return ERR_OK;
}
}
switch (pcb->state) {
case SYN_SENT:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %s %"U32_F"\n",
ackno, pcb->snd_nxt, pcb->unacked ? "" : " empty:",
pcb->unacked ? lwip_ntohl(pcb->unacked->tcphdr->seqno) : 0));
if ((flags & TCP_ACK) && (flags & TCP_SYN)
&& (ackno == pcb->lastack + 1)) {
pcb->rcv_nxt = seqno + 1;
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->lastack = ackno;
pcb->snd_wnd = tcphdr->wnd;
pcb->snd_wnd_max = pcb->snd_wnd;
pcb->snd_wl1 = seqno - 1;
pcb->state = ESTABLISHED;
#if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
#endif
pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SENT): cwnd %"TCPWNDSIZE_F
" ssthresh %"TCPWNDSIZE_F"\n",
pcb->cwnd, pcb->ssthresh));
LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
--pcb->snd_queuelen;
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
rseg = pcb->unacked;
if (rseg == NULL) {
rseg = pcb->unsent;
LWIP_ASSERT("no segment to free", rseg != NULL);
pcb->unsent = rseg->next;
} else {
pcb->unacked = rseg->next;
}
tcp_seg_free(rseg);
if (pcb->unacked == NULL) {
pcb->rtime = -1;
} else {
pcb->rtime = 0;
pcb->nrtx = 0;
}
TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
if (err == ERR_ABRT) {
return ERR_ABRT;
}
tcp_ack_now(pcb);
}
else if (flags & TCP_ACK) {
tcp_rst(pcb, ackno, seqno + tcplen, ip_current_dest_addr(),
ip_current_src_addr(), tcphdr->dest, tcphdr->src);
if (pcb->nrtx < TCP_SYNMAXRTX) {
pcb->rtime = 0;
tcp_rexmit_rto(pcb);
}
}
break;
case SYN_RCVD:
if (flags & TCP_SYN) {
if (seqno == pcb->rcv_nxt - 1) {
tcp_rexmit(pcb);
}
} else if (flags & TCP_ACK) {
if (TCP_SEQ_BETWEEN(ackno, pcb->lastack + 1, pcb->snd_nxt)) {
pcb->state = ESTABLISHED;
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
if (pcb->listener == NULL) {
err = ERR_VAL;
} else
#endif
{
#if LWIP_CALLBACK_API
LWIP_ASSERT("pcb->listener->accept != NULL", pcb->listener->accept != NULL);
#endif
tcp_backlog_accepted(pcb);
TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);
}
if (err != ERR_OK) {
if (err != ERR_ABRT) {
tcp_abort(pcb);
}
return ERR_ABRT;
}
tcp_receive(pcb);
if (recv_acked != 0) {
recv_acked--;
}
pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SYN_RCVD): cwnd %"TCPWNDSIZE_F
" ssthresh %"TCPWNDSIZE_F"\n",
pcb->cwnd, pcb->ssthresh));
if (recv_flags & TF_GOT_FIN) {
tcp_ack_now(pcb);
pcb->state = CLOSE_WAIT;
}
} else {
tcp_rst(pcb, ackno, seqno + tcplen, ip_current_dest_addr(),
ip_current_src_addr(), tcphdr->dest, tcphdr->src);
}
}
break;
case CLOSE_WAIT:
case ESTABLISHED:
tcp_receive(pcb);
if (recv_flags & TF_GOT_FIN) {
tcp_ack_now(pcb);
pcb->state = CLOSE_WAIT;
}
break;
case FIN_WAIT_1:
tcp_receive(pcb);
if (recv_flags & TF_GOT_FIN) {
if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
pcb->unsent == NULL) {
LWIP_DEBUGF(TCP_DEBUG,
("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
TCP_RMV_ACTIVE(pcb);
pcb->state = TIME_WAIT;
TCP_REG(&tcp_tw_pcbs, pcb);
} else {
tcp_ack_now(pcb);
pcb->state = CLOSING;
}
} else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
pcb->unsent == NULL) {
pcb->state = FIN_WAIT_2;
}
break;
case FIN_WAIT_2:
tcp_receive(pcb);
if (recv_flags & TF_GOT_FIN) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
TCP_RMV_ACTIVE(pcb);
pcb->state = TIME_WAIT;
TCP_REG(&tcp_tw_pcbs, pcb);
}
break;
case CLOSING:
tcp_receive(pcb);
if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_pcb_purge(pcb);
TCP_RMV_ACTIVE(pcb);
pcb->state = TIME_WAIT;
TCP_REG(&tcp_tw_pcbs, pcb);
}
break;
case LAST_ACK:
tcp_receive(pcb);
if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
recv_flags |= TF_CLOSED;
}
break;
default:
break;
}
return ERR_OK;
}
#if TCP_QUEUE_OOSEQ
static void
tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
{
struct tcp_seg *old_seg;
LWIP_ASSERT("tcp_oos_insert_segment: invalid cseg", cseg != NULL);
if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
tcp_segs_free(next);
next = NULL;
} else {
while (next &&
TCP_SEQ_GEQ((seqno + cseg->len),
(next->tcphdr->seqno + next->len))) {
if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
}
old_seg = next;
next = next->next;
tcp_seg_free(old_seg);
}
if (next &&
TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
pbuf_realloc(cseg->p, cseg->len);
}
}
cseg->next = next;
}
#endif
static struct tcp_seg *
tcp_free_acked_segments(struct tcp_pcb *pcb, struct tcp_seg *seg_list, const char *dbg_list_name,
struct tcp_seg *dbg_other_seg_list)
{
struct tcp_seg *next;
u16_t clen;
LWIP_UNUSED_ARG(dbg_list_name);
LWIP_UNUSED_ARG(dbg_other_seg_list);
while (seg_list != NULL &&
TCP_SEQ_LEQ(lwip_ntohl(seg_list->tcphdr->seqno) +
TCP_TCPLEN(seg_list), ackno)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->%s\n",
lwip_ntohl(seg_list->tcphdr->seqno),
lwip_ntohl(seg_list->tcphdr->seqno) + TCP_TCPLEN(seg_list),
dbg_list_name));
next = seg_list;
seg_list = seg_list->next;
clen = pbuf_clen(next->p);
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ",
(tcpwnd_size_t)pcb->snd_queuelen));
LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= clen));
pcb->snd_queuelen = (u16_t)(pcb->snd_queuelen - clen);
recv_acked = (tcpwnd_size_t)(recv_acked + next->len);
tcp_seg_free(next);
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing %s)\n",
(tcpwnd_size_t)pcb->snd_queuelen,
dbg_list_name));
if (pcb->snd_queuelen != 0) {
LWIP_ASSERT("tcp_receive: valid queue length",
seg_list != NULL || dbg_other_seg_list != NULL);
}
}
return seg_list;
}
static void
tcp_receive(struct tcp_pcb *pcb)
{
s16_t m;
u32_t right_wnd_edge;
LWIP_ASSERT("tcp_receive: invalid pcb", pcb != NULL);
LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
if (flags & TCP_ACK) {
right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
(pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
(pcb->snd_wl2 == ackno && (u32_t)SND_WND_SCALE(pcb, tcphdr->wnd) > pcb->snd_wnd)) {
pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd);
if (pcb->snd_wnd_max < pcb->snd_wnd) {
pcb->snd_wnd_max = pcb->snd_wnd;
}
pcb->snd_wl1 = seqno;
pcb->snd_wl2 = ackno;
LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"TCPWNDSIZE_F"\n", pcb->snd_wnd));
#if TCP_WND_DEBUG
} else {
if (pcb->snd_wnd != (tcpwnd_size_t)SND_WND_SCALE(pcb, tcphdr->wnd)) {
LWIP_DEBUGF(TCP_WND_DEBUG,
("tcp_receive: no window update lastack %"U32_F" ackno %"
U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
}
#endif
}
if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
if (tcplen == 0) {
if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge) {
if (pcb->rtime >= 0) {
if (pcb->lastack == ackno) {
if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) {
++pcb->dupacks;
}
if (pcb->dupacks > 3) {
TCP_WND_INC(pcb->cwnd, pcb->mss);
}
if (pcb->dupacks >= 3) {
tcp_rexmit_fast(pcb);
}
}
}
}
}
} else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack + 1, pcb->snd_nxt)) {
tcpwnd_size_t acked;
if (pcb->flags & TF_INFR) {
tcp_clear_flags(pcb, TF_INFR);
pcb->cwnd = pcb->ssthresh;
pcb->bytes_acked = 0;
}
pcb->nrtx = 0;
pcb->rto = (s16_t)((pcb->sa >> 3) + pcb->sv);
acked = (tcpwnd_size_t)(ackno - pcb->lastack);
pcb->dupacks = 0;
pcb->lastack = ackno;
if (pcb->state >= ESTABLISHED) {
if (pcb->cwnd < pcb->ssthresh) {
tcpwnd_size_t increase;
u8_t num_seg = (pcb->flags & TF_RTO) ? 1 : 2;
increase = LWIP_MIN(acked, (tcpwnd_size_t)(num_seg * pcb->mss));
TCP_WND_INC(pcb->cwnd, increase);
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
} else {
TCP_WND_INC(pcb->bytes_acked, acked);
if (pcb->bytes_acked >= pcb->cwnd) {
pcb->bytes_acked = (tcpwnd_size_t)(pcb->bytes_acked - pcb->cwnd);
TCP_WND_INC(pcb->cwnd, pcb->mss);
}
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
}
}
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
ackno,
pcb->unacked != NULL ?
lwip_ntohl(pcb->unacked->tcphdr->seqno) : 0,
pcb->unacked != NULL ?
lwip_ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked) : 0));
pcb->unacked = tcp_free_acked_segments(pcb, pcb->unacked, "unacked", pcb->unsent);
pcb->unsent = tcp_free_acked_segments(pcb, pcb->unsent, "unsent", pcb->unacked);
if (pcb->unacked == NULL) {
pcb->rtime = -1;
} else {
pcb->rtime = 0;
}
pcb->polltmr = 0;
#if TCP_OVERSIZE
if (pcb->unsent == NULL) {
pcb->unsent_oversize = 0;
}
#endif
#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
if (ip_current_is_v6()) {
nd6_reachability_hint(ip6_current_src_addr());
}
#endif
pcb->snd_buf = (tcpwnd_size_t)(pcb->snd_buf + recv_acked);
if (pcb->flags & TF_RTO) {
if (pcb->unacked == NULL) {
if ((pcb->unsent == NULL) ||
(TCP_SEQ_LEQ(pcb->rto_end, lwip_ntohl(pcb->unsent->tcphdr->seqno)))) {
tcp_clear_flags(pcb, TF_RTO);
}
} else if (TCP_SEQ_LEQ(pcb->rto_end, lwip_ntohl(pcb->unacked->tcphdr->seqno))) {
tcp_clear_flags(pcb, TF_RTO);
}
}
} else {
tcp_send_empty_ack(pcb);
}
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
pcb->rttest, pcb->rtseq, ackno));
if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
m = (s16_t)(tcp_ticks - pcb->rttest);
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
m, (u16_t)(m * TCP_SLOW_INTERVAL)));
m = (s16_t)(m - (pcb->sa >> 3));
pcb->sa = (s16_t)(pcb->sa + m);
if (m < 0) {
m = (s16_t) - m;
}
m = (s16_t)(m - (pcb->sv >> 2));
pcb->sv = (s16_t)(pcb->sv + m);
pcb->rto = (s16_t)((pcb->sa >> 3) + pcb->sv);
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL)));
pcb->rttest = 0;
}
}
if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) {
if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)) {
struct pbuf *p = inseg.p;
u32_t off32 = pcb->rcv_nxt - seqno;
u16_t new_tot_len, off;
LWIP_ASSERT("inseg.p != NULL", inseg.p);
LWIP_ASSERT("insane offset!", (off32 < 0xffff));
off = (u16_t)off32;
LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
inseg.len -= off;
new_tot_len = (u16_t)(inseg.p->tot_len - off);
while (p->len < off) {
off -= p->len;
p->tot_len = new_tot_len;
p->len = 0;
p = p->next;
}
pbuf_remove_header(p, off);
inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
} else {
if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
tcp_ack_now(pcb);
}
}
if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
if (pcb->rcv_nxt == seqno) {
tcplen = TCP_TCPLEN(&inseg);
if (tcplen > pcb->rcv_wnd) {
LWIP_DEBUGF(TCP_INPUT_DEBUG,
("tcp_receive: other end overran receive window"
"seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) & ~(unsigned int)TCP_FIN);
}
TCPWND_CHECK16(pcb->rcv_wnd);
inseg.len = (u16_t)pcb->rcv_wnd;
if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
inseg.len -= 1;
}
pbuf_realloc(inseg.p, inseg.len);
tcplen = TCP_TCPLEN(&inseg);
LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd",
(seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
}
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL) {
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
LWIP_DEBUGF(TCP_INPUT_DEBUG,
("tcp_receive: received in-order FIN, binning ooseq queue\n"));
while (pcb->ooseq != NULL) {
struct tcp_seg *old_ooseq = pcb->ooseq;
pcb->ooseq = pcb->ooseq->next;
tcp_seg_free(old_ooseq);
}
} else {
struct tcp_seg *next = pcb->ooseq;
while (next &&
TCP_SEQ_GEQ(seqno + tcplen,
next->tcphdr->seqno + next->len)) {
struct tcp_seg *tmp;
if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
(TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
tcplen = TCP_TCPLEN(&inseg);
}
tmp = next;
next = next->next;
tcp_seg_free(tmp);
}
if (next &&
TCP_SEQ_GT(seqno + tcplen,
next->tcphdr->seqno)) {
inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
inseg.len -= 1;
}
pbuf_realloc(inseg.p, inseg.len);
tcplen = TCP_TCPLEN(&inseg);
LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue",
(seqno + tcplen) == next->tcphdr->seqno);
}
pcb->ooseq = next;
}
}
#endif
pcb->rcv_nxt = seqno + tcplen;
LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd", pcb->rcv_wnd >= tcplen);
pcb->rcv_wnd -= tcplen;
tcp_update_rcv_ann_wnd(pcb);
if (inseg.p->tot_len > 0) {
recv_data = inseg.p;
inseg.p = NULL;
}
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
recv_flags |= TF_GOT_FIN;
}
#if TCP_QUEUE_OOSEQ
while (pcb->ooseq != NULL &&
pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
struct tcp_seg *cseg = pcb->ooseq;
seqno = pcb->ooseq->tcphdr->seqno;
pcb->rcv_nxt += TCP_TCPLEN(cseg);
LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd",
pcb->rcv_wnd >= TCP_TCPLEN(cseg));
pcb->rcv_wnd -= TCP_TCPLEN(cseg);
tcp_update_rcv_ann_wnd(pcb);
if (cseg->p->tot_len > 0) {
if (recv_data) {
pbuf_cat(recv_data, cseg->p);
} else {
recv_data = cseg->p;
}
cseg->p = NULL;
}
if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
recv_flags |= TF_GOT_FIN;
if (pcb->state == ESTABLISHED) {
pcb->state = CLOSE_WAIT;
}
}
pcb->ooseq = cseg->next;
tcp_seg_free(cseg);
}
#if LWIP_TCP_SACK_OUT
if (pcb->flags & TF_SACK) {
if (pcb->ooseq != NULL) {
tcp_remove_sacks_lt(pcb, pcb->ooseq->tcphdr->seqno);
} else if (LWIP_TCP_SACK_VALID(pcb, 0)) {
memset(pcb->rcv_sacks, 0, sizeof(pcb->rcv_sacks));
}
}
#endif
#endif
tcp_ack(pcb);
#if LWIP_TCP_SACK_OUT
if (LWIP_TCP_SACK_VALID(pcb, 0)) {
tcp_send_empty_ack(pcb);
}
#endif
#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
if (ip_current_is_v6()) {
nd6_reachability_hint(ip6_current_src_addr());
}
#endif
} else {
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq == NULL) {
pcb->ooseq = tcp_seg_copy(&inseg);
#if LWIP_TCP_SACK_OUT
if (pcb->flags & TF_SACK) {
pcb->rcv_sacks[0].left = seqno;
pcb->rcv_sacks[0].right = seqno + inseg.len;
}
#endif
} else {
#if LWIP_TCP_SACK_OUT
u32_t sackbeg = TCP_SEQ_LT(seqno, pcb->ooseq->tcphdr->seqno) ? seqno : pcb->ooseq->tcphdr->seqno;
#endif
struct tcp_seg *next, *prev = NULL;
for (next = pcb->ooseq; next != NULL; next = next->next) {
if (seqno == next->tcphdr->seqno) {
if (inseg.len > next->len) {
struct tcp_seg* cseg;
if (next->next == NULL) {
break;
}
cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) {
if (prev != NULL) {
prev->next = cseg;
} else {
pcb->ooseq = cseg;
}
tcp_oos_insert_segment(cseg, next);
}
break;
} else {
break;
}
} else {
if (prev == NULL) {
if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
struct tcp_seg *cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) {
pcb->ooseq = cseg;
tcp_oos_insert_segment(cseg, next);
}
break;
}
} else {
if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno + 1, next->tcphdr->seqno - 1)) {
struct tcp_seg *cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) {
if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
pbuf_realloc(prev->p, prev->len);
}
prev->next = cseg;
tcp_oos_insert_segment(cseg, next);
}
break;
}
}
#if LWIP_TCP_SACK_OUT
if (prev != NULL && prev->tcphdr->seqno + prev->len != next->tcphdr->seqno) {
sackbeg = next->tcphdr->seqno;
}
#endif
prev = next;
if (next->next == NULL &&
TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
break;
}
next->next = tcp_seg_copy(&inseg);
if (next->next != NULL) {
if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
next->len = (u16_t)(seqno - next->tcphdr->seqno);
pbuf_realloc(next->p, next->len);
}
if (TCP_SEQ_GT((u32_t)tcplen + seqno, pcb->rcv_nxt + (u32_t)pcb->rcv_wnd)) {
LWIP_DEBUGF(TCP_INPUT_DEBUG,
("tcp_receive: other end overran receive window"
"seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) & ~TCP_FIN);
}
next->next->len = (u16_t)(pcb->rcv_nxt + pcb->rcv_wnd - seqno);
pbuf_realloc(next->next->p, next->next->len);
tcplen = TCP_TCPLEN(next->next);
LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd",
(seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
}
}
break;
}
}
}
#if LWIP_TCP_SACK_OUT
if (pcb->flags & TF_SACK) {
if (prev == NULL) {
next = pcb->ooseq;
} else if (prev->next != NULL) {
next = prev->next;
if (prev->tcphdr->seqno + prev->len != next->tcphdr->seqno) {
sackbeg = next->tcphdr->seqno;
}
} else {
next = NULL;
}
if (next != NULL) {
u32_t sackend = next->tcphdr->seqno;
for ( ; (next != NULL) && (sackend == next->tcphdr->seqno); next = next->next) {
sackend += next->len;
}
tcp_add_sack(pcb, sackbeg, sackend);
}
}
#endif
}
#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
{
#ifdef TCP_OOSEQ_BYTES_LIMIT
const u32_t ooseq_max_blen = TCP_OOSEQ_BYTES_LIMIT(pcb);
u32_t ooseq_blen = 0;
#endif
#ifdef TCP_OOSEQ_PBUFS_LIMIT
const u16_t ooseq_max_qlen = TCP_OOSEQ_PBUFS_LIMIT(pcb);
u16_t ooseq_qlen = 0;
#endif
struct tcp_seg *next, *prev = NULL;
for (next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
struct pbuf *p = next->p;
int stop_here = 0;
#ifdef TCP_OOSEQ_BYTES_LIMIT
ooseq_blen += p->tot_len;
if (ooseq_blen > ooseq_max_blen) {
stop_here = 1;
}
#endif
#ifdef TCP_OOSEQ_PBUFS_LIMIT
ooseq_qlen += pbuf_clen(p);
if (ooseq_qlen > ooseq_max_qlen) {
stop_here = 1;
}
#endif
if (stop_here) {
#if LWIP_TCP_SACK_OUT
if (pcb->flags & TF_SACK) {
tcp_remove_sacks_gt(pcb, next->tcphdr->seqno);
}
#endif
tcp_segs_free(next);
if (prev == NULL) {
pcb->ooseq = NULL;
} else {
prev->next = NULL;
}
break;
}
}
}
#endif
#endif
tcp_send_empty_ack(pcb);
}
} else {
tcp_send_empty_ack(pcb);
}
} else {
if (!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
tcp_ack_now(pcb);
}
}
}
static u8_t
tcp_get_next_optbyte(void)
{
u16_t optidx = tcp_optidx++;
if ((tcphdr_opt2 == NULL) || (optidx < tcphdr_opt1len)) {
u8_t *opts = (u8_t *)tcphdr + TCP_HLEN;
return opts[optidx];
} else {
u8_t idx = (u8_t)(optidx - tcphdr_opt1len);
return tcphdr_opt2[idx];
}
}
static void
tcp_parseopt(struct tcp_pcb *pcb)
{
u8_t data;
u16_t mss;
#if LWIP_TCP_TIMESTAMPS
u32_t tsval;
#endif
LWIP_ASSERT("tcp_parseopt: invalid pcb", pcb != NULL);
if (tcphdr_optlen != 0) {
for (tcp_optidx = 0; tcp_optidx < tcphdr_optlen; ) {
u8_t opt = tcp_get_next_optbyte();
switch (opt) {
case LWIP_TCP_OPT_EOL:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
return;
case LWIP_TCP_OPT_NOP:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
break;
case LWIP_TCP_OPT_MSS:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_MSS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_MSS) > tcphdr_optlen) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
return;
}
mss = (u16_t)(tcp_get_next_optbyte() << 8);
mss |= tcp_get_next_optbyte();
pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
break;
#if LWIP_WND_SCALE
case LWIP_TCP_OPT_WS:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: WND_SCALE\n"));
if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_WS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_WS) > tcphdr_optlen) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
return;
}
data = tcp_get_next_optbyte();
if ((flags & TCP_SYN) && !(pcb->flags & TF_WND_SCALE)) {
pcb->snd_scale = data;
if (pcb->snd_scale > 14U) {
pcb->snd_scale = 14U;
}
pcb->rcv_scale = TCP_RCV_SCALE;
tcp_set_flags(pcb, TF_WND_SCALE);
LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND));
LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND));
pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND;
}
break;
#endif
#if LWIP_TCP_TIMESTAMPS
case LWIP_TCP_OPT_TS:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_TS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_TS) > tcphdr_optlen) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
return;
}
tsval = tcp_get_next_optbyte();
tsval |= (tcp_get_next_optbyte() << 8);
tsval |= (tcp_get_next_optbyte() << 16);
tsval |= (tcp_get_next_optbyte() << 24);
if (flags & TCP_SYN) {
pcb->ts_recent = lwip_ntohl(tsval);
tcp_set_flags(pcb, TF_TIMESTAMP);
} else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno + tcplen)) {
pcb->ts_recent = lwip_ntohl(tsval);
}
tcp_optidx += LWIP_TCP_OPT_LEN_TS - 6;
break;
#endif
#if LWIP_TCP_SACK_OUT
case LWIP_TCP_OPT_SACK_PERM:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: SACK_PERM\n"));
if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_SACK_PERM || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_SACK_PERM) > tcphdr_optlen) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
return;
}
if (flags & TCP_SYN) {
tcp_set_flags(pcb, TF_SACK);
}
break;
#endif
default:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
data = tcp_get_next_optbyte();
if (data < 2) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
return;
}
tcp_optidx += data - 2;
}
}
}
}
void
tcp_trigger_input_pcb_close(void)
{
recv_flags |= TF_CLOSED;
}
#if LWIP_TCP_SACK_OUT
static void
tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right)
{
u8_t i;
u8_t unused_idx;
if ((pcb->flags & TF_SACK) == 0 || !TCP_SEQ_LT(left, right)) {
return;
}
for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
if (TCP_SEQ_LEQ(pcb->rcv_sacks[i].right, left) || TCP_SEQ_LEQ(right, pcb->rcv_sacks[i].left)) {
if (unused_idx != i) {
pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
}
++unused_idx;
}
}
for (i = LWIP_TCP_MAX_SACK_NUM - 1; i > 0; --i) {
if (i - 1 >= unused_idx) {
pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
} else {
pcb->rcv_sacks[i] = pcb->rcv_sacks[i - 1];
}
}
pcb->rcv_sacks[0].left = left;
pcb->rcv_sacks[0].right = right;
}
static void
tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq)
{
u8_t i;
u8_t unused_idx;
for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
if (TCP_SEQ_GT(pcb->rcv_sacks[i].right, seq)) {
if (unused_idx != i) {
pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
}
if (TCP_SEQ_LT(pcb->rcv_sacks[unused_idx].left, seq)) {
pcb->rcv_sacks[unused_idx].left = seq;
}
++unused_idx;
}
}
for (i = unused_idx; i < LWIP_TCP_MAX_SACK_NUM; ++i) {
pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
}
}
#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
static void
tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq)
{
u8_t i;
u8_t unused_idx;
for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
if (TCP_SEQ_LT(pcb->rcv_sacks[i].left, seq)) {
if (unused_idx != i) {
pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
}
if (TCP_SEQ_GT(pcb->rcv_sacks[unused_idx].right, seq)) {
pcb->rcv_sacks[unused_idx].right = seq;
}
++unused_idx;
}
}
for (i = unused_idx; i < LWIP_TCP_MAX_SACK_NUM; ++i) {
pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
}
}
#endif
#endif
#endif