*
*/
-/*-----------------------------------------------------------------------------------*/
+
/* tcp_input.c
*
* The input processing functions of TCP.
* tcp_process() -> tcp_receive() (-> application).
*
*/
-/*-----------------------------------------------------------------------------------*/
+
#include "lwip/def.h"
static err_t tcp_listen_input(struct tcp_pcb_listen *pcb);
static err_t tcp_timewait_input(struct tcp_pcb *pcb);
-/*-----------------------------------------------------------------------------------*/
+
/* tcp_input:
*
* The initial input processing of TCP. It verifies the TCP header, demultiplexes
* the TCP finite state machine. This function is called by the IP layer (in
* ip_input()).
*/
-/*-----------------------------------------------------------------------------------*/
+
void
tcp_input(struct pbuf *p, struct netif *inp)
{
struct tcp_pcb *pcb, *prev;
struct tcp_pcb_listen *lpcb;
- u8_t offset;
+ u8_t hdrlen;
err_t err;
+#ifdef SO_REUSE
+ struct tcp_pcb *pcb_temp;
+ int reuse = 0;
+ int reuse_port = 0;
+#endif /* SO_REUSE */
PERF_START;
-
-#ifdef TCP_STATS
- ++lwip_stats.tcp.recv;
-#endif /* TCP_STATS */
+ TCP_STATS_INC(tcp.recv);
iphdr = p->payload;
tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
+#if TCP_INPUT_DEBUG
+ tcp_debug_print(tcphdr);
+#endif
+
/* remove header from payload */
if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
/* drop short packets */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%u bytes) discarded\n", p->tot_len));
-#ifdef TCP_STATS
- ++lwip_stats.tcp.lenerr;
- ++lwip_stats.tcp.drop;
-#endif /* TCP_STATS */
+ TCP_STATS_INC(tcp.lenerr);
+ TCP_STATS_INC(tcp.drop);
pbuf_free(p);
return;
}
#if TCP_DEBUG
tcp_debug_print(tcphdr);
#endif /* TCP_DEBUG */
-#ifdef TCP_STATS
- ++lwip_stats.tcp.chkerr;
- ++lwip_stats.tcp.drop;
-#endif /* TCP_STATS */
+ TCP_STATS_INC(tcp.chkerr);
+ TCP_STATS_INC(tcp.drop);
pbuf_free(p);
return;
/* Move the payload pointer in the pbuf so that it points to the
TCP data instead of the TCP header. */
- offset = TCPH_OFFSET(tcphdr) >> 4;
- pbuf_header(p, -(offset * 4));
+ hdrlen = TCPH_HDRLEN(tcphdr);
+ pbuf_header(p, -(hdrlen * 4));
/* Convert fields in TCP header to host byte order. */
tcphdr->src = ntohs(tcphdr->src);
/* Demultiplex an incoming segment. First, we check if it is destined
for an active connection. */
prev = NULL;
+
+#ifdef SO_REUSE
+ pcb_temp = tcp_active_pcbs;
+
+ again_1:
+
+ /* Iterate through the TCP pcb list for a fully matching pcb */
+ for(pcb = pcb_temp; pcb != NULL; pcb = pcb->next) {
+#else /* SO_REUSE */
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+#endif /* SO_REUSE */
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);
ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&
ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {
+#ifdef SO_REUSE
+ if(pcb->so_options & SOF_REUSEPORT) {
+ if(reuse) {
+ /* We processed one PCB already */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,("tcp_input: second or later PCB and SOF_REUSEPORT set.\n"));
+ } else {
+ /* First PCB with this address */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: first PCB and SOF_REUSEPORT set.\n"));
+ reuse = 1;
+ }
+
+ reuse_port = 1;
+ p->ref++;
+
+ /* We want to search on next socket after receiving */
+ pcb_temp = pcb->next;
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: reference counter on PBUF set to %i\n", p->ref));
+ } else {
+ if(reuse) {
+ /* We processed one PCB already */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: second or later PCB but SOF_REUSEPORT not set !\n"));
+ }
+ }
+#endif /* SO_REUSE */
+
/* Move this PCB to the front of the list so that subsequent
lookups will be faster (we exploit locality in TCP segment
arrivals). */
tcp_debug_print_state(pcb->state);
#endif /* TCP_DEBUG */
#endif /* TCP_INPUT_DEBUG */
+#ifdef SO_REUSE
+ /* First socket should receive now */
+ if(reuse_port) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: searching next PCB.\n"));
+ reuse_port = 0;
+
+ /* We are searching connected sockets */
+ goto again_1;
+ }
+#endif /* SO_REUSE */
} else {
+#ifdef SO_REUSE
+ if(reuse) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: freeing PBUF with reference counter set to %i\n", p->ref));
+ pbuf_free(p);
+ goto end;
+ }
+#endif /* SO_REUSE */
/* If no matching PCB was found, send a TCP RST (reset) to the
sender. */
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
-#ifdef TCP_STATS
- ++lwip_stats.tcp.proterr;
- ++lwip_stats.tcp.drop;
-#endif /* TCP_STATS */
+ TCP_STATS_INC(tcp.proterr);
+ TCP_STATS_INC(tcp.drop);
tcp_rst(ackno, seqno + tcplen,
&(iphdr->dest), &(iphdr->src),
tcphdr->dest, tcphdr->src);
}
pbuf_free(p);
}
-
+#ifdef SO_REUSE
+ end:
+#endif /* SO_REUSE */
LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
PERF_STOP("tcp_input");
}
-/*-----------------------------------------------------------------------------------*/
+
/* tcp_listen_input():
*
* Called by tcp_input() when a segment arrives for a listening
* connection.
*/
-/*-----------------------------------------------------------------------------------*/
+
static err_t
tcp_listen_input(struct tcp_pcb_listen *pcb)
{
SYN at a time when we have more memory available. */
if (npcb == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
-#ifdef TCP_STATS
- ++lwip_stats.tcp.memerr;
-#endif /* TCP_STATS */
+ TCP_STATS_INC(tcp.memerr);
return ERR_MEM;
}
/* Set up the new PCB. */
npcb->rcv_nxt = seqno + 1;
npcb->snd_wnd = tcphdr->wnd;
npcb->ssthresh = npcb->snd_wnd;
- npcb->snd_wl1 = seqno;
+ npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
npcb->callback_arg = pcb->callback_arg;
#if LWIP_CALLBACK_API
npcb->accept = pcb->accept;
#endif /* LWIP_CALLBACK_API */
-
+ /* inherit socket options */
+ npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER);
/* Register the new PCB so that we can begin receiving segments
for it. */
TCP_REG(&tcp_active_pcbs, npcb);
}
return ERR_OK;
}
-/*-----------------------------------------------------------------------------------*/
+
/* tcp_timewait_input():
*
* Called by tcp_input() when a segment arrives for a connection in
* TIME_WAIT.
*/
-/*-----------------------------------------------------------------------------------*/
+
static err_t
tcp_timewait_input(struct tcp_pcb *pcb)
{
}
return tcp_output(pcb);
}
-/*-----------------------------------------------------------------------------------*/
+
/* tcp_process
*
* Implements the TCP state machine. Called by tcp_input. In some
* argument will be freed by the caller (tcp_input()) unless the
* recv_data pointer in the pcb is set.
*/
-/*-----------------------------------------------------------------------------------*/
+
static err_t
tcp_process(struct tcp_pcb *pcb)
{
/* Update the PCB (in)activity timer. */
pcb->tmr = tcp_ticks;
+ pcb->keep_cnt = 0;
/* Do different things depending on the TCP state. */
switch (pcb->state) {
ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
pcb->rcv_nxt = seqno + 1;
pcb->lastack = ackno;
- pcb->snd_wnd = pcb->snd_wl1 = tcphdr->wnd;
+ pcb->snd_wnd = tcphdr->wnd;
+ pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
pcb->state = ESTABLISHED;
pcb->cwnd = pcb->mss;
--pcb->snd_queuelen;
return ERR_OK;
}
-/*-----------------------------------------------------------------------------------*/
+
/* tcp_receive:
*
* Called by tcp_process. Checks if the given segment is an ACK for outstanding
* If the incoming segment constitutes an ACK for a segment that was used for RTT
* estimation, the RTT is estimated here as well.
*/
-/*-----------------------------------------------------------------------------------*/
+
static void
tcp_receive(struct tcp_pcb *pcb)
{
inseg.p = NULL;
}
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN."));
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
recv_flags = TF_GOT_FIN;
}
/* Chain this pbuf onto the pbuf that we will pass to
the application. */
if (recv_data) {
- pbuf_chain(recv_data, cseg->p);
- pbuf_free(cseg->p);
+ pbuf_cat(recv_data, cseg->p);
} else {
recv_data = cseg->p;
}
cseg->p = NULL;
}
if (flags & TCP_FIN) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN."));
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
recv_flags = TF_GOT_FIN;
}
}
}
}
-/*-----------------------------------------------------------------------------------*/
+
/*
* tcp_parseopt:
*
* from uIP with only small changes.)
*
*/
-/*-----------------------------------------------------------------------------------*/
+
static void
tcp_parseopt(struct tcp_pcb *pcb)
{
opts = (u8_t *)tcphdr + TCP_HLEN;
/* Parse the TCP MSS option, if present. */
- if ((TCPH_OFFSET(tcphdr) & 0xf0) > 0x50) {
- for(c = 0; c < ((TCPH_OFFSET(tcphdr) >> 4) - 5) << 2 ;) {
+ if(TCPH_HDRLEN(tcphdr) > 0x5) {
+ for(c = 0; c < (TCPH_HDRLEN(tcphdr) - 5) << 2 ;) {
opt = opts[c];
if (opt == 0x00) {
/* End of options. */
}
}
#endif /* LWIP_TCP */
-/*-----------------------------------------------------------------------------------*/
+