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