--- /dev/null
+/** @file\r
+ Misc support routines for TCP driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+LIST_ENTRY mTcpRunQue = {\r
+ &mTcpRunQue,\r
+ &mTcpRunQue\r
+};\r
+\r
+LIST_ENTRY mTcpListenQue = {\r
+ &mTcpListenQue,\r
+ &mTcpListenQue\r
+};\r
+\r
+TCP_SEQNO mTcpGlobalIss = TCP_BASE_ISS;\r
+\r
+CHAR16 *mTcpStateName[] = {\r
+ L"TCP_CLOSED",\r
+ L"TCP_LISTEN",\r
+ L"TCP_SYN_SENT",\r
+ L"TCP_SYN_RCVD",\r
+ L"TCP_ESTABLISHED",\r
+ L"TCP_FIN_WAIT_1",\r
+ L"TCP_FIN_WAIT_2",\r
+ L"TCP_CLOSING",\r
+ L"TCP_TIME_WAIT",\r
+ L"TCP_CLOSE_WAIT",\r
+ L"TCP_LAST_ACK"\r
+};\r
+\r
+\r
+/**\r
+ Initialize the Tcb local related members.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbLocal (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ //\r
+ // Compute the checksum of the fixed parts of pseudo header\r
+ //\r
+ if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
+ Tcb->HeadSum = NetPseudoHeadChecksum (\r
+ Tcb->LocalEnd.Ip.Addr[0],\r
+ Tcb->RemoteEnd.Ip.Addr[0],\r
+ 0x06,\r
+ 0\r
+ );\r
+ } else {\r
+ Tcb->HeadSum = NetIp6PseudoHeadChecksum (\r
+ &Tcb->LocalEnd.Ip.v6,\r
+ &Tcb->RemoteEnd.Ip.v6,\r
+ 0x06,\r
+ 0\r
+ );\r
+ }\r
+\r
+ Tcb->Iss = TcpGetIss ();\r
+ Tcb->SndUna = Tcb->Iss;\r
+ Tcb->SndNxt = Tcb->Iss;\r
+\r
+ Tcb->SndWl2 = Tcb->Iss;\r
+ Tcb->SndWnd = 536;\r
+\r
+ Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+\r
+ //\r
+ // First window size is never scaled\r
+ //\r
+ Tcb->RcvWndScale = 0;\r
+\r
+ Tcb->ProbeTimerOn = FALSE;\r
+}\r
+\r
+/**\r
+ Initialize the peer related members.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seg Pointer to the segment that contains the peer's intial info.\r
+ @param[in] Opt Pointer to the options announced by the peer.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbPeer (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN TCP_SEG *Seg,\r
+ IN TCP_OPTION *Opt\r
+ )\r
+{\r
+ UINT16 RcvMss;\r
+\r
+ ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL));\r
+ ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));\r
+\r
+ Tcb->SndWnd = Seg->Wnd;\r
+ Tcb->SndWndMax = Tcb->SndWnd;\r
+ Tcb->SndWl1 = Seg->Seq;\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+ Tcb->SndWl2 = Seg->Ack;\r
+ } else {\r
+ Tcb->SndWl2 = Tcb->Iss + 1;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {\r
+ Tcb->SndMss = (UINT16) MAX (64, Opt->Mss);\r
+\r
+ RcvMss = TcpGetRcvMss (Tcb->Sk);\r
+ if (Tcb->SndMss > RcvMss) {\r
+ Tcb->SndMss = RcvMss;\r
+ }\r
+\r
+ } else {\r
+ //\r
+ // One end doesn't support MSS option, use default.\r
+ //\r
+ Tcb->RcvMss = 536;\r
+ }\r
+\r
+ Tcb->CWnd = Tcb->SndMss;\r
+\r
+ Tcb->Irs = Seg->Seq;\r
+ Tcb->RcvNxt = Tcb->Irs + 1;\r
+\r
+ Tcb->RcvWl2 = Tcb->RcvNxt;\r
+\r
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {\r
+\r
+ Tcb->SndWndScale = Opt->WndScale;\r
+\r
+ Tcb->RcvWndScale = TcpComputeScale (Tcb);\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);\r
+\r
+ } else {\r
+ //\r
+ // One end doesn't support window scale option. use zero.\r
+ //\r
+ Tcb->RcvWndScale = 0;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);\r
+\r
+ //\r
+ // Compute the effective SndMss per RFC1122\r
+ // section 4.2.2.6. If timestamp option is\r
+ // enabled, it will always occupy 12 bytes.\r
+ //\r
+ Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;\r
+ }\r
+}\r
+\r
+/**\r
+ Check whether one IP address equals the other.\r
+\r
+ @param[in] Ip1 Pointer to IP address to be checked.\r
+ @param[in] Ip2 Pointer to IP address to be checked.\r
+ @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
+ IP_VERSION_6 indicates the IP address is an IPv6 address.\r
+\r
+ @retval TRUE Ip1 equals Ip2.\r
+ @retval FALSE Ip1 does not equal Ip2.\r
+\r
+**/\r
+BOOLEAN\r
+TcpIsIpEqual (\r
+ IN EFI_IP_ADDRESS *Ip1,\r
+ IN EFI_IP_ADDRESS *Ip2,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+ if (Version == IP_VERSION_4) {\r
+ return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]);\r
+ } else {\r
+ return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6);\r
+ }\r
+}\r
+\r
+/**\r
+ Check whether one IP address is filled with ZERO.\r
+\r
+ @param[in] Ip Pointer to the IP address to be checked.\r
+ @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
+ IP_VERSION_6 indicates the IP address is an IPv6 address.\r
+\r
+ @retval TRUE Ip is all zero address.\r
+ @retval FALSE Ip is not all zero address.\r
+\r
+**/\r
+BOOLEAN\r
+TcpIsIpZero (\r
+ IN EFI_IP_ADDRESS *Ip,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+ if (Version == IP_VERSION_4) {\r
+ return (BOOLEAN) (Ip->Addr[0] == 0);\r
+ } else {\r
+ return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) &&\r
+ (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0));\r
+ }\r
+}\r
+\r
+/**\r
+ Locate a listen TCB that matchs the Local and Remote.\r
+\r
+ @param[in] Local Pointer to the local (IP, Port).\r
+ @param[in] Remote Pointer to the remote (IP, Port).\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+ @return Pointer to the TCP_CB with the least number of wildcards,\r
+ if NULL no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateListenTcb (\r
+ IN TCP_PEER *Local,\r
+ IN TCP_PEER *Remote,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *Node;\r
+ TCP_CB *Match;\r
+ INTN Last;\r
+ INTN Cur;\r
+\r
+ Last = 4;\r
+ Match = NULL;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version != Node->Sk->IpVersion) ||\r
+ (Local->Port != Node->LocalEnd.Port) ||\r
+ !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) ||\r
+ !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version)\r
+ ) {\r
+\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Compute the number of wildcard\r
+ //\r
+ Cur = 0;\r
+ if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) {\r
+ Cur++;\r
+ }\r
+\r
+ if (Node->RemoteEnd.Port == 0) {\r
+ Cur++;\r
+ }\r
+\r
+ if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) {\r
+ Cur++;\r
+ }\r
+\r
+ if (Cur < Last) {\r
+ if (Cur == 0) {\r
+ return Node;\r
+ }\r
+\r
+ Last = Cur;\r
+ Match = Node;\r
+ }\r
+ }\r
+\r
+ return Match;\r
+}\r
+\r
+/**\r
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.\r
+\r
+ @param[in] Addr Pointer to the IP address needs to match.\r
+ @param[in] Port The port number needs to match.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+\r
+ @retval TRUE The Tcb which matches the <Addr Port> pair exists.\r
+ @retval FALSE Otherwise\r
+\r
+**/\r
+BOOLEAN\r
+TcpFindTcbByPeer (\r
+ IN EFI_IP_ADDRESS *Addr,\r
+ IN TCP_PORTNO Port,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ TCP_PORTNO LocalPort;\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *Tcb;\r
+\r
+ ASSERT ((Addr != NULL) && (Port != 0));\r
+\r
+ LocalPort = HTONS (Port);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version == Tcb->Sk->IpVersion) &&\r
+ TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
+ (LocalPort == Tcb->LocalEnd.Port)\r
+ ) {\r
+\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version == Tcb->Sk->IpVersion) &&\r
+ TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
+ (LocalPort == Tcb->LocalEnd.Port)\r
+ ) {\r
+\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Locate the TCP_CB related to the socket pair.\r
+\r
+ @param[in] LocalPort The local port number.\r
+ @param[in] LocalIp The local IP address.\r
+ @param[in] RemotePort The remote port number.\r
+ @param[in] RemoteIp The remote IP address.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+ @param[in] Syn If TRUE, the listen sockets are searched.\r
+\r
+ @return Pointer to the related TCP_CB. If NULL, no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateTcb (\r
+ IN TCP_PORTNO LocalPort,\r
+ IN EFI_IP_ADDRESS *LocalIp,\r
+ IN TCP_PORTNO RemotePort,\r
+ IN EFI_IP_ADDRESS *RemoteIp,\r
+ IN UINT8 Version,\r
+ IN BOOLEAN Syn\r
+ )\r
+{\r
+ TCP_PEER Local;\r
+ TCP_PEER Remote;\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *Tcb;\r
+\r
+ Local.Port = LocalPort;\r
+ Remote.Port = RemotePort;\r
+\r
+ CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+ //\r
+ // First check for exact match.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version == Tcb->Sk->IpVersion) &&\r
+ TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) &&\r
+ TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version)\r
+ ) {\r
+\r
+ RemoveEntryList (&Tcb->List);\r
+ InsertHeadList (&mTcpRunQue, &Tcb->List);\r
+\r
+ return Tcb;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Only check the listen queue when the SYN flag is on.\r
+ //\r
+ if (Syn) {\r
+ return TcpLocateListenTcb (&Local, &Remote, Version);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Insert a Tcb into the proper queue.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB to be inserted.\r
+\r
+ @retval 0 The Tcb was inserted successfully.\r
+ @retval -1 Error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpInsertTcb (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Head;\r
+ TCP_CB *Node;\r
+ TCP_PROTO_DATA *TcpProto;\r
+\r
+ ASSERT (\r
+ (Tcb != NULL) &&\r
+ (\r
+ (Tcb->State == TCP_LISTEN) ||\r
+ (Tcb->State == TCP_SYN_SENT) ||\r
+ (Tcb->State == TCP_SYN_RCVD) ||\r
+ (Tcb->State == TCP_CLOSED)\r
+ )\r
+ );\r
+\r
+ if (Tcb->LocalEnd.Port == 0) {\r
+ return -1;\r
+ }\r
+\r
+ Head = &mTcpRunQue;\r
+\r
+ if (Tcb->State == TCP_LISTEN) {\r
+ Head = &mTcpListenQue;\r
+ }\r
+\r
+ //\r
+ // Check that the Tcb isn't already on the list.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, Head) {\r
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd, Tcb->Sk->IpVersion) &&\r
+ TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion)\r
+ ) {\r
+\r
+ return -1;\r
+ }\r
+ }\r
+\r
+ InsertHeadList (Head, &Tcb->List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) Tcb->Sk->ProtoReserved;\r
+ TcpSetVariableData (TcpProto->TcpService);\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Clone a TCP_CB from Tcb.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB to be cloned.\r
+\r
+ @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.\r
+\r
+**/\r
+TCP_CB *\r
+TcpCloneTcb (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ TCP_CB *Clone;\r
+\r
+ Clone = AllocateZeroPool (sizeof (TCP_CB));\r
+\r
+ if (Clone == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ CopyMem (Clone, Tcb, sizeof (TCP_CB));\r
+\r
+ //\r
+ // Increase the reference count of the shared IpInfo.\r
+ //\r
+ NET_GET_REF (Tcb->IpInfo);\r
+\r
+ InitializeListHead (&Clone->List);\r
+ InitializeListHead (&Clone->SndQue);\r
+ InitializeListHead (&Clone->RcvQue);\r
+\r
+ Clone->Sk = SockClone (Tcb->Sk);\r
+ if (Clone->Sk == NULL) {\r
+ DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n"));\r
+ FreePool (Clone);\r
+ return NULL;\r
+ }\r
+\r
+ ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;\r
+\r
+ return Clone;\r
+}\r
+\r
+/**\r
+ Compute an ISS to be used by a new connection.\r
+\r
+ @return The resulting ISS.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetIss (\r
+ VOID\r
+ )\r
+{\r
+ mTcpGlobalIss += TCP_ISS_INCREMENT_1;\r
+ return mTcpGlobalIss;\r
+}\r
+\r
+/**\r
+ Get the local mss.\r
+\r
+ @param[in] Sock Pointer to the socket to get mss.\r
+\r
+ @return The mss size.\r
+\r
+**/\r
+UINT16\r
+TcpGetRcvMss (\r
+ IN SOCKET *Sock\r
+ )\r
+{\r
+ EFI_IP4_MODE_DATA Ip4Mode;\r
+ EFI_IP6_MODE_DATA Ip6Mode;\r
+ EFI_IP4_PROTOCOL *Ip4;\r
+ EFI_IP6_PROTOCOL *Ip6;\r
+ TCP_PROTO_DATA *TcpProto;\r
+\r
+ ASSERT (Sock != NULL);\r
+\r
+ ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA));\r
+ ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA));\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+\r
+ if (Sock->IpVersion == IP_VERSION_4) {\r
+ Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4;\r
+ ASSERT (Ip4 != NULL);\r
+ Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL);\r
+\r
+ return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
+ } else {\r
+ Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6;\r
+ ASSERT (Ip6 != NULL);\r
+ Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL);\r
+\r
+ return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
+ }\r
+}\r
+\r
+/**\r
+ Set the Tcb's state.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] State The state to be set.\r
+\r
+**/\r
+VOID\r
+TcpSetState (\r
+ IN TCP_CB *Tcb,\r
+ IN UINT8 State\r
+ )\r
+{\r
+ ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
+ ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "Tcb (%p) state %s --> %s\n",\r
+ Tcb,\r
+ mTcpStateName[Tcb->State],\r
+ mTcpStateName[State])\r
+ );\r
+\r
+ Tcb->State = State;\r
+\r
+ switch (State) {\r
+ case TCP_ESTABLISHED:\r
+\r
+ SockConnEstablished (Tcb->Sk);\r
+\r
+ if (Tcb->Parent != NULL) {\r
+ //\r
+ // A new connection is accepted by a listening socket. Install\r
+ // the device path.\r
+ //\r
+ TcpInstallDevicePath (Tcb->Sk);\r
+ }\r
+\r
+ break;\r
+\r
+ case TCP_CLOSED:\r
+\r
+ SockConnClosed (Tcb->Sk);\r
+\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ Compute the TCP segment's checksum.\r
+\r
+ @param[in] Nbuf Pointer to the buffer that contains the TCP segment.\r
+ @param[in] HeadSum The checksum value of the fixed part of pseudo header.\r
+\r
+ @return The checksum value.\r
+\r
+**/\r
+UINT16\r
+TcpChecksum (\r
+ IN NET_BUF *Nbuf,\r
+ IN UINT16 HeadSum\r
+ )\r
+{\r
+ UINT16 Checksum;\r
+\r
+ Checksum = NetbufChecksum (Nbuf);\r
+ Checksum = NetAddChecksum (Checksum, HeadSum);\r
+\r
+ Checksum = NetAddChecksum (\r
+ Checksum,\r
+ HTONS ((UINT16) Nbuf->TotalSize)\r
+ );\r
+\r
+ return (UINT16) (~Checksum);\r
+}\r
+\r
+/**\r
+ Translate the information from the head of the received TCP\r
+ segment Nbuf contents and fill it into a TCP_SEG structure.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.\r
+\r
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
+\r
+**/\r
+TCP_SEG *\r
+TcpFormatNetbuf (\r
+ IN TCP_CB *Tcb,\r
+ IN OUT NET_BUF *Nbuf\r
+ )\r
+{\r
+ TCP_SEG *Seg;\r
+ TCP_HEAD *Head;\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
+ ASSERT (Head != NULL);\r
+\r
+ Nbuf->Tcp = Head;\r
+\r
+ Seg->Seq = NTOHL (Head->Seq);\r
+ Seg->Ack = NTOHL (Head->Ack);\r
+ Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));\r
+\r
+ Seg->Urg = NTOHS (Head->Urg);\r
+ Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale);\r
+ Seg->Flag = Head->Flag;\r
+\r
+ //\r
+ // SYN and FIN flag occupy one sequence space each.\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+ //\r
+ // RFC requires that the initial window not be scaled.\r
+ //\r
+ Seg->Wnd = NTOHS (Head->Wnd);\r
+ Seg->End++;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+ Seg->End++;\r
+ }\r
+\r
+ return Seg;\r
+}\r
+\r
+/**\r
+ Initialize an active connection.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a\r
+ connection.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConnect (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ TcpInitTcbLocal (Tcb);\r
+ TcpSetState (Tcb, TCP_SYN_SENT);\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
+ TcpToSendData (Tcb, 1);\r
+}\r
+\r
+/**\r
+ Initiate the connection close procedure, called when\r
+ applications want to close the connection.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppClose (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ ASSERT (Tcb != NULL);\r
+\r
+ if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpOnAppClose: connection reset because data is lost for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpResetConnection (Tcb);\r
+ TcpClose (Tcb);\r
+ return;\r
+ }\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_CLOSED:\r
+ case TCP_LISTEN:\r
+ case TCP_SYN_SENT:\r
+ TcpSetState (Tcb, TCP_CLOSED);\r
+ break;\r
+\r
+ case TCP_SYN_RCVD:\r
+ case TCP_ESTABLISHED:\r
+ TcpSetState (Tcb, TCP_FIN_WAIT_1);\r
+ break;\r
+\r
+ case TCP_CLOSE_WAIT:\r
+ TcpSetState (Tcb, TCP_LAST_ACK);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ TcpToSendData (Tcb, 1);\r
+}\r
+\r
+/**\r
+ Check whether the application's newly delivered data can be sent out.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @retval 0 The data has been sent out successfully.\r
+ @retval -1 The Tcb is not in a state that data is permitted to\r
+ be sent out.\r
+\r
+**/\r
+INTN\r
+TcpOnAppSend (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_CLOSED:\r
+ return -1;\r
+\r
+ case TCP_LISTEN:\r
+ return -1;\r
+\r
+ case TCP_SYN_SENT:\r
+ case TCP_SYN_RCVD:\r
+ return 0;\r
+\r
+ case TCP_ESTABLISHED:\r
+ case TCP_CLOSE_WAIT:\r
+ TcpToSendData (Tcb, 0);\r
+ return 0;\r
+\r
+ case TCP_FIN_WAIT_1:\r
+ case TCP_FIN_WAIT_2:\r
+ case TCP_CLOSING:\r
+ case TCP_LAST_ACK:\r
+ case TCP_TIME_WAIT:\r
+ return -1;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Application has consumed some data. Check whether\r
+ to send a window update ack or a delayed ack.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConsume (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ UINT32 TcpOld;\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_ESTABLISHED:\r
+ TcpOld = TcpRcvWinOld (Tcb);\r
+ if (TcpRcvWinNow (Tcb) > TcpOld) {\r
+\r
+ if (TcpOld < Tcb->RcvMss) {\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpSendAck (Tcb);\r
+ } else if (Tcb->DelayedAck == 0) {\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ Tcb->DelayedAck = 1;\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ Abort the connection by sending a reset segment. Called\r
+ when the application wants to abort the connection.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of the TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppAbort (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpOnAppAbort: connection reset issued by application for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_SYN_RCVD:\r
+ case TCP_ESTABLISHED:\r
+ case TCP_FIN_WAIT_1:\r
+ case TCP_FIN_WAIT_2:\r
+ case TCP_CLOSE_WAIT:\r
+ TcpResetConnection (Tcb);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ TcpSetState (Tcb, TCP_CLOSED);\r
+}\r
+\r
+/**\r
+ Reset the connection related with Tcb.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.\r
+\r
+**/\r
+VOID\r
+TcpResetConnection (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ TCP_HEAD *Nhead;\r
+\r
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+ if (Nbuf == NULL) {\r
+ return ;\r
+ }\r
+\r
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
+ Nbuf,\r
+ sizeof (TCP_HEAD),\r
+ NET_BUF_TAIL\r
+ );\r
+\r
+ ASSERT (Nhead != NULL);\r
+\r
+ Nbuf->Tcp = Nhead;\r
+\r
+ Nhead->Flag = TCP_FLG_RST;\r
+ Nhead->Seq = HTONL (Tcb->SndNxt);\r
+ Nhead->Ack = HTONL (Tcb->RcvNxt);\r
+ Nhead->SrcPort = Tcb->LocalEnd.Port;\r
+ Nhead->DstPort = Tcb->RemoteEnd.Port;\r
+ Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
+ Nhead->Res = 0;\r
+ Nhead->Wnd = HTONS (0xFFFF);\r
+ Nhead->Checksum = 0;\r
+ Nhead->Urg = 0;\r
+ Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
+\r
+ TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);\r
+\r
+ NetbufFree (Nbuf);\r
+}\r
+\r
+/**\r
+ Set the Tcp variable data.\r
+\r
+ @param[in] TcpService Tcp service data.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpSetVariableData (\r
+ IN TCP_SERVICE_DATA *TcpService\r
+ )\r
+{\r
+ EFI_GUID *ServiceBindingGuid;\r
+ UINT32 NumConfiguredInstance;\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *TcpPcb;\r
+ TCP_PROTO_DATA *TcpProto;\r
+ UINTN VariableDataSize;\r
+ EFI_TCP4_VARIABLE_DATA *Tcp4VariableData;\r
+ EFI_TCP4_SERVICE_POINT *Tcp4ServicePoint;\r
+ EFI_TCP6_VARIABLE_DATA *Tcp6VariableData;\r
+ EFI_TCP6_SERVICE_POINT *Tcp6ServicePoint;\r
+ VOID *VariableData;\r
+ CHAR16 *NewMacString;\r
+ EFI_STATUS Status;\r
+\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+ } else {\r
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+ }\r
+\r
+ NumConfiguredInstance = 0;\r
+ Tcp4VariableData = NULL;\r
+ Tcp6VariableData = NULL;\r
+\r
+ //\r
+ // Go through the running queue to count the instances.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ NumConfiguredInstance++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Go through the listening queue to count the instances.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ NumConfiguredInstance++;\r
+ }\r
+ }\r
+\r
+ Tcp4ServicePoint = NULL;\r
+ Tcp6ServicePoint = NULL;\r
+\r
+ //\r
+ // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,\r
+ // we should add extra buffers for the service points only if the number of configured\r
+ // children is more than one.\r
+ //\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);\r
+\r
+ if (NumConfiguredInstance > 1) {\r
+ VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+ }\r
+\r
+ Tcp4VariableData = AllocateZeroPool (VariableDataSize);\r
+ if (Tcp4VariableData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Tcp4VariableData->DriverHandle = TcpService->DriverBindingHandle;\r
+ Tcp4VariableData->ServiceCount = NumConfiguredInstance;\r
+\r
+ Tcp4ServicePoint = &Tcp4VariableData->Services[0];\r
+ VariableData = Tcp4VariableData;\r
+ } else {\r
+ VariableDataSize = sizeof (EFI_TCP6_VARIABLE_DATA);\r
+\r
+ if (NumConfiguredInstance > 1) {\r
+ VariableDataSize += sizeof (EFI_TCP6_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+ }\r
+\r
+ Tcp6VariableData = AllocateZeroPool (VariableDataSize);\r
+ if (Tcp6VariableData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Tcp6VariableData->DriverHandle = TcpService->DriverBindingHandle;\r
+ Tcp6VariableData->ServiceCount = NumConfiguredInstance;\r
+\r
+ Tcp6ServicePoint = &Tcp6VariableData->Services[0];\r
+ VariableData = Tcp6VariableData;\r
+ }\r
+\r
+ //\r
+ // Go through the running queue to fill the service points.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp4ServicePoint++;\r
+ } else {\r
+ Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);\r
+ Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);\r
+ Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp6ServicePoint++;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Go through the listening queue to fill the service points.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp4ServicePoint++;\r
+ } else {\r
+ Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);\r
+ Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);\r
+ Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp6ServicePoint++;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get the mac string.\r
+ //\r
+ Status = NetLibGetMacString (\r
+ TcpService->ControllerHandle,\r
+ TcpService->DriverBindingHandle,\r
+ &NewMacString\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ if (TcpService->MacString != NULL) {\r
+ //\r
+ // The variable is set already. We're going to update it.\r
+ //\r
+ if (StrCmp (TcpService->MacString, NewMacString) != 0) {\r
+ //\r
+ // The mac address is changed. Delete the previous variable first.\r
+ //\r
+ gRT->SetVariable (\r
+ TcpService->MacString,\r
+ ServiceBindingGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ FreePool (TcpService->MacString);\r
+ }\r
+\r
+ TcpService->MacString = NewMacString;\r
+\r
+ Status = gRT->SetVariable (\r
+ TcpService->MacString,\r
+ ServiceBindingGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ VariableDataSize,\r
+ VariableData\r
+ );\r
+\r
+ON_ERROR:\r
+\r
+ FreePool (VariableData);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param[in] TcpService Tcp service data.\r
+\r
+**/\r
+VOID\r
+TcpClearVariableData (\r
+ IN TCP_SERVICE_DATA *TcpService\r
+ )\r
+{\r
+ EFI_GUID *ServiceBindingGuid;\r
+\r
+ ASSERT (TcpService->MacString != NULL);\r
+\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+ } else {\r
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+ }\r
+\r
+ gRT->SetVariable (\r
+ TcpService->MacString,\r
+ ServiceBindingGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ FreePool (TcpService->MacString);\r
+ TcpService->MacString = NULL;\r
+}\r
+\r
+/**\r
+ Install the device path protocol on the TCP instance.\r
+\r
+ @param[in] Sock Pointer to the socket representing the TCP instance.\r
+\r
+ @retval EFI_SUCCESS The device path protocol was installed.\r
+ @retval other Failed to install the device path protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpInstallDevicePath (\r
+ IN SOCKET *Sock\r
+ )\r
+{\r
+ TCP_PROTO_DATA *TcpProto;\r
+ TCP_SERVICE_DATA *TcpService;\r
+ TCP_CB *Tcb;\r
+ IPv4_DEVICE_PATH Ip4DPathNode;\r
+ IPv6_DEVICE_PATH Ip6DPathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ EFI_STATUS Status;\r
+ TCP_PORTNO LocalPort;\r
+ TCP_PORTNO RemotePort;\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+ TcpService = TcpProto->TcpService;\r
+ Tcb = TcpProto->TcpPcb;\r
+\r
+ LocalPort = NTOHS (Tcb->LocalEnd.Port);\r
+ RemotePort = NTOHS (Tcb->RemoteEnd.Port);\r
+ if (Sock->IpVersion == IP_VERSION_4) {\r
+ NetLibCreateIPv4DPathNode (\r
+ &Ip4DPathNode,\r
+ TcpService->ControllerHandle,\r
+ Tcb->LocalEnd.Ip.Addr[0],\r
+ LocalPort,\r
+ Tcb->RemoteEnd.Ip.Addr[0],\r
+ RemotePort,\r
+ EFI_IP_PROTO_TCP,\r
+ Tcb->UseDefaultAddr\r
+ );\r
+\r
+ DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode;\r
+ } else {\r
+ NetLibCreateIPv6DPathNode (\r
+ &Ip6DPathNode,\r
+ TcpService->ControllerHandle,\r
+ &Tcb->LocalEnd.Ip.v6,\r
+ LocalPort,\r
+ &Tcb->RemoteEnd.Ip.v6,\r
+ RemotePort,\r
+ EFI_IP_PROTO_TCP\r
+ );\r
+\r
+ DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode;\r
+ }\r
+\r
+ Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath);\r
+ if (Sock->DevicePath == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = gBS->InstallProtocolInterface (\r
+ &Sock->SockHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ Sock->DevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Sock->DevicePath);\r
+ Sock->DevicePath = NULL;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r