2 Misc support routines for TCP driver.
4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 LIST_ENTRY mTcpRunQue
= {
23 LIST_ENTRY mTcpListenQue
= {
28 TCP_SEQNO mTcpGlobalIss
= TCP_BASE_ISS
;
30 CHAR16
*mTcpStateName
[] = {
46 Initialize the Tcb local related members.
48 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
57 // Compute the checksum of the fixed parts of pseudo header
59 if (Tcb
->Sk
->IpVersion
== IP_VERSION_4
) {
60 Tcb
->HeadSum
= NetPseudoHeadChecksum (
61 Tcb
->LocalEnd
.Ip
.Addr
[0],
62 Tcb
->RemoteEnd
.Ip
.Addr
[0],
67 Tcb
->HeadSum
= NetIp6PseudoHeadChecksum (
69 &Tcb
->RemoteEnd
.Ip
.v6
,
75 Tcb
->Iss
= TcpGetIss ();
76 Tcb
->SndUna
= Tcb
->Iss
;
77 Tcb
->SndNxt
= Tcb
->Iss
;
79 Tcb
->SndWl2
= Tcb
->Iss
;
82 Tcb
->RcvWnd
= GET_RCV_BUFFSIZE (Tcb
->Sk
);
85 // First window size is never scaled
89 Tcb
->ProbeTimerOn
= FALSE
;
93 Initialize the peer related members.
95 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
96 @param[in] Seg Pointer to the segment that contains the peer's intial info.
97 @param[in] Opt Pointer to the options announced by the peer.
109 ASSERT ((Tcb
!= NULL
) && (Seg
!= NULL
) && (Opt
!= NULL
));
110 ASSERT (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_SYN
));
112 Tcb
->SndWnd
= Seg
->Wnd
;
113 Tcb
->SndWndMax
= Tcb
->SndWnd
;
114 Tcb
->SndWl1
= Seg
->Seq
;
116 if (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_ACK
)) {
117 Tcb
->SndWl2
= Seg
->Ack
;
119 Tcb
->SndWl2
= Tcb
->Iss
+ 1;
122 if (TCP_FLG_ON (Opt
->Flag
, TCP_OPTION_RCVD_MSS
)) {
123 Tcb
->SndMss
= (UINT16
) MAX (64, Opt
->Mss
);
125 RcvMss
= TcpGetRcvMss (Tcb
->Sk
);
126 if (Tcb
->SndMss
> RcvMss
) {
127 Tcb
->SndMss
= RcvMss
;
132 // One end doesn't support MSS option, use default.
137 Tcb
->CWnd
= Tcb
->SndMss
;
140 Tcb
->RcvNxt
= Tcb
->Irs
+ 1;
142 Tcb
->RcvWl2
= Tcb
->RcvNxt
;
144 if (TCP_FLG_ON (Opt
->Flag
, TCP_OPTION_RCVD_WS
) && !TCP_FLG_ON (Tcb
->CtrlFlag
, TCP_CTRL_NO_WS
)) {
146 Tcb
->SndWndScale
= Opt
->WndScale
;
148 Tcb
->RcvWndScale
= TcpComputeScale (Tcb
);
149 TCP_SET_FLG (Tcb
->CtrlFlag
, TCP_CTRL_RCVD_WS
);
153 // One end doesn't support window scale option. use zero.
155 Tcb
->RcvWndScale
= 0;
158 if (TCP_FLG_ON (Opt
->Flag
, TCP_OPTION_RCVD_TS
) && !TCP_FLG_ON (Tcb
->CtrlFlag
, TCP_CTRL_NO_TS
)) {
160 TCP_SET_FLG (Tcb
->CtrlFlag
, TCP_CTRL_SND_TS
);
161 TCP_SET_FLG (Tcb
->CtrlFlag
, TCP_CTRL_RCVD_TS
);
164 // Compute the effective SndMss per RFC1122
165 // section 4.2.2.6. If timestamp option is
166 // enabled, it will always occupy 12 bytes.
168 Tcb
->SndMss
-= TCP_OPTION_TS_ALIGNED_LEN
;
173 Check whether one IP address equals the other.
175 @param[in] Ip1 Pointer to IP address to be checked.
176 @param[in] Ip2 Pointer to IP address to be checked.
177 @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,
178 IP_VERSION_6 indicates the IP address is an IPv6 address.
180 @retval TRUE Ip1 equals Ip2.
181 @retval FALSE Ip1 does not equal Ip2.
186 IN EFI_IP_ADDRESS
*Ip1
,
187 IN EFI_IP_ADDRESS
*Ip2
,
191 ASSERT ((Version
== IP_VERSION_4
) || (Version
== IP_VERSION_6
));
193 if (Version
== IP_VERSION_4
) {
194 return (BOOLEAN
) (Ip1
->Addr
[0] == Ip2
->Addr
[0]);
196 return (BOOLEAN
) EFI_IP6_EQUAL (&Ip1
->v6
, &Ip2
->v6
);
201 Check whether one IP address is filled with ZERO.
203 @param[in] Ip Pointer to the IP address to be checked.
204 @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,
205 IP_VERSION_6 indicates the IP address is an IPv6 address.
207 @retval TRUE Ip is all zero address.
208 @retval FALSE Ip is not all zero address.
213 IN EFI_IP_ADDRESS
*Ip
,
217 ASSERT ((Version
== IP_VERSION_4
) || (Version
== IP_VERSION_6
));
219 if (Version
== IP_VERSION_4
) {
220 return (BOOLEAN
) (Ip
->Addr
[0] == 0);
222 return (BOOLEAN
) ((Ip
->Addr
[0] == 0) && (Ip
->Addr
[1] == 0) &&
223 (Ip
->Addr
[2] == 0) && (Ip
->Addr
[3] == 0));
228 Locate a listen TCB that matchs the Local and Remote.
230 @param[in] Local Pointer to the local (IP, Port).
231 @param[in] Remote Pointer to the remote (IP, Port).
232 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
233 IP_VERSION_6 indicates TCP is running on IP6 stack.
235 @return Pointer to the TCP_CB with the least number of wildcards,
236 if NULL no match is found.
255 NET_LIST_FOR_EACH (Entry
, &mTcpListenQue
) {
256 Node
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
258 if ((Version
!= Node
->Sk
->IpVersion
) ||
259 (Local
->Port
!= Node
->LocalEnd
.Port
) ||
260 !TCP_PEER_MATCH (Remote
, &Node
->RemoteEnd
, Version
) ||
261 !TCP_PEER_MATCH (Local
, &Node
->LocalEnd
, Version
)
268 // Compute the number of wildcard
271 if (TcpIsIpZero (&Node
->RemoteEnd
.Ip
, Version
)) {
275 if (Node
->RemoteEnd
.Port
== 0) {
279 if (TcpIsIpZero (&Node
->LocalEnd
.Ip
, Version
)) {
297 Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.
299 @param[in] Addr Pointer to the IP address needs to match.
300 @param[in] Port The port number needs to match.
301 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
302 IP_VERSION_6 indicates TCP is running on IP6 stack.
305 @retval TRUE The Tcb which matches the <Addr Port> pair exists.
306 @retval FALSE Otherwise
311 IN EFI_IP_ADDRESS
*Addr
,
316 TCP_PORTNO LocalPort
;
320 ASSERT ((Addr
!= NULL
) && (Port
!= 0));
322 LocalPort
= HTONS (Port
);
324 NET_LIST_FOR_EACH (Entry
, &mTcpListenQue
) {
325 Tcb
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
327 if ((Version
== Tcb
->Sk
->IpVersion
) &&
328 TcpIsIpEqual (Addr
, &Tcb
->LocalEnd
.Ip
, Version
) &&
329 (LocalPort
== Tcb
->LocalEnd
.Port
)
336 NET_LIST_FOR_EACH (Entry
, &mTcpRunQue
) {
337 Tcb
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
339 if ((Version
== Tcb
->Sk
->IpVersion
) &&
340 TcpIsIpEqual (Addr
, &Tcb
->LocalEnd
.Ip
, Version
) &&
341 (LocalPort
== Tcb
->LocalEnd
.Port
)
352 Locate the TCP_CB related to the socket pair.
354 @param[in] LocalPort The local port number.
355 @param[in] LocalIp The local IP address.
356 @param[in] RemotePort The remote port number.
357 @param[in] RemoteIp The remote IP address.
358 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
359 IP_VERSION_6 indicates TCP is running on IP6 stack.
360 @param[in] Syn If TRUE, the listen sockets are searched.
362 @return Pointer to the related TCP_CB. If NULL, no match is found.
367 IN TCP_PORTNO LocalPort
,
368 IN EFI_IP_ADDRESS
*LocalIp
,
369 IN TCP_PORTNO RemotePort
,
370 IN EFI_IP_ADDRESS
*RemoteIp
,
380 Local
.Port
= LocalPort
;
381 Remote
.Port
= RemotePort
;
383 CopyMem (&Local
.Ip
, LocalIp
, sizeof (EFI_IP_ADDRESS
));
384 CopyMem (&Remote
.Ip
, RemoteIp
, sizeof (EFI_IP_ADDRESS
));
387 // First check for exact match.
389 NET_LIST_FOR_EACH (Entry
, &mTcpRunQue
) {
390 Tcb
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
392 if ((Version
== Tcb
->Sk
->IpVersion
) &&
393 TCP_PEER_EQUAL (&Remote
, &Tcb
->RemoteEnd
, Version
) &&
394 TCP_PEER_EQUAL (&Local
, &Tcb
->LocalEnd
, Version
)
397 RemoveEntryList (&Tcb
->List
);
398 InsertHeadList (&mTcpRunQue
, &Tcb
->List
);
405 // Only check the listen queue when the SYN flag is on.
408 return TcpLocateListenTcb (&Local
, &Remote
, Version
);
415 Insert a Tcb into the proper queue.
417 @param[in] Tcb Pointer to the TCP_CB to be inserted.
419 @retval 0 The Tcb was inserted successfully.
420 @retval -1 Error condition occurred.
431 TCP_PROTO_DATA
*TcpProto
;
436 (Tcb
->State
== TCP_LISTEN
) ||
437 (Tcb
->State
== TCP_SYN_SENT
) ||
438 (Tcb
->State
== TCP_SYN_RCVD
) ||
439 (Tcb
->State
== TCP_CLOSED
)
443 if (Tcb
->LocalEnd
.Port
== 0) {
449 if (Tcb
->State
== TCP_LISTEN
) {
450 Head
= &mTcpListenQue
;
454 // Check that the Tcb isn't already on the list.
456 NET_LIST_FOR_EACH (Entry
, Head
) {
457 Node
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
459 if (TCP_PEER_EQUAL (&Tcb
->LocalEnd
, &Node
->LocalEnd
, Tcb
->Sk
->IpVersion
) &&
460 TCP_PEER_EQUAL (&Tcb
->RemoteEnd
, &Node
->RemoteEnd
, Tcb
->Sk
->IpVersion
)
467 InsertHeadList (Head
, &Tcb
->List
);
469 TcpProto
= (TCP_PROTO_DATA
*) Tcb
->Sk
->ProtoReserved
;
475 Clone a TCP_CB from Tcb.
477 @param[in] Tcb Pointer to the TCP_CB to be cloned.
479 @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.
489 Clone
= AllocateZeroPool (sizeof (TCP_CB
));
495 CopyMem (Clone
, Tcb
, sizeof (TCP_CB
));
498 // Increase the reference count of the shared IpInfo.
500 NET_GET_REF (Tcb
->IpInfo
);
502 InitializeListHead (&Clone
->List
);
503 InitializeListHead (&Clone
->SndQue
);
504 InitializeListHead (&Clone
->RcvQue
);
506 Clone
->Sk
= SockClone (Tcb
->Sk
);
507 if (Clone
->Sk
== NULL
) {
508 DEBUG ((EFI_D_ERROR
, "TcpCloneTcb: failed to clone a sock\n"));
513 ((TCP_PROTO_DATA
*) (Clone
->Sk
->ProtoReserved
))->TcpPcb
= Clone
;
519 Compute an ISS to be used by a new connection.
521 @return The resulting ISS.
529 mTcpGlobalIss
+= TCP_ISS_INCREMENT_1
;
530 return mTcpGlobalIss
;
536 @param[in] Sock Pointer to the socket to get mss.
538 @return The mss size.
546 EFI_IP4_MODE_DATA Ip4Mode
;
547 EFI_IP6_MODE_DATA Ip6Mode
;
548 EFI_IP4_PROTOCOL
*Ip4
;
549 EFI_IP6_PROTOCOL
*Ip6
;
550 TCP_PROTO_DATA
*TcpProto
;
552 ASSERT (Sock
!= NULL
);
554 ZeroMem (&Ip4Mode
, sizeof (EFI_IP4_MODE_DATA
));
555 ZeroMem (&Ip6Mode
, sizeof (EFI_IP6_MODE_DATA
));
557 TcpProto
= (TCP_PROTO_DATA
*) Sock
->ProtoReserved
;
559 if (Sock
->IpVersion
== IP_VERSION_4
) {
560 Ip4
= TcpProto
->TcpService
->IpIo
->Ip
.Ip4
;
561 ASSERT (Ip4
!= NULL
);
562 Ip4
->GetModeData (Ip4
, &Ip4Mode
, NULL
, NULL
);
564 return (UINT16
) (Ip4Mode
.MaxPacketSize
- sizeof (TCP_HEAD
));
566 Ip6
= TcpProto
->TcpService
->IpIo
->Ip
.Ip6
;
567 ASSERT (Ip6
!= NULL
);
568 Ip6
->GetModeData (Ip6
, &Ip6Mode
, NULL
, NULL
);
570 return (UINT16
) (Ip6Mode
.MaxPacketSize
- sizeof (TCP_HEAD
));
577 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
578 @param[in] State The state to be set.
587 ASSERT (Tcb
->State
< (sizeof (mTcpStateName
) / sizeof (CHAR16
*)));
588 ASSERT (State
< (sizeof (mTcpStateName
) / sizeof (CHAR16
*)));
592 "Tcb (%p) state %s --> %s\n",
594 mTcpStateName
[Tcb
->State
],
595 mTcpStateName
[State
])
601 case TCP_ESTABLISHED
:
603 SockConnEstablished (Tcb
->Sk
);
605 if (Tcb
->Parent
!= NULL
) {
607 // A new connection is accepted by a listening socket. Install
610 TcpInstallDevicePath (Tcb
->Sk
);
617 SockConnClosed (Tcb
->Sk
);
626 Compute the TCP segment's checksum.
628 @param[in] Nbuf Pointer to the buffer that contains the TCP segment.
629 @param[in] HeadSum The checksum value of the fixed part of pseudo header.
631 @return The checksum value.
642 Checksum
= NetbufChecksum (Nbuf
);
643 Checksum
= NetAddChecksum (Checksum
, HeadSum
);
645 Checksum
= NetAddChecksum (
647 HTONS ((UINT16
) Nbuf
->TotalSize
)
650 return (UINT16
) (~Checksum
);
654 Translate the information from the head of the received TCP
655 segment Nbuf contents and fill it into a TCP_SEG structure.
657 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
658 @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.
660 @return Pointer to the TCP_SEG that contains the translated TCP head information.
672 Seg
= TCPSEG_NETBUF (Nbuf
);
673 Head
= (TCP_HEAD
*) NetbufGetByte (Nbuf
, 0, NULL
);
674 ASSERT (Head
!= NULL
);
678 Seg
->Seq
= NTOHL (Head
->Seq
);
679 Seg
->Ack
= NTOHL (Head
->Ack
);
680 Seg
->End
= Seg
->Seq
+ (Nbuf
->TotalSize
- (Head
->HeadLen
<< 2));
682 Seg
->Urg
= NTOHS (Head
->Urg
);
683 Seg
->Wnd
= (NTOHS (Head
->Wnd
) << Tcb
->SndWndScale
);
684 Seg
->Flag
= Head
->Flag
;
687 // SYN and FIN flag occupy one sequence space each.
689 if (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_SYN
)) {
691 // RFC requires that the initial window not be scaled.
693 Seg
->Wnd
= NTOHS (Head
->Wnd
);
697 if (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_FIN
)) {
705 Initialize an active connection.
707 @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a
716 TcpInitTcbLocal (Tcb
);
717 TcpSetState (Tcb
, TCP_SYN_SENT
);
719 TcpSetTimer (Tcb
, TCP_TIMER_CONNECT
, Tcb
->ConnectTimeout
);
720 TcpToSendData (Tcb
, 1);
724 Initiate the connection close procedure, called when
725 applications want to close the connection.
727 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
735 ASSERT (Tcb
!= NULL
);
737 if (!IsListEmpty (&Tcb
->RcvQue
) || GET_RCV_DATASIZE (Tcb
->Sk
) != 0) {
741 "TcpOnAppClose: connection reset because data is lost for TCB %p\n",
745 TcpResetConnection (Tcb
);
750 switch (Tcb
->State
) {
754 TcpSetState (Tcb
, TCP_CLOSED
);
758 case TCP_ESTABLISHED
:
759 TcpSetState (Tcb
, TCP_FIN_WAIT_1
);
763 TcpSetState (Tcb
, TCP_LAST_ACK
);
769 TcpToSendData (Tcb
, 1);
773 Check whether the application's newly delivered data can be sent out.
775 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
777 @retval 0 The data has been sent out successfully.
778 @retval -1 The Tcb is not in a state that data is permitted to
788 switch (Tcb
->State
) {
799 case TCP_ESTABLISHED
:
801 TcpToSendData (Tcb
, 0);
819 Application has consumed some data. Check whether
820 to send a window update ack or a delayed ack.
822 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
832 switch (Tcb
->State
) {
833 case TCP_ESTABLISHED
:
834 TcpOld
= TcpRcvWinOld (Tcb
);
835 if (TcpRcvWinNow (Tcb
) > TcpOld
) {
837 if (TcpOld
< Tcb
->RcvMss
) {
841 "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",
846 } else if (Tcb
->DelayedAck
== 0) {
850 "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",
866 Abort the connection by sending a reset segment. Called
867 when the application wants to abort the connection.
869 @param[in] Tcb Pointer to the TCP_CB of the TCP instance.
879 "TcpOnAppAbort: connection reset issued by application for TCB %p\n",
883 switch (Tcb
->State
) {
885 case TCP_ESTABLISHED
:
889 TcpResetConnection (Tcb
);
895 TcpSetState (Tcb
, TCP_CLOSED
);
899 Reset the connection related with Tcb.
901 @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.
912 Nbuf
= NetbufAlloc (TCP_MAX_HEAD
);
918 Nhead
= (TCP_HEAD
*) NetbufAllocSpace (
924 ASSERT (Nhead
!= NULL
);
928 Nhead
->Flag
= TCP_FLG_RST
;
929 Nhead
->Seq
= HTONL (Tcb
->SndNxt
);
930 Nhead
->Ack
= HTONL (Tcb
->RcvNxt
);
931 Nhead
->SrcPort
= Tcb
->LocalEnd
.Port
;
932 Nhead
->DstPort
= Tcb
->RemoteEnd
.Port
;
933 Nhead
->HeadLen
= (UINT8
) (sizeof (TCP_HEAD
) >> 2);
935 Nhead
->Wnd
= HTONS (0xFFFF);
938 Nhead
->Checksum
= TcpChecksum (Nbuf
, Tcb
->HeadSum
);
940 TcpSendIpPacket (Tcb
, Nbuf
, &Tcb
->LocalEnd
.Ip
, &Tcb
->RemoteEnd
.Ip
, Tcb
->Sk
->IpVersion
);
946 Install the device path protocol on the TCP instance.
948 @param[in] Sock Pointer to the socket representing the TCP instance.
950 @retval EFI_SUCCESS The device path protocol was installed.
951 @retval other Failed to install the device path protocol.
955 TcpInstallDevicePath (
959 TCP_PROTO_DATA
*TcpProto
;
960 TCP_SERVICE_DATA
*TcpService
;
962 IPv4_DEVICE_PATH Ip4DPathNode
;
963 IPv6_DEVICE_PATH Ip6DPathNode
;
964 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
966 TCP_PORTNO LocalPort
;
967 TCP_PORTNO RemotePort
;
969 TcpProto
= (TCP_PROTO_DATA
*) Sock
->ProtoReserved
;
970 TcpService
= TcpProto
->TcpService
;
971 Tcb
= TcpProto
->TcpPcb
;
973 LocalPort
= NTOHS (Tcb
->LocalEnd
.Port
);
974 RemotePort
= NTOHS (Tcb
->RemoteEnd
.Port
);
975 if (Sock
->IpVersion
== IP_VERSION_4
) {
976 NetLibCreateIPv4DPathNode (
978 TcpService
->ControllerHandle
,
979 Tcb
->LocalEnd
.Ip
.Addr
[0],
981 Tcb
->RemoteEnd
.Ip
.Addr
[0],
987 IP4_COPY_ADDRESS (&Ip4DPathNode
.SubnetMask
, &Tcb
->SubnetMask
);
989 DevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) &Ip4DPathNode
;
991 NetLibCreateIPv6DPathNode (
993 TcpService
->ControllerHandle
,
994 &Tcb
->LocalEnd
.Ip
.v6
,
996 &Tcb
->RemoteEnd
.Ip
.v6
,
1001 DevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) &Ip6DPathNode
;
1004 Sock
->DevicePath
= AppendDevicePathNode (Sock
->ParentDevicePath
, DevicePath
);
1005 if (Sock
->DevicePath
== NULL
) {
1006 return EFI_OUT_OF_RESOURCES
;
1009 Status
= gBS
->InstallProtocolInterface (
1011 &gEfiDevicePathProtocolGuid
,
1012 EFI_NATIVE_INTERFACE
,
1015 if (EFI_ERROR (Status
)) {
1016 FreePool (Sock
->DevicePath
);
1017 Sock
->DevicePath
= NULL
;