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