2 Misc support routines for TCP driver.
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php.
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 LIST_ENTRY mTcpRunQue
= {
24 LIST_ENTRY mTcpListenQue
= {
29 TCP_SEQNO mTcpGlobalIss
= TCP_BASE_ISS
;
31 CHAR16
*mTcpStateName
[] = {
47 Initialize the Tcb local related members.
49 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
58 // Compute the checksum of the fixed parts of pseudo header
60 if (Tcb
->Sk
->IpVersion
== IP_VERSION_4
) {
61 Tcb
->HeadSum
= NetPseudoHeadChecksum (
62 Tcb
->LocalEnd
.Ip
.Addr
[0],
63 Tcb
->RemoteEnd
.Ip
.Addr
[0],
68 Tcb
->HeadSum
= NetIp6PseudoHeadChecksum (
70 &Tcb
->RemoteEnd
.Ip
.v6
,
76 Tcb
->Iss
= TcpGetIss ();
77 Tcb
->SndUna
= Tcb
->Iss
;
78 Tcb
->SndNxt
= Tcb
->Iss
;
80 Tcb
->SndWl2
= Tcb
->Iss
;
83 Tcb
->RcvWnd
= GET_RCV_BUFFSIZE (Tcb
->Sk
);
86 // First window size is never scaled
90 Tcb
->ProbeTimerOn
= FALSE
;
94 Initialize the peer related members.
96 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
97 @param[in] Seg Pointer to the segment that contains the peer's intial info.
98 @param[in] Opt Pointer to the options announced by the peer.
110 ASSERT ((Tcb
!= NULL
) && (Seg
!= NULL
) && (Opt
!= NULL
));
111 ASSERT (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_SYN
));
113 Tcb
->SndWnd
= Seg
->Wnd
;
114 Tcb
->SndWndMax
= Tcb
->SndWnd
;
115 Tcb
->SndWl1
= Seg
->Seq
;
117 if (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_ACK
)) {
118 Tcb
->SndWl2
= Seg
->Ack
;
120 Tcb
->SndWl2
= Tcb
->Iss
+ 1;
123 if (TCP_FLG_ON (Opt
->Flag
, TCP_OPTION_RCVD_MSS
)) {
124 Tcb
->SndMss
= (UINT16
) MAX (64, Opt
->Mss
);
126 RcvMss
= TcpGetRcvMss (Tcb
->Sk
);
127 if (Tcb
->SndMss
> RcvMss
) {
128 Tcb
->SndMss
= RcvMss
;
133 // One end doesn't support MSS option, use default.
138 Tcb
->CWnd
= Tcb
->SndMss
;
141 Tcb
->RcvNxt
= Tcb
->Irs
+ 1;
143 Tcb
->RcvWl2
= Tcb
->RcvNxt
;
145 if (TCP_FLG_ON (Opt
->Flag
, TCP_OPTION_RCVD_WS
) && !TCP_FLG_ON (Tcb
->CtrlFlag
, TCP_CTRL_NO_WS
)) {
147 Tcb
->SndWndScale
= Opt
->WndScale
;
149 Tcb
->RcvWndScale
= TcpComputeScale (Tcb
);
150 TCP_SET_FLG (Tcb
->CtrlFlag
, TCP_CTRL_RCVD_WS
);
154 // One end doesn't support window scale option. use zero.
156 Tcb
->RcvWndScale
= 0;
159 if (TCP_FLG_ON (Opt
->Flag
, TCP_OPTION_RCVD_TS
) && !TCP_FLG_ON (Tcb
->CtrlFlag
, TCP_CTRL_NO_TS
)) {
161 TCP_SET_FLG (Tcb
->CtrlFlag
, TCP_CTRL_SND_TS
);
162 TCP_SET_FLG (Tcb
->CtrlFlag
, TCP_CTRL_RCVD_TS
);
165 // Compute the effective SndMss per RFC1122
166 // section 4.2.2.6. If timestamp option is
167 // enabled, it will always occupy 12 bytes.
169 Tcb
->SndMss
-= TCP_OPTION_TS_ALIGNED_LEN
;
174 Check whether one IP address equals the other.
176 @param[in] Ip1 Pointer to IP address to be checked.
177 @param[in] Ip2 Pointer to IP address to be checked.
178 @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,
179 IP_VERSION_6 indicates the IP address is an IPv6 address.
181 @retval TRUE Ip1 equals Ip2.
182 @retval FALSE Ip1 does not equal Ip2.
187 IN EFI_IP_ADDRESS
*Ip1
,
188 IN EFI_IP_ADDRESS
*Ip2
,
192 ASSERT ((Version
== IP_VERSION_4
) || (Version
== IP_VERSION_6
));
194 if (Version
== IP_VERSION_4
) {
195 return (BOOLEAN
) (Ip1
->Addr
[0] == Ip2
->Addr
[0]);
197 return (BOOLEAN
) EFI_IP6_EQUAL (&Ip1
->v6
, &Ip2
->v6
);
202 Check whether one IP address is filled with ZERO.
204 @param[in] Ip Pointer to the IP address to be checked.
205 @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,
206 IP_VERSION_6 indicates the IP address is an IPv6 address.
208 @retval TRUE Ip is all zero address.
209 @retval FALSE Ip is not all zero address.
214 IN EFI_IP_ADDRESS
*Ip
,
218 ASSERT ((Version
== IP_VERSION_4
) || (Version
== IP_VERSION_6
));
220 if (Version
== IP_VERSION_4
) {
221 return (BOOLEAN
) (Ip
->Addr
[0] == 0);
223 return (BOOLEAN
) ((Ip
->Addr
[0] == 0) && (Ip
->Addr
[1] == 0) &&
224 (Ip
->Addr
[2] == 0) && (Ip
->Addr
[3] == 0));
229 Locate a listen TCB that matchs the Local and Remote.
231 @param[in] Local Pointer to the local (IP, Port).
232 @param[in] Remote Pointer to the remote (IP, Port).
233 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
234 IP_VERSION_6 indicates TCP is running on IP6 stack.
236 @return Pointer to the TCP_CB with the least number of wildcards,
237 if NULL no match is found.
256 NET_LIST_FOR_EACH (Entry
, &mTcpListenQue
) {
257 Node
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
259 if ((Version
!= Node
->Sk
->IpVersion
) ||
260 (Local
->Port
!= Node
->LocalEnd
.Port
) ||
261 !TCP_PEER_MATCH (Remote
, &Node
->RemoteEnd
, Version
) ||
262 !TCP_PEER_MATCH (Local
, &Node
->LocalEnd
, Version
)
269 // Compute the number of wildcard
272 if (TcpIsIpZero (&Node
->RemoteEnd
.Ip
, Version
)) {
276 if (Node
->RemoteEnd
.Port
== 0) {
280 if (TcpIsIpZero (&Node
->LocalEnd
.Ip
, Version
)) {
298 Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.
300 @param[in] Addr Pointer to the IP address needs to match.
301 @param[in] Port The port number needs to match.
302 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
303 IP_VERSION_6 indicates TCP is running on IP6 stack.
306 @retval TRUE The Tcb which matches the <Addr Port> pair exists.
307 @retval FALSE Otherwise
312 IN EFI_IP_ADDRESS
*Addr
,
317 TCP_PORTNO LocalPort
;
321 ASSERT ((Addr
!= NULL
) && (Port
!= 0));
323 LocalPort
= HTONS (Port
);
325 NET_LIST_FOR_EACH (Entry
, &mTcpListenQue
) {
326 Tcb
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
328 if ((Version
== Tcb
->Sk
->IpVersion
) &&
329 TcpIsIpEqual (Addr
, &Tcb
->LocalEnd
.Ip
, Version
) &&
330 (LocalPort
== Tcb
->LocalEnd
.Port
)
337 NET_LIST_FOR_EACH (Entry
, &mTcpRunQue
) {
338 Tcb
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
340 if ((Version
== Tcb
->Sk
->IpVersion
) &&
341 TcpIsIpEqual (Addr
, &Tcb
->LocalEnd
.Ip
, Version
) &&
342 (LocalPort
== Tcb
->LocalEnd
.Port
)
353 Locate the TCP_CB related to the socket pair.
355 @param[in] LocalPort The local port number.
356 @param[in] LocalIp The local IP address.
357 @param[in] RemotePort The remote port number.
358 @param[in] RemoteIp The remote IP address.
359 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
360 IP_VERSION_6 indicates TCP is running on IP6 stack.
361 @param[in] Syn If TRUE, the listen sockets are searched.
363 @return Pointer to the related TCP_CB. If NULL, no match is found.
368 IN TCP_PORTNO LocalPort
,
369 IN EFI_IP_ADDRESS
*LocalIp
,
370 IN TCP_PORTNO RemotePort
,
371 IN EFI_IP_ADDRESS
*RemoteIp
,
381 Local
.Port
= LocalPort
;
382 Remote
.Port
= RemotePort
;
384 CopyMem (&Local
.Ip
, LocalIp
, sizeof (EFI_IP_ADDRESS
));
385 CopyMem (&Remote
.Ip
, RemoteIp
, sizeof (EFI_IP_ADDRESS
));
388 // First check for exact match.
390 NET_LIST_FOR_EACH (Entry
, &mTcpRunQue
) {
391 Tcb
= NET_LIST_USER_STRUCT (Entry
, TCP_CB
, List
);
393 if ((Version
== Tcb
->Sk
->IpVersion
) &&
394 TCP_PEER_EQUAL (&Remote
, &Tcb
->RemoteEnd
, Version
) &&
395 TCP_PEER_EQUAL (&Local
, &Tcb
->LocalEnd
, Version
)
398 RemoveEntryList (&Tcb
->List
);
399 InsertHeadList (&mTcpRunQue
, &Tcb
->List
);
406 // Only check the listen queue when the SYN flag is on.
409 return TcpLocateListenTcb (&Local
, &Remote
, Version
);
416 Insert a Tcb into the proper queue.
418 @param[in] Tcb Pointer to the TCP_CB to be inserted.
420 @retval 0 The Tcb was inserted successfully.
421 @retval -1 Error condition occurred.
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
);
474 Clone a TCP_CB from Tcb.
476 @param[in] Tcb Pointer to the TCP_CB to be cloned.
478 @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.
488 Clone
= AllocateZeroPool (sizeof (TCP_CB
));
494 CopyMem (Clone
, Tcb
, sizeof (TCP_CB
));
497 // Increase the reference count of the shared IpInfo.
499 NET_GET_REF (Tcb
->IpInfo
);
501 InitializeListHead (&Clone
->List
);
502 InitializeListHead (&Clone
->SndQue
);
503 InitializeListHead (&Clone
->RcvQue
);
505 Clone
->Sk
= SockClone (Tcb
->Sk
);
506 if (Clone
->Sk
== NULL
) {
507 DEBUG ((EFI_D_ERROR
, "TcpCloneTcb: failed to clone a sock\n"));
512 ((TCP_PROTO_DATA
*) (Clone
->Sk
->ProtoReserved
))->TcpPcb
= Clone
;
518 Compute an ISS to be used by a new connection.
520 @return The resulting ISS.
528 mTcpGlobalIss
+= TCP_ISS_INCREMENT_1
;
529 return mTcpGlobalIss
;
535 @param[in] Sock Pointer to the socket to get mss.
537 @return The mss size.
545 EFI_IP4_MODE_DATA Ip4Mode
;
546 EFI_IP6_MODE_DATA Ip6Mode
;
547 EFI_IP4_PROTOCOL
*Ip4
;
548 EFI_IP6_PROTOCOL
*Ip6
;
549 TCP_PROTO_DATA
*TcpProto
;
551 ASSERT (Sock
!= NULL
);
553 ZeroMem (&Ip4Mode
, sizeof (EFI_IP4_MODE_DATA
));
554 ZeroMem (&Ip6Mode
, sizeof (EFI_IP6_MODE_DATA
));
556 TcpProto
= (TCP_PROTO_DATA
*) Sock
->ProtoReserved
;
558 if (Sock
->IpVersion
== IP_VERSION_4
) {
559 Ip4
= TcpProto
->TcpService
->IpIo
->Ip
.Ip4
;
560 ASSERT (Ip4
!= NULL
);
561 Ip4
->GetModeData (Ip4
, &Ip4Mode
, NULL
, NULL
);
563 return (UINT16
) (Ip4Mode
.MaxPacketSize
- sizeof (TCP_HEAD
));
565 Ip6
= TcpProto
->TcpService
->IpIo
->Ip
.Ip6
;
566 ASSERT (Ip6
!= NULL
);
567 Ip6
->GetModeData (Ip6
, &Ip6Mode
, NULL
, NULL
);
569 return (UINT16
) (Ip6Mode
.MaxPacketSize
- sizeof (TCP_HEAD
));
576 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
577 @param[in] State The state to be set.
586 ASSERT (Tcb
->State
< (sizeof (mTcpStateName
) / sizeof (CHAR16
*)));
587 ASSERT (State
< (sizeof (mTcpStateName
) / sizeof (CHAR16
*)));
591 "Tcb (%p) state %s --> %s\n",
593 mTcpStateName
[Tcb
->State
],
594 mTcpStateName
[State
])
600 case TCP_ESTABLISHED
:
602 SockConnEstablished (Tcb
->Sk
);
604 if (Tcb
->Parent
!= NULL
) {
606 // A new connection is accepted by a listening socket. Install
609 TcpInstallDevicePath (Tcb
->Sk
);
616 SockConnClosed (Tcb
->Sk
);
625 Compute the TCP segment's checksum.
627 @param[in] Nbuf Pointer to the buffer that contains the TCP segment.
628 @param[in] HeadSum The checksum value of the fixed part of pseudo header.
630 @return The checksum value.
641 Checksum
= NetbufChecksum (Nbuf
);
642 Checksum
= NetAddChecksum (Checksum
, HeadSum
);
644 Checksum
= NetAddChecksum (
646 HTONS ((UINT16
) Nbuf
->TotalSize
)
649 return (UINT16
) (~Checksum
);
653 Translate the information from the head of the received TCP
654 segment Nbuf contents and fill it into a TCP_SEG structure.
656 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
657 @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.
659 @return Pointer to the TCP_SEG that contains the translated TCP head information.
671 Seg
= TCPSEG_NETBUF (Nbuf
);
672 Head
= (TCP_HEAD
*) NetbufGetByte (Nbuf
, 0, NULL
);
673 ASSERT (Head
!= NULL
);
677 Seg
->Seq
= NTOHL (Head
->Seq
);
678 Seg
->Ack
= NTOHL (Head
->Ack
);
679 Seg
->End
= Seg
->Seq
+ (Nbuf
->TotalSize
- (Head
->HeadLen
<< 2));
681 Seg
->Urg
= NTOHS (Head
->Urg
);
682 Seg
->Wnd
= (NTOHS (Head
->Wnd
) << Tcb
->SndWndScale
);
683 Seg
->Flag
= Head
->Flag
;
686 // SYN and FIN flag occupy one sequence space each.
688 if (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_SYN
)) {
690 // RFC requires that the initial window not be scaled.
692 Seg
->Wnd
= NTOHS (Head
->Wnd
);
696 if (TCP_FLG_ON (Seg
->Flag
, TCP_FLG_FIN
)) {
704 Initialize an active connection.
706 @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a
715 TcpInitTcbLocal (Tcb
);
716 TcpSetState (Tcb
, TCP_SYN_SENT
);
718 TcpSetTimer (Tcb
, TCP_TIMER_CONNECT
, Tcb
->ConnectTimeout
);
719 TcpToSendData (Tcb
, 1);
723 Initiate the connection close procedure, called when
724 applications want to close the connection.
726 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
734 ASSERT (Tcb
!= NULL
);
736 if (!IsListEmpty (&Tcb
->RcvQue
) || GET_RCV_DATASIZE (Tcb
->Sk
) != 0) {
740 "TcpOnAppClose: connection reset because data is lost for TCB %p\n",
744 TcpResetConnection (Tcb
);
749 switch (Tcb
->State
) {
753 TcpSetState (Tcb
, TCP_CLOSED
);
757 case TCP_ESTABLISHED
:
758 TcpSetState (Tcb
, TCP_FIN_WAIT_1
);
762 TcpSetState (Tcb
, TCP_LAST_ACK
);
768 TcpToSendData (Tcb
, 1);
772 Check whether the application's newly delivered data can be sent out.
774 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
776 @retval 0 The data has been sent out successfully.
777 @retval -1 The Tcb is not in a state that data is permitted to
787 switch (Tcb
->State
) {
798 case TCP_ESTABLISHED
:
800 TcpToSendData (Tcb
, 0);
818 Application has consumed some data. Check whether
819 to send a window update ack or a delayed ack.
821 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
831 switch (Tcb
->State
) {
832 case TCP_ESTABLISHED
:
833 TcpOld
= TcpRcvWinOld (Tcb
);
834 if (TcpRcvWinNow (Tcb
) > TcpOld
) {
836 if (TcpOld
< Tcb
->RcvMss
) {
840 "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",
845 } else if (Tcb
->DelayedAck
== 0) {
849 "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",
865 Abort the connection by sending a reset segment. Called
866 when the application wants to abort the connection.
868 @param[in] Tcb Pointer to the TCP_CB of the TCP instance.
878 "TcpOnAppAbort: connection reset issued by application for TCB %p\n",
882 switch (Tcb
->State
) {
884 case TCP_ESTABLISHED
:
888 TcpResetConnection (Tcb
);
894 TcpSetState (Tcb
, TCP_CLOSED
);
898 Reset the connection related with Tcb.
900 @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.
911 Nbuf
= NetbufAlloc (TCP_MAX_HEAD
);
917 Nhead
= (TCP_HEAD
*) NetbufAllocSpace (
923 ASSERT (Nhead
!= NULL
);
927 Nhead
->Flag
= TCP_FLG_RST
;
928 Nhead
->Seq
= HTONL (Tcb
->SndNxt
);
929 Nhead
->Ack
= HTONL (Tcb
->RcvNxt
);
930 Nhead
->SrcPort
= Tcb
->LocalEnd
.Port
;
931 Nhead
->DstPort
= Tcb
->RemoteEnd
.Port
;
932 Nhead
->HeadLen
= (UINT8
) (sizeof (TCP_HEAD
) >> 2);
934 Nhead
->Wnd
= HTONS (0xFFFF);
937 Nhead
->Checksum
= TcpChecksum (Nbuf
, Tcb
->HeadSum
);
939 TcpSendIpPacket (Tcb
, Nbuf
, &Tcb
->LocalEnd
.Ip
, &Tcb
->RemoteEnd
.Ip
, Tcb
->Sk
->IpVersion
);
945 Install the device path protocol on the TCP instance.
947 @param[in] Sock Pointer to the socket representing the TCP instance.
949 @retval EFI_SUCCESS The device path protocol was installed.
950 @retval other Failed to install the device path protocol.
954 TcpInstallDevicePath (
958 TCP_PROTO_DATA
*TcpProto
;
959 TCP_SERVICE_DATA
*TcpService
;
961 IPv4_DEVICE_PATH Ip4DPathNode
;
962 IPv6_DEVICE_PATH Ip6DPathNode
;
963 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
965 TCP_PORTNO LocalPort
;
966 TCP_PORTNO RemotePort
;
968 TcpProto
= (TCP_PROTO_DATA
*) Sock
->ProtoReserved
;
969 TcpService
= TcpProto
->TcpService
;
970 Tcb
= TcpProto
->TcpPcb
;
972 LocalPort
= NTOHS (Tcb
->LocalEnd
.Port
);
973 RemotePort
= NTOHS (Tcb
->RemoteEnd
.Port
);
974 if (Sock
->IpVersion
== IP_VERSION_4
) {
975 NetLibCreateIPv4DPathNode (
977 TcpService
->ControllerHandle
,
978 Tcb
->LocalEnd
.Ip
.Addr
[0],
980 Tcb
->RemoteEnd
.Ip
.Addr
[0],
986 IP4_COPY_ADDRESS (&Ip4DPathNode
.SubnetMask
, &Tcb
->SubnetMask
);
988 DevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) &Ip4DPathNode
;
990 NetLibCreateIPv6DPathNode (
992 TcpService
->ControllerHandle
,
993 &Tcb
->LocalEnd
.Ip
.v6
,
995 &Tcb
->RemoteEnd
.Ip
.v6
,
1000 DevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) &Ip6DPathNode
;
1003 Sock
->DevicePath
= AppendDevicePathNode (Sock
->ParentDevicePath
, DevicePath
);
1004 if (Sock
->DevicePath
== NULL
) {
1005 return EFI_OUT_OF_RESOURCES
;
1008 Status
= gBS
->InstallProtocolInterface (
1010 &gEfiDevicePathProtocolGuid
,
1011 EFI_NATIVE_INTERFACE
,
1014 if (EFI_ERROR (Status
)) {
1015 FreePool (Sock
->DevicePath
);
1016 Sock
->DevicePath
= NULL
;