TCP protocol data input of lwip source code analysis (2)

1. Introduction

After the tcp message entered in the previous chapter finds its corresponding TCP control block, the message needs to be processed in different ways according to the different states of the tcp control block. This may cause a change in the tcp state, and the tcp state machine is used to represent such a state change.

Insert picture description here
(Figure intrusion and deletion)
Combine the process of tcp connection and disconnection to understand the tcp state machine

Insert picture description here
Insert picture description here

Second, source code analysis

The source code of the tcp state machine is the realization of the state machine in the figure above; the
first is to process the rst, if the input message is an rst message, it is judged whether the rst is legal or not, and the tcp connection is reset if it is legal:

  /*---------------------------------------优先处理rst报文----------------------------------------*/
  if (flags & TCP_RST) {
    
      //报文中带rst标志
   if (pcb->state == SYN_SENT) {
    
     //连接建立过程中,判断ackno来识别一个rst是否正确
      if (ackno == pcb->snd_nxt) {
    
    
        //连接建立过程。应答序号等于下一个要发送的序号,是正确的
        acceptable = 1;
      }
    } else {
    
      //其他状态的tcp需要检查报文的序号以判断rst是否合法
      
      //报文序号等于期待接收的序号时,pcb复位
      if (seqno == pcb->rcv_nxt) {
    
    
        acceptable = 1; 
      } else  if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
                                  pcb->rcv_nxt + pcb->rcv_wnd)) {
    
    
        //如果序号是在接收窗口内,则发送ack,等待回复重新检查报文序号
        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; //process处理结果标志为复位
      pcb->flags &= ~TF_ACK_DELAY;  //清除ack
      return ERR_RST;
    } else {
    
      //不需要复位,忽略rst报文
      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;
    }
  }
  //本地不处于SYN_SENT或SYN_RCVD,却收到了SYN,是异常情况,可能是对方重启了
  if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
    
    
    /* Cope with new connection attempt after remote end crashed */
    //应对远程端崩溃后的新连接尝试。
    tcp_ack_now(pcb);
    return ERR_OK;
  }

  //如果本地接收未关闭,就复位保活计时器
  if ((pcb->flags & TF_RXCLOSED) == 0) {
    
    
    /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
    pcb->tmr = tcp_ticks; 
  }
  pcb->keep_cnt_sent = 0; //清除保活计数值

  tcp_parseopt(pcb);  //解析出mss

Now enter the specific code of the state machine

  switch (pcb->state) {
    
    
  /*--------------------------------客户端发起连接请求等待服务器返回syn和ack----------------------------------------*/
  case SYN_SENT:  
    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
     pcb->snd_nxt, lwip_ntohl(pcb->unacked->tcphdr->seqno)));

    //报文是ack和syn,且序号能对的上,则发送ack,并进入连接建立状态
    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;

      //计算ip路径下的mss
      pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);

      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;  //发送报文队列减1
      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
      
      //既然收到了ack,那么就将unack队列的第一个报文删除
      rseg = pcb->unacked;
      //lwip将重传定时到的unacked放到了unsent,所以unacked的报文可能被放到了unsent
      if (rseg == NULL) {
    
    
        rseg = pcb->unsent;
        LWIP_ASSERT("no segment to free", rseg != NULL);
        pcb->unsent = rseg->next;
      } else {
    
    
        pcb->unacked = rseg->next;  //重新组织unacked
      }
      tcp_seg_free(rseg); //释放被acked的报文

      //如果接下来没有需要应答的报文,则关闭重传定时器,否则重置它
      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;
      }
      //最后发送一个ack,三次握手结束
      tcp_ack_now(pcb);
    }

    //只收到ack报文,可能是半连接
    else if (flags & TCP_ACK) {
    
    
    	//发送rst报文重新连接
      tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
        ip_current_src_addr(), tcphdr->dest, tcphdr->src);

      //重传次数不超限制则立即重传,包括SYN
      if (pcb->nrtx < TCP_SYNMAXRTX) {
    
    
        pcb->rtime = 0;
        tcp_rexmit_rto(pcb);
      }
    }
    break;
  /*------------------------------服务器收到SYN并发送完syn+ack,等待客户端回答-----------------------------------*/
  case SYN_RCVD:  
    if (flags & TCP_ACK) {
    
      //报文是客户端的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 LWIP_CALLBACK_API
        LWIP_ASSERT("pcb->listener->accept != NULL",
          (pcb->listener == NULL) || (pcb->listener->accept != NULL));
#endif
        if (pcb->listener == NULL) {
    
    
     
          err = ERR_VAL;  //listen pcb可能已经被关闭了
        } else
#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
        {
    
    
          tcp_backlog_accepted(pcb);  //无操作
          
          //回调连接建立函数
          TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);
        }
        //如果回调函数返回错误,或者listen pcb已经被关闭,则终止tcp
        if (err != ERR_OK) {
    
    
          if (err != ERR_ABRT) {
    
    
            tcp_abort(pcb);
          }
          return ERR_ABRT;
        }
        //报文中还有其他的数据,则交给应用层处理
        tcp_receive(pcb);

        //如果本地发送的数据被报文中的ack确认,则实际acked-1,因为syn占一个字节
        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));
        //如果有fin标志,则回答,并进入closewait,等待应用程序
        if (recv_flags & TF_GOT_FIN) {
    
    
          tcp_ack_now(pcb);
          pcb->state = CLOSE_WAIT;
        }
      } else {
    
      //非法的ack确认序号,发送rst
        
        tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
          ip_current_src_addr(), tcphdr->dest, tcphdr->src);
      }
    } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
    
      

      //收到对方重复的syn,说明服务器发出的syn+ack丢失,重发
      tcp_rexmit(pcb);
    }
    break;
  case CLOSE_WAIT:  //服务器不会接收数据
    /* FALLTHROUGH */
  case ESTABLISHED: //已经建立连接
    tcp_receive(pcb); //将数据交给上层

    //被动关闭连接
    if (recv_flags & TF_GOT_FIN) {
    
     /* passive close */
      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) && //同时收到本地FIN的ack,且本地没有未发送的数据,跳过wait2,进入timewait
          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); //发送ack
        tcp_pcb_purge(pcb); //释放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;  //收到ack,进入FIN_WAIT_2,等待服务器的FIN
    }
    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);

    //收到了对方的ack且没有未发送的数据,进入TIME_WAIT
    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);

    //收到了对方的ack且没有未发送的数据,结束tcp一生
    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;  //tcp_input会对tcp进一步释放
    }
    break;
  default:
    break;
  }
  return ERR_OK;
}

It can be seen that the main thing the tcp state machine completes is the logic of the transition between tcp states. The data in the real tcp message is handed over tcp_receive(). This function checks the tcp message data and passes the ordered data to the application layer.
Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_44821644/article/details/111357509