]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/TcpDxe/TcpMisc.c
NetworkPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / NetworkPkg / TcpDxe / TcpMisc.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 Misc support routines for TCP driver.\r
3\r
8f586b85 4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
3696ceae 5 Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>\r
a3bcde70 6\r
ecf98fbc 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
a3bcde70
HT
8\r
9**/\r
10\r
11#include "TcpMain.h"\r
12\r
13LIST_ENTRY mTcpRunQue = {\r
14 &mTcpRunQue,\r
15 &mTcpRunQue\r
16};\r
17\r
18LIST_ENTRY mTcpListenQue = {\r
19 &mTcpListenQue,\r
20 &mTcpListenQue\r
21};\r
22\r
23TCP_SEQNO mTcpGlobalIss = TCP_BASE_ISS;\r
24\r
25CHAR16 *mTcpStateName[] = {\r
26 L"TCP_CLOSED",\r
27 L"TCP_LISTEN",\r
28 L"TCP_SYN_SENT",\r
29 L"TCP_SYN_RCVD",\r
30 L"TCP_ESTABLISHED",\r
31 L"TCP_FIN_WAIT_1",\r
32 L"TCP_FIN_WAIT_2",\r
33 L"TCP_CLOSING",\r
34 L"TCP_TIME_WAIT",\r
35 L"TCP_CLOSE_WAIT",\r
36 L"TCP_LAST_ACK"\r
37};\r
38\r
39\r
40/**\r
41 Initialize the Tcb local related members.\r
42\r
43 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
44\r
45**/\r
46VOID\r
47TcpInitTcbLocal (\r
48 IN OUT TCP_CB *Tcb\r
49 )\r
50{\r
51 //\r
52 // Compute the checksum of the fixed parts of pseudo header\r
53 //\r
54 if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
55 Tcb->HeadSum = NetPseudoHeadChecksum (\r
56 Tcb->LocalEnd.Ip.Addr[0],\r
57 Tcb->RemoteEnd.Ip.Addr[0],\r
58 0x06,\r
59 0\r
60 );\r
61 } else {\r
62 Tcb->HeadSum = NetIp6PseudoHeadChecksum (\r
63 &Tcb->LocalEnd.Ip.v6,\r
64 &Tcb->RemoteEnd.Ip.v6,\r
65 0x06,\r
66 0\r
67 );\r
68 }\r
69\r
70 Tcb->Iss = TcpGetIss ();\r
71 Tcb->SndUna = Tcb->Iss;\r
72 Tcb->SndNxt = Tcb->Iss;\r
73\r
74 Tcb->SndWl2 = Tcb->Iss;\r
75 Tcb->SndWnd = 536;\r
76\r
77 Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
78\r
79 //\r
80 // First window size is never scaled\r
81 //\r
82 Tcb->RcvWndScale = 0;\r
3696ceae 83 Tcb->RetxmitSeqMax = 0;\r
a3bcde70
HT
84\r
85 Tcb->ProbeTimerOn = FALSE;\r
86}\r
87\r
88/**\r
89 Initialize the peer related members.\r
90\r
91 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
92 @param[in] Seg Pointer to the segment that contains the peer's intial info.\r
93 @param[in] Opt Pointer to the options announced by the peer.\r
94\r
95**/\r
96VOID\r
97TcpInitTcbPeer (\r
98 IN OUT TCP_CB *Tcb,\r
99 IN TCP_SEG *Seg,\r
100 IN TCP_OPTION *Opt\r
101 )\r
102{\r
103 UINT16 RcvMss;\r
104\r
105 ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL));\r
106 ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));\r
107\r
108 Tcb->SndWnd = Seg->Wnd;\r
109 Tcb->SndWndMax = Tcb->SndWnd;\r
110 Tcb->SndWl1 = Seg->Seq;\r
111\r
112 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
113 Tcb->SndWl2 = Seg->Ack;\r
114 } else {\r
115 Tcb->SndWl2 = Tcb->Iss + 1;\r
116 }\r
117\r
118 if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {\r
119 Tcb->SndMss = (UINT16) MAX (64, Opt->Mss);\r
120\r
121 RcvMss = TcpGetRcvMss (Tcb->Sk);\r
122 if (Tcb->SndMss > RcvMss) {\r
123 Tcb->SndMss = RcvMss;\r
124 }\r
125\r
126 } else {\r
127 //\r
128 // One end doesn't support MSS option, use default.\r
129 //\r
130 Tcb->RcvMss = 536;\r
131 }\r
132\r
133 Tcb->CWnd = Tcb->SndMss;\r
134\r
135 Tcb->Irs = Seg->Seq;\r
136 Tcb->RcvNxt = Tcb->Irs + 1;\r
137\r
138 Tcb->RcvWl2 = Tcb->RcvNxt;\r
139\r
140 if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {\r
141\r
142 Tcb->SndWndScale = Opt->WndScale;\r
143\r
144 Tcb->RcvWndScale = TcpComputeScale (Tcb);\r
145 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);\r
146\r
147 } else {\r
148 //\r
149 // One end doesn't support window scale option. use zero.\r
150 //\r
151 Tcb->RcvWndScale = 0;\r
152 }\r
153\r
154 if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {\r
155\r
156 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);\r
157 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);\r
158\r
185395a4
BZ
159 Tcb->TsRecent = Opt->TSVal;\r
160\r
a3bcde70
HT
161 //\r
162 // Compute the effective SndMss per RFC1122\r
163 // section 4.2.2.6. If timestamp option is\r
164 // enabled, it will always occupy 12 bytes.\r
165 //\r
166 Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;\r
167 }\r
168}\r
169\r
170/**\r
171 Check whether one IP address equals the other.\r
172\r
173 @param[in] Ip1 Pointer to IP address to be checked.\r
174 @param[in] Ip2 Pointer to IP address to be checked.\r
175 @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
176 IP_VERSION_6 indicates the IP address is an IPv6 address.\r
177\r
178 @retval TRUE Ip1 equals Ip2.\r
179 @retval FALSE Ip1 does not equal Ip2.\r
180\r
181**/\r
182BOOLEAN\r
183TcpIsIpEqual (\r
184 IN EFI_IP_ADDRESS *Ip1,\r
185 IN EFI_IP_ADDRESS *Ip2,\r
186 IN UINT8 Version\r
187 )\r
188{\r
189 ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
190\r
191 if (Version == IP_VERSION_4) {\r
192 return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]);\r
193 } else {\r
194 return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6);\r
195 }\r
196}\r
197\r
198/**\r
199 Check whether one IP address is filled with ZERO.\r
200\r
201 @param[in] Ip Pointer to the IP address to be checked.\r
202 @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
203 IP_VERSION_6 indicates the IP address is an IPv6 address.\r
204\r
205 @retval TRUE Ip is all zero address.\r
206 @retval FALSE Ip is not all zero address.\r
207\r
208**/\r
209BOOLEAN\r
210TcpIsIpZero (\r
211 IN EFI_IP_ADDRESS *Ip,\r
212 IN UINT8 Version\r
213 )\r
214{\r
215 ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
216\r
217 if (Version == IP_VERSION_4) {\r
218 return (BOOLEAN) (Ip->Addr[0] == 0);\r
219 } else {\r
220 return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) &&\r
221 (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0));\r
222 }\r
223}\r
224\r
225/**\r
226 Locate a listen TCB that matchs the Local and Remote.\r
227\r
228 @param[in] Local Pointer to the local (IP, Port).\r
229 @param[in] Remote Pointer to the remote (IP, Port).\r
230 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
231 IP_VERSION_6 indicates TCP is running on IP6 stack.\r
232\r
233 @return Pointer to the TCP_CB with the least number of wildcards,\r
234 if NULL no match is found.\r
235\r
236**/\r
237TCP_CB *\r
238TcpLocateListenTcb (\r
239 IN TCP_PEER *Local,\r
240 IN TCP_PEER *Remote,\r
241 IN UINT8 Version\r
242 )\r
243{\r
244 LIST_ENTRY *Entry;\r
245 TCP_CB *Node;\r
246 TCP_CB *Match;\r
247 INTN Last;\r
248 INTN Cur;\r
249\r
250 Last = 4;\r
251 Match = NULL;\r
252\r
253 NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
254 Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
255\r
256 if ((Version != Node->Sk->IpVersion) ||\r
257 (Local->Port != Node->LocalEnd.Port) ||\r
258 !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) ||\r
259 !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version)\r
260 ) {\r
261\r
262 continue;\r
263 }\r
264\r
265 //\r
266 // Compute the number of wildcard\r
267 //\r
268 Cur = 0;\r
269 if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) {\r
270 Cur++;\r
271 }\r
272\r
273 if (Node->RemoteEnd.Port == 0) {\r
274 Cur++;\r
275 }\r
276\r
277 if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) {\r
278 Cur++;\r
279 }\r
280\r
281 if (Cur < Last) {\r
282 if (Cur == 0) {\r
283 return Node;\r
284 }\r
285\r
286 Last = Cur;\r
287 Match = Node;\r
288 }\r
289 }\r
290\r
291 return Match;\r
292}\r
293\r
294/**\r
295 Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.\r
296\r
297 @param[in] Addr Pointer to the IP address needs to match.\r
298 @param[in] Port The port number needs to match.\r
299 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
300 IP_VERSION_6 indicates TCP is running on IP6 stack.\r
301\r
302\r
303 @retval TRUE The Tcb which matches the <Addr Port> pair exists.\r
304 @retval FALSE Otherwise\r
305\r
306**/\r
307BOOLEAN\r
308TcpFindTcbByPeer (\r
309 IN EFI_IP_ADDRESS *Addr,\r
310 IN TCP_PORTNO Port,\r
311 IN UINT8 Version\r
312 )\r
313{\r
314 TCP_PORTNO LocalPort;\r
315 LIST_ENTRY *Entry;\r
316 TCP_CB *Tcb;\r
317\r
318 ASSERT ((Addr != NULL) && (Port != 0));\r
319\r
320 LocalPort = HTONS (Port);\r
321\r
322 NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
323 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
324\r
325 if ((Version == Tcb->Sk->IpVersion) &&\r
326 TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
327 (LocalPort == Tcb->LocalEnd.Port)\r
328 ) {\r
329\r
330 return TRUE;\r
331 }\r
332 }\r
333\r
334 NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
335 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
336\r
337 if ((Version == Tcb->Sk->IpVersion) &&\r
338 TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
339 (LocalPort == Tcb->LocalEnd.Port)\r
340 ) {\r
341\r
342 return TRUE;\r
343 }\r
344 }\r
345\r
346 return FALSE;\r
347}\r
348\r
349/**\r
350 Locate the TCP_CB related to the socket pair.\r
351\r
352 @param[in] LocalPort The local port number.\r
353 @param[in] LocalIp The local IP address.\r
354 @param[in] RemotePort The remote port number.\r
355 @param[in] RemoteIp The remote IP address.\r
356 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
357 IP_VERSION_6 indicates TCP is running on IP6 stack.\r
358 @param[in] Syn If TRUE, the listen sockets are searched.\r
359\r
360 @return Pointer to the related TCP_CB. If NULL, no match is found.\r
361\r
362**/\r
363TCP_CB *\r
364TcpLocateTcb (\r
365 IN TCP_PORTNO LocalPort,\r
366 IN EFI_IP_ADDRESS *LocalIp,\r
367 IN TCP_PORTNO RemotePort,\r
368 IN EFI_IP_ADDRESS *RemoteIp,\r
369 IN UINT8 Version,\r
370 IN BOOLEAN Syn\r
371 )\r
372{\r
373 TCP_PEER Local;\r
374 TCP_PEER Remote;\r
375 LIST_ENTRY *Entry;\r
376 TCP_CB *Tcb;\r
377\r
378 Local.Port = LocalPort;\r
379 Remote.Port = RemotePort;\r
380\r
381 CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS));\r
382 CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS));\r
383\r
384 //\r
385 // First check for exact match.\r
386 //\r
387 NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
388 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
389\r
390 if ((Version == Tcb->Sk->IpVersion) &&\r
391 TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) &&\r
392 TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version)\r
393 ) {\r
394\r
395 RemoveEntryList (&Tcb->List);\r
396 InsertHeadList (&mTcpRunQue, &Tcb->List);\r
397\r
398 return Tcb;\r
399 }\r
400 }\r
401\r
402 //\r
403 // Only check the listen queue when the SYN flag is on.\r
404 //\r
405 if (Syn) {\r
406 return TcpLocateListenTcb (&Local, &Remote, Version);\r
407 }\r
408\r
409 return NULL;\r
410}\r
411\r
412/**\r
413 Insert a Tcb into the proper queue.\r
414\r
415 @param[in] Tcb Pointer to the TCP_CB to be inserted.\r
416\r
417 @retval 0 The Tcb was inserted successfully.\r
418 @retval -1 Error condition occurred.\r
419\r
420**/\r
421INTN\r
422TcpInsertTcb (\r
423 IN TCP_CB *Tcb\r
424 )\r
425{\r
426 LIST_ENTRY *Entry;\r
427 LIST_ENTRY *Head;\r
428 TCP_CB *Node;\r
a3bcde70
HT
429\r
430 ASSERT (\r
431 (Tcb != NULL) &&\r
432 (\r
433 (Tcb->State == TCP_LISTEN) ||\r
434 (Tcb->State == TCP_SYN_SENT) ||\r
435 (Tcb->State == TCP_SYN_RCVD) ||\r
436 (Tcb->State == TCP_CLOSED)\r
437 )\r
438 );\r
439\r
440 if (Tcb->LocalEnd.Port == 0) {\r
441 return -1;\r
442 }\r
443\r
444 Head = &mTcpRunQue;\r
445\r
446 if (Tcb->State == TCP_LISTEN) {\r
447 Head = &mTcpListenQue;\r
448 }\r
449\r
450 //\r
451 // Check that the Tcb isn't already on the list.\r
452 //\r
453 NET_LIST_FOR_EACH (Entry, Head) {\r
454 Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
455\r
456 if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd, Tcb->Sk->IpVersion) &&\r
457 TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion)\r
458 ) {\r
459\r
460 return -1;\r
461 }\r
462 }\r
463\r
464 InsertHeadList (Head, &Tcb->List);\r
465\r
a3bcde70
HT
466\r
467 return 0;\r
468}\r
469\r
470/**\r
471 Clone a TCP_CB from Tcb.\r
472\r
473 @param[in] Tcb Pointer to the TCP_CB to be cloned.\r
474\r
475 @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.\r
476\r
477**/\r
478TCP_CB *\r
479TcpCloneTcb (\r
480 IN TCP_CB *Tcb\r
481 )\r
482{\r
483 TCP_CB *Clone;\r
484\r
485 Clone = AllocateZeroPool (sizeof (TCP_CB));\r
486\r
487 if (Clone == NULL) {\r
488 return NULL;\r
489 }\r
490\r
491 CopyMem (Clone, Tcb, sizeof (TCP_CB));\r
492\r
493 //\r
494 // Increase the reference count of the shared IpInfo.\r
495 //\r
496 NET_GET_REF (Tcb->IpInfo);\r
497\r
498 InitializeListHead (&Clone->List);\r
499 InitializeListHead (&Clone->SndQue);\r
500 InitializeListHead (&Clone->RcvQue);\r
501\r
502 Clone->Sk = SockClone (Tcb->Sk);\r
503 if (Clone->Sk == NULL) {\r
504 DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n"));\r
505 FreePool (Clone);\r
506 return NULL;\r
507 }\r
508\r
509 ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;\r
510\r
511 return Clone;\r
512}\r
513\r
514/**\r
515 Compute an ISS to be used by a new connection.\r
516\r
517 @return The resulting ISS.\r
518\r
519**/\r
520TCP_SEQNO\r
521TcpGetIss (\r
522 VOID\r
523 )\r
524{\r
525 mTcpGlobalIss += TCP_ISS_INCREMENT_1;\r
526 return mTcpGlobalIss;\r
527}\r
528\r
529/**\r
530 Get the local mss.\r
531\r
532 @param[in] Sock Pointer to the socket to get mss.\r
533\r
534 @return The mss size.\r
535\r
536**/\r
537UINT16\r
538TcpGetRcvMss (\r
539 IN SOCKET *Sock\r
540 )\r
541{\r
542 EFI_IP4_MODE_DATA Ip4Mode;\r
543 EFI_IP6_MODE_DATA Ip6Mode;\r
544 EFI_IP4_PROTOCOL *Ip4;\r
545 EFI_IP6_PROTOCOL *Ip6;\r
546 TCP_PROTO_DATA *TcpProto;\r
547\r
548 ASSERT (Sock != NULL);\r
549\r
550 ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA));\r
551 ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA));\r
552\r
553 TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
554\r
555 if (Sock->IpVersion == IP_VERSION_4) {\r
556 Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4;\r
557 ASSERT (Ip4 != NULL);\r
558 Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL);\r
559\r
560 return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
561 } else {\r
562 Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6;\r
563 ASSERT (Ip6 != NULL);\r
ce22514e
ZL
564 if (!EFI_ERROR (Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL))) {\r
565 if (Ip6Mode.AddressList != NULL) {\r
566 FreePool (Ip6Mode.AddressList);\r
567 }\r
568\r
569 if (Ip6Mode.GroupTable != NULL) {\r
570 FreePool (Ip6Mode.GroupTable);\r
571 }\r
572\r
573 if (Ip6Mode.RouteTable != NULL) {\r
574 FreePool (Ip6Mode.RouteTable);\r
575 }\r
576\r
577 if (Ip6Mode.NeighborCache != NULL) {\r
578 FreePool (Ip6Mode.NeighborCache);\r
579 }\r
580\r
581 if (Ip6Mode.PrefixTable != NULL) {\r
582 FreePool (Ip6Mode.PrefixTable);\r
583 }\r
584\r
585 if (Ip6Mode.IcmpTypeList != NULL) {\r
586 FreePool (Ip6Mode.IcmpTypeList);\r
587 }\r
588 }\r
a3bcde70
HT
589\r
590 return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
591 }\r
592}\r
593\r
594/**\r
595 Set the Tcb's state.\r
596\r
597 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
598 @param[in] State The state to be set.\r
599\r
600**/\r
601VOID\r
602TcpSetState (\r
603 IN TCP_CB *Tcb,\r
604 IN UINT8 State\r
605 )\r
606{\r
607 ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
608 ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
609\r
610 DEBUG (\r
f3612a8d 611 (EFI_D_NET,\r
a3bcde70
HT
612 "Tcb (%p) state %s --> %s\n",\r
613 Tcb,\r
614 mTcpStateName[Tcb->State],\r
615 mTcpStateName[State])\r
616 );\r
617\r
618 Tcb->State = State;\r
619\r
620 switch (State) {\r
621 case TCP_ESTABLISHED:\r
622\r
623 SockConnEstablished (Tcb->Sk);\r
624\r
625 if (Tcb->Parent != NULL) {\r
626 //\r
627 // A new connection is accepted by a listening socket. Install\r
628 // the device path.\r
629 //\r
630 TcpInstallDevicePath (Tcb->Sk);\r
631 }\r
632\r
633 break;\r
634\r
635 case TCP_CLOSED:\r
636\r
637 SockConnClosed (Tcb->Sk);\r
638\r
639 break;\r
640 default:\r
641 break;\r
642 }\r
643}\r
644\r
645/**\r
646 Compute the TCP segment's checksum.\r
647\r
648 @param[in] Nbuf Pointer to the buffer that contains the TCP segment.\r
649 @param[in] HeadSum The checksum value of the fixed part of pseudo header.\r
650\r
651 @return The checksum value.\r
652\r
653**/\r
654UINT16\r
655TcpChecksum (\r
656 IN NET_BUF *Nbuf,\r
657 IN UINT16 HeadSum\r
658 )\r
659{\r
660 UINT16 Checksum;\r
661\r
662 Checksum = NetbufChecksum (Nbuf);\r
663 Checksum = NetAddChecksum (Checksum, HeadSum);\r
664\r
665 Checksum = NetAddChecksum (\r
666 Checksum,\r
667 HTONS ((UINT16) Nbuf->TotalSize)\r
668 );\r
669\r
670 return (UINT16) (~Checksum);\r
671}\r
672\r
673/**\r
674 Translate the information from the head of the received TCP\r
675 segment Nbuf contents and fill it into a TCP_SEG structure.\r
676\r
677 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
678 @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.\r
679\r
680 @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
681\r
682**/\r
683TCP_SEG *\r
684TcpFormatNetbuf (\r
685 IN TCP_CB *Tcb,\r
686 IN OUT NET_BUF *Nbuf\r
687 )\r
688{\r
689 TCP_SEG *Seg;\r
690 TCP_HEAD *Head;\r
691\r
692 Seg = TCPSEG_NETBUF (Nbuf);\r
693 Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
694 ASSERT (Head != NULL);\r
695\r
696 Nbuf->Tcp = Head;\r
697\r
698 Seg->Seq = NTOHL (Head->Seq);\r
699 Seg->Ack = NTOHL (Head->Ack);\r
700 Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));\r
701\r
702 Seg->Urg = NTOHS (Head->Urg);\r
703 Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale);\r
704 Seg->Flag = Head->Flag;\r
705\r
706 //\r
707 // SYN and FIN flag occupy one sequence space each.\r
708 //\r
709 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
710 //\r
711 // RFC requires that the initial window not be scaled.\r
712 //\r
713 Seg->Wnd = NTOHS (Head->Wnd);\r
714 Seg->End++;\r
715 }\r
716\r
717 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
718 Seg->End++;\r
719 }\r
720\r
721 return Seg;\r
722}\r
723\r
724/**\r
725 Initialize an active connection.\r
726\r
727 @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a\r
728 connection.\r
729\r
730**/\r
731VOID\r
732TcpOnAppConnect (\r
733 IN OUT TCP_CB *Tcb\r
734 )\r
735{\r
736 TcpInitTcbLocal (Tcb);\r
737 TcpSetState (Tcb, TCP_SYN_SENT);\r
738\r
739 TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
740 TcpToSendData (Tcb, 1);\r
741}\r
742\r
743/**\r
744 Initiate the connection close procedure, called when\r
745 applications want to close the connection.\r
746\r
747 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
748\r
749**/\r
750VOID\r
751TcpOnAppClose (\r
752 IN OUT TCP_CB *Tcb\r
753 )\r
754{\r
755 ASSERT (Tcb != NULL);\r
756\r
757 if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) {\r
758\r
759 DEBUG (\r
760 (EFI_D_WARN,\r
761 "TcpOnAppClose: connection reset because data is lost for TCB %p\n",\r
762 Tcb)\r
763 );\r
764\r
765 TcpResetConnection (Tcb);\r
766 TcpClose (Tcb);\r
767 return;\r
768 }\r
769\r
770 switch (Tcb->State) {\r
771 case TCP_CLOSED:\r
772 case TCP_LISTEN:\r
773 case TCP_SYN_SENT:\r
774 TcpSetState (Tcb, TCP_CLOSED);\r
775 break;\r
776\r
777 case TCP_SYN_RCVD:\r
778 case TCP_ESTABLISHED:\r
779 TcpSetState (Tcb, TCP_FIN_WAIT_1);\r
780 break;\r
781\r
782 case TCP_CLOSE_WAIT:\r
783 TcpSetState (Tcb, TCP_LAST_ACK);\r
784 break;\r
785 default:\r
786 break;\r
787 }\r
788\r
789 TcpToSendData (Tcb, 1);\r
790}\r
791\r
792/**\r
793 Check whether the application's newly delivered data can be sent out.\r
794\r
795 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
796\r
797 @retval 0 The data has been sent out successfully.\r
798 @retval -1 The Tcb is not in a state that data is permitted to\r
799 be sent out.\r
800\r
801**/\r
802INTN\r
803TcpOnAppSend (\r
804 IN OUT TCP_CB *Tcb\r
805 )\r
806{\r
807\r
808 switch (Tcb->State) {\r
809 case TCP_CLOSED:\r
810 return -1;\r
811\r
812 case TCP_LISTEN:\r
813 return -1;\r
814\r
815 case TCP_SYN_SENT:\r
816 case TCP_SYN_RCVD:\r
817 return 0;\r
818\r
819 case TCP_ESTABLISHED:\r
820 case TCP_CLOSE_WAIT:\r
821 TcpToSendData (Tcb, 0);\r
822 return 0;\r
823\r
824 case TCP_FIN_WAIT_1:\r
825 case TCP_FIN_WAIT_2:\r
826 case TCP_CLOSING:\r
827 case TCP_LAST_ACK:\r
828 case TCP_TIME_WAIT:\r
829 return -1;\r
830\r
831 default:\r
832 break;\r
833 }\r
834\r
835 return 0;\r
836}\r
837\r
838/**\r
839 Application has consumed some data. Check whether\r
840 to send a window update ack or a delayed ack.\r
841\r
842 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
843\r
844**/\r
845VOID\r
846TcpOnAppConsume (\r
847 IN TCP_CB *Tcb\r
848 )\r
849{\r
850 UINT32 TcpOld;\r
851\r
852 switch (Tcb->State) {\r
853 case TCP_ESTABLISHED:\r
854 TcpOld = TcpRcvWinOld (Tcb);\r
855 if (TcpRcvWinNow (Tcb) > TcpOld) {\r
856\r
857 if (TcpOld < Tcb->RcvMss) {\r
858\r
859 DEBUG (\r
f3612a8d 860 (EFI_D_NET,\r
a3bcde70
HT
861 "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",\r
862 Tcb)\r
863 );\r
864\r
865 TcpSendAck (Tcb);\r
866 } else if (Tcb->DelayedAck == 0) {\r
867\r
868 DEBUG (\r
f3612a8d 869 (EFI_D_NET,\r
a3bcde70
HT
870 "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",\r
871 Tcb)\r
872 );\r
873\r
874 Tcb->DelayedAck = 1;\r
875 }\r
876 }\r
877\r
878 break;\r
879\r
880 default:\r
881 break;\r
882 }\r
883}\r
884\r
885/**\r
886 Abort the connection by sending a reset segment. Called\r
887 when the application wants to abort the connection.\r
888\r
889 @param[in] Tcb Pointer to the TCP_CB of the TCP instance.\r
890\r
891**/\r
892VOID\r
893TcpOnAppAbort (\r
894 IN TCP_CB *Tcb\r
895 )\r
896{\r
897 DEBUG (\r
898 (EFI_D_WARN,\r
899 "TcpOnAppAbort: connection reset issued by application for TCB %p\n",\r
900 Tcb)\r
901 );\r
902\r
903 switch (Tcb->State) {\r
904 case TCP_SYN_RCVD:\r
905 case TCP_ESTABLISHED:\r
906 case TCP_FIN_WAIT_1:\r
907 case TCP_FIN_WAIT_2:\r
908 case TCP_CLOSE_WAIT:\r
909 TcpResetConnection (Tcb);\r
910 break;\r
911 default:\r
912 break;\r
913 }\r
914\r
915 TcpSetState (Tcb, TCP_CLOSED);\r
916}\r
917\r
918/**\r
919 Reset the connection related with Tcb.\r
920\r
921 @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.\r
922\r
923**/\r
924VOID\r
925TcpResetConnection (\r
926 IN TCP_CB *Tcb\r
927 )\r
928{\r
929 NET_BUF *Nbuf;\r
930 TCP_HEAD *Nhead;\r
931\r
932 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
933\r
934 if (Nbuf == NULL) {\r
935 return ;\r
936 }\r
937\r
938 Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
939 Nbuf,\r
940 sizeof (TCP_HEAD),\r
941 NET_BUF_TAIL\r
942 );\r
943\r
944 ASSERT (Nhead != NULL);\r
945\r
946 Nbuf->Tcp = Nhead;\r
947\r
948 Nhead->Flag = TCP_FLG_RST;\r
949 Nhead->Seq = HTONL (Tcb->SndNxt);\r
950 Nhead->Ack = HTONL (Tcb->RcvNxt);\r
951 Nhead->SrcPort = Tcb->LocalEnd.Port;\r
952 Nhead->DstPort = Tcb->RemoteEnd.Port;\r
953 Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
954 Nhead->Res = 0;\r
955 Nhead->Wnd = HTONS (0xFFFF);\r
956 Nhead->Checksum = 0;\r
957 Nhead->Urg = 0;\r
958 Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
959\r
960 TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);\r
961\r
962 NetbufFree (Nbuf);\r
963}\r
964\r
a3bcde70
HT
965/**\r
966 Install the device path protocol on the TCP instance.\r
967\r
968 @param[in] Sock Pointer to the socket representing the TCP instance.\r
969\r
970 @retval EFI_SUCCESS The device path protocol was installed.\r
971 @retval other Failed to install the device path protocol.\r
972\r
973**/\r
974EFI_STATUS\r
975TcpInstallDevicePath (\r
976 IN SOCKET *Sock\r
977 )\r
978{\r
979 TCP_PROTO_DATA *TcpProto;\r
980 TCP_SERVICE_DATA *TcpService;\r
981 TCP_CB *Tcb;\r
982 IPv4_DEVICE_PATH Ip4DPathNode;\r
983 IPv6_DEVICE_PATH Ip6DPathNode;\r
984 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
985 EFI_STATUS Status;\r
986 TCP_PORTNO LocalPort;\r
987 TCP_PORTNO RemotePort;\r
988\r
989 TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
990 TcpService = TcpProto->TcpService;\r
991 Tcb = TcpProto->TcpPcb;\r
992\r
993 LocalPort = NTOHS (Tcb->LocalEnd.Port);\r
994 RemotePort = NTOHS (Tcb->RemoteEnd.Port);\r
995 if (Sock->IpVersion == IP_VERSION_4) {\r
996 NetLibCreateIPv4DPathNode (\r
997 &Ip4DPathNode,\r
998 TcpService->ControllerHandle,\r
999 Tcb->LocalEnd.Ip.Addr[0],\r
1000 LocalPort,\r
1001 Tcb->RemoteEnd.Ip.Addr[0],\r
1002 RemotePort,\r
1003 EFI_IP_PROTO_TCP,\r
1004 Tcb->UseDefaultAddr\r
1005 );\r
1006\r
501793fa
RN
1007 IP4_COPY_ADDRESS (&Ip4DPathNode.SubnetMask, &Tcb->SubnetMask);\r
1008\r
a3bcde70
HT
1009 DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode;\r
1010 } else {\r
1011 NetLibCreateIPv6DPathNode (\r
1012 &Ip6DPathNode,\r
1013 TcpService->ControllerHandle,\r
1014 &Tcb->LocalEnd.Ip.v6,\r
1015 LocalPort,\r
1016 &Tcb->RemoteEnd.Ip.v6,\r
1017 RemotePort,\r
1018 EFI_IP_PROTO_TCP\r
1019 );\r
1020\r
1021 DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode;\r
1022 }\r
1023\r
1024 Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath);\r
1025 if (Sock->DevicePath == NULL) {\r
1026 return EFI_OUT_OF_RESOURCES;\r
1027 }\r
1028\r
1029 Status = gBS->InstallProtocolInterface (\r
1030 &Sock->SockHandle,\r
1031 &gEfiDevicePathProtocolGuid,\r
1032 EFI_NATIVE_INTERFACE,\r
1033 Sock->DevicePath\r
1034 );\r
1035 if (EFI_ERROR (Status)) {\r
1036 FreePool (Sock->DevicePath);\r
1037 Sock->DevicePath = NULL;\r
1038 }\r
1039\r
1040 return Status;\r
1041}\r
1042\r