1 /** @file
2   Routines to process TCP option.
3 
4   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "TcpMain.h"
11 
12 /**
13   Get a UINT16 value from buffer.
14 
15   @param[in] Buf              Pointer to input buffer.
16 
17   @return                     The UINT16 value obtained from the buffer.
18 
19 **/
20 UINT16
TcpGetUint16(IN UINT8 * Buf)21 TcpGetUint16 (
22   IN UINT8 *Buf
23   )
24 {
25   UINT16  Value;
26   CopyMem (&Value, Buf, sizeof (UINT16));
27   return NTOHS (Value);
28 }
29 
30 /**
31   Get a UINT32 value from buffer.
32 
33   @param[in] Buf              Pointer to input buffer.
34 
35   @return                     The UINT32 value obtained from the buffer.
36 
37 **/
38 UINT32
TcpGetUint32(IN UINT8 * Buf)39 TcpGetUint32 (
40   IN UINT8 *Buf
41   )
42 {
43   UINT32  Value;
44   CopyMem (&Value, Buf, sizeof (UINT32));
45   return NTOHL (Value);
46 }
47 
48 /**
49   Put a UINT32 value in buffer.
50 
51   @param[out] Buf             Pointer to the buffer.
52   @param[in] Data             The UINT32 Date to put in the buffer.
53 
54 **/
55 VOID
TcpPutUint32(OUT UINT8 * Buf,IN UINT32 Data)56 TcpPutUint32 (
57      OUT UINT8  *Buf,
58   IN     UINT32 Data
59   )
60 {
61   Data = HTONL (Data);
62   CopyMem (Buf, &Data, sizeof (UINT32));
63 }
64 
65 /**
66   Compute the window scale value according to the given buffer size.
67 
68   @param[in]  Tcb Pointer to the TCP_CB of this TCP instance.
69 
70   @return         The scale value.
71 
72 **/
73 UINT8
TcpComputeScale(IN TCP_CB * Tcb)74 TcpComputeScale (
75   IN TCP_CB *Tcb
76   )
77 {
78   UINT8   Scale;
79   UINT32  BufSize;
80 
81   ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
82 
83   BufSize = GET_RCV_BUFFSIZE (Tcb->Sk);
84 
85   Scale   = 0;
86   while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) {
87 
88     Scale++;
89   }
90 
91   return Scale;
92 }
93 
94 /**
95   Build the TCP option in three-way handshake.
96 
97   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
98   @param[in]  Nbuf    Pointer to the buffer to store the options.
99 
100   @return             The total length of the TCP option field.
101 
102 **/
103 UINT16
TcpSynBuildOption(IN TCP_CB * Tcb,IN NET_BUF * Nbuf)104 TcpSynBuildOption (
105   IN TCP_CB  *Tcb,
106   IN NET_BUF *Nbuf
107   )
108 {
109   UINT8   *Data;
110   UINT16  Len;
111 
112   ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
113 
114   Len = 0;
115 
116   //
117   // Add a timestamp option if not disabled by the application
118   // and it is the first SYN segment, or the peer has sent
119   // us its timestamp.
120   //
121   if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) &&
122       (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
123         TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))
124       ) {
125 
126     Data = NetbufAllocSpace (
127              Nbuf,
128              TCP_OPTION_TS_ALIGNED_LEN,
129              NET_BUF_HEAD
130              );
131 
132     ASSERT (Data != NULL);
133     Len += TCP_OPTION_TS_ALIGNED_LEN;
134 
135     TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
136     TcpPutUint32 (Data + 4, mTcpTick);
137     TcpPutUint32 (Data + 8, 0);
138   }
139 
140   //
141   // Build window scale option, only when configured
142   // to send WS option, and either we are doing active
143   // open or we have received WS option from peer.
144   //
145   if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) &&
146       (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
147         TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))
148       ) {
149 
150     Data = NetbufAllocSpace (
151              Nbuf,
152              TCP_OPTION_WS_ALIGNED_LEN,
153              NET_BUF_HEAD
154              );
155 
156     ASSERT (Data != NULL);
157 
158     Len += TCP_OPTION_WS_ALIGNED_LEN;
159     TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb));
160   }
161 
162   //
163   // Build the MSS option.
164   //
165   Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1);
166   ASSERT (Data != NULL);
167 
168   Len += TCP_OPTION_MSS_LEN;
169   TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss);
170 
171   return Len;
172 }
173 
174 /**
175   Build the TCP option in synchronized states.
176 
177   @param[in]  Tcb     Pointer to the TCP_CB of this TCP instance.
178   @param[in]  Nbuf    Pointer to the buffer to store the options.
179 
180   @return             The total length of the TCP option field.
181 
182 **/
183 UINT16
TcpBuildOption(IN TCP_CB * Tcb,IN NET_BUF * Nbuf)184 TcpBuildOption (
185   IN TCP_CB  *Tcb,
186   IN NET_BUF *Nbuf
187   )
188 {
189   UINT8   *Data;
190   UINT16  Len;
191 
192   ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
193   Len = 0;
194 
195   //
196   // Build the Timestamp option.
197   //
198   if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) &&
199       !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)
200       ) {
201 
202     Data = NetbufAllocSpace (
203             Nbuf,
204             TCP_OPTION_TS_ALIGNED_LEN,
205             NET_BUF_HEAD
206             );
207 
208     ASSERT (Data != NULL);
209     Len += TCP_OPTION_TS_ALIGNED_LEN;
210 
211     TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
212     TcpPutUint32 (Data + 4, mTcpTick);
213     TcpPutUint32 (Data + 8, Tcb->TsRecent);
214   }
215 
216   return Len;
217 }
218 
219 /**
220   Parse the supported options.
221 
222   @param[in]       Tcp     Pointer to the TCP_CB of this TCP instance.
223   @param[in, out]  Option  Pointer to the TCP_OPTION used to store the
224                            successfully pasrsed options.
225 
226   @retval          0       The options are successfully pasrsed.
227   @retval          -1      Illegal option was found.
228 
229 **/
230 INTN
TcpParseOption(IN TCP_HEAD * Tcp,IN OUT TCP_OPTION * Option)231 TcpParseOption (
232   IN     TCP_HEAD   *Tcp,
233   IN OUT TCP_OPTION *Option
234   )
235 {
236   UINT8 *Head;
237   UINT8 TotalLen;
238   UINT8 Cur;
239   UINT8 Type;
240   UINT8 Len;
241 
242   ASSERT ((Tcp != NULL) && (Option != NULL));
243 
244   Option->Flag  = 0;
245 
246   TotalLen      = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD));
247   if (TotalLen <= 0) {
248     return 0;
249   }
250 
251   Head = (UINT8 *) (Tcp + 1);
252 
253   //
254   // Fast process of the timestamp option.
255   //
256   if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) {
257 
258     Option->TSVal = TcpGetUint32 (Head + 4);
259     Option->TSEcr = TcpGetUint32 (Head + 8);
260     Option->Flag  = TCP_OPTION_RCVD_TS;
261 
262     return 0;
263   }
264   //
265   // Slow path to process the options.
266   //
267   Cur = 0;
268 
269   while (Cur < TotalLen) {
270     Type = Head[Cur];
271 
272     switch (Type) {
273     case TCP_OPTION_MSS:
274       Len = Head[Cur + 1];
275 
276       if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) {
277 
278         return -1;
279       }
280 
281       Option->Mss = TcpGetUint16 (&Head[Cur + 2]);
282       TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS);
283 
284       Cur += TCP_OPTION_MSS_LEN;
285       break;
286 
287     case TCP_OPTION_WS:
288       Len = Head[Cur + 1];
289 
290       if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) {
291 
292         return -1;
293       }
294 
295       Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]);
296       TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS);
297 
298       Cur += TCP_OPTION_WS_LEN;
299       break;
300 
301     case TCP_OPTION_TS:
302       Len = Head[Cur + 1];
303 
304       if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) {
305 
306         return -1;
307       }
308 
309       Option->TSVal = TcpGetUint32 (&Head[Cur + 2]);
310       Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]);
311       TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS);
312 
313       Cur += TCP_OPTION_TS_LEN;
314       break;
315 
316     case TCP_OPTION_NOP:
317       Cur++;
318       break;
319 
320     case TCP_OPTION_EOP:
321       Cur = TotalLen;
322       break;
323 
324     default:
325       Len = Head[Cur + 1];
326 
327       if ((TotalLen - Cur) < Len || Len < 2) {
328         return -1;
329       }
330 
331       Cur = (UINT8) (Cur + Len);
332       break;
333     }
334 
335   }
336 
337   return 0;
338 }
339