/** @file TCP output process routines. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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)); if (TcpVerifySegment (Nbuf) == 0) { return -1; } 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. // Because 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); } if (TcpVerifySegment (Nbuf) == 0) { NetbufFree (Nbuf); return NULL; } 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 maximum 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. // // // Handle the Window Retraction if TCP window scale is enabled according to RFC7323: // On first retransmission, or if the sequence number is out of // window by less than 2^Rcv.Wind.Shift, then do normal // retransmission(s) without regard to the receiver window as long // as the original segment was in window when it was sent. // if ((Tcb->SndWndScale != 0) && (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax) || TCP_SEQ_BETWEEN (Tcb->SndWl2 + Tcb->SndWnd, Seq, Tcb->SndWl2 + Tcb->SndWnd + (1 << Tcb->SndWndScale)))) { Len = TCP_SUB_SEQ (Tcb->SndNxt, Seq); DEBUG ( (EFI_D_WARN, "TcpRetransmit: retransmission without regard to the receiver window for TCB %p\n", Tcb) ); } else if (TCP_SEQ_GEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq)) { Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq); } else { DEBUG ( (EFI_D_WARN, "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n", Tcb) ); return 0; } Len = MIN (Len, Tcb->SndMss); Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); if (Nbuf == NULL) { return -1; } if (TcpVerifySegment (Nbuf) == 0) { goto OnError; } if (TcpTransmitSegment (Tcb, Nbuf) != 0) { goto OnError; } if (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax)) { Tcb->RetxmitSeqMax = Seq; } // // 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) < (ARRAY_SIZE (mTcpOutFlag))); 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_NET, "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; if (TcpVerifySegment (Nbuf) == 0 || TcpCheckSndQue (&Tcb->SndQue) == 0) { DEBUG ( (EFI_D_ERROR, "TcpToSendData: discard a broken segment for TCB %p\n", Tcb) ); goto OnError; } // // 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) ); goto OnError; } 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_NET, "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_NET, "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; }