git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1220 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1220 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   TCP output process routines.
 | |
| 
 | |
|   Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php.
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "TcpMain.h"
 | |
| 
 | |
| UINT8  mTcpOutFlag[] = {
 | |
|   0,                          // TCP_CLOSED
 | |
|   0,                          // TCP_LISTEN
 | |
|   TCP_FLG_SYN,                // TCP_SYN_SENT
 | |
|   TCP_FLG_SYN | TCP_FLG_ACK,  // TCP_SYN_RCVD
 | |
|   TCP_FLG_ACK,                // TCP_ESTABLISHED
 | |
|   TCP_FLG_FIN | TCP_FLG_ACK,  // TCP_FIN_WAIT_1
 | |
|   TCP_FLG_ACK,                // TCP_FIN_WAIT_2
 | |
|   TCP_FLG_ACK | TCP_FLG_FIN,  // TCP_CLOSING
 | |
|   TCP_FLG_ACK,                // TCP_TIME_WAIT
 | |
|   TCP_FLG_ACK,                // TCP_CLOSE_WAIT
 | |
|   TCP_FLG_FIN | TCP_FLG_ACK   // TCP_LAST_ACK
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Compute the sequence space left in the old receive window.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
|   @return The sequence space left in the old receive window.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| TcpRcvWinOld (
 | |
|   IN TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   UINT32  OldWin;
 | |
| 
 | |
|   OldWin = 0;
 | |
| 
 | |
|   if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {
 | |
| 
 | |
|     OldWin = TCP_SUB_SEQ (
 | |
|               Tcb->RcvWl2 + Tcb->RcvWnd,
 | |
|               Tcb->RcvNxt
 | |
|               );
 | |
|   }
 | |
| 
 | |
|   return OldWin;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Compute the current receive window.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
|   @return The size of the current receive window, in bytes.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| TcpRcvWinNow (
 | |
|   IN TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   SOCKET  *Sk;
 | |
|   UINT32  Win;
 | |
|   UINT32  Increase;
 | |
|   UINT32  OldWin;
 | |
| 
 | |
|   Sk = Tcb->Sk;
 | |
|   ASSERT (Sk != NULL);
 | |
| 
 | |
|   OldWin    = TcpRcvWinOld (Tcb);
 | |
| 
 | |
|   Win       = SockGetFreeSpace (Sk, SOCK_RCV_BUF);
 | |
| 
 | |
|   Increase  = 0;
 | |
|   if (Win > OldWin) {
 | |
|     Increase = Win - OldWin;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Receiver's SWS: don't advertise a bigger window
 | |
|   // unless it can be increased by at least one Mss or
 | |
|   // half of the receive buffer.
 | |
|   //
 | |
|   if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {
 | |
| 
 | |
|     return Win;
 | |
|   }
 | |
| 
 | |
|   return OldWin;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Compute the value to fill in the window size field of the outgoing segment.
 | |
| 
 | |
|   @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]       Syn     The flag to indicate whether the outgoing segment
 | |
|                            is a SYN segment.
 | |
| 
 | |
|   @return The value of the local receive window size used to fill the outgoing segment.
 | |
| 
 | |
| **/
 | |
| UINT16
 | |
| TcpComputeWnd (
 | |
|   IN OUT TCP_CB  *Tcb,
 | |
|   IN     BOOLEAN Syn
 | |
|   )
 | |
| {
 | |
|   UINT32  Wnd;
 | |
| 
 | |
|   //
 | |
|   // RFC requires that initial window not be scaled
 | |
|   //
 | |
|   if (Syn) {
 | |
| 
 | |
|     Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);
 | |
|   } else {
 | |
| 
 | |
|     Wnd         = TcpRcvWinNow (Tcb);
 | |
| 
 | |
|     Tcb->RcvWnd = Wnd;
 | |
|   }
 | |
| 
 | |
|   Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);
 | |
|   return NTOHS ((UINT16) Wnd);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the maximum SndNxt.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
|   @return The sequence number of the maximum SndNxt.
 | |
| 
 | |
| **/
 | |
| TCP_SEQNO
 | |
| TcpGetMaxSndNxt (
 | |
|   IN TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY      *Entry;
 | |
|   NET_BUF         *Nbuf;
 | |
| 
 | |
|   if (IsListEmpty (&Tcb->SndQue)) {
 | |
|     return Tcb->SndNxt;
 | |
|   }
 | |
| 
 | |
|   Entry = Tcb->SndQue.BackLink;
 | |
|   Nbuf  = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
 | |
| 
 | |
|   ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));
 | |
|   return TCPSEG_NETBUF (Nbuf)->End;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Compute how much data to send.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]  Force   If TRUE, to ignore the sender's SWS avoidance algorithm and send
 | |
|                       out data by force.
 | |
| 
 | |
|   @return The length of the data can be sent. If 0, no data can be sent.
 | |
| 
 | |
| **/
 | |
| UINT32
 | |
| TcpDataToSend (
 | |
|   IN TCP_CB *Tcb,
 | |
|   IN INTN   Force
 | |
|   )
 | |
| {
 | |
|   SOCKET  *Sk;
 | |
|   UINT32  Win;
 | |
|   UINT32  Len;
 | |
|   UINT32  Left;
 | |
|   UINT32  Limit;
 | |
| 
 | |
|   Sk = Tcb->Sk;
 | |
|   ASSERT (Sk != NULL);
 | |
| 
 | |
|   //
 | |
|   // TCP should NOT send data beyond the send window
 | |
|   // and congestion window. The right edge of send
 | |
|   // window is defined as SND.WL2 + SND.WND. The right
 | |
|   // edge of congestion window is defined as SND.UNA +
 | |
|   // CWND.
 | |
|   //
 | |
|   Win   = 0;
 | |
|   Limit = Tcb->SndWl2 + Tcb->SndWnd;
 | |
| 
 | |
|   if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {
 | |
| 
 | |
|     Limit = Tcb->SndUna + Tcb->CWnd;
 | |
|   }
 | |
| 
 | |
|   if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {
 | |
|     Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // The data to send contains two parts: the data on the
 | |
|   // socket send queue, and the data on the TCB's send
 | |
|   // buffer. The later can be non-zero if the peer shrinks
 | |
|   // its advertised window.
 | |
|   //
 | |
|   Left  = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);
 | |
| 
 | |
|   Len   = MIN (Win, Left);
 | |
| 
 | |
|   if (Len > Tcb->SndMss) {
 | |
|     Len = Tcb->SndMss;
 | |
|   }
 | |
| 
 | |
|   if ((Force != 0)|| (Len == 0 && Left == 0)) {
 | |
|     return Len;
 | |
|   }
 | |
| 
 | |
|   if (Len == 0 && Left != 0) {
 | |
|     goto SetPersistTimer;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Sender's SWS avoidance: Don't send a small segment unless
 | |
|   // a)A full-sized segment can be sent,
 | |
|   // b)At least one-half of the maximum sized windows that
 | |
|   // the other end has ever advertised.
 | |
|   // c)It can send everything it has, and either it isn't
 | |
|   // expecting an ACK, or the Nagle algorithm is disabled.
 | |
|   //
 | |
|   if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {
 | |
| 
 | |
|     return Len;
 | |
|   }
 | |
| 
 | |
|   if ((Len == Left) &&
 | |
|       ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))
 | |
|       ) {
 | |
| 
 | |
|     return Len;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // RFC1122 suggests to set a timer when SWSA forbids TCP
 | |
|   // sending more data, and combines it with a probe timer.
 | |
|   //
 | |
| SetPersistTimer:
 | |
|   if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
 | |
| 
 | |
|     DEBUG (
 | |
|       (EFI_D_WARN,
 | |
|       "TcpDataToSend: enter persistent state for TCB %p\n",
 | |
|       Tcb)
 | |
|       );
 | |
| 
 | |
|     if (!Tcb->ProbeTimerOn) {
 | |
|       TcpSetProbeTimer (Tcb);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Build the TCP header of the TCP segment and transmit the segment by IP.
 | |
| 
 | |
|   @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]       Nbuf    Pointer to the buffer containing the segment to be
 | |
|                            sent out.
 | |
| 
 | |
|   @retval 0       The segment was sent out successfully.
 | |
|   @retval -1      An error condition occurred.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| TcpTransmitSegment (
 | |
|   IN OUT TCP_CB  *Tcb,
 | |
|   IN     NET_BUF *Nbuf
 | |
|   )
 | |
| {
 | |
|   UINT16    Len;
 | |
|   TCP_HEAD  *Head;
 | |
|   TCP_SEG   *Seg;
 | |
|   BOOLEAN   Syn;
 | |
|   UINT32    DataLen;
 | |
| 
 | |
|   ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0));
 | |
| 
 | |
|   DataLen = Nbuf->TotalSize;
 | |
| 
 | |
|   Seg     = TCPSEG_NETBUF (Nbuf);
 | |
|   Syn     = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);
 | |
| 
 | |
|   if (Syn) {
 | |
| 
 | |
|     Len = TcpSynBuildOption (Tcb, Nbuf);
 | |
|   } else {
 | |
| 
 | |
|     Len = TcpBuildOption (Tcb, Nbuf);
 | |
|   }
 | |
| 
 | |
|   ASSERT ((Len % 4 == 0) && (Len <= 40));
 | |
| 
 | |
|   Len += sizeof (TCP_HEAD);
 | |
| 
 | |
|   Head = (TCP_HEAD *) NetbufAllocSpace (
 | |
|                         Nbuf,
 | |
|                         sizeof (TCP_HEAD),
 | |
|                         NET_BUF_HEAD
 | |
|                         );
 | |
| 
 | |
|   ASSERT (Head != NULL);
 | |
| 
 | |
|   Nbuf->Tcp       = Head;
 | |
| 
 | |
|   Head->SrcPort   = Tcb->LocalEnd.Port;
 | |
|   Head->DstPort   = Tcb->RemoteEnd.Port;
 | |
|   Head->Seq       = NTOHL (Seg->Seq);
 | |
|   Head->Ack       = NTOHL (Tcb->RcvNxt);
 | |
|   Head->HeadLen   = (UINT8) (Len >> 2);
 | |
|   Head->Res       = 0;
 | |
|   Head->Wnd       = TcpComputeWnd (Tcb, Syn);
 | |
|   Head->Checksum  = 0;
 | |
| 
 | |
|   //
 | |
|   // Check whether to set the PSH flag.
 | |
|   //
 | |
|   TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);
 | |
| 
 | |
|   if (DataLen != 0) {
 | |
|     if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&
 | |
|         TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)
 | |
|         ) {
 | |
| 
 | |
|       TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
 | |
|       TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
 | |
| 
 | |
|     } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) {
 | |
| 
 | |
|       TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check whether to set the URG flag and the urgent pointer.
 | |
|   //
 | |
|   TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);
 | |
| 
 | |
|   if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {
 | |
| 
 | |
|     TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);
 | |
| 
 | |
|     if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {
 | |
| 
 | |
|       Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);
 | |
|     } else {
 | |
| 
 | |
|       Seg->Urg = (UINT16) MIN (
 | |
|                             TCP_SUB_SEQ (Tcb->SndUp,
 | |
|                             Seg->Seq),
 | |
|                             0xffff
 | |
|                             );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Head->Flag      = Seg->Flag;
 | |
|   Head->Urg       = NTOHS (Seg->Urg);
 | |
|   Head->Checksum  = TcpChecksum (Nbuf, Tcb->HeadSum);
 | |
| 
 | |
|   //
 | |
|   // Update the TCP session's control information.
 | |
|   //
 | |
|   Tcb->RcvWl2 = Tcb->RcvNxt;
 | |
|   if (Syn) {
 | |
|     Tcb->RcvWnd = NTOHS (Head->Wnd);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Clear the delayedack flag.
 | |
|   //
 | |
|   Tcb->DelayedAck = 0;
 | |
| 
 | |
|   return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get a segment from the Tcb's SndQue.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]  Seq     The sequence number of the segment.
 | |
|   @param[in]  Len     The maximum length of the segment.
 | |
| 
 | |
|   @return Pointer to the segment. If NULL, some error occurred.
 | |
| 
 | |
| **/
 | |
| NET_BUF *
 | |
| TcpGetSegmentSndQue (
 | |
|   IN TCP_CB    *Tcb,
 | |
|   IN TCP_SEQNO Seq,
 | |
|   IN UINT32    Len
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY      *Head;
 | |
|   LIST_ENTRY      *Cur;
 | |
|   NET_BUF         *Node;
 | |
|   TCP_SEG         *Seg;
 | |
|   NET_BUF         *Nbuf;
 | |
|   TCP_SEQNO       End;
 | |
|   UINT8           *Data;
 | |
|   UINT8           Flag;
 | |
|   INT32           Offset;
 | |
|   INT32           CopyLen;
 | |
| 
 | |
|   ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));
 | |
| 
 | |
|   //
 | |
|   // Find the segment that contains the Seq.
 | |
|   //
 | |
|   Head  = &Tcb->SndQue;
 | |
| 
 | |
|   Node  = NULL;
 | |
|   Seg   = NULL;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Cur, Head) {
 | |
|     Node  = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
 | |
|     Seg   = TCPSEG_NETBUF (Node);
 | |
| 
 | |
|     if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {
 | |
| 
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Return the buffer if it can be returned without
 | |
|   // adjustment:
 | |
|   //
 | |
|   if ((Seg->Seq == Seq) &&
 | |
|       TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&
 | |
|       !NET_BUF_SHARED (Node)
 | |
|       ) {
 | |
| 
 | |
|     NET_GET_REF (Node);
 | |
|     return Node;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create a new buffer and copy data there.
 | |
|   //
 | |
|   Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
 | |
| 
 | |
|   if (Nbuf == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   NetbufReserve (Nbuf, TCP_MAX_HEAD);
 | |
| 
 | |
|   Flag  = Seg->Flag;
 | |
|   End   = Seg->End;
 | |
| 
 | |
|   if (TCP_SEQ_LT (Seq + Len, Seg->End)) {
 | |
|     End = Seq + Len;
 | |
|   }
 | |
| 
 | |
|   CopyLen = TCP_SUB_SEQ (End, Seq);
 | |
|   Offset  = TCP_SUB_SEQ (Seq, Seg->Seq);
 | |
| 
 | |
|   //
 | |
|   // If SYN is set and out of the range, clear the flag.
 | |
|   // Becuase the sequence of the first byte is SEG.SEQ+1,
 | |
|   // adjust Offset by -1. If SYN is in the range, copy
 | |
|   // one byte less.
 | |
|   //
 | |
|   if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
 | |
| 
 | |
|     if (TCP_SEQ_LT (Seg->Seq, Seq)) {
 | |
| 
 | |
|       TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);
 | |
|       Offset--;
 | |
|     } else {
 | |
| 
 | |
|       CopyLen--;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If FIN is set and in the range, copy one byte less,
 | |
|   // and if it is out of the range, clear the flag.
 | |
|   //
 | |
|   if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
 | |
| 
 | |
|     if (Seg->End == End) {
 | |
| 
 | |
|       CopyLen--;
 | |
|     } else {
 | |
| 
 | |
|       TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ASSERT (CopyLen >= 0);
 | |
| 
 | |
|   //
 | |
|   // Copy data to the segment
 | |
|   //
 | |
|   if (CopyLen != 0) {
 | |
|     Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);
 | |
|     ASSERT (Data != NULL);
 | |
| 
 | |
|     if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {
 | |
|       goto OnError;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));
 | |
| 
 | |
|   TCPSEG_NETBUF (Nbuf)->Seq   = Seq;
 | |
|   TCPSEG_NETBUF (Nbuf)->End   = End;
 | |
|   TCPSEG_NETBUF (Nbuf)->Flag  = Flag;
 | |
| 
 | |
|   return Nbuf;
 | |
| 
 | |
| OnError:
 | |
|   NetbufFree (Nbuf);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get a segment from the Tcb's socket buffer.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]  Seq     The sequence number of the segment.
 | |
|   @param[in]  Len     The maximum length of the segment.
 | |
| 
 | |
|   @return Pointer to the segment. If NULL, some error occurred.
 | |
| 
 | |
| **/
 | |
| NET_BUF *
 | |
| TcpGetSegmentSock (
 | |
|   IN TCP_CB    *Tcb,
 | |
|   IN TCP_SEQNO Seq,
 | |
|   IN UINT32    Len
 | |
|   )
 | |
| {
 | |
|   NET_BUF *Nbuf;
 | |
|   UINT8   *Data;
 | |
|   UINT32  DataGet;
 | |
| 
 | |
|   ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
 | |
| 
 | |
|   Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
 | |
| 
 | |
|   if (Nbuf == NULL) {
 | |
|     DEBUG (
 | |
|       (EFI_D_ERROR,
 | |
|       "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n",
 | |
|       Tcb)
 | |
|       );
 | |
| 
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   NetbufReserve (Nbuf, TCP_MAX_HEAD);
 | |
| 
 | |
|   DataGet = 0;
 | |
| 
 | |
|   if (Len != 0) {
 | |
|     //
 | |
|     // copy data to the segment.
 | |
|     //
 | |
|     Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);
 | |
|     ASSERT (Data != NULL);
 | |
| 
 | |
|     DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);
 | |
|   }
 | |
| 
 | |
|   NET_GET_REF (Nbuf);
 | |
| 
 | |
|   TCPSEG_NETBUF (Nbuf)->Seq = Seq;
 | |
|   TCPSEG_NETBUF (Nbuf)->End = Seq + Len;
 | |
| 
 | |
|   InsertTailList (&(Tcb->SndQue), &(Nbuf->List));
 | |
| 
 | |
|   if (DataGet != 0) {
 | |
| 
 | |
|     SockDataSent (Tcb->Sk, DataGet);
 | |
|   }
 | |
| 
 | |
|   return Nbuf;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get a segment starting from sequence Seq of a maximum
 | |
|   length of Len.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]  Seq     The sequence number of the segment.
 | |
|   @param[in]  Len     The maximum length of the segment.
 | |
| 
 | |
|   @return Pointer to the segment. If NULL, some error occurred.
 | |
| 
 | |
| **/
 | |
| NET_BUF *
 | |
| TcpGetSegment (
 | |
|   IN TCP_CB    *Tcb,
 | |
|   IN TCP_SEQNO Seq,
 | |
|   IN UINT32    Len
 | |
|   )
 | |
| {
 | |
|   NET_BUF *Nbuf;
 | |
| 
 | |
|   ASSERT (Tcb != NULL);
 | |
| 
 | |
|   //
 | |
|   // 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) != 0);
 | |
|   return Nbuf;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Retransmit the segment from sequence Seq.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]  Seq     The sequence number of the segment to be retransmitted.
 | |
| 
 | |
|   @retval 0       Retransmission succeeded.
 | |
|   @retval -1      Error condition occurred.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| TcpRetransmit (
 | |
|   IN TCP_CB    *Tcb,
 | |
|   IN TCP_SEQNO Seq
 | |
|   )
 | |
| {
 | |
|   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)) {
 | |
|     DEBUG (
 | |
|       (EFI_D_WARN,
 | |
|       "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",
 | |
|       Tcb)
 | |
|       );
 | |
| 
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   Len   = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);
 | |
|   Len   = MIN (Len, Tcb->SndMss);
 | |
| 
 | |
|   Nbuf  = TcpGetSegmentSndQue (Tcb, Seq, Len);
 | |
|   if (Nbuf == NULL) {
 | |
|     return -1;
 | |
|   }
 | |
| 
 | |
|   ASSERT (TcpVerifySegment (Nbuf) != 0);
 | |
| 
 | |
|   if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
 | |
|     goto OnError;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // The retransmitted buffer may be on the SndQue,
 | |
|   // trim TCP head because all the buffers on SndQue
 | |
|   // are headless.
 | |
|   //
 | |
|   ASSERT (Nbuf->Tcp != NULL);
 | |
|   NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
 | |
|   Nbuf->Tcp = NULL;
 | |
| 
 | |
|   NetbufFree (Nbuf);
 | |
|   return 0;
 | |
| 
 | |
| OnError:
 | |
|   if (Nbuf != NULL) {
 | |
|     NetbufFree (Nbuf);
 | |
|   }
 | |
| 
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Verify that all the segments in SndQue are in good shape.
 | |
| 
 | |
|   @param[in]  Head    Pointer to the head node of the SndQue.
 | |
| 
 | |
|   @retval     0       At least one segment is broken.
 | |
|   @retval     1       All segments in the specific queue are in good shape.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| TcpCheckSndQue (
 | |
|   IN LIST_ENTRY     *Head
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY      *Entry;
 | |
|   NET_BUF         *Nbuf;
 | |
|   TCP_SEQNO       Seq;
 | |
| 
 | |
|   if (IsListEmpty (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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check whether to send data/SYN/FIN and piggyback an ACK.
 | |
| 
 | |
|   @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]       Force   If TRUE, ignore the sender's SWS avoidance algorithm
 | |
|                            and send out data by force.
 | |
| 
 | |
|   @return The number of bytes sent.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| TcpToSendData (
 | |
|   IN OUT TCP_CB *Tcb,
 | |
|   IN     INTN   Force
 | |
|   )
 | |
| {
 | |
|   UINT32    Len;
 | |
|   INTN      Sent;
 | |
|   UINT8     Flag;
 | |
|   NET_BUF   *Nbuf;
 | |
|   TCP_SEG   *Seg;
 | |
|   TCP_SEQNO Seq;
 | |
|   TCP_SEQNO End;
 | |
| 
 | |
|   ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));
 | |
| 
 | |
|   Sent = 0;
 | |
| 
 | |
|   if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {
 | |
| 
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   do {
 | |
|     //
 | |
|     // Compute how much data can be sent
 | |
|     //
 | |
|     Len   = TcpDataToSend (Tcb, Force);
 | |
|     Seq   = Tcb->SndNxt;
 | |
| 
 | |
|     ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0])));
 | |
|     Flag  = mTcpOutFlag[Tcb->State];
 | |
| 
 | |
|     if ((Flag & TCP_FLG_SYN) != 0) {
 | |
| 
 | |
|       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)) == 0)) {
 | |
|       return Sent;
 | |
|     }
 | |
| 
 | |
|     Nbuf = TcpGetSegment (Tcb, Seq, Len);
 | |
| 
 | |
|     if (Nbuf == NULL) {
 | |
|       DEBUG (
 | |
|         (EFI_D_ERROR,
 | |
|         "TcpToSendData: failed to get a segment for TCB %p\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) != 0) {
 | |
|       //
 | |
|       // 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)
 | |
|             ) {
 | |
|         DEBUG (
 | |
|           (EFI_D_INFO,
 | |
|           "TcpToSendData: send FIN to peer for TCB %p in state %s\n",
 | |
|           Tcb,
 | |
|           mTcpStateName[Tcb->State])
 | |
|           );
 | |
| 
 | |
|         End++;
 | |
|       } else {
 | |
|         TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Seg->Seq  = Seq;
 | |
|     Seg->End  = End;
 | |
|     Seg->Flag = Flag;
 | |
| 
 | |
|     ASSERT (TcpVerifySegment (Nbuf) != 0);
 | |
|     ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);
 | |
| 
 | |
|     //
 | |
|     // Don't send an empty segment here.
 | |
|     //
 | |
|     if (Seg->End == Seg->Seq) {
 | |
|       DEBUG (
 | |
|         (EFI_D_WARN,
 | |
|         "TcpToSendData: created a empty segment for TCB %p, free it now\n",
 | |
|         Tcb)
 | |
|         );
 | |
| 
 | |
|       NetbufFree (Nbuf);
 | |
|       return Sent;
 | |
|     }
 | |
| 
 | |
|     if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
 | |
|       NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
 | |
|       Nbuf->Tcp = NULL;
 | |
| 
 | |
|       if ((Flag & TCP_FLG_FIN) != 0)  {
 | |
|         TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
 | |
|       }
 | |
| 
 | |
|       goto OnError;
 | |
|     }
 | |
| 
 | |
|     Sent += TCP_SUB_SEQ (End, Seq);
 | |
| 
 | |
|     //
 | |
|     // All the buffers in the SndQue are headless.
 | |
|     //
 | |
|     ASSERT (Nbuf->Tcp != NULL);
 | |
| 
 | |
|     NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
 | |
|     Nbuf->Tcp = NULL;
 | |
| 
 | |
|     NetbufFree (Nbuf);
 | |
| 
 | |
|     //
 | |
|     // Update the status in TCB.
 | |
|     //
 | |
|     Tcb->DelayedAck = 0;
 | |
| 
 | |
|     if ((Flag & TCP_FLG_FIN) != 0) {
 | |
|       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 requires not to update RTT when in loss.
 | |
|     //
 | |
|     if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
 | |
| 
 | |
|       DEBUG (
 | |
|         (EFI_D_INFO,
 | |
|         "TcpToSendData: set RTT measure sequence %d for TCB %p\n",
 | |
|         Seq,
 | |
|         Tcb)
 | |
|         );
 | |
| 
 | |
|       TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
 | |
|       Tcb->RttSeq     = Seq;
 | |
|       Tcb->RttMeasure = 0;
 | |
|     }
 | |
| 
 | |
|   } while (Len == Tcb->SndMss);
 | |
| 
 | |
|   return Sent;
 | |
| 
 | |
| OnError:
 | |
|   if (Nbuf != NULL) {
 | |
|     NetbufFree (Nbuf);
 | |
|   }
 | |
| 
 | |
|   return Sent;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send an ACK immediately.
 | |
| 
 | |
|   @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpSendAck (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   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);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send a zero probe segment. It can be used by keepalive and zero window probe.
 | |
| 
 | |
|   @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
|   @retval 0       The zero probe segment was sent out successfully.
 | |
|   @retval other   An error condition occurred.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| TcpSendZeroProbe (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check whether to send an ACK or delayed ACK.
 | |
| 
 | |
|   @param[in, out]  Tcb     Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpToSendAck (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   UINT32 TcpNow;
 | |
| 
 | |
|   //
 | |
|   // 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)) {
 | |
|     TcpSendAck (Tcb);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   TcpNow = TcpRcvWinNow (Tcb);
 | |
| 
 | |
|   if (TcpNow > TcpRcvWinOld (Tcb)) {
 | |
|     TcpSendAck (Tcb);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   DEBUG (
 | |
|     (EFI_D_INFO,
 | |
|     "TcpToSendAck: scheduled a delayed ACK for TCB %p\n",
 | |
|     Tcb)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Schedule a delayed ACK.
 | |
|   //
 | |
|   Tcb->DelayedAck++;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send a RESET segment in response to the segment received.
 | |
| 
 | |
|   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance. May be NULL.
 | |
|   @param[in]  Head    TCP header of the segment that triggers the reset.
 | |
|   @param[in]  Len     Length of the segment that triggers the reset.
 | |
|   @param[in]  Local   Local IP address.
 | |
|   @param[in]  Remote  Remote peer's IP address.
 | |
|   @param[in]  Version IP_VERSION_4 indicates TCP is running on IP4 stack,
 | |
|                       IP_VERSION_6 indicates TCP is running on IP6 stack.
 | |
| 
 | |
|   @retval     0       A reset was sent or there is no need to send it.
 | |
|   @retval     -1      No reset is sent.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| TcpSendReset (
 | |
|   IN TCP_CB          *Tcb,
 | |
|   IN TCP_HEAD        *Head,
 | |
|   IN INT32           Len,
 | |
|   IN EFI_IP_ADDRESS  *Local,
 | |
|   IN EFI_IP_ADDRESS  *Remote,
 | |
|   IN UINT8           Version
 | |
|   )
 | |
| {
 | |
|   NET_BUF   *Nbuf;
 | |
|   TCP_HEAD  *Nhead;
 | |
|   UINT16    HeadSum;
 | |
| 
 | |
|   //
 | |
|   // Don't respond to a Reset with reset.
 | |
|   //
 | |
|   if ((Head->Flag & TCP_FLG_RST) != 0) {
 | |
|     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
 | |
|   // is associated with it, otherwise derive 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  = (UINT8) (sizeof (TCP_HEAD) >> 2);
 | |
|   Nhead->Res      = 0;
 | |
|   Nhead->Wnd      = HTONS (0xFFFF);
 | |
|   Nhead->Checksum = 0;
 | |
|   Nhead->Urg      = 0;
 | |
| 
 | |
|   if (Version == IP_VERSION_4) {
 | |
|     HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0);
 | |
|   } else {
 | |
|     HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0);
 | |
|   }
 | |
| 
 | |
|   Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);
 | |
| 
 | |
|   TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version);
 | |
| 
 | |
|   NetbufFree (Nbuf);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Verify that the segment is in good shape.
 | |
| 
 | |
|   @param[in]  Nbuf    The buffer that contains the segment to be checked.
 | |
| 
 | |
|   @retval     0       The segment is broken.
 | |
|   @retval     1       The segment is in good shape.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| TcpVerifySegment (
 | |
|   IN NET_BUF *Nbuf
 | |
|   )
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 |