tcp4output.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 1,320 行 · 第 1/2 页

C
1,320
字号
}

NET_BUF *
TcpGetSegment (
  IN TCP_CB    *Tcb,
  IN TCP_SEQNO Seq,
  IN UINT32    Len
  )
/*++

Routine Description:

  Get a segment starting from sequence Seq of a maximum
  length of Len.

Arguments:

  Tcb - Pointer to the TCP_CB of this TCP instance.
  Seq - The sequence number of the segment.
  Len - The maximum length of the segment.

Returns:

  Pointer to the segment, if NULL some error occurred.

--*/
{
  NET_BUF *Nbuf;

  ASSERT (Tcb);

  //
  // Compare the SndNxt with the max sequence number sent.
  //
  if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {

    Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
  } else {

    Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);
  }

  ASSERT (TcpVerifySegment (Nbuf));
  return Nbuf;
}

INTN
TcpRetransmit (
  IN TCP_CB    *Tcb,
  IN TCP_SEQNO Seq
  )
/*++

Routine Description:

  Retransmit the segment from sequence Seq.

Arguments:

  Tcb - Pointer to the TCP_CB of this TCP instance.
  Seq - The sequence number of the segment to be retransmitted.

Returns:

  0   - Retransmission succeeded.
  -1  - Error condition occurred.

--*/
{
  NET_BUF *Nbuf;
  UINT32  Len;

  //
  // Compute the maxium length of retransmission. It is
  // limited by three factors:
  // 1. Less than SndMss
  // 2. must in the current send window
  // 3. will not change the boundaries of queued segments.
  //
  if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {
    TCP4_DEBUG_WARN (("TcpRetransmit: retransmission cancelled "
      "because send window too small for TCB %x\n", Tcb));

    return 0;
  }

  Len   = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);
  Len   = NET_MIN (Len, Tcb->SndMss);

  Nbuf  = TcpGetSegmentSndQue (Tcb, Seq, Len);
  if (Nbuf == NULL) {
    return -1;
  }

  ASSERT (TcpVerifySegment (Nbuf));

  if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
    goto OnError;
  }

  //
  // The retransmitted buffer may be on the SndQue,
  // trim TCP head because all the buffer on SndQue
  // are headless.
  //
  ASSERT (Nbuf->Tcp);
  NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
  Nbuf->Tcp = NULL;

  NetbufFree (Nbuf);
  return 0;

OnError:
  if (Nbuf != NULL) {
    NetbufFree (Nbuf);
  }

  return -1;
}

INTN
TcpToSendData (
  IN TCP_CB *Tcb,
  IN INTN Force
  )
/*++

Routine Description:

  Check whether to send data/SYN/FIN and piggy back an ACK.

Arguments:

  Tcb   - Pointer to the TCP_CB of this TCP instance.
  Force - Whether to ignore the sender's SWS avoidance algorithm
          and send out data by force.

Returns:

  The number of bytes sent.

--*/
{
  UINT32    Len;
  INTN      Sent;
  UINT8     Flag;
  NET_BUF   *Nbuf;
  TCP_SEG   *Seg;
  TCP_SEQNO Seq;
  TCP_SEQNO End;

  ASSERT (Tcb && Tcb->Sk && (Tcb->State != TCP_LISTEN));

  Sent = 0;

  if ((Tcb->State == TCP_CLOSED) ||
      TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {

    return 0;
  }

SEND_AGAIN:
  //
  // compute how much data can be sent
  //
  Len   = TcpDataToSend (Tcb, Force);
  Seq   = Tcb->SndNxt;

  Flag  = mTcpOutFlag[Tcb->State];

  if (Flag & TCP_FLG_SYN) {

    Seq = Tcb->Iss;
    Len = 0;
  }

  //
  // only send a segment without data if SYN or
  // FIN is set.
  //
  if ((Len == 0) && !(Flag & (TCP_FLG_SYN | TCP_FLG_FIN))) {
    return Sent;
  }

  Nbuf = TcpGetSegment (Tcb, Seq, Len);

  if (Nbuf == NULL) {
    TCP4_DEBUG_ERROR (
      ("TcpToSendData: failed to get a segment for TCB %x\n",
      Tcb)
      );

    goto OnError;
  }

  Seg = TCPSEG_NETBUF (Nbuf);

  //
  // Set the TcpSeg in Nbuf.
  //
  Len = Nbuf->TotalSize;
  End = Seq + Len;
  if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {
    End++;
  }

  if (Flag & TCP_FLG_FIN) {
    //
    // Send FIN if all data is sent, and FIN is
    // in the window
    //
    if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&
        (GET_SND_DATASIZE (Tcb->Sk) == 0) &&
        TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)
          ) {

      TCP4_DEBUG_TRACE (("TcpToSendData: send FIN "
        "to peer for TCB %x in state %d\n", Tcb, Tcb->State));

      End++;
    } else {
      TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
    }
  }

  Seg->Seq  = Seq;
  Seg->End  = End;
  Seg->Flag = Flag;

  ASSERT (TcpVerifySegment (Nbuf));
  ASSERT (TcpCheckSndQue (&Tcb->SndQue));

  //
  // don't send an empty segment here.
  //
  if (Seg->End == Seg->Seq) {
    TCP4_DEBUG_WARN (("TcpToSendData: created a empty"
      " segment for TCB %x, free it now\n", Tcb));

    NetbufFree (Nbuf);
    return Sent;
  }

  if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
    //
    // TODO: double check this
    //
    NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
    Nbuf->Tcp = NULL;

    if (Flag & TCP_FLG_FIN) {
      TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
    }

    goto OnError;
  }

  Sent += TCP_SUB_SEQ (End, Seq);

  //
  // All the buffer in the SndQue is headless
  //
  ASSERT (Nbuf->Tcp);

  NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
  Nbuf->Tcp = NULL;

  NetbufFree (Nbuf);

  //
  // update status in TCB
  //
  Tcb->DelayedAck = 0;

  if (Flag & TCP_FLG_FIN) {
    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
  }

  if (TCP_SEQ_GT (End, Tcb->SndNxt)) {
    Tcb->SndNxt = End;
  }

  if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
    TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
  }

  //
  // Enable RTT measurement only if not in retransmit.
  // Karn's algorithm reqires not to update RTT when in loss.
  //
  if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&
      !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {

    TCP4_DEBUG_TRACE (("TcpToSendData: set RTT measure "
      "sequence %d for TCB %x\n", Seq, Tcb));

    TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
    Tcb->RttSeq     = Seq;
    Tcb->RttMeasure = 0;
  }

  if (Len == Tcb->SndMss) {
    goto SEND_AGAIN;
  }

  return Sent;

OnError:
  if (Nbuf != NULL) {
    NetbufFree (Nbuf);
  }

  return Sent;
}

VOID
TcpSendAck (
  IN TCP_CB *Tcb
  )
/*++

Routine Description:

  Send an ACK immediately.

Arguments:

  Tcb - Pointer to the TCP_CB of this TCP instance.

Returns:

  None.

--*/
{
  NET_BUF *Nbuf;
  TCP_SEG *Seg;

  Nbuf = NetbufAlloc (TCP_MAX_HEAD);

  if (Nbuf == NULL) {
    return;
  }

  NetbufReserve (Nbuf, TCP_MAX_HEAD);

  Seg       = TCPSEG_NETBUF (Nbuf);
  Seg->Seq  = Tcb->SndNxt;
  Seg->End  = Tcb->SndNxt;
  Seg->Flag = TCP_FLG_ACK;

  if (TcpTransmitSegment (Tcb, Nbuf) == 0) {
    TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
    Tcb->DelayedAck = 0;
  }

  NetbufFree (Nbuf);
}

INTN
TcpSendZeroProbe (
  IN TCP_CB *Tcb
  )
/*++

Routine Description:

  Send a zero probe segment. It can be used by keepalive
  and zero window probe.

Arguments:

  Tcb - Pointer to the TCP_CB of this TCP instance.

Returns:

  0     - The zero probe segment was sent out successfully.
  other - Error condition occurred.

--*/
{
  NET_BUF *Nbuf;
  TCP_SEG *Seg;
  INTN     Result;

  Nbuf = NetbufAlloc (TCP_MAX_HEAD);

  if (Nbuf == NULL) {
    return -1;
  }

  NetbufReserve (Nbuf, TCP_MAX_HEAD);

  //
  // SndNxt-1 is out of window. The peer should respond 
  // with an ACK.
  //
  Seg       = TCPSEG_NETBUF (Nbuf);
  Seg->Seq  = Tcb->SndNxt - 1;
  Seg->End  = Tcb->SndNxt - 1;
  Seg->Flag = TCP_FLG_ACK;

  Result    = TcpTransmitSegment (Tcb, Nbuf);
  NetbufFree (Nbuf);

  return Result;
}

VOID
TcpToSendAck (
  IN TCP_CB *Tcb
  )
/*++

Routine Description:

  Check whether to send an ACK or delayed ACK.

Arguments:

  Tcb - Pointer to the TCP_CB of this TCP instance.

Returns:

  None.

--*/
{
  //
  // Generally, TCP should send a delayed ACK unless:
  //   1. ACK at least every other FULL sized segment received,
  //   2. Packets received out of order
  //   3. Receiving window is open
  //
  if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||
      (Tcb->DelayedAck >= 1) ||
      (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb))
      ) {
    TcpSendAck (Tcb);
    return;
  }

  TCP4_DEBUG_TRACE (("TcpToSendAck: scheduled a delayed"
    " ACK for TCB %x\n", Tcb));

  //
  // schedule a delayed ACK
  //
  Tcb->DelayedAck++;
}

INTN
TcpSendReset (
  IN TCP_CB    *Tcb,
  IN TCP_HEAD  *Head,
  IN INT32     Len,
  IN UINT32    Local,
  IN UINT32    Remote
  )
/*++

Routine Description:

  Send a RESET segment in response to the segment received.

Arguments:

  Tcb     - Pointer to the TCP_CB of this TCP instance, may be NULL.
  Head    - TCP header of the segment that triggers the reset.
  Len     - Length of the segment that triggers the reset.
  Local   - Local IP address.
  Remote  - Remote peer's IP address.

Returns:

  0       - A reset is sent or no need to send it.
  -1      - No reset is sent.

--*/
{
  NET_BUF   *Nbuf;
  TCP_HEAD  *Nhead;
  UINT16    HeadSum;

  //
  // Don't respond to a Reset with reset
  //
  if (Head->Flag & TCP_FLG_RST) {
    return 0;
  }

  Nbuf = NetbufAlloc (TCP_MAX_HEAD);

  if (Nbuf == NULL) {
    return -1;
  }

  Nhead = (TCP_HEAD *) NetbufAllocSpace (
                        Nbuf,
                        sizeof (TCP_HEAD),
                        NET_BUF_TAIL
                        );

  ASSERT (Nhead != NULL);

  Nbuf->Tcp   = Nhead;
  Nhead->Flag = TCP_FLG_RST;

  //
  // Derive Seq/ACK from the segment if no TCB
  // associated with it, otherwise from the Tcb
  //
  if (Tcb == NULL) {

    if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {
      Nhead->Seq  = Head->Ack;
      Nhead->Ack  = 0;
    } else {
      Nhead->Seq = 0;
      TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
      Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);
    }
  } else {

    Nhead->Seq  = HTONL (Tcb->SndNxt);
    Nhead->Ack  = HTONL (Tcb->RcvNxt);
    TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
  }

  Nhead->SrcPort  = Head->DstPort;
  Nhead->DstPort  = Head->SrcPort;
  Nhead->HeadLen  = (sizeof (TCP_HEAD) >> 2);
  Nhead->Res      = 0;
  Nhead->Wnd      = HTONS (0xFFFF);
  Nhead->Checksum = 0;
  Nhead->Urg      = 0;

  HeadSum         = NetPseudoHeadChecksum (Local, Remote, 6, 0);
  Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);

  TcpSendIpPacket (Tcb, Nbuf, Local, Remote);

  NetbufFree (Nbuf);
  return 0;
}

INTN
TcpVerifySegment (
  IN NET_BUF *Nbuf
  )
/*++

Routine Description:

  Verify that the segment is in good shape.

Arguments:

  Nbuf  - Buffer that contains the segment to be checked.

Returns:

  0     - The segment is broken.
  1     - The segment is in good shape.

--*/
{
  TCP_HEAD  *Head;
  TCP_SEG   *Seg;
  UINT32    Len;

  if (Nbuf == NULL) {
    return 1;
  }

  NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);

  Seg   = TCPSEG_NETBUF (Nbuf);
  Len   = Nbuf->TotalSize;
  Head  = Nbuf->Tcp;

  if (Head != NULL) {
    if (Head->Flag != Seg->Flag) {
      return 0;
    }

    Len -= (Head->HeadLen << 2);
  }

  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
    Len++;
  }

  if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
    Len++;
  }

  if (Seg->Seq + Len != Seg->End) {
    return 0;
  }

  return 1;
}

INTN
TcpCheckSndQue (
  IN NET_LIST_ENTRY *Head
  )
/*++

Routine Description:

  Verify that all the segments in SndQue are in good shape.

Arguments:

  Head  - Pointer to the head node of the SndQue.

Returns:

  0     - At least one segment is broken.
  1     - All segments in the specific queue are in good shape.

--*/
{
  NET_LIST_ENTRY  *Entry;
  NET_BUF         *Nbuf;
  TCP_SEQNO       Seq;

  if (NetListIsEmpty (Head)) {
    return 1;
  }
  //
  // Initialize the Seq
  //
  Entry = Head->ForwardLink;
  Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
  Seq   = TCPSEG_NETBUF (Nbuf)->Seq;

  NET_LIST_FOR_EACH (Entry, Head) {
    Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

    if (TcpVerifySegment (Nbuf) == 0) {
      return 0;
    }
    
    //
    // All the node in the SndQue should has:
    // SEG.SEQ = LAST_SEG.END
    //
    if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {
      return 0;
    }

    Seq = TCPSEG_NETBUF (Nbuf)->End;
  }

  return 1;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?