2010-11-01 07:13:54 +01:00
|
|
|
/** @file
|
|
|
|
Routines to process TCP option.
|
|
|
|
|
2018-08-16 02:58:04 +02:00
|
|
|
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
|
2010-11-01 07:13:54 +01:00
|
|
|
|
2019-04-04 01:06:13 +02:00
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
2010-11-01 07:13:54 +01:00
|
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
#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 Ilegal 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;
|
|
|
|
}
|