/** @file | |
Routines to process TCP option. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "TcpMain.h" | |
/** | |
Get a UINT16 value from buffer. | |
@param[in] Buf Pointer to input buffer. | |
@return The UINT16 value obtained from the buffer. | |
**/ | |
UINT16 | |
TcpGetUint16 ( | |
IN UINT8 *Buf | |
) | |
{ | |
UINT16 Value; | |
CopyMem (&Value, Buf, sizeof (UINT16)); | |
return NTOHS (Value); | |
} | |
/** | |
Get a UINT32 value from buffer. | |
@param[in] Buf Pointer to input buffer. | |
@return The UINT32 value obtained from the buffer. | |
**/ | |
UINT32 | |
TcpGetUint32 ( | |
IN UINT8 *Buf | |
) | |
{ | |
UINT32 Value; | |
CopyMem (&Value, Buf, sizeof (UINT32)); | |
return NTOHL (Value); | |
} | |
/** | |
Put a UINT32 value in buffer. | |
@param[out] Buf Pointer to the buffer. | |
@param[in] Data The UINT32 Date to put in the buffer. | |
**/ | |
VOID | |
TcpPutUint32 ( | |
OUT UINT8 *Buf, | |
IN UINT32 Data | |
) | |
{ | |
Data = HTONL (Data); | |
CopyMem (Buf, &Data, sizeof (UINT32)); | |
} | |
/** | |
Compute the window scale value according to the given buffer size. | |
@param[in] Tcb Pointer to the TCP_CB of this TCP instance. | |
@return The scale value. | |
**/ | |
UINT8 | |
TcpComputeScale ( | |
IN TCP_CB *Tcb | |
) | |
{ | |
UINT8 Scale; | |
UINT32 BufSize; | |
ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); | |
BufSize = GET_RCV_BUFFSIZE (Tcb->Sk); | |
Scale = 0; | |
while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32)(TCP_OPTION_MAX_WIN << Scale) < BufSize)) { | |
Scale++; | |
} | |
return Scale; | |
} | |
/** | |
Build the TCP option in three-way handshake. | |
@param[in] Tcb Pointer to the TCP_CB of this TCP instance. | |
@param[in] Nbuf Pointer to the buffer to store the options. | |
@return The total length of the TCP option field. | |
**/ | |
UINT16 | |
TcpSynBuildOption ( | |
IN TCP_CB *Tcb, | |
IN NET_BUF *Nbuf | |
) | |
{ | |
UINT8 *Data; | |
UINT16 Len; | |
ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); | |
Len = 0; | |
// | |
// Add a timestamp option if not disabled by the application | |
// and it is the first SYN segment, or the peer has sent | |
// us its timestamp. | |
// | |
if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) && | |
(!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || | |
TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS)) | |
) | |
{ | |
Data = NetbufAllocSpace ( | |
Nbuf, | |
TCP_OPTION_TS_ALIGNED_LEN, | |
NET_BUF_HEAD | |
); | |
ASSERT (Data != NULL); | |
Len += TCP_OPTION_TS_ALIGNED_LEN; | |
TcpPutUint32 (Data, TCP_OPTION_TS_FAST); | |
TcpPutUint32 (Data + 4, mTcpTick); | |
TcpPutUint32 (Data + 8, 0); | |
} | |
// | |
// Build window scale option, only when configured | |
// to send WS option, and either we are doing active | |
// open or we have received WS option from peer. | |
// | |
if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) && | |
(!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || | |
TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS)) | |
) | |
{ | |
Data = NetbufAllocSpace ( | |
Nbuf, | |
TCP_OPTION_WS_ALIGNED_LEN, | |
NET_BUF_HEAD | |
); | |
ASSERT (Data != NULL); | |
Len += TCP_OPTION_WS_ALIGNED_LEN; | |
TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb)); | |
} | |
// | |
// Build the MSS option. | |
// | |
Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1); | |
ASSERT (Data != NULL); | |
Len += TCP_OPTION_MSS_LEN; | |
TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss); | |
return Len; | |
} | |
/** | |
Build the TCP option in synchronized states. | |
@param[in] Tcb Pointer to the TCP_CB of this TCP instance. | |
@param[in] Nbuf Pointer to the buffer to store the options. | |
@return The total length of the TCP option field. | |
**/ | |
UINT16 | |
TcpBuildOption ( | |
IN TCP_CB *Tcb, | |
IN NET_BUF *Nbuf | |
) | |
{ | |
UINT8 *Data; | |
UINT16 Len; | |
ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); | |
Len = 0; | |
// | |
// Build the Timestamp option. | |
// | |
if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) && | |
!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST) | |
) | |
{ | |
Data = NetbufAllocSpace ( | |
Nbuf, | |
TCP_OPTION_TS_ALIGNED_LEN, | |
NET_BUF_HEAD | |
); | |
ASSERT (Data != NULL); | |
Len += TCP_OPTION_TS_ALIGNED_LEN; | |
TcpPutUint32 (Data, TCP_OPTION_TS_FAST); | |
TcpPutUint32 (Data + 4, mTcpTick); | |
TcpPutUint32 (Data + 8, Tcb->TsRecent); | |
} | |
return Len; | |
} | |
/** | |
Parse the supported options. | |
@param[in] Tcp Pointer to the TCP_CB of this TCP instance. | |
@param[in, out] Option Pointer to the TCP_OPTION used to store the | |
successfully pasrsed options. | |
@retval 0 The options are successfully pasrsed. | |
@retval -1 Illegal option was found. | |
**/ | |
INTN | |
TcpParseOption ( | |
IN TCP_HEAD *Tcp, | |
IN OUT TCP_OPTION *Option | |
) | |
{ | |
UINT8 *Head; | |
UINT8 TotalLen; | |
UINT8 Cur; | |
UINT8 Type; | |
UINT8 Len; | |
ASSERT ((Tcp != NULL) && (Option != NULL)); | |
Option->Flag = 0; | |
TotalLen = (UINT8)((Tcp->HeadLen << 2) - sizeof (TCP_HEAD)); | |
if (TotalLen <= 0) { | |
return 0; | |
} | |
Head = (UINT8 *)(Tcp + 1); | |
// | |
// Fast process of the timestamp option. | |
// | |
if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) { | |
Option->TSVal = TcpGetUint32 (Head + 4); | |
Option->TSEcr = TcpGetUint32 (Head + 8); | |
Option->Flag = TCP_OPTION_RCVD_TS; | |
return 0; | |
} | |
// | |
// Slow path to process the options. | |
// | |
Cur = 0; | |
while (Cur < TotalLen) { | |
Type = Head[Cur]; | |
switch (Type) { | |
case TCP_OPTION_MSS: | |
Len = Head[Cur + 1]; | |
if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) { | |
return -1; | |
} | |
Option->Mss = TcpGetUint16 (&Head[Cur + 2]); | |
TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS); | |
Cur += TCP_OPTION_MSS_LEN; | |
break; | |
case TCP_OPTION_WS: | |
Len = Head[Cur + 1]; | |
if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) { | |
return -1; | |
} | |
Option->WndScale = (UINT8)MIN (14, Head[Cur + 2]); | |
TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS); | |
Cur += TCP_OPTION_WS_LEN; | |
break; | |
case TCP_OPTION_TS: | |
Len = Head[Cur + 1]; | |
if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) { | |
return -1; | |
} | |
Option->TSVal = TcpGetUint32 (&Head[Cur + 2]); | |
Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]); | |
TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS); | |
Cur += TCP_OPTION_TS_LEN; | |
break; | |
case TCP_OPTION_NOP: | |
Cur++; | |
break; | |
case TCP_OPTION_EOP: | |
Cur = TotalLen; | |
break; | |
default: | |
Len = Head[Cur + 1]; | |
if (((TotalLen - Cur) < Len) || (Len < 2)) { | |
return -1; | |
} | |
Cur = (UINT8)(Cur + Len); | |
break; | |
} | |
} | |
return 0; | |
} |