12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223 |
- /** @file
- TCP output process routines.
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- 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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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 (
- (DEBUG_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;
- }
|