📄 tcp_in.c
字号:
j=iphdr->daddr[3];
j=j<<8;
j+=iphdr->daddr[2];
j=j<<8;
j+=iphdr->daddr[1];
j=j<<8;
tcp_rst(ackno, seqno + tcplen,
j, i,
tcphdr->dest, tcphdr->src);
}
pbuf_free(p);
}
#if SO_REUSE
end:
#endif /* SO_REUSE */
}
/* 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)
{
struct tcp_pcb *npcb;
u32_t optdata;
Uint32 IPHdrSrc;
Uint32 IPHdrDes;
IPHdrSrc=iphdr->saddr[3];
IPHdrSrc=IPHdrSrc<<8;
IPHdrSrc+=iphdr->saddr[2];
IPHdrSrc=IPHdrSrc<<8;
IPHdrSrc+=iphdr->saddr[1];
IPHdrSrc=IPHdrSrc<<8;
IPHdrSrc+=iphdr->saddr[0];
IPHdrDes=iphdr->daddr[3];
IPHdrDes=IPHdrDes<<8;
IPHdrDes+=iphdr->daddr[2];
IPHdrDes=IPHdrDes<<8;
IPHdrDes+=iphdr->daddr[1];
IPHdrDes=IPHdrDes<<8;
/* In the LISTEN state, we check for incoming SYN segments,
creates a new PCB, and responds with a SYN|ACK. */
if (flags & TCP_ACK)
{
/* For incoming segments with the ACK flag set, respond with a
RST. */
tcp_rst(ackno + 1, seqno + tcplen,
IPHdrDes, IPHdrSrc,
tcphdr->dest, tcphdr->src);
}
else if (flags & TCP_SYN)
{
npcb = tcp_alloc(pcb->prio);
/* If a new PCB could not be created (probably due to lack of memory),
we don't do anything, but rely on the sender will retransmit the
SYN at a time when we have more memory available. */
if (npcb == NULL)
{
TCP_STATS_INC(tcp.memerr);
return ERR_MEM;
}
/* Set up the new PCB. */
ip_addr_set((struct ip_addr *)&(npcb->local_ip), (struct ip_addr *)&IPHdrDes);
npcb->local_port = pcb->local_port;
ip_addr_set((struct ip_addr *)&(npcb->dest_ip), (struct ip_addr *)&IPHdrSrc);
npcb->remote_port = tcphdr->src;
npcb->state = SYN_RCVD;
npcb->rcv_nxt = seqno + 1;
npcb->snd_wnd = tcphdr->wnd;
npcb->ssthresh = npcb->snd_wnd;
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);
/* Parse any options in the SYN. */
tcp_parseopt(npcb);
/* Build an MSS option. */
optdata = htonl(((u32_t)2 << 24) |
((u32_t)4 << 16) |
(((u32_t)npcb->mss / 256) << 8) |
(npcb->mss & 255));
/* Send a SYN|ACK together with the MSS option. */
tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4);
return tcp_output(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)
{
if (TCP_SEQ_GT(seqno + tcplen, pcb->rcv_nxt)) {
pcb->rcv_nxt = seqno + tcplen;
}
if (tcplen > 0) {
tcp_ack_now(pcb);
}
return tcp_output(pcb);
}
/* tcp_process
*
* Implements the TCP state machine. Called by tcp_input. In some
* states tcp_receive() is called to receive data. The tcp_seg
* 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)
{
struct tcp_seg *rseg;
u8_t acceptable = 0;
err_t err;
err = ERR_OK;
/* Process incoming RST segments. */
if (flags & TCP_RST) {
/* First, determine if the reset is acceptable. */
if (pcb->state == SYN_SENT) {
if (ackno == pcb->snd_nxt) {
acceptable = 1;
}
} else {
if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) &&
TCP_SEQ_LEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {
acceptable = 1;
}
}
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;
pcb->flags &= ~TF_ACK_DELAY;
return ERR_RST;
} else {
// LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %lu rcv_nxt %lu\n",
// seqno, pcb->rcv_nxt));
// LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %lu rcv_nxt %lu\n",
// seqno, pcb->rcv_nxt));
return ERR_OK;
}
}
/* 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) {
case SYN_SENT:
// LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %lu pcb->snd_nxt %lu unacked %lu\n", ackno,
// pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
if ((flags & TCP_ACK) && (flags & TCP_SYN)
&& ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
pcb->rcv_nxt = seqno + 1;
pcb->lastack = ackno;
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;
// LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %u\n", (unsigned int)pcb->snd_queuelen));
rseg = pcb->unacked;
pcb->unacked = rseg->next;
tcp_seg_free(rseg);
/* Parse any options in the SYNACK. */
tcp_parseopt(pcb);
/* Call the user specified function to call when sucessfully
* connected. */
TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
tcp_ack(pcb);
}
break;
case SYN_RCVD:
if (flags & TCP_ACK &&
!(flags & TCP_RST)) {
if (TCP_SEQ_LT(pcb->lastack, ackno) &&
TCP_SEQ_LEQ(ackno, pcb->snd_nxt)) {
pcb->state = ESTABLISHED;
// LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %u -> %u.\n", inseg.tcphdr->src, inseg.tcphdr->dest));
#if LWIP_CALLBACK_API
// LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
#endif
/* Call the accept function. */
TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
if (err != ERR_OK) {
/* If the accept function returns with an error, we abort
* the connection. */
tcp_abort(pcb);
return ERR_ABRT;
}
/* If there was any data contained within this ACK,
* we'd better pass it on to the application as well. */
tcp_receive(pcb);
pcb->cwnd = pcb->mss;
}
}
break;
case CLOSE_WAIT:
/* FALLTHROUGH */
case ESTABLISHED:
tcp_receive(pcb);
if (flags & TCP_FIN) {
tcp_ack_now(pcb);
pcb->state = CLOSE_WAIT;
}
break;
case FIN_WAIT_1:
tcp_receive(pcb);
if (flags & TCP_FIN) {
if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
TCP_RMV(&tcp_active_pcbs, 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->state = FIN_WAIT_2;
}
break;
case FIN_WAIT_2:
tcp_receive(pcb);
if (flags & TCP_FIN) {
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
TCP_RMV(&tcp_active_pcbs, 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) {
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
TCP_RMV(&tcp_active_pcbs, 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->state = CLOSED;
recv_flags = TF_CLOSED;
}
break;
default:
break;
}
return ERR_OK;
}
/* tcp_receive:
*
* Called by tcp_process. Checks if the given segment is an ACK for outstanding
* data, and if so frees the memory of the buffered data. Next, is places the
* segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
* is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
* i it has been removed from the buffer.
*
* 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)
{
struct tcp_seg *next;
#if TCP_QUEUE_OOSEQ
struct tcp_seg *prev, *cseg;
#endif
struct pbuf *p;
s32_t off;
int m;
u32_t right_wnd_edge;
u16_t new_tot_len;
if (flags & TCP_ACK) {
right_wnd_edge = pcb->snd_wnd + pcb->snd_wl1;
/* Update window. */
if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
(pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
(pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
pcb->snd_wnd = tcphdr->wnd;
pcb->snd_wl1 = seqno;
pcb->snd_wl2 = ackno;
#if TCP_WND_DEBUG
} else {
if (pcb->snd_wnd != tcphdr->wnd) {
LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: no window update lastack %lu snd_max %lu ackno %lu wl1 %lu seqno %lu wl2 %lu\n",
pcb->lastack, pcb->snd_max, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
}
#endif /* TCP_WND_DEBUG */
}
if (pcb->lastack == ackno) {
pcb->acked = 0;
if (pcb->snd_wl1 + pcb->snd_wnd == right_wnd_edge){
++pcb->dupacks;
if (pcb->dupacks >= 3 && pcb->unacked != NULL) {
if (!(pcb->flags & TF_INFR)) {
/* This is fast retransmit. Retransmit the first unacked segment. */
tcp_rexmit(pcb);
/* Set ssthresh to max (FlightSize / 2, 2*SMSS) */
/*pcb->ssthresh = LWIP_MAX((pcb->snd_max -
pcb->lastack) / 2,
2 * pcb->mss);*/
/* Set ssthresh to half of the minimum of the currenct cwnd and the advertised window */
if(pcb->cwnd > pcb->snd_wnd)
pcb->ssthresh = pcb->snd_wnd / 2;
else
pcb->ssthresh = pcb->cwnd / 2;
pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
pcb->flags |= TF_INFR;
} else {
/* Inflate the congestion window, but not if it means that
the value overflows. */
if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
pcb->cwnd += pcb->mss;
}
}
}
} else {
}
} else if (TCP_SEQ_LT(pcb->lastack, ackno) &&
TCP_SEQ_LEQ(ackno, pcb->snd_max)) {
/* We come here when the ACK acknowledges new data. */
/* Reset the "IN Fast Retransmit" flag, since we are no longer
in fast retransmit. Also reset the congestion window to the
slow start threshold. */
if (pcb->flags & TF_INFR) {
pcb->flags &= ~TF_INFR;
pcb->cwnd = pcb->ssthresh;
}
/* Reset the number of retransmissions. */
pcb->nrtx = 0;
/* Reset the retransmission time-out. */
pcb->rto = (pcb->sa >> 3) + pcb->sv;
/* Update the send buffer space. */
pcb->acked = ackno - pcb->lastack;
pcb->snd_buf += pcb->acked;
/* Reset the fast retransmit variables. */
pcb->dupacks = 0;
pcb->lastack = ackno;
/* Update the congestion control variables (cwnd and
ssthresh). */
if (pcb->state >= ESTABLISHED) {
if (pcb->cwnd < pcb->ssthresh) {
if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
pcb->cwnd += pcb->mss;
}
} else {
u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
if (new_cwnd > pcb->cwnd) {
pcb->cwnd = new_cwnd;
}
}
}
/* Remove segment from the unacknowledged list if the incoming
ACK acknowlegdes them. */
while (pcb->unacked != NULL &&
TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
TCP_TCPLEN(pcb->unacked), ackno)) {
next = pcb->unacked;
pcb->unacked = pcb->unacked->next;
pcb->snd_queuelen -= pbuf_clen(next->p);
tcp_seg_free(next);
if (pcb->snd_queuelen != 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -