From: hhtian Date: Mon, 1 Nov 2010 06:13:54 +0000 (+0000) Subject: Add NetworkPkg (P.UDK2010.UP3.Network.P1) X-Git-Tag: edk2-stable201903~15453 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=a3bcde70e6dc69000f85cc5deee98101d2ae200a Add NetworkPkg (P.UDK2010.UP3.Network.P1) git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.c b/NetworkPkg/Application/IfConfig6/IfConfig6.c new file mode 100644 index 0000000000..b2eada639a --- /dev/null +++ b/NetworkPkg/Application/IfConfig6/IfConfig6.c @@ -0,0 +1,1781 @@ +/** @file + The implementation for Shell application IfConfig6. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "IfConfig6.h" + +EFI_HII_HANDLE mHiiHandle; + +EFI_GUID mEfiIfConfig6Guid = EFI_IFCONFIG6_GUID; +SHELL_PARAM_ITEM mIfConfig6CheckList[] = { + { + L"-b", + TypeFlag + }, + { + L"-s", + TypeMaxValue + }, + { + L"-l", + TypeValue + }, + { + L"-r", + TypeValue + }, + { + L"-?", + TypeFlag + }, + { + NULL, + TypeMax + }, +}; + +VAR_CHECK_ITEM mSetCheckList[] = { + { + L"auto", + 0x00000001, + 0x00000001, + FlagTypeSingle + }, + { + L"man", + 0x00000002, + 0x00000001, + FlagTypeSingle + }, + { + L"host", + 0x00000004, + 0x00000002, + FlagTypeSingle + }, + { + L"dad", + 0x00000008, + 0x00000004, + FlagTypeSingle + }, + { + L"gw", + 0x00000010, + 0x00000008, + FlagTypeSingle + }, + { + L"dns", + 0x00000020, + 0x00000010, + FlagTypeSingle + }, + { + L"id", + 0x00000040, + 0x00000020, + FlagTypeSingle + }, + { + NULL, + 0x0, + 0x0, + FlagTypeSkipUnknown + }, +}; + +/** + Split a string with specified separator and save the substring to a list. + + @param[in] String The pointer of the input string. + @param[in] Separator The specified separator. + + @return The pointer of headnode of ARG_LIST. + +**/ +ARG_LIST * +SplitStrToList ( + IN CONST CHAR16 *String, + IN CHAR16 Separator + ) +{ + CHAR16 *Str; + CHAR16 *ArgStr; + ARG_LIST *ArgList; + ARG_LIST *ArgNode; + + if (*String == L'\0') { + return NULL; + } + + // + // Copy the CONST string to a local copy. + // + Str = (CHAR16 *) AllocateZeroPool (StrSize (String)); + ASSERT (Str != NULL); + Str = StrCpy (Str, String); + ArgStr = Str; + + // + // init a node for the list head. + // + ArgNode = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST)); + ASSERT (ArgNode != NULL); + ArgList = ArgNode; + + // + // Split the local copy and save in the list node. + // + while (*Str != L'\0') { + if (*Str == Separator) { + *Str = L'\0'; + ArgNode->Arg = ArgStr; + ArgStr = Str + 1; + ArgNode->Next = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST)); + ASSERT (ArgNode->Next != NULL); + ArgNode = ArgNode->Next; + } + + Str++; + } + + ArgNode->Arg = ArgStr; + ArgNode->Next = NULL; + + return ArgList; +} + +/** + Check the correctness of input Args with '-s' option. + + @param[in] CheckList The pointer of VAR_CHECK_ITEM array. + @param[in] Name The pointer of input arg. + @param[in] Init The switch to execute the check. + + @return The value of VAR_CHECK_CODE. + +**/ +VAR_CHECK_CODE +IfConfig6RetriveCheckListByName( + IN VAR_CHECK_ITEM *CheckList, + IN CHAR16 *Name, + IN BOOLEAN Init +) +{ + STATIC UINT32 CheckDuplicate; + STATIC UINT32 CheckConflict; + VAR_CHECK_CODE RtCode; + UINT32 Index; + VAR_CHECK_ITEM Arg; + + if (Init) { + CheckDuplicate = 0; + CheckConflict = 0; + return VarCheckOk; + } + + RtCode = VarCheckOk; + Index = 0; + Arg = CheckList[Index]; + + // + // Check the Duplicated/Conflicted/Unknown input Args. + // + while (Arg.FlagStr != NULL) { + if (StrCmp (Arg.FlagStr, Name) == 0) { + + if (CheckDuplicate & Arg.FlagID) { + RtCode = VarCheckDuplicate; + break; + } + + if (CheckConflict & Arg.ConflictMask) { + RtCode = VarCheckConflict; + break; + } + + CheckDuplicate |= Arg.FlagID; + CheckConflict |= Arg.ConflictMask; + break; + } + + Arg = CheckList[++Index]; + } + + if (Arg.FlagStr == NULL) { + RtCode = VarCheckUnknown; + } + + return RtCode; +} + +/** + The notify function of create event when performing a manual config. + + @param[in] CheckList The pointer of Event. + @param[in] Context The pointer of Context. + +**/ +VOID +EFIAPI +IfConfig6ManualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Print MAC address. + + @param[in] Node The pointer of MAC address buffer. + @param[in] Size The size of MAC address buffer. + +**/ +VOID +IfConfig6PrintMacAddr ( + IN UINT8 *Node, + IN UINT32 Size + ) +{ + UINTN Index; + + ASSERT (Size <= MACADDRMAXSIZE); + + for (Index = 0; Index < Size; Index++) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_BODY), mHiiHandle, Node[Index]); + if (Index + 1 < Size) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + } + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); +} + +/** + Print IPv6 address. + + @param[in] Ip The pointer of Ip bufffer in EFI_IPv6_ADDRESS format. + @param[in] PrefixLen The pointer of PrefixLen that describes the size Prefix. + +**/ +VOID +IfConfig6PrintIpAddr ( + IN EFI_IPv6_ADDRESS *Ip, + IN UINT8 *PrefixLen + ) +{ + UINTN Index; + BOOLEAN Short; + + Short = FALSE; + + for (Index = 0; Index < PREFIXMAXLEN; Index = Index + 2) { + + if (!Short && (Index + 1 < PREFIXMAXLEN) && (Index % 2 == 0) && (Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0)) { + // + // Deal with the case of ::. + // + if (Index == 0) { + // + // :: is at the beginning of the address. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + } + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + + while ((Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0) && (Index < PREFIXMAXLEN)) { + Index = Index + 2; + if (Index > PREFIXMAXLEN - 2) { + break; + } + } + + Short = TRUE; + + if (Index == PREFIXMAXLEN) { + // + // :: is at the end of the address. + // + break; + } + } + + if (Index < PREFIXMAXLEN - 1) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index]); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index + 1]); + } + + if (Index + 2 < PREFIXMAXLEN) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + } + } + + if (PrefixLen != NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_PREFIX_LEN), mHiiHandle, *PrefixLen); + } +} + +/** + Pick up host IPv6 address in string format from Args with "-s" option and convert it to EFI_IP6_CONFIG_MANUAL_ADDRESS format. + + @param[in, out] Arg The pointer of the address of ARG_LIST which save Args with the "-s" option. + @param[out] Buf The pointer of the address of EFI_IP6_CONFIG_MANUAL_ADDRESS. + @param[out] Address The pointer of BufSize that describes the size of Buf in bytes. + + @retval EFI_SUCCESS The convertion is successful. + @retval Others Does't find the host address, or it is an invalid IPv6 address in string format. + +**/ +EFI_STATUS +IfConfig6ParseManualAddressList ( + IN OUT ARG_LIST **Arg, + OUT EFI_IP6_CONFIG_MANUAL_ADDRESS **Buf, + OUT UINTN *BufSize + ) +{ + EFI_STATUS Status; + EFI_IP6_CONFIG_MANUAL_ADDRESS *AddrBuf; + ARG_LIST *VarArg; + EFI_IPv6_ADDRESS Address; + UINT8 Prefix; + UINT8 AddrCnt; + + AddrCnt = 0; + *BufSize = 0; + *Buf = NULL; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to check the correctness of input host ip6 address. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + // + // host ip ip ... gw + // + break; + } + + VarArg = VarArg->Next; + AddrCnt++; + } + + if (AddrCnt == 0) { + return EFI_INVALID_PARAMETER; + } + + AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + ASSERT (AddrBuf != NULL); + + AddrCnt = 0; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to fill in the EFI_IP6_CONFIG_MANUAL_ADDRESS structure. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + break; + } + + // + // If prefix length is not set, set it as Zero here. In the IfConfigSetInterfaceInfo() + // Zero prefix, length will be transfered to default prefix length. + // + if (Prefix == 0xFF) { + Prefix = 0; + } + AddrBuf[AddrCnt].IsAnycast = FALSE; + AddrBuf[AddrCnt].PrefixLength = Prefix; + IP6_COPY_ADDRESS (&AddrBuf[AddrCnt].Address, &Address); + VarArg = VarArg->Next; + AddrCnt++; + } + + *Arg = VarArg; + + if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) { + goto ON_ERROR; + } + + *Buf = AddrBuf; + *BufSize = AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + + return EFI_SUCCESS; + +ON_ERROR: + + FreePool (AddrBuf); + return Status; +} + +/** + Pick up gw/dns IPv6 address in string format from Args with "-s" option and convert it to EFI_IPv6_ADDRESS format. + + @param[in, out] Arg The pointer of the address of ARG_LIST that save Args with the "-s" option. + @param[out] Buf The pointer of the address of EFI_IPv6_ADDRESS. + @param[out] Address The pointer of BufSize that describes the size of Buf in bytes. + + @retval EFI_SUCCESS The conversion is successful. + @retval Others Doesn't find the host address, or it is an invalid IPv6 address in string format. + +**/ +EFI_STATUS +IfConfig6ParseGwDnsAddressList ( + IN OUT ARG_LIST **Arg, + OUT EFI_IPv6_ADDRESS **Buf, + OUT UINTN *BufSize + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS *AddrBuf; + ARG_LIST *VarArg; + EFI_IPv6_ADDRESS Address; + UINT8 Prefix; + UINT8 AddrCnt; + + AddrCnt = 0; + *BufSize = 0; + *Buf = NULL; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to check the correctness of input gw/dns address. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + // + // gw ip ip ... host + // + break; + } + + VarArg = VarArg->Next; + AddrCnt++; + } + + if (AddrCnt == 0) { + return EFI_INVALID_PARAMETER; + } + + AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IPv6_ADDRESS)); + ASSERT (AddrBuf != NULL); + + AddrCnt = 0; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to fill in the EFI_IPv6_ADDRESS structure. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + break; + } + + IP6_COPY_ADDRESS (&AddrBuf[AddrCnt], &Address); + + VarArg = VarArg->Next; + AddrCnt++; + } + + *Arg = VarArg; + + if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) { + goto ON_ERROR; + } + + *Buf = AddrBuf; + *BufSize = AddrCnt * sizeof (EFI_IPv6_ADDRESS); + + return EFI_SUCCESS; + +ON_ERROR: + + FreePool (AddrBuf); + return Status; +} + +/** + Parse InterfaceId in string format from Args with the "-s" option and convert it to EFI_IP6_CONFIG_INTERFACE_ID format. + + @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option. + @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The get status processed successfullly. + @retval EFI_INVALID_PARAMETER The get status process failed. + +**/ +EFI_STATUS +IfConfig6ParseInterfaceId ( + IN OUT ARG_LIST **Arg, + OUT EFI_IP6_CONFIG_INTERFACE_ID **IfId + ) +{ + UINT8 Index; + UINT8 NodeVal; + CHAR16 *IdStr; + + if (*Arg == NULL) { + return EFI_INVALID_PARAMETER; + } + + Index = 0; + IdStr = (*Arg)->Arg; + ASSERT (IfId != NULL); + *IfId = AllocateZeroPool (sizeof (EFI_IP6_CONFIG_INTERFACE_ID)); + ASSERT (*IfId != NULL); + + while ((*IdStr != L'\0') && (Index < 8)) { + + NodeVal = 0; + while ((*IdStr != L':') && (*IdStr != L'\0')) { + + if ((*IdStr <= L'F') && (*IdStr >= L'A')) { + NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'A' + 10); + } else if ((*IdStr <= L'f') && (*IdStr >= L'a')) { + NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'a' + 10); + } else if ((*IdStr <= L'9') && (*IdStr >= L'0')) { + NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'0'); + } else { + FreePool (*IfId); + return EFI_INVALID_PARAMETER; + } + + IdStr++; + } + + (*IfId)->Id[Index++] = NodeVal; + + if (*IdStr == L':') { + IdStr++; + } + } + + *Arg = (*Arg)->Next; + return EFI_SUCCESS; +} + +/** + Parse dad in string format from Args with the "-s" option and convert it to UINT32 format. + + @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option. + @param[out] Xmits The pointer of Xmits. + + @retval EFI_SUCCESS The get status processed successfully. + @retval others The get status process failed. + +**/ +EFI_STATUS +IfConfig6ParseDadXmits ( + IN OUT ARG_LIST **Arg, + OUT UINT32 *Xmits + ) +{ + CHAR16 *ValStr; + + if (*Arg == NULL) { + return EFI_INVALID_PARAMETER; + } + + ValStr = (*Arg)->Arg; + *Xmits = 0; + + while (*ValStr != L'\0') { + + if ((*ValStr <= L'9') && (*ValStr >= L'0')) { + + *Xmits = (*Xmits * 10) + (*ValStr - L'0'); + + } else { + + return EFI_INVALID_PARAMETER; + } + + ValStr++; + } + + *Arg = (*Arg)->Next; + return EFI_SUCCESS; +} + +/** + The get current status of all handles. + + @param[in] ImageHandle The handle of ImageHandle. + @param[in] IfName The pointer of IfName(interface name). + @param[in] IfList The pointer of IfList(interface list). + + @retval EFI_SUCCESS The get status processed successfully. + @retval others The get status process failed. + +**/ +EFI_STATUS +IfConfig6GetInterfaceInfo ( + IN EFI_HANDLE ImageHandle, + IN CHAR16 *IfName, + IN LIST_ENTRY *IfList + ) +{ + EFI_STATUS Status; + UINTN HandleIndex; + UINTN HandleNum; + EFI_HANDLE *HandleBuffer; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + IFCONFIG6_INTERFACE_CB *IfCb; + UINTN DataSize; + + HandleBuffer = NULL; + HandleNum = 0; + + IfInfo = NULL; + IfCb = NULL; + + // + // Locate all the handles with ip6 service binding protocol. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + &HandleNum, + &HandleBuffer + ); + if (EFI_ERROR (Status) || (HandleNum == 0)) { + return EFI_ABORTED; + } + + // + // Enumerate all handles that installed with ip6 service binding protocol. + // + for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) { + IfCb = NULL; + IfInfo = NULL; + DataSize = 0; + + // + // Ip6config protocol and ip6 service binding protocol are installed + // on the same handle. + // + ASSERT (HandleBuffer != NULL); + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Ip6Cfg + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + // + // Get the interface information size. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + NULL + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + IfInfo = AllocateZeroPool (DataSize); + + if (IfInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + // + // Get the interface info. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + IfInfo + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + // + // Check the interface name if required. + // + if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) != 0)) { + FreePool (IfInfo); + continue; + } + + DataSize = 0; + // + // Get the size of dns server list. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDnsServer, + &DataSize, + NULL + ); + + if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + IfCb = AllocateZeroPool (sizeof (IFCONFIG6_INTERFACE_CB) + DataSize); + + if (IfCb == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + IfCb->NicHandle = HandleBuffer[HandleIndex]; + IfCb->IfInfo = IfInfo; + IfCb->IfCfg = Ip6Cfg; + IfCb->DnsCnt = (UINT32) (DataSize / sizeof (EFI_IPv6_ADDRESS)); + + // + // Get the dns server list if has. + // + if (DataSize > 0) { + + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDnsServer, + &DataSize, + IfCb->DnsAddr + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + } + // + // Get the interface id if has. + // + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + IfCb->IfId = AllocateZeroPool (DataSize); + + if (IfCb->IfId == NULL) { + goto ON_ERROR; + } + + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + IfCb->IfId + ); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + if (Status == EFI_NOT_FOUND) { + FreePool (IfCb->IfId); + IfCb->IfId = NULL; + } + // + // Get the config policy. + // + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + &DataSize, + &IfCb->Policy + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + // + // Get the dad transmits. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &IfCb->Xmits + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + InsertTailList (IfList, &IfCb->Link); + + if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) == 0)) { + // + // Only need the appointed interface, keep the allocated buffer. + // + IfCb = NULL; + IfInfo = NULL; + break; + } + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (IfInfo != NULL) { + FreePool (IfInfo); + } + + if (IfCb != NULL) { + if (IfCb->IfId != NULL) { + FreePool (IfCb->IfId); + } + + FreePool (IfCb); + } + + return Status; +} + +/** + The list process of the IfConfig6 application. + + @param[in] IfList The pointer of IfList(interface list). + + @retval EFI_SUCCESS The IfConfig6 list processed successfully. + @retval others The IfConfig6 list process failed. + +**/ +EFI_STATUS +IfConfig6ShowInterfaceInfo ( + IN LIST_ENTRY *IfList + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + IFCONFIG6_INTERFACE_CB *IfCb; + UINTN Index; + + Entry = IfList->ForwardLink; + Status = EFI_SUCCESS; + + if (IsListEmpty (IfList)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle); + } + + // + // Go through the interface list. + // + while (Entry != IfList) { + + IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link); + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle); + + // + // Print interface name. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IF_NAME), mHiiHandle, IfCb->IfInfo->Name); + + // + // Print interface config policy. + // + if (IfCb->Policy == Ip6ConfigPolicyAutomatic) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_AUTO), mHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_MAN), mHiiHandle); + } + + // + // Print dad transmit. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DAD_TRANSMITS), mHiiHandle, IfCb->Xmits); + + // + // Print interface id if has. + // + if (IfCb->IfId != NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_INTERFACE_ID_HEAD), mHiiHandle); + + IfConfig6PrintMacAddr ( + IfCb->IfId->Id, + 8 + ); + } + // + // Print mac address of the interface. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_HEAD), mHiiHandle); + + IfConfig6PrintMacAddr ( + IfCb->IfInfo->HwAddress.Addr, + IfCb->IfInfo->HwAddressSize + ); + + // + // Print ip addresses list of the interface. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_HEAD), mHiiHandle); + + for (Index = 0; Index < IfCb->IfInfo->AddressInfoCount; Index++) { + IfConfig6PrintIpAddr ( + &IfCb->IfInfo->AddressInfo[Index].Address, + &IfCb->IfInfo->AddressInfo[Index].PrefixLength + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); + } + + // + // Print dns server addresses list of the interface if has. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DNS_ADDR_HEAD), mHiiHandle); + + for (Index = 0; Index < IfCb->DnsCnt; Index++) { + IfConfig6PrintIpAddr ( + &IfCb->DnsAddr[Index], + NULL + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); + } + + // + // Print route table of the interface if has. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_ROUTE_HEAD), mHiiHandle); + + for (Index = 0; Index < IfCb->IfInfo->RouteCount; Index++) { + IfConfig6PrintIpAddr ( + &IfCb->IfInfo->RouteTable[Index].Destination, + &IfCb->IfInfo->RouteTable[Index].PrefixLength + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_JOINT), mHiiHandle); + + IfConfig6PrintIpAddr ( + &IfCb->IfInfo->RouteTable[Index].Gateway, + NULL + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); + } + + Entry = Entry->ForwardLink; + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle); + + return Status; +} + +/** + The clean process of the IfConfig6 application. + + @param[in] IfList The pointer of IfList(interface list). + + @retval EFI_SUCCESS The IfConfig6 clean processed successfully. + @retval others The IfConfig6 clean process failed. + +**/ +EFI_STATUS +IfConfig6ClearInterfaceInfo ( + IN LIST_ENTRY *IfList + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + IFCONFIG6_INTERFACE_CB *IfCb; + EFI_IP6_CONFIG_POLICY Policy; + + Policy = Ip6ConfigPolicyAutomatic; + Entry = IfList->ForwardLink; + Status = EFI_SUCCESS; + + if (IsListEmpty (IfList)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle); + } + + // + // Go through the interface list. + // + while (Entry != IfList) { + + IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link); + + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Policy + ); + + if (EFI_ERROR (Status)) { + break; + } + + Entry = Entry->ForwardLink; + } + + return Status; +} + +/** + The set process of the IfConfig6 application. + + @param[in] IfList The pointer of IfList(interface list). + @param[in] VarArg The pointer of ARG_LIST(Args with "-s" option). + + @retval EFI_SUCCESS The IfConfig6 set processed successfully. + @retval others The IfConfig6 set process failed. + +**/ +EFI_STATUS +IfConfig6SetInterfaceInfo ( + IN LIST_ENTRY *IfList, + IN ARG_LIST *VarArg + ) +{ + EFI_STATUS Status; + IFCONFIG6_INTERFACE_CB *IfCb; + EFI_IP6_CONFIG_MANUAL_ADDRESS *CfgManAddr; + EFI_IPv6_ADDRESS *CfgAddr; + UINTN AddrSize; + EFI_IP6_CONFIG_INTERFACE_ID *InterfaceId; + UINT32 DadXmits; + UINT32 CurDadXmits; + UINTN CurDadXmitsLen; + EFI_IP6_CONFIG_POLICY Policy; + + VAR_CHECK_CODE CheckCode; + EFI_EVENT TimeOutEvt; + EFI_EVENT MappedEvt; + BOOLEAN IsAddressOk; + + UINTN DataSize; + UINT32 Index; + UINT32 Index2; + BOOLEAN IsAddressSet; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + + CfgManAddr = NULL; + CfgAddr = NULL; + TimeOutEvt = NULL; + MappedEvt = NULL; + IfInfo = NULL; + InterfaceId = NULL; + CurDadXmits = 0; + + if (IsListEmpty (IfList)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle); + return EFI_INVALID_PARAMETER; + } + // + // Make sure to set only one interface each time. + // + IfCb = BASE_CR (IfList->ForwardLink, IFCONFIG6_INTERFACE_CB, Link); + Status = EFI_SUCCESS; + + // + // Initialize check list mechanism. + // + CheckCode = IfConfig6RetriveCheckListByName( + NULL, + NULL, + TRUE + ); + + // + // Create events & timers for asynchronous settings. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IfConfig6ManualAddressNotify, + &IsAddressOk, + &MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Parse the setting variables. + // + while (VarArg != NULL) { + // + // Check invalid parameters (duplication & unknown & conflict). + // + CheckCode = IfConfig6RetriveCheckListByName( + mSetCheckList, + VarArg->Arg, + FALSE + ); + + if (VarCheckOk != CheckCode) { + switch (CheckCode) { + case VarCheckDuplicate: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_DUPLICATE_COMMAND), mHiiHandle, VarArg->Arg); + break; + + case VarCheckConflict: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_CONFLICT_COMMAND), mHiiHandle, VarArg->Arg); + break; + + case VarCheckUnknown: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_UNKNOWN_COMMAND), mHiiHandle, VarArg->Arg); + break; + + default: + break; + } + + VarArg = VarArg->Next; + continue; + } + // + // Process valid variables. + // + if (StrCmp(VarArg->Arg, L"auto") == 0) { + // + // Set automaic config policy + // + Policy = Ip6ConfigPolicyAutomatic; + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Policy + ); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + VarArg= VarArg->Next; + + } else if (StrCmp (VarArg->Arg, L"man") == 0) { + // + // Set manual config policy. + // + Policy = Ip6ConfigPolicyManual; + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Policy + ); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + VarArg= VarArg->Next; + + } else if (StrCmp (VarArg->Arg, L"host") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseManualAddressList ( + &VarArg, + &CfgManAddr, + &AddrSize + ); + + if (EFI_ERROR (Status)) { + if (Status == EFI_INVALID_PARAMETER) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"host"); + continue; + } else { + goto ON_EXIT; + } + } + // + // Set static host ip6 address list. + // This is a asynchronous process. + // + IsAddressOk = FALSE; + + Status = IfCb->IfCfg->RegisterDataNotify ( + IfCb->IfCfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeManualAddress, + AddrSize, + CfgManAddr + ); + + if (Status == EFI_NOT_READY) { + // + // Get current dad transmits count. + // + CurDadXmitsLen = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + IfCb->IfCfg->GetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &CurDadXmitsLen, + &CurDadXmits + ); + + gBS->SetTimer (TimeOutEvt, TimerRelative, 50000000 + 10000000 * CurDadXmits); + + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + if (IsAddressOk) { + Status = EFI_SUCCESS; + break; + } + } + } + + IfCb->IfCfg->UnregisterDataNotify ( + IfCb->IfCfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_MAN_HOST), mHiiHandle, Status); + goto ON_EXIT; + } + + // + // Check whether the address is set successfully. + // + DataSize = 0; + + Status = IfCb->IfCfg->GetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + NULL + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_EXIT; + } + + IfInfo = AllocateZeroPool (DataSize); + + if (IfInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = IfCb->IfCfg->GetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + IfInfo + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_EXIT; + } + + for ( Index = 0; Index < (UINTN) (AddrSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); Index++) { + IsAddressSet = FALSE; + // + // By default, the prefix length 0 is regarded as 64. + // + if (CfgManAddr[Index].PrefixLength == 0) { + CfgManAddr[Index].PrefixLength = 64; + } + + for (Index2 = 0; Index2 < IfInfo->AddressInfoCount; Index2++) { + if (EFI_IP6_EQUAL (&IfInfo->AddressInfo[Index2].Address, &CfgManAddr[Index].Address) && + (IfInfo->AddressInfo[Index2].PrefixLength == CfgManAddr[Index].PrefixLength)) { + IsAddressSet = TRUE; + break; + } + } + + if (!IsAddressSet) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_ADDRESS_FAILED), mHiiHandle); + IfConfig6PrintIpAddr ( + &CfgManAddr[Index].Address, + &CfgManAddr[Index].PrefixLength + ); + } + } + + } else if (StrCmp (VarArg->Arg, L"gw") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseGwDnsAddressList ( + &VarArg, + &CfgAddr, + &AddrSize + ); + + if (EFI_ERROR (Status)) { + if (Status == EFI_INVALID_PARAMETER) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"gw"); + continue; + } else { + goto ON_EXIT; + } + } + // + // Set static gateway ip6 address list. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeGateway, + AddrSize, + CfgAddr + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else if (StrCmp (VarArg->Arg, L"dns") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseGwDnsAddressList ( + &VarArg, + &CfgAddr, + &AddrSize + ); + + if (EFI_ERROR (Status)) { + if (Status == EFI_INVALID_PARAMETER) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"dns"); + continue; + } else { + goto ON_EXIT; + } + } + // + // Set static dhs server ip6 address list. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeDnsServer, + AddrSize, + CfgAddr + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else if (StrCmp (VarArg->Arg, L"id") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseInterfaceId (&VarArg, &InterfaceId); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Set alternative interface id. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeAltInterfaceId, + sizeof (EFI_IP6_CONFIG_INTERFACE_ID), + InterfaceId + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else if (StrCmp (VarArg->Arg, L"dad") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseDadXmits (&VarArg, &DadXmits); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Set dad transmits count. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS), + &DadXmits + ); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + } + } + +ON_EXIT: + + if (CfgManAddr != NULL) { + FreePool (CfgManAddr); + } + + if (CfgAddr != NULL) { + FreePool (CfgAddr); + } + + if (MappedEvt != NULL) { + gBS->CloseEvent (MappedEvt); + } + + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + + if (IfInfo != NULL) { + FreePool (IfInfo); + } + + return Status; + +} + +/** + The IfConfig6 main process. + + @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA. + + @retval EFI_SUCCESS IfConfig6 processed successfully. + @retval others The IfConfig6 process failed. + +**/ +EFI_STATUS +IfConfig6 ( + IN IFCONFIG6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + // + // Get configure information of all interfaces. + // + Status = IfConfig6GetInterfaceInfo ( + Private->ImageHandle, + Private->IfName, + &Private->IfList + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + switch (Private->OpCode) { + case IfConfig6OpList: + Status = IfConfig6ShowInterfaceInfo (&Private->IfList); + break; + + case IfConfig6OpClear: + Status = IfConfig6ClearInterfaceInfo (&Private->IfList); + break; + + case IfConfig6OpSet: + Status = IfConfig6SetInterfaceInfo (&Private->IfList, Private->VarArg); + break; + + default: + Status = EFI_ABORTED; + } + +ON_EXIT: + + return Status; +} + +/** + The IfConfig6 cleanup process, free the allocated memory. + + @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA. + +**/ +VOID +IfConfig6Cleanup ( + IN IFCONFIG6_PRIVATE_DATA *Private + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + IFCONFIG6_INTERFACE_CB *IfCb; + ARG_LIST *ArgNode; + ARG_LIST *ArgHead; + + ASSERT (Private != NULL); + + // + // Clean the list which save the set config Args. + // + if (Private->VarArg != NULL) { + ArgHead = Private->VarArg; + + while (ArgHead->Next != NULL) { + ArgNode = ArgHead->Next; + FreePool (ArgHead); + ArgHead = ArgNode; + } + + FreePool (ArgHead); + } + + if (Private->IfName != NULL) + FreePool (Private->IfName); + + + // + // Clean the IFCONFIG6_INTERFACE_CB list. + // + Entry = Private->IfList.ForwardLink; + NextEntry = Entry->ForwardLink; + + while (Entry != &Private->IfList) { + + IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link); + + RemoveEntryList (&IfCb->Link); + + if (IfCb->IfId != NULL) { + + FreePool (IfCb->IfId); + } + + if (IfCb->IfInfo != NULL) { + + FreePool (IfCb->IfInfo); + } + + FreePool (IfCb); + + Entry = NextEntry; + NextEntry = Entry->ForwardLink; + } + + FreePool (Private); +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for the IfConfig6 application which parses the command line input and calls the IfConfig6 process. + + @param[in] ImageHandle The image handle of this application. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others Some errors occur. + +**/ +EFI_STATUS +EFIAPI +IfConfig6Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + IFCONFIG6_PRIVATE_DATA *Private; + LIST_ENTRY *ParamPackage; + CONST CHAR16 *ValueStr; + ARG_LIST *ArgList; + CHAR16 *ProblemParam; + CHAR16 *Str; + + Private = NULL; + + // + // Register our string package with HII and return the handle to it. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IfConfig6Strings, NULL); + ASSERT (mHiiHandle != NULL); + + Status = ShellCommandLineParseEx (mIfConfig6CheckList, &ParamPackage, &ProblemParam, TRUE, FALSE); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_COMMAND), mHiiHandle, ProblemParam); + goto ON_EXIT; + } + + // + // To handle no option. + // + if (!ShellCommandLineGetFlag (ParamPackage, L"-r") && !ShellCommandLineGetFlag (ParamPackage, L"-s") && + !ShellCommandLineGetFlag (ParamPackage, L"-?") && !ShellCommandLineGetFlag (ParamPackage, L"-l")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_LACK_OPTION), mHiiHandle); + goto ON_EXIT; + } + // + // To handle conflict options. + // + if (((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-s"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-l")) && (ShellCommandLineGetFlag (ParamPackage, L"-?")))) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_CONFLICT_OPTIONS), mHiiHandle); + goto ON_EXIT; + } + // + // To show the help information of ifconfig6 command. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-?")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_HELP), mHiiHandle); + goto ON_EXIT; + } + + Status = EFI_INVALID_PARAMETER; + + Private = AllocateZeroPool (sizeof (IFCONFIG6_PRIVATE_DATA)); + + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + InitializeListHead (&Private->IfList); + + // + // To get interface name for the list option. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-l")) { + Private->OpCode = IfConfig6OpList; + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l"); + if (ValueStr != NULL) { + Str = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr)); + ASSERT (Str != NULL); + + Str = StrCpy (Str, ValueStr); + Private->IfName = Str; + } + } + // + // To get interface name for the clear option. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-r")) { + Private->OpCode = IfConfig6OpClear; + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-r"); + if (ValueStr != NULL) { + Str = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr)); + ASSERT (Str != NULL); + + Str = StrCpy (Str, ValueStr); + Private->IfName = Str; + } + } + // + // To get interface name and corresponding Args for the set option. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-s")) { + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_INTERFACE), mHiiHandle); + goto ON_EXIT; + } + // + // To split the configuration into multi-section. + // + ArgList = SplitStrToList (ValueStr, L' '); + ASSERT (ArgList != NULL); + + Private->OpCode = IfConfig6OpSet; + Private->IfName = ArgList->Arg; + + Private->VarArg = ArgList->Next; + + if (Private->IfName == NULL || Private->VarArg == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_COMMAND), mHiiHandle); + goto ON_EXIT; + } + } + // + // Main process of ifconfig6. + // + Status = IfConfig6 (Private); + +ON_EXIT: + + ShellCommandLineFreeVarList (ParamPackage); + HiiRemovePackages (mHiiHandle); + if (Private != NULL) + IfConfig6Cleanup (Private); + + return Status; +} + diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.h b/NetworkPkg/Application/IfConfig6/IfConfig6.h new file mode 100644 index 0000000000..eea7df55d1 --- /dev/null +++ b/NetworkPkg/Application/IfConfig6/IfConfig6.h @@ -0,0 +1,84 @@ +/** @file + The interface function declaration of shell application IfConfig6. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _IFCONFIG6_H_ +#define _IFCONFIG6_H_ + +#define EFI_IFCONFIG6_GUID \ + { \ + 0xbab7296b, 0x222c, 0x4408, {0x9e, 0x6c, 0xc2, 0x5c, 0x18, 0x7e, 0xff, 0x33} \ + } + +enum { + IfConfig6OpList = 1, + IfConfig6OpSet = 2, + IfConfig6OpClear = 3 +}; + +typedef enum { + VarCheckReserved = -1, + VarCheckOk = 0, + VarCheckDuplicate, + VarCheckConflict, + VarCheckUnknown, + VarCheckLackValue, + VarCheckOutOfMem +} VAR_CHECK_CODE; + +typedef enum { + FlagTypeSingle = 0, + FlagTypeNeedVar, + FlagTypeNeedSet, + FlagTypeSkipUnknown +} VAR_CHECK_FLAG_TYPE; + +#define MACADDRMAXSIZE 32 +#define PREFIXMAXLEN 16 + +typedef struct _IFCONFIG6_INTERFACE_CB { + EFI_HANDLE NicHandle; + LIST_ENTRY Link; + EFI_IP6_CONFIG_PROTOCOL *IfCfg; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + EFI_IP6_CONFIG_INTERFACE_ID *IfId; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS Xmits; + UINT32 DnsCnt; + EFI_IPv6_ADDRESS DnsAddr[1]; +} IFCONFIG6_INTERFACE_CB; + +typedef struct _ARG_LIST ARG_LIST; + +struct _ARG_LIST { + ARG_LIST *Next; + CHAR16 *Arg; +}; + +typedef struct _IFCONFIG6_PRIVATE_DATA { + EFI_HANDLE ImageHandle; + LIST_ENTRY IfList; + + UINT32 OpCode; + CHAR16 *IfName; + ARG_LIST *VarArg; +} IFCONFIG6_PRIVATE_DATA; + +typedef struct _VAR_CHECK_ITEM{ + CHAR16 *FlagStr; + UINT32 FlagID; + UINT32 ConflictMask; + VAR_CHECK_FLAG_TYPE FlagType; +} VAR_CHECK_ITEM; +#endif diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.inf b/NetworkPkg/Application/IfConfig6/IfConfig6.inf new file mode 100644 index 0000000000..dd3ab64493 --- /dev/null +++ b/NetworkPkg/Application/IfConfig6/IfConfig6.inf @@ -0,0 +1,52 @@ +## @file +# Component description file for Shell application IfConfig6. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = IfConfig6 + FILE_GUID = 6F71926E-60CE-428d-AA58-A3D9FB879429 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = IfConfig6Initialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# +[Sources] + IfConfig6Strings.uni + IfConfig6.c + IfConfig6.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + BaseLib + UefiBootServicesTableLib + UefiApplicationEntryPoint + BaseMemoryLib + ShellLib + MemoryAllocationLib + DebugLib + HiiLib + NetLib + +[Protocols] + gEfiIp6ServiceBindingProtocolGuid ## CONSUMS + gEfiIp6ConfigProtocolGuid ## CONSUMS diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni new file mode 100644 index 0000000000..a5e7fd04cf Binary files /dev/null and b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni differ diff --git a/NetworkPkg/Application/IpsecConfig/Delete.c b/NetworkPkg/Application/IpsecConfig/Delete.c new file mode 100644 index 0000000000..caeb1c8350 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Delete.c @@ -0,0 +1,110 @@ +/** @file + The implementation of delete policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfig.h" +#include "Indexer.h" +#include "Delete.h" +#include "Match.h" +#include "ForEach.h" + +/** + Private function to delete entry information in database. + + @param[in] Selector The pointer to EFI_IPSEC_CONFIG_SELECTOR structure. + @param[in] Data The pointer to Data. + @param[in] Context The pointer to DELETE_POLICY_ENTRY_CONTEXT. + + @retval EFI_ABORTED Abort the iteration. + @retval EFI_SUCCESS Continue the iteration. +**/ +EFI_STATUS +DeletePolicyEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN DELETE_POLICY_ENTRY_CONTEXT *Context + ) +{ + if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) { + Context->Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Selector, + NULL, + NULL + ); + // + // Abort the iteration after the insertion. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Flush or delete entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Delete entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +FlushOrDeletePolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + DELETE_POLICY_ENTRY_CONTEXT Context; + CONST CHAR16 *ValueStr; + + // + // If user wants to remove all. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-f")) { + Status = mIpSecConfig->SetData ( + mIpSecConfig, + DataType, + NULL, + NULL, + NULL + ); + } else { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr); + return EFI_NOT_FOUND; + } + + Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage); + if (!EFI_ERROR (Status)) { + Context.DataType = DataType; + Context.Status = EFI_NOT_FOUND; + ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) DeletePolicyEntry, &Context); + Status = Context.Status; + + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr); + } else if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DELETE_FAILED), mHiiHandle, mAppName); + } + } + } + + return Status; +} diff --git a/NetworkPkg/Application/IpsecConfig/Delete.h b/NetworkPkg/Application/IpsecConfig/Delete.h new file mode 100644 index 0000000000..49f3b0d5fa --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Delete.h @@ -0,0 +1,42 @@ +/** @file + The internal structure and function declaration of delete policy entry function + in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __DELETE_H_ +#define __DELETE_H_ + +typedef struct { + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + POLICY_ENTRY_INDEXER Indexer; + EFI_STATUS Status; //Indicate whether deletion succeeds. +} DELETE_POLICY_ENTRY_CONTEXT; + +/** + Flush or delete entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Delete entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +FlushOrDeletePolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); + +#endif diff --git a/NetworkPkg/Application/IpsecConfig/Dump.c b/NetworkPkg/Application/IpsecConfig/Dump.c new file mode 100644 index 0000000000..004ab1089c --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Dump.c @@ -0,0 +1,530 @@ +/** @file + The implementation of dump policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfig.h" +#include "Dump.h" +#include "ForEach.h" +#include "Helper.h" + +/** + Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure. + + @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure. + + @return the value of version. +**/ +UINTN +GetVerFromAddrInfo ( + IN EFI_IP_ADDRESS_INFO *AddressInfo +) +{ + if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) && + (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) { + return IP_VERSION_4; + } else { + return IP_VERSION_6; + } +} + +/** + Private function called to get the version information from a EFI_IP_ADDRESS structure. + + @param[in] Address The pointer to the EFI_IP_ADDRESS structure. + + @return The value of the version. +**/ +UINTN +GetVerFromIpAddr ( + IN EFI_IP_ADDRESS *Address +) +{ + if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) { + return IP_VERSION_4; + } else { + return IP_VERSION_6; + } +} + +/** + Private function called to print an ASCII string in unicode char format. + + @param[in] Str The pointer to the ASCII string. + @param[in] Length The value of the ASCII string length. +**/ +VOID +DumpAsciiString ( + IN CHAR8 *Str, + IN UINTN Length + ) +{ + UINTN Index; + for (Index = 0; Index < Length; Index++) { + Print (L"%c", (CHAR16) Str[Index]); + } +} + +/** + Private function called to print EFI_IP_ADDRESS_INFO content. + + @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure. +**/ +VOID +DumpAddressInfo ( + IN EFI_IP_ADDRESS_INFO *AddressInfo + ) +{ + if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) { + Print ( + L"%d.%d.%d.%d", + (UINTN) AddressInfo->Address.v4.Addr[0], + (UINTN) AddressInfo->Address.v4.Addr[1], + (UINTN) AddressInfo->Address.v4.Addr[2], + (UINTN) AddressInfo->Address.v4.Addr[3] + ); + if (AddressInfo->PrefixLength != 32) { + Print (L"/%d", (UINTN) AddressInfo->PrefixLength); + } + } + + if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) { + Print ( + L"%x:%x:%x:%x:%x:%x:%x:%x", + (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]), + (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]), + (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]), + (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]), + (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]), + (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]), + (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]), + (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15]) + ); + if (AddressInfo->PrefixLength != 128) { + Print (L"/%d", AddressInfo->PrefixLength); + } + } +} + +/** + Private function called to print EFI_IP_ADDRESS content. + + @param[in] IpAddress The pointer to the EFI_IP_ADDRESS structure. +**/ +VOID +DumpIpAddress ( + IN EFI_IP_ADDRESS *IpAddress + ) +{ + if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) { + Print ( + L"%d.%d.%d.%d", + (UINTN) IpAddress->v4.Addr[0], + (UINTN) IpAddress->v4.Addr[1], + (UINTN) IpAddress->v4.Addr[2], + (UINTN) IpAddress->v4.Addr[3] + ); + } + + if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) { + Print ( + L"%x:%x:%x:%x:%x:%x:%x:%x", + (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]), + (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]), + (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]), + (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]), + (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]), + (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]), + (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]), + (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15]) + ); + } + +} + +/** + Private function called to print EFI_IPSEC_SPD_SELECTOR content. + + @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. +**/ +VOID +DumpSpdSelector ( + IN EFI_IPSEC_SPD_SELECTOR *Selector + ) +{ + UINT32 Index; + CHAR16 *Str; + + for (Index = 0; Index < Selector->LocalAddressCount; Index++) { + if (Index > 0) { + Print (L","); + } + + DumpAddressInfo (&Selector->LocalAddress[Index]); + } + + if (Index == 0) { + Print (L"localhost"); + } + + Print (L" -> "); + + for (Index = 0; Index < Selector->RemoteAddressCount; Index++) { + if (Index > 0) { + Print (L","); + } + + DumpAddressInfo (&Selector->RemoteAddress[Index]); + } + + Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol); + if (Str != NULL) { + Print (L" %s", Str); + } else { + Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol); + } + + if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) { + Print (L" port:"); + if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) { + Print (L"%d", Selector->LocalPort); + if (Selector->LocalPortRange != 0) { + Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange); + } + } else { + Print (L"any"); + } + + Print (L" -> "); + if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) { + Print (L"%d", Selector->RemotePort); + if (Selector->RemotePortRange != 0) { + Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange); + } + } else { + Print (L"any"); + } + } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) { + Print (L" class/code:"); + if (Selector->LocalPort != 0) { + Print (L"%d", (UINTN) (UINT8) Selector->LocalPort); + } else { + Print (L"any"); + } + + Print (L"/"); + if (Selector->RemotePort != 0) { + Print (L"%d", (UINTN) (UINT8) Selector->RemotePort); + } else { + Print (L"any"); + } + } +} + +/** + Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content. + + @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] EntryIndex The pointer to the Index in SPD Database. + + @retval EFI_SUCCESS Dump SPD information successfully. +**/ +EFI_STATUS +DumpSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN EFI_IPSEC_SPD_DATA *Data, + IN UINTN *EntryIndex + ) +{ + BOOLEAN HasPre; + CHAR16 DataName[128]; + CHAR16 *String1; + CHAR16 *String2; + CHAR16 *String3; + UINT8 Index; + + Print (L"%d.", (*EntryIndex)++); + + // + // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400 + // Protect PF:0x34323423 Name:First Entry + // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600] + // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set] + // + + DumpSpdSelector (Selector); + Print (L"\n "); + + Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction)); + Print (L"PF:%08x ", Data->PackageFlag); + + Index = 0; + while (Data->Name[Index] != 0) { + DataName[Index] = (CHAR16) Data->Name[Index]; + Index++; + ASSERT (Index < 128); + } + DataName[Index] = L'\0'; + + Print (L"Name:%s", DataName); + + if (Data->Action == EfiIPsecActionProtect) { + Print (L"\n "); + if (Data->ProcessingPolicy->ExtSeqNum) { + Print (L"ext-sequence "); + } + + if (Data->ProcessingPolicy->SeqOverflow) { + Print (L"sequence-overflow "); + } + + if (Data->ProcessingPolicy->FragCheck) { + Print (L"fragment-check "); + } + + HasPre = FALSE; + if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount); + HasPre = TRUE; + } + + if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime); + HasPre = TRUE; + } + + if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime); + HasPre = TRUE; + } + + if (HasPre) { + Print (L"]"); + } + + if (HasPre || Data->ProcessingPolicy->ExtSeqNum || + Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) { + Print (L"\n "); + } + + String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol); + String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo); + String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo); + Print ( + L"%s Auth:%s Encrypt:%s ", + String1, + String2, + String3 + ); + + Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode)); + if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + Print (L"["); + DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress); + Print (L" -> "); + DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress); + Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption)); + } + } + + Print (L"\n"); + + return EFI_SUCCESS; +} + +/** + Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA content. + + @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_SA_DATA structure. + @param[in] EntryIndex The pointer to the Index in the SAD Database. + + @retval EFI_SUCCESS Dump SAD information successfully. +**/ +EFI_STATUS +DumpSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN EFI_IPSEC_SA_DATA *Data, + IN UINTN *EntryIndex + ) +{ + BOOLEAN HasPre; + CHAR16 *String1; + CHAR16 *String2; + + // + // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx + // Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34 + // Auth:xxxx/password Encrypt:yyyy/password + // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400 + // + + Print (L"%d.", (*EntryIndex)++); + Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol)); + Print (L"Destination:"); + DumpIpAddress (&SaId->DestAddress); + Print (L"\n"); + + Print ( + L" Mode:%s SeqNum:%lx AntiReplayWin:%d ", + MapIntegerToString (Data->Mode, mMapIpSecMode), + Data->SNCount, + (UINTN) Data->AntiReplayWindows + ); + + HasPre = FALSE; + if (Data->SaLifetime.ByteCount != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxB", Data->SaLifetime.ByteCount); + HasPre = TRUE; + } + + if (Data->SaLifetime.SoftLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxs", Data->SaLifetime.SoftLifetime); + HasPre = TRUE; + } + + if (Data->SaLifetime.HardLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxS", Data->SaLifetime.HardLifetime); + HasPre = TRUE; + } + + if (HasPre) { + Print (L"] "); + } + + Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU); + + if (SaId->Proto == EfiIPsecAH) { + Print ( + L" Auth:%s/%s\n", + MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo), + Data->AlgoInfo.AhAlgoInfo.AuthKey + ); + } else { + String1 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo); + String2 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo); + Print ( + L" Auth:%s/%s Encrypt:%s/%s\n", + String1, + Data->AlgoInfo.EspAlgoInfo.AuthKey, + String2, + Data->AlgoInfo.EspAlgoInfo.EncKey + ); + } + + if (Data->SpdSelector != NULL) { + Print (L" "); + DumpSpdSelector (Data->SpdSelector); + Print (L"\n"); + } + + return EFI_SUCCESS; +} + +/** + Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content. + + @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] EntryIndex The pointer to the Index in the PAD Database. + + @retval EFI_SUCCESS Dump PAD information successfully. +**/ +EFI_STATUS +DumpPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN EFI_IPSEC_PAD_DATA *Data, + IN UINTN *EntryIndex + ) +{ + CHAR16 *String1; + CHAR16 *String2; + + // + // ADDR:10.23.17.34/15 + // IDEv1 PreSharedSecret IKE-ID + // password + // + + Print (L"%d.", (*EntryIndex)++); + + if (PadId->PeerIdValid) { + Print (L"ID:%s", PadId->Id.PeerId); + } else { + Print (L"ADDR:"); + DumpAddressInfo (&PadId->Id.IpAddress); + } + + Print (L"\n"); + + String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto); + String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod); + Print ( + L" %s %s", + String1, + String2 + ); + + if (Data->IkeIdFlag) { + Print (L"IKE-ID"); + } + + Print (L"\n"); + + if (Data->AuthData != NULL) { + DumpAsciiString (Data->AuthData, Data->AuthDataSize); + Print (L"\n"); + } + + if (Data->RevocationData != NULL) { + Print (L" %s\n", Data->RevocationData); + } + + return EFI_SUCCESS; + +} + +VISIT_POLICY_ENTRY mDumpPolicyEntry[] = { + (VISIT_POLICY_ENTRY) DumpSpdEntry, + (VISIT_POLICY_ENTRY) DumpSadEntry, + (VISIT_POLICY_ENTRY) DumpPadEntry +}; + +/** + Print all entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Dump all information successfully. + @retval Others Some mistaken case. +**/ +EFI_STATUS +ListPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + UINTN EntryIndex; + + EntryIndex = 0; + return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex); +} + diff --git a/NetworkPkg/Application/IpsecConfig/Dump.h b/NetworkPkg/Application/IpsecConfig/Dump.h new file mode 100644 index 0000000000..3c475dd304 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Dump.h @@ -0,0 +1,34 @@ +/** @file + The function declaration of dump policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _DUMP_H_ +#define _DUMP_H_ + +/** + Print all entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Dump all information successfully. + @retval Others Some mistaken case. +**/ +EFI_STATUS +ListPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); + +#endif diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.c b/NetworkPkg/Application/IpsecConfig/ForEach.c new file mode 100644 index 0000000000..7b3b9b17a8 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/ForEach.c @@ -0,0 +1,115 @@ +/** @file + The implementation to go through each entry in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfig.h" +#include "ForEach.h" + + +/** + Enumerate all entries in the database to execute specified operations according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] Routine The pointer to the function of a specified operation. + @param[in] Context The pointer to the context of a function. + + @retval EFI_SUCCESS Execute specified operation successfully. +**/ +EFI_STATUS +ForeachPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN VISIT_POLICY_ENTRY Routine, + IN VOID *Context + ) +{ + EFI_STATUS GetNextStatus; + EFI_STATUS GetDataStatus; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + UINTN SelectorSize; + UINTN DataSize; + BOOLEAN FirstGetNext; + + FirstGetNext = TRUE; + SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR); + Selector = AllocateZeroPool (SelectorSize); + + DataSize = 0; + Data = NULL; + + while (TRUE) { + GetNextStatus = mIpSecConfig->GetNextSelector ( + mIpSecConfig, + DataType, + &SelectorSize, + Selector + ); + if (GetNextStatus == EFI_BUFFER_TOO_SMALL) { + gBS->FreePool (Selector); + Selector = FirstGetNext ? AllocateZeroPool (SelectorSize) : AllocatePool (SelectorSize); + + GetNextStatus = mIpSecConfig->GetNextSelector ( + mIpSecConfig, + DataType, + &SelectorSize, + Selector + ); + } + + if (EFI_ERROR (GetNextStatus)) { + break; + } + + FirstGetNext = FALSE; + + GetDataStatus = mIpSecConfig->GetData ( + mIpSecConfig, + DataType, + Selector, + &DataSize, + Data + ); + if (GetDataStatus == EFI_BUFFER_TOO_SMALL) { + if (Data != NULL) { + gBS->FreePool (Data); + } + + Data = AllocateZeroPool (DataSize); + GetDataStatus = mIpSecConfig->GetData ( + mIpSecConfig, + DataType, + Selector, + &DataSize, + Data + ); + } + + ASSERT_EFI_ERROR (GetDataStatus); + + if (EFI_ERROR (Routine (Selector, Data, Context))) { + break; + } + } + + if (Data != NULL) { + gBS->FreePool (Data); + } + + if (Selector != NULL) { + gBS->FreePool (Selector); + } + + return EFI_SUCCESS; +} + diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.h b/NetworkPkg/Application/IpsecConfig/ForEach.h new file mode 100644 index 0000000000..fc309300c0 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/ForEach.h @@ -0,0 +1,54 @@ +/** @file + The internal structure and function declaration of the implementation + to go through each entry in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _FOREACH_H_ +#define _FOREACH_H_ + +/** + The prototype for the DumpSpdEntry()/DumpSadEntry()/DumpPadEntry(). + Print EFI_IPSEC_CONFIG_SELECTOR and corresponding content. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union. + @param[in] Data The pointer to the corresponding data. + @param[in] Context The pointer to the Index in SPD/SAD/PAD Database. + + @retval EFI_SUCCESS Dump SPD/SAD/PAD information successfully. +**/ +typedef +EFI_STATUS +(*VISIT_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context + ); + +/** + Enumerate all entry in the database to execute a specified operation according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] Routine The pointer to function of a specified operation. + @param[in] Context The pointer to the context of a function. + + @retval EFI_SUCCESS Execute specified operation successfully. +**/ +EFI_STATUS +ForeachPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN VISIT_POLICY_ENTRY Routine, + IN VOID *Context + ); + +#endif diff --git a/NetworkPkg/Application/IpsecConfig/Helper.c b/NetworkPkg/Application/IpsecConfig/Helper.c new file mode 100644 index 0000000000..5013ad9b76 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Helper.c @@ -0,0 +1,419 @@ +/** @file + The assistant function implementation for IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfig.h" +#include "Helper.h" + +/** + Helper function called to change an input parameter in the string format to a number. + + @param[in] FlagStr The pointer to the flag string. + @param[in] Maximum Greatest value number. + @param[in, out] ValuePtr The pointer to the input parameter in string format. + @param[in] ByteCount The valid byte count + @param[in] Map The pointer to the STR2INT table. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[in] FormatMask The bit mask. + BIT 0 set indicates the value of a flag might be a number. + BIT 1 set indicates the value of a flag might be a string that needs to be looked up. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The input parameter can't be found. + @retval EFI_INVALID_PARAMETER The input parameter is an invalid input. +**/ +EFI_STATUS +GetNumber ( + IN CHAR16 *FlagStr, + IN UINT64 Maximum, + IN OUT VOID *ValuePtr, + IN UINTN ByteCount, + IN STR2INT *Map, + IN LIST_ENTRY *ParamPackage, + IN UINT32 FormatMask + ) +{ + EFI_STATUS Status; + UINT64 Value64; + BOOLEAN Converted; + UINTN Index; + CONST CHAR16 *ValueStr; + + ASSERT (FormatMask & (FORMAT_NUMBER | FORMAT_STRING)); + + Converted = FALSE; + Value64 = 0; + ValueStr = ShellCommandLineGetValue (ParamPackage, FlagStr); + + if (ValueStr == NULL) { + return EFI_NOT_FOUND; + } else { + // + // Try to convert to integer directly if MaybeNumber is TRUE. + // + if ((FormatMask & FORMAT_NUMBER) != 0) { + Value64 = StrToUInteger (ValueStr, &Status); + if (!EFI_ERROR (Status)) { + // + // Convert successfully. + // + if (Value64 > Maximum) { + // + // But the result is invalid + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + FlagStr, + ValueStr + ); + return EFI_INVALID_PARAMETER; + } + + Converted = TRUE; + } + } + + if (!Converted && ((FormatMask & FORMAT_STRING) != 0)) { + // + // Convert falied, so use String->Integer map. + // + Value64 = MapStringToInteger (ValueStr, Map); + if (Value64 == (UINT32) -1) { + // + // Cannot find the string in the map. + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + FlagStr, + ValueStr + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ACCEPT_PARAMETERS), mHiiHandle); + for (Index = 0; Map[Index].String != NULL; Index++) { + Print (L" %s", Map[Index].String); + } + + Print (L"\n"); + return EFI_INVALID_PARAMETER; + } + } + + CopyMem (ValuePtr, &Value64, ByteCount); + return EFI_SUCCESS; + } +} + +/** + Helper function called to convert a string containing an Ipv4 or Ipv6 Internet Protocol address + into a proper address for the EFI_IP_ADDRESS structure. + + @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address. + @param[out] Ip The pointer to the EFI_IP_ADDRESS structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EfiInetAddr2 ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS *Ip + ) +{ + EFI_STATUS Status; + + if ((Ptr == NULL) || (Ip == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Parse the input address as Ipv4 Address first. + // + Status = NetLibStrToIp4 (Ptr, &Ip->v4); + if (!EFI_ERROR (Status)) { + return Status; + } + + Status = NetLibStrToIp6 (Ptr, &Ip->v6); + return Status; +} + +/** + Helper function called to calculate the prefix length associated with the string + containing an Ipv4 or Ipv6 Internet Protocol address. + + @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address. + @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetAddrRange ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS_INFO *Addr + ) +{ + EFI_STATUS Status; + + if ((Ptr == NULL) || (Addr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = NetLibStrToIp4 (Ptr, &Addr->Address.v4); + if (!EFI_ERROR (Status)) { + if ((UINT32)(*Addr->Address.v4.Addr) == 0) { + Addr->PrefixLength = 0; + } else { + Addr->PrefixLength = 32; + } + return Status; + } + + Status = NetLibStrToIp6andPrefix (Ptr, &Addr->Address.v6, &Addr->PrefixLength); + if (!EFI_ERROR (Status) && (Addr->PrefixLength == 0xFF)) { + Addr->PrefixLength = 128; + } + + return Status; +} + +/** + Helper function called to calculate the port range associated with the string. + + @param[in] Ptr The pointer to the string containing a port and range. + @param[out] Port The pointer to the Port to contain the result. + @param[out] PortRange The pointer to the PortRange to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetPortRange ( + IN CHAR16 *Ptr, + OUT UINT16 *Port, + OUT UINT16 *PortRange + ) +{ + CHAR16 *BreakPtr; + CHAR16 Ch; + EFI_STATUS Status; + + for (BreakPtr = Ptr; (*BreakPtr != L'\0') && (*BreakPtr != L':'); BreakPtr++) { + ; + } + + Ch = *BreakPtr; + *BreakPtr = L'\0'; + *Port = (UINT16) StrToUInteger (Ptr, &Status); + *BreakPtr = Ch; + if (EFI_ERROR (Status)) { + return Status; + } + + *PortRange = 0; + if (*BreakPtr == L':') { + BreakPtr++; + *PortRange = (UINT16) StrToUInteger (BreakPtr, &Status); + if (EFI_ERROR (Status)) { + return Status; + } + + if (*PortRange < *Port) { + return EFI_INVALID_PARAMETER; + } + + *PortRange = (UINT16) (*PortRange - *Port); + } + + return EFI_SUCCESS; +} + +/** + Helper function called to transfer a string to an unsigned integer. + + @param[in] Str The pointer to the string. + @param[out] Status The operation status. + + @return The integer value of converted Str. +**/ +UINT64 +StrToUInteger ( + IN CONST CHAR16 *Str, + OUT EFI_STATUS *Status + ) +{ + UINT64 Value; + UINT64 NewValue; + CHAR16 *StrTail; + CHAR16 Char; + UINTN Base; + UINTN Len; + + Base = 10; + Value = 0; + *Status = EFI_ABORTED; + + // + // Skip leading white space. + // + while ((*Str != 0) && (*Str == ' ')) { + Str++; + } + // + // For NULL Str, just return. + // + if (*Str == 0) { + return 0; + } + // + // Skip white space in tail. + // + Len = StrLen (Str); + StrTail = (CHAR16 *) (Str + Len - 1); + while (*StrTail == ' ') { + *StrTail = 0; + StrTail--; + } + + Len = StrTail - Str + 1; + + // + // Check hex prefix '0x'. + // + if ((Len >= 2) && (*Str == '0') && ((*(Str + 1) == 'x') || (*(Str + 1) == 'X'))) { + Str += 2; + Len -= 2; + Base = 16; + } + + if (Len == 0) { + return 0; + } + // + // Convert the string to value. + // + for (; Str <= StrTail; Str++) { + + Char = *Str; + + if (Base == 16) { + if (RShiftU64 (Value, 60) != 0) { + // + // Overflow here x16. + // + return 0; + } + + NewValue = LShiftU64 (Value, 4); + } else { + if (RShiftU64 (Value, 61) != 0) { + // + // Overflow here x8. + // + return 0; + } + + NewValue = LShiftU64 (Value, 3); + Value = LShiftU64 (Value, 1); + NewValue += Value; + if (NewValue < Value) { + // + // Overflow here. + // + return 0; + } + } + + Value = NewValue; + + if ((Base == 16) && (Char >= 'a') && (Char <= 'f')) { + Char = (CHAR16) (Char - 'a' + 'A'); + } + + if ((Base == 16) && (Char >= 'A') && (Char <= 'F')) { + Value += (Char - 'A') + 10; + } else if ((Char >= '0') && (Char <= '9')) { + Value += (Char - '0'); + } else { + // + // Unexpected Char encountered. + // + return 0; + } + } + + *Status = EFI_SUCCESS; + return Value; +} + +/** + Helper function called to transfer a string to an unsigned integer according to the map table. + + @param[in] Str The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The integer value of converted Str. If not found, then return -1. +**/ +UINT32 +MapStringToInteger ( + IN CONST CHAR16 *Str, + IN STR2INT *Map + ) +{ + STR2INT *Item; + + for (Item = Map; Item->String != NULL; Item++) { + if (StrCmp (Item->String, Str) == 0) { + return Item->Integer; + } + } + + return (UINT32) -1; +} + +/** + Helper function called to transfer an unsigned integer to a string according to the map table. + + @param[in] Integer The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The converted Str. If not found, then return NULL. +**/ +CHAR16 * +MapIntegerToString ( + IN UINT32 Integer, + IN STR2INT *Map + ) +{ + STR2INT *Item; + + for (Item = Map; Item->String != NULL; Item++) { + if (Integer == Item->Integer) { + return Item->String; + } + } + + return NULL; +} diff --git a/NetworkPkg/Application/IpsecConfig/Helper.h b/NetworkPkg/Application/IpsecConfig/Helper.h new file mode 100644 index 0000000000..d893145a76 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Helper.h @@ -0,0 +1,143 @@ +/** @file + The assistant function declaration for IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _HELPER_H_ +#define _HELPER_H_ + +#define FORMAT_NUMBER 0x1 +#define FORMAT_STRING 0x2 + +/** + Helper function called to change input parameter in string format to number. + + @param[in] FlagStr The pointer to the flag string. + @param[in] Maximum most value number. + @param[in, out] ValuePtr The pointer to the input parameter in string format. + @param[in] ByteCount The valid byte count + @param[in] Map The pointer to the STR2INT table. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[in] FormatMask The bit mask. + BIT 0 set indicates the value of flag might be number. + BIT 1 set indicates the value of flag might be a string that needs to be looked up. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The input parameter can't be found. + @retval EFI_INVALID_PARAMETER The input parameter is an invalid input. +**/ +EFI_STATUS +GetNumber ( + IN CHAR16 *FlagStr, + IN UINT64 Maximum, + IN OUT VOID *ValuePtr, + IN UINTN ByteCount, + IN STR2INT *Map, + IN LIST_ENTRY *ParamPackage, + IN UINT32 FormatMask + ); + +/** + Helper function called to convert a string containing an (Ipv4) Internet Protocol dotted address + into a proper address for the EFI_IP_ADDRESS structure. + + @param[in] Ptr The pointer to the string containing an (Ipv4) Internet Protocol dotted address. + @param[out] Ip The pointer to the Ip address structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EfiInetAddr2 ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS *Ip + ); + +/** + Helper function called to calculate the prefix length associated with the string + containing an Ipv4 or Ipv6 Internet Protocol address. + + @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address. + @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetAddrRange ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS_INFO *Addr + ); + +/** + Helper function called to calculate the port range associated with the string. + + @param[in] Ptr The pointer to the string containing a port and range. + @param[out] Port The pointer to the Port to contain the result. + @param[out] PortRange The pointer to the PortRange to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetPortRange ( + IN CHAR16 *Ptr, + OUT UINT16 *Port, + OUT UINT16 *PortRange + ); + +/** + Helper function called to transfer a string to an unsigned integer. + + @param[in] Str The pointer to the string. + @param[out] Status The operation status. + + @return The integer value of a converted str. +**/ +UINT64 +StrToUInteger ( + IN CONST CHAR16 *Str, + OUT EFI_STATUS *Status + ); + +/** + Helper function called to transfer a string to an unsigned integer according to the map table. + + @param[in] Str The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The integer value of converted str. If not found, then return -1. +**/ +UINT32 +MapStringToInteger ( + IN CONST CHAR16 *Str, + IN STR2INT *Map + ); + +/** + Helper function called to transfer an unsigned integer to a string according to the map table. + + @param[in] Integer The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The converted str. If not found, then return NULL. +**/ +CHAR16 * +MapIntegerToString ( + IN UINT32 Integer, + IN STR2INT *Map + ); + +#endif diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.c b/NetworkPkg/Application/IpsecConfig/Indexer.c new file mode 100644 index 0000000000..1762bbeb58 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Indexer.c @@ -0,0 +1,248 @@ +/** @file + The implementation of construct ENTRY_INDEXER in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfig.h" +#include "Indexer.h" +#include "Helper.h" + +/** + Fill in SPD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully. +**/ +EFI_STATUS +ConstructSpdIndexer ( + IN OUT SPD_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + UINT64 Value64; + CONST CHAR16 *ValueStr; + + ValueStr = NULL; + + if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + } else { + ASSERT (FALSE); + } + + ASSERT (ValueStr != NULL); + + Value64 = StrToUInteger (ValueStr, &Status); + if (!EFI_ERROR (Status)) { + Indexer->Index = (UINTN) Value64; + Indexer->Name = NULL; + } else { + UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) Indexer->Name); + } + + return EFI_SUCCESS; +} + +/** + Fill in SAD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the SAD_ENTRY_INDEXER structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully. + @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list. +**/ +EFI_STATUS +ConstructSadIndexer ( + IN OUT SAD_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + UINT64 Value64; + CONST CHAR16 *ValueStr; + + ValueStr = NULL; + + if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + } else { + ASSERT (FALSE); + } + + ASSERT (ValueStr != NULL); + + Value64 = StrToUInteger (ValueStr, &Status); + if (!EFI_ERROR (Status)) { + Indexer->Index = (UINTN) Value64; + ZeroMem (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID)); + } else { + if ((!ShellCommandLineGetFlag (ParamPackage, L"--lookup-spi")) || + (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-ipsec-proto")) || + (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-dest"))) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--lookup-spi --lookup-ipsec-proto --lookup-dest" + ); + return EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lookup-spi", + (UINT32) -1, + &Indexer->SaId.Spi, + sizeof (UINT32), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + Status1 = GetNumber ( + L"--lookup-ipsec-proto", + 0, + &Indexer->SaId.Proto, + sizeof (EFI_IPSEC_PROTOCOL_TYPE), + mMapIpSecProtocol, + ParamPackage, + FORMAT_STRING + ); + + if (EFI_ERROR (Status) || EFI_ERROR (Status1)) { + return EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-dest"); + ASSERT (ValueStr != NULL); + + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &Indexer->SaId.DestAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--lookup-dest", + ValueStr + ); + return EFI_INVALID_PARAMETER; + } + } + + return EFI_SUCCESS; +} + +/** + Fill in PAD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the PAD_ENTRY_INDEXER structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in PAD_ENTRY_INDEXER successfully. + @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list. +**/ +EFI_STATUS +ConstructPadIndexer ( + IN OUT PAD_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + UINT64 Value64; + CONST CHAR16 *ValueStr; + + ValueStr = NULL; + + if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + } else { + ASSERT (FALSE); + } + + ASSERT (ValueStr != NULL); + + Value64 = StrToUInteger (ValueStr, &Status); + + if (!EFI_ERROR (Status)) { + Indexer->Index = (UINTN) Value64; + ZeroMem (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID)); + } else { + + if (ShellCommandLineGetFlag (ParamPackage, L"--lookup-peer-address")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-address"); + ASSERT (ValueStr != NULL); + + Indexer->PadId.PeerIdValid = FALSE; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &Indexer->PadId.Id.IpAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--lookup-peer-address", + ValueStr + ); + return EFI_INVALID_PARAMETER; + } + } else { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-id"); + if (ValueStr == NULL) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--lookup-peer-address --lookup-peer-id" + ); + return EFI_INVALID_PARAMETER; + } + + Indexer->PadId.PeerIdValid = TRUE; + StrnCpy ((CHAR16 *) Indexer->PadId.Id.PeerId, ValueStr, ARRAY_SIZE (Indexer->PadId.Id.PeerId) - 1); + } + } + + return EFI_SUCCESS; +} + +CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[] = { + (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSpdIndexer, + (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSadIndexer, + (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructPadIndexer +}; diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.h b/NetworkPkg/Application/IpsecConfig/Indexer.h new file mode 100644 index 0000000000..078f38a312 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Indexer.h @@ -0,0 +1,58 @@ +/** @file + The internal structure and function declaration to construct ENTRY_INDEXER in + IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _INDEXER_H_ +#define _INDEXER_H_ + +typedef struct { + UINT8 *Name; + UINTN Index; // Used only if Name is NULL. +} SPD_ENTRY_INDEXER; + +typedef struct { + EFI_IPSEC_SA_ID SaId; + UINTN Index; +} SAD_ENTRY_INDEXER; + +typedef struct { + EFI_IPSEC_PAD_ID PadId; + UINTN Index; +} PAD_ENTRY_INDEXER; + +typedef union { + SPD_ENTRY_INDEXER Spd; + SAD_ENTRY_INDEXER Sad; + PAD_ENTRY_INDEXER Pad; +} POLICY_ENTRY_INDEXER; + +/** + The prototype for the ConstructSpdIndexer()/ConstructSadIndexer()/ConstructPadIndexer(). + Fill in SPD_ENTRY_INDEXER/SAD_ENTRY_INDEXER/PAD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the POLICY_ENTRY_INDEXER union. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in POLICY_ENTRY_INDEXER successfully. +**/ +typedef +EFI_STATUS +(* CONSTRUCT_POLICY_ENTRY_INDEXER) ( + IN POLICY_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage +); + +extern CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[]; +#endif diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.c b/NetworkPkg/Application/IpsecConfig/IpSecConfig.c new file mode 100644 index 0000000000..8006d84860 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.c @@ -0,0 +1,809 @@ +/** @file + The main process for IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 +#include + +#include + +#include "IpSecConfig.h" +#include "Dump.h" +#include "Indexer.h" +#include "PolicyEntryOperation.h" +#include "Delete.h" +#include "Helper.h" + +// +// Used for ShellCommandLineParseEx only +// and to ensure user inputs are in valid format +// +SHELL_PARAM_ITEM mIpSecConfigParamList[] = { + { L"-p", TypeValue }, + { L"-a", TypeValue }, + { L"-i", TypeValue }, + { L"-e", TypeValue }, + { L"-d", TypeValue }, + { L"-f", TypeFlag }, + { L"-l", TypeFlag }, + { L"-enable", TypeFlag }, + { L"-disable", TypeFlag }, + { L"-status", TypeFlag }, + { L"-?", TypeFlag }, + + // + // SPD Selector + // + { L"--local", TypeValue }, + { L"--remote", TypeValue }, + { L"--proto", TypeValue }, + { L"--local-port", TypeValue }, + { L"--remote-port", TypeValue }, + { L"--icmp-type", TypeValue }, + { L"--icmp-code", TypeValue }, + + // + // SPD Data + // + { L"--name", TypeValue }, + { L"--packet-flag", TypeValue }, + { L"--action", TypeValue }, + { L"--lifebyte", TypeValue }, + { L"--lifetime-soft", TypeValue }, + { L"--lifetime", TypeValue }, + { L"--mode", TypeValue }, + { L"--tunnel-local", TypeValue }, + { L"--tunnel-remote", TypeValue }, + { L"--dont-fragment", TypeValue }, + { L"--ipsec-proto", TypeValue }, + { L"--auth-algo", TypeValue }, + { L"--encrypt-algo", TypeValue }, + + { L"--ext-sequence", TypeFlag }, + { L"--sequence-overflow", TypeFlag }, + { L"--fragment-check", TypeFlag }, + { L"--ext-sequence-", TypeFlag }, + { L"--sequence-overflow-", TypeFlag }, + { L"--fragment-check-", TypeFlag }, + + // + // SA ID + // --ipsec-proto + // + { L"--spi", TypeValue }, + { L"--dest", TypeValue }, + { L"--lookup-spi", TypeValue }, + { L"--lookup-ipsec-proto", TypeValue }, + { L"--lookup-dest", TypeValue }, + + // + // SA DATA + // --mode + // --auth-algo + // --encrypt-algo + // + { L"--sequence-number", TypeValue }, + { L"--antireplay-window", TypeValue }, + { L"--auth-key", TypeValue }, + { L"--encrypt-key", TypeValue }, + { L"--path-mtu", TypeValue }, + + // + // PAD ID + // + { L"--peer-id", TypeValue }, + { L"--peer-address", TypeValue }, + { L"--auth-proto", TypeValue }, + { L"--auth-method", TypeValue }, + { L"--ike-id", TypeValue }, + { L"--ike-id-", TypeValue }, + { L"--auth-data", TypeValue }, + { L"--revocation-data", TypeValue }, + { L"--lookup-peer-id", TypeValue }, + { L"--lookup-peer-address", TypeValue }, + + { NULL, TypeMax }, +}; + +// +// -P +// +STR2INT mMapPolicy[] = { + { L"SPD", IPsecConfigDataTypeSpd }, + { L"SAD", IPsecConfigDataTypeSad }, + { L"PAD", IPsecConfigDataTypePad }, + { NULL, 0 }, +}; + +// +// --proto +// +STR2INT mMapIpProtocol[] = { + { L"TCP", EFI_IP4_PROTO_TCP }, + { L"UDP", EFI_IP4_PROTO_UDP }, + { L"ICMP", EFI_IP4_PROTO_ICMP }, + { NULL, 0 }, +}; + +// +// --action +// +STR2INT mMapIpSecAction[] = { + { L"Bypass", EfiIPsecActionBypass }, + { L"Discard", EfiIPsecActionDiscard }, + { L"Protect", EfiIPsecActionProtect }, + { NULL, 0 }, +}; + +// +// --mode +// +STR2INT mMapIpSecMode[] = { + { L"Transport", EfiIPsecTransport }, + { L"Tunnel", EfiIPsecTunnel }, + { NULL, 0 }, +}; + +// +// --dont-fragment +// +STR2INT mMapDfOption[] = { + { L"clear", EfiIPsecTunnelClearDf }, + { L"set", EfiIPsecTunnelSetDf }, + { L"copy", EfiIPsecTunnelCopyDf }, + { NULL, 0 }, +}; + +// +// --ipsec-proto +// +STR2INT mMapIpSecProtocol[] = { + { L"AH", EfiIPsecAH }, + { L"ESP", EfiIPsecESP }, + { NULL, 0 }, +}; + +// +// --auth-algo +// +STR2INT mMapAuthAlgo[] = { + { L"NONE", EFI_IPSEC_AALG_NONE }, + { L"MD5HMAC", EFI_IPSEC_AALG_MD5HMAC }, + { L"SHA1HMAC", EFI_IPSEC_AALG_SHA1HMAC }, + { L"SHA2-256HMAC", EFI_IPSEC_AALG_SHA2_256HMAC }, + { L"SHA2-384HMAC", EFI_IPSEC_AALG_SHA2_384HMAC }, + { L"SHA2-512HMAC", EFI_IPSEC_AALG_SHA2_512HMAC }, + { L"AES-XCBC-MAC", EFI_IPSEC_AALG_AES_XCBC_MAC }, + { L"NULL", EFI_IPSEC_AALG_NULL }, + { NULL, 0 }, +}; + +// +// --encrypt-algo +// +STR2INT mMapEncAlgo[] = { + { L"NONE", EFI_IPSEC_EALG_NONE }, + { L"DESCBC", EFI_IPSEC_EALG_DESCBC }, + { L"3DESCBC", EFI_IPSEC_EALG_3DESCBC }, + { L"CASTCBC", EFI_IPSEC_EALG_CASTCBC }, + { L"BLOWFISHCBC", EFI_IPSEC_EALG_BLOWFISHCBC }, + { L"NULL", EFI_IPSEC_EALG_NULL }, + { L"AESCBC", EFI_IPSEC_EALG_AESCBC }, + { L"AESCTR", EFI_IPSEC_EALG_AESCTR }, + { L"AES-CCM-ICV8", EFI_IPSEC_EALG_AES_CCM_ICV8 }, + { L"AES-CCM-ICV12",EFI_IPSEC_EALG_AES_CCM_ICV12 }, + { L"AES-CCM-ICV16",EFI_IPSEC_EALG_AES_CCM_ICV16 }, + { L"AES-GCM-ICV8", EFI_IPSEC_EALG_AES_GCM_ICV8 }, + { L"AES-GCM-ICV12",EFI_IPSEC_EALG_AES_GCM_ICV12 }, + { L"AES-GCM-ICV16",EFI_IPSEC_EALG_AES_GCM_ICV16 }, + { NULL, 0 }, +}; + +// +// --auth-proto +// +STR2INT mMapAuthProto[] = { + { L"IKEv1", EfiIPsecAuthProtocolIKEv1 }, + { L"IKEv2", EfiIPsecAuthProtocolIKEv2 }, + { NULL, 0 }, +}; + +// +// --auth-method +// +STR2INT mMapAuthMethod[] = { + { L"PreSharedSecret", EfiIPsecAuthMethodPreSharedSecret }, + { L"Certificates", EfiIPsecAuthMethodCertificates }, + { NULL, 0 }, +}; + +EFI_IPSEC_PROTOCOL *mIpSec; +EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig; +EFI_HII_HANDLE mHiiHandle; +EFI_GUID mEfiIpSecConfigGuid = EFI_IPSEC_CONFIG_GUID; +CHAR16 mAppName[] = L"IpSecConfig"; + +// +// Used for IpSecConfigRetriveCheckListByName only to check the validation of user input +// +VAR_CHECK_ITEM mIpSecConfigVarCheckList[] = { + { L"-enable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-disable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-status", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-p", BIT(1), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + + { L"-a", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-i", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-d", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-e", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-l", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-f", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + + { L"-?", BIT(0), BIT(0), BIT(2)|BIT(1)|BIT(0), 0 }, + + // + // SPD Selector + // + { L"--local", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--remote", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--proto", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--local-port", 0, 0, BIT(2)|BIT(1), BIT(0) }, + { L"--remote-port", 0, 0, BIT(2)|BIT(1), BIT(0) }, + { L"--icmp-type", 0, 0, BIT(2)|BIT(1), BIT(1) }, + { L"--icmp-code", 0, 0, BIT(2)|BIT(1), BIT(1) }, + + // + // SPD Data + // + { L"--name", 0, 0, BIT(2), 0 }, + { L"--packet-flag", 0, 0, BIT(2), 0 }, + { L"--action", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--lifebyte", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--lifetime-soft", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--lifetime", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--mode", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--tunnel-local", 0, 0, BIT(2), 0 }, + { L"--tunnel-remote", 0, 0, BIT(2), 0 }, + { L"--dont-fragment", 0, 0, BIT(2), 0 }, + { L"--ipsec-proto", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--auth-algo", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--encrypt-algo", 0, 0, BIT(2)|BIT(1), 0 }, + + { L"--ext-sequence", 0, 0, BIT(2), BIT(2) }, + { L"--sequence-overflow", 0, 0, BIT(2), BIT(2) }, + { L"--fragment-check", 0, 0, BIT(2), BIT(2) }, + { L"--ext-sequence-", 0, 0, BIT(2), BIT(3) }, + { L"--sequence-overflow-", 0, 0, BIT(2), BIT(3) }, + { L"--fragment-check-", 0, 0, BIT(2), BIT(3) }, + + // + // SA ID + // --ipsec-proto + // + { L"--spi", 0, 0, BIT(1), 0 }, + { L"--dest", 0, 0, BIT(1), 0 }, + { L"--lookup-spi", 0, 0, BIT(1), 0 }, + { L"--lookup-ipsec-proto", 0, 0, BIT(1), 0 }, + { L"--lookup-dest", 0, 0, BIT(1), 0 }, + + // + // SA DATA + // --mode + // --auth-algo + // --encrypt-algo + // + { L"--sequence-number", 0, 0, BIT(1), 0 }, + { L"--antireplay-window", 0, 0, BIT(1), 0 }, + { L"--auth-key", 0, 0, BIT(1), 0 }, + { L"--encrypt-key", 0, 0, BIT(1), 0 }, + { L"--path-mtu", 0, 0, BIT(1), 0 }, + + // + // The example to add a PAD: + // "-A --peer-id Mike [--peer-address 10.23.2.2] --auth-proto IKE1/IKE2 + // --auth-method PreSharedSeceret/Certificate --ike-id + // --auth-data 343343 --revocation-data 2342432" + // The example to delete a PAD: + // "-D * --lookup-peer-id Mike [--lookup-peer-address 10.23.2.2]" + // "-D 1" + // The example to edit a PAD: + // "-E * --lookup-peer-id Mike --auth-method Certificate" + + // + // PAD ID + // + { L"--peer-id", 0, 0, BIT(0), BIT(4) }, + { L"--peer-address", 0, 0, BIT(0), BIT(5) }, + { L"--auth-proto", 0, 0, BIT(0), 0 }, + { L"--auth-method", 0, 0, BIT(0), 0 }, + { L"--IKE-ID", 0, 0, BIT(0), BIT(6) }, + { L"--IKE-ID-", 0, 0, BIT(0), BIT(7) }, + { L"--auth-data", 0, 0, BIT(0), 0 }, + { L"--revocation-data", 0, 0, BIT(0), 0 }, + { L"--lookup-peer-id", 0, 0, BIT(0), BIT(4) }, + { L"--lookup-peer-address",0, 0, BIT(0), BIT(5) }, + + { NULL, 0, 0, 0, 0 }, +}; + +/** + The function to allocate the proper sized buffer for various + EFI interfaces. + + @param[in, out] Status Current status. + @param[in, out] Buffer Current allocated buffer, or NULL. + @param[in] BufferSize Current buffer size needed + + @retval TRUE If the buffer was reallocated and the caller should try the API again. + @retval FALSE If the buffer was not reallocated successfully. +**/ +BOOLEAN +GrowBuffer ( + IN OUT EFI_STATUS *Status, + IN OUT VOID **Buffer, + IN UINTN BufferSize + ) +{ + BOOLEAN TryAgain; + + ASSERT (Status != NULL); + ASSERT (Buffer != NULL); + + // + // If this is an initial request, buffer will be null with a new buffer size. + // + if ((NULL == *Buffer) && (BufferSize != 0)) { + *Status = EFI_BUFFER_TOO_SMALL; + } + + // + // If the status code is "buffer too small", resize the buffer. + // + TryAgain = FALSE; + if (*Status == EFI_BUFFER_TOO_SMALL) { + + if (*Buffer != NULL) { + FreePool (*Buffer); + } + + *Buffer = AllocateZeroPool (BufferSize); + + if (*Buffer != NULL) { + TryAgain = TRUE; + } else { + *Status = EFI_OUT_OF_RESOURCES; + } + } + + // + // If there's an error, free the buffer. + // + if (!TryAgain && EFI_ERROR (*Status) && (*Buffer != NULL)) { + FreePool (*Buffer); + *Buffer = NULL; + } + + return TryAgain; +} + +/** + Function returns an array of handles that support the requested protocol + in a buffer allocated from a pool. + + @param[in] SearchType Specifies which handle(s) are to be returned. + @param[in] Protocol Provides the protocol to search by. + This parameter is only valid for SearchType ByProtocol. + + @param[in] SearchKey Supplies the search key depending on the SearchType. + @param[in, out] NoHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested array of + handles that support Protocol. + + @retval EFI_SUCCESS The resulting array of handles was returned. + @retval Others Other mistake case. +**/ +EFI_STATUS +LocateHandle ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *NoHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + ASSERT (NoHandles != NULL); + ASSERT (Buffer != NULL); + + // + // Initialize for GrowBuffer loop. + // + Status = EFI_SUCCESS; + *Buffer = NULL; + BufferSize = 50 * sizeof (EFI_HANDLE); + + // + // Call the real function. + // + while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) { + Status = gBS->LocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + } + + *NoHandles = BufferSize / sizeof (EFI_HANDLE); + if (EFI_ERROR (Status)) { + *NoHandles = 0; + } + + return Status; +} + +/** + Find the first instance of this protocol in the system and return its interface. + + @param[in] ProtocolGuid The guid of the protocol. + @param[out] Interface The pointer to the first instance of the protocol. + + @retval EFI_SUCCESS A protocol instance matching ProtocolGuid was found. + @retval Others A protocol instance matching ProtocolGuid was not found. +**/ +EFI_STATUS +LocateProtocol ( + IN EFI_GUID *ProtocolGuid, + OUT VOID **Interface + ) + +{ + EFI_STATUS Status; + UINTN NumberHandles; + UINTN Index; + EFI_HANDLE *Handles; + + *Interface = NULL; + Handles = NULL; + NumberHandles = 0; + + Status = LocateHandle (ByProtocol, ProtocolGuid, NULL, &NumberHandles, &Handles); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "LibLocateProtocol: Handle not found\n")); + return Status; + } + + for (Index = 0; Index < NumberHandles; Index++) { + ASSERT (Handles != NULL); + Status = gBS->HandleProtocol ( + Handles[Index], + ProtocolGuid, + Interface + ); + + if (!EFI_ERROR (Status)) { + break; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + + return Status; +} + +/** + Helper function called to check the conflicted flags. + + @param[in] CheckList The pointer to the VAR_CHECK_ITEM table. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS No conflicted flags. + @retval EFI_INVALID_PARAMETER The input parameter is erroroneous or there are some conflicted flags. +**/ +EFI_STATUS +IpSecConfigRetriveCheckListByName ( + IN VAR_CHECK_ITEM *CheckList, + IN LIST_ENTRY *ParamPackage +) +{ + + LIST_ENTRY *Node; + VAR_CHECK_ITEM *Item; + UINT32 Attribute1; + UINT32 Attribute2; + UINT32 Attribute3; + UINT32 Attribute4; + UINT32 Index; + + Attribute1 = 0; + Attribute2 = 0; + Attribute3 = 0; + Attribute4 = 0; + Index = 0; + Item = mIpSecConfigVarCheckList; + + if ((ParamPackage == NULL) || (CheckList == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Enumerate through the list of parameters that are input by user. + // + for (Node = GetFirstNode (ParamPackage); !IsNull (ParamPackage, Node); Node = GetNextNode (ParamPackage, Node)) { + if (((SHELL_PARAM_PACKAGE *) Node)->Name != NULL) { + // + // Enumerate the check list that defines the conflicted attributes of each flag. + // + for (; Item->VarName != NULL; Item++) { + if (StrCmp (((SHELL_PARAM_PACKAGE *) Node)->Name, Item->VarName) == 0) { + Index++; + if (Index == 1) { + Attribute1 = Item->Attribute1; + Attribute2 = Item->Attribute2; + Attribute3 = Item->Attribute3; + Attribute4 = Item->Attribute4; + } else { + Attribute1 &= Item->Attribute1; + Attribute2 |= Item->Attribute2; + Attribute3 &= Item->Attribute3; + Attribute4 |= Item->Attribute4; + if (Attribute1 != 0) { + return EFI_INVALID_PARAMETER; + } + + if (Attribute2 != 0) { + if ((Index == 2) && (StrCmp (Item->VarName, L"-p") == 0)) { + continue; + } + + return EFI_INVALID_PARAMETER; + } + + if (Attribute3 == 0) { + return EFI_INVALID_PARAMETER; + } + if (((Attribute4 & 0xFF) == 0x03) || ((Attribute4 & 0xFF) == 0x0C) || + ((Attribute4 & 0xFF) == 0x30) || ((Attribute4 & 0xFF) == 0xC0)) { + return EFI_INVALID_PARAMETER; + } + } + break; + } + } + + Item = mIpSecConfigVarCheckList; + } + } + + return EFI_SUCCESS; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for IpSecConfig application that parse the command line input and call an IpSecConfig process. + + @param[in] ImageHandle The image handle of this application. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +EFIAPI +InitializeIpSecConfig ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + UINT8 Value; + LIST_ENTRY *ParamPackage; + CONST CHAR16 *ValueStr; + CHAR16 *ProblemParam; + UINTN NonOptionCount; + + // + // Register our string package with HII and return the handle to it. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IpSecConfigStrings, NULL); + ASSERT (mHiiHandle != NULL); + + Status = ShellCommandLineParseEx (mIpSecConfigParamList, &ParamPackage, &ProblemParam, TRUE, FALSE); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, ProblemParam); + goto Done; + } + + Status = IpSecConfigRetriveCheckListByName (mIpSecConfigVarCheckList, ParamPackage); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_MISTAKEN_OPTIONS), mHiiHandle); + goto Done; + } + + Status = LocateProtocol (&gEfiIpSecConfigProtocolGuid, (VOID **) &mIpSecConfig); + if (EFI_ERROR (Status) || mIpSecConfig == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName); + goto Done; + } + + Status = LocateProtocol (&gEfiIpSecProtocolGuid, (VOID **) &mIpSec); + if (EFI_ERROR (Status) || mIpSec == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName); + goto Done; + } + + // + // Enable IPsec. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-enable")) { + if (!(mIpSec->DisabledFlag)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_ENABLE), mHiiHandle, mAppName); + } else { + // + // Set enable flag. + // + Value = IPSEC_STATUS_ENABLED; + Status = gRT->SetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Value), + &Value + ); + if (!EFI_ERROR (Status)) { + mIpSec->DisabledFlag = FALSE; + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_SUCCESS), mHiiHandle, mAppName); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_FAILED), mHiiHandle, mAppName); + } + } + + goto Done; + } + + // + // Disable IPsec. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-disable")) { + if (mIpSec->DisabledFlag) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_DISABLE), mHiiHandle, mAppName); + } else { + // + // Set disable flag; however, leave it to be disabled in the callback function of DisabledEvent. + // + gBS->SignalEvent (mIpSec->DisabledEvent); + if (mIpSec->DisabledFlag) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_SUCCESS), mHiiHandle, mAppName); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_FAILED), mHiiHandle, mAppName); + } + } + + goto Done; + } + + // + //IPsec Status. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-status")) { + if (mIpSec->DisabledFlag) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_DISABLE), mHiiHandle, mAppName); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_ENABLE), mHiiHandle, mAppName); + } + + goto Done; + } + + // + // Try to get policy database type. + // + DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) -1; + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-p"); + if (ValueStr != NULL) { + DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) MapStringToInteger (ValueStr, mMapPolicy); + if (DataType == -1) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle, mAppName, ValueStr); + goto Done; + } + } + + if (ShellCommandLineGetFlag (ParamPackage, L"-?")) { + switch (DataType) { + case (EFI_IPSEC_CONFIG_DATA_TYPE) -1: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_HELP), mHiiHandle); + break; + + case IPsecConfigDataTypeSpd: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SPD_HELP), mHiiHandle); + break; + + case IPsecConfigDataTypeSad: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SAD_HELP), mHiiHandle); + break; + + case IPsecConfigDataTypePad: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PAD_HELP), mHiiHandle); + break; + + default: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle); + break; + } + + goto Done; + } + + NonOptionCount = ShellCommandLineGetCount (); + if ((NonOptionCount - 1) > 0) { + ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32) (NonOptionCount - 1)); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_REDUNDANCY_MANY), mHiiHandle, mAppName, ValueStr); + goto Done; + } + + if (DataType == -1) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_DB), mHiiHandle, mAppName); + goto Done; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"-a")) { + Status = AddOrInsertPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + Status = AddOrInsertPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + Status = EditPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + Status = FlushOrDeletePolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-f")) { + Status = FlushOrDeletePolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-l")) { + Status = ListPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, mAppName); + goto Done; + } + +Done: + ShellCommandLineFreeVarList (ParamPackage); + HiiRemovePackages (mHiiHandle); + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.h b/NetworkPkg/Application/IpsecConfig/IpSecConfig.h new file mode 100644 index 0000000000..d1a7681012 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.h @@ -0,0 +1,123 @@ +/** @file + The internal structure and function declaration in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _IPSEC_CONFIG_H_ +#define _IPSEC_CONFIG_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define EFI_IPSEC_CONFIG_GUID \ + { \ + 0x9db0c3ac, 0xd9d2, 0x4f96, {0x9e, 0xd7, 0x6d, 0xa6, 0x12, 0xa4, 0xf3, 0x27} \ + } + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define IPSECCONFIG_STATUS_NAME L"IpSecStatus" + +#define BIT(x) (UINT32) (1 << (x)) + +#define IPSEC_STATUS_DISABLED 0x0 +#define IPSEC_STATUS_ENABLED 0x1 + +#define EFI_IP4_PROTO_ICMP 0x1 +#define EFI_IP4_PROTO_TCP 0x6 +#define EFI_IP4_PROTO_UDP 0x11 + +#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF +#define EFI_IPSEC_ANY_PORT 0 + +typedef struct _VAR_CHECK_ITEM { + CHAR16 *VarName; + UINT32 Attribute1; + UINT32 Attribute2; + UINT32 Attribute3; + UINT32 Attribute4; +} VAR_CHECK_ITEM; + +typedef struct _SHELL_PARAM_PACKAGE{ + LIST_ENTRY Link; + CHAR16 *Name; + ParamType Type; + CHAR16 *Value; + UINTN OriginalPosition; +} SHELL_PARAM_PACKAGE; + +typedef struct _STR2INT { + CHAR16 *String; + UINT32 Integer; +} STR2INT; + +extern EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig; +extern EFI_HII_HANDLE mHiiHandle; +extern CHAR16 mAppName[]; + +// +// -P +// +extern STR2INT mMapPolicy[]; + +// +// --proto +// +extern STR2INT mMapIpProtocol[]; + +// +// --action +// +extern STR2INT mMapIpSecAction[]; + +// +// --mode +// +extern STR2INT mMapIpSecMode[]; + +// +// --dont-fragment +// +extern STR2INT mMapDfOption[]; + +// +// --ipsec-proto +// +extern STR2INT mMapIpSecProtocol[]; +// +// --auth-algo +// +extern STR2INT mMapAuthAlgo[]; + +// +// --encrypt-algo +// +extern STR2INT mMapEncAlgo[]; +// +// --auth-proto +// +extern STR2INT mMapAuthProto[]; + +// +// --auth-method +// +extern STR2INT mMapAuthMethod[]; + +#endif diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf b/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf new file mode 100644 index 0000000000..1e0d4f44db --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf @@ -0,0 +1,61 @@ +## @file +# Component description file for IpSecConfig6 application. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = IpSecConfig + FILE_GUID = 0922E604-F5EC-42ef-980D-A35E9A2B1844 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeIpSecConfig + +[Sources] + IpSecConfigStrings.uni + IpSecConfig.c + IpSecConfig.h + Dump.c + Dump.h + Indexer.c + Indexer.h + Match.c + Match.h + Delete.h + Delete.c + Helper.c + Helper.h + ForEach.c + ForEach.h + PolicyEntryOperation.c + PolicyEntryOperation.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiApplicationEntryPoint + BaseMemoryLib + ShellLib + MemoryAllocationLib + DebugLib + HiiLib + NetLib + UefiLib + +[Protocols] + gEfiIpSecProtocolGuid ##CONSUMS + gEfiIpSecConfigProtocolGuid ##CONSUMS diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni new file mode 100644 index 0000000000..fb0e27d802 Binary files /dev/null and b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni differ diff --git a/NetworkPkg/Application/IpsecConfig/Match.c b/NetworkPkg/Application/IpsecConfig/Match.c new file mode 100644 index 0000000000..d6595ee8b8 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Match.c @@ -0,0 +1,163 @@ +/** @file + The implementation of match policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfig.h" +#include "Indexer.h" +#include "Match.h" + +/** + Private function to validate a buffer that should be filled with zero. + + @param[in] Memory The pointer to the buffer. + @param[in] Size The size of the buffer. + + @retval TRUE The memory is filled with zero. + @retval FALSE The memory isn't filled with zero. +**/ +BOOLEAN +IsMemoryZero ( + IN VOID *Memory, + IN UINTN Size + ) +{ + UINTN Index; + + for (Index = 0; Index < Size; Index++) { + if (*((UINT8 *) Memory + Index) != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Find the matching SPD with Indexer. + + @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + + @retval TRUE The matched SPD is found. + @retval FALSE The matched SPD is not found. +**/ +BOOLEAN +MatchSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN EFI_IPSEC_SPD_DATA *Data, + IN SPD_ENTRY_INDEXER *Indexer + ) +{ + BOOLEAN Match; + + Match = FALSE; + if (Indexer->Name != NULL) { + if ((Data->Name != NULL) && (AsciiStrCmp ((CHAR8 *) Indexer->Name, (CHAR8 *) Data->Name) == 0)) { + Match = TRUE; + } + } else { + if (Indexer->Index == 0) { + Match = TRUE; + } + + Indexer->Index--; + } + + return Match; +} + +/** + Find the matching SAD with Indexer. + + @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_SA_DATA structure. + @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + + @retval TRUE The matched SAD is found. + @retval FALSE The matched SAD is not found. +**/ +BOOLEAN +MatchSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN EFI_IPSEC_SA_DATA *Data, + IN SAD_ENTRY_INDEXER *Indexer + ) +{ + BOOLEAN Match; + + Match = FALSE; + if (!IsMemoryZero (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID))) { + Match = (BOOLEAN) (CompareMem (&Indexer->SaId, SaId, sizeof (EFI_IPSEC_SA_ID)) == 0); + } else { + if (Indexer->Index == 0) { + Match = TRUE; + } + Indexer->Index--; + } + + return Match; +} + +/** + Find the matching PAD with Indexer. + + @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + + @retval TRUE The matched PAD is found. + @retval FALSE The matched PAD is not found. +**/ +BOOLEAN +MatchPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN EFI_IPSEC_PAD_DATA *Data, + IN PAD_ENTRY_INDEXER *Indexer + ) +{ + BOOLEAN Match; + + Match = FALSE; + if (!IsMemoryZero (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID))) { + Match = (BOOLEAN) ((Indexer->PadId.PeerIdValid == PadId->PeerIdValid) && + ((PadId->PeerIdValid && + (StrCmp ( + (CONST CHAR16 *) Indexer->PadId.Id.PeerId, + (CONST CHAR16 *) PadId->Id.PeerId + ) == 0)) || + ((!PadId->PeerIdValid) && + (Indexer->PadId.Id.IpAddress.PrefixLength == PadId->Id.IpAddress.PrefixLength) && + (CompareMem ( + &Indexer->PadId.Id.IpAddress.Address, + &PadId->Id.IpAddress.Address, + sizeof (EFI_IP_ADDRESS) + ) == 0)))); + } else { + if (Indexer->Index == 0) { + Match = TRUE; + } + + Indexer->Index--; + } + + return Match; +} + +MATCH_POLICY_ENTRY mMatchPolicyEntry[] = { + (MATCH_POLICY_ENTRY) MatchSpdEntry, + (MATCH_POLICY_ENTRY) MatchSadEntry, + (MATCH_POLICY_ENTRY) MatchPadEntry +}; + diff --git a/NetworkPkg/Application/IpsecConfig/Match.h b/NetworkPkg/Application/IpsecConfig/Match.h new file mode 100644 index 0000000000..1d73c5cfbc --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/Match.h @@ -0,0 +1,41 @@ +/** @file + The internal structure and function declaration of + match policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _MATCH_H_ +#define _MATCH_H_ + +/** + The prototype for the MatchSpdEntry()/MatchSadEntry()/MatchPadEntry(). + The functionality is to find the matching SPD/SAD/PAD with Indexer. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union. + @param[in] Data The pointer to corresponding Data. + @param[in] Indexer The pointer to the POLICY_ENTRY_INDEXER union. + + @retval TRUE The matched SPD/SAD/PAD is found. + @retval FALSE The matched SPD/SAD/PAD is not found. +**/ +typedef +BOOLEAN +(* MATCH_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN POLICY_ENTRY_INDEXER *Indexer + ); + +extern MATCH_POLICY_ENTRY mMatchPolicyEntry[]; + +#endif diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c new file mode 100644 index 0000000000..ddfbb4c504 --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c @@ -0,0 +1,2016 @@ +/** @file + The implementation of policy entry operation function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfig.h" +#include "Indexer.h" +#include "Match.h" +#include "Helper.h" +#include "ForEach.h" +#include "PolicyEntryOperation.h" + +/** + Fill in EFI_IPSEC_SPD_SELECTOR through ParamPackage list. + + @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[in, out] ParamPackage The pointer to the Mask. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreateSpdSelector ( + OUT EFI_IPSEC_SPD_SELECTOR *Selector, + IN LIST_ENTRY *ParamPackage, + IN OUT UINT32 *Mask + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + CONST CHAR16 *ValueStr; + + Status = EFI_SUCCESS; + ReturnStatus = EFI_SUCCESS; + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local"); + if (ValueStr != NULL) { + Selector->LocalAddressCount = 1; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->LocalAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--local", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= LOCAL; + } + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote"); + if (ValueStr != NULL) { + Selector->RemoteAddressCount = 1; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->RemoteAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--remote", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= REMOTE; + } + } + + Selector->NextLayerProtocol = EFI_IPSEC_ANY_PROTOCOL; + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + Status = GetNumber ( + L"--proto", + (UINT16) -1, + &Selector->NextLayerProtocol, + sizeof (UINT16), + mMapIpProtocol, + ParamPackage, + FORMAT_NUMBER | FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Selector->LocalPort = EFI_IPSEC_ANY_PORT; + Selector->RemotePort = EFI_IPSEC_ANY_PORT; + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local-port"); + if (ValueStr != NULL) { + Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->LocalPort, &Selector->LocalPortRange); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--local-port", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= LOCAL_PORT; + } + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote-port"); + if (ValueStr != NULL) { + Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->RemotePort, &Selector->RemotePortRange); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--remote-port", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= REMOTE_PORT; + } + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + Status = GetNumber ( + L"--icmp-type", + (UINT8) -1, + &Selector->LocalPort, + sizeof (UINT16), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= ICMP_TYPE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + Status = GetNumber ( + L"--icmp-code", + (UINT8) -1, + &Selector->RemotePort, + sizeof (UINT16), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= ICMP_CODE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + return ReturnStatus; +} + +/** + Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA through ParamPackage list. + + @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[out] Data The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreateSpdEntry ( + OUT EFI_IPSEC_SPD_SELECTOR **Selector, + OUT EFI_IPSEC_SPD_DATA **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + CONST CHAR16 *ValueStr; + UINTN DataSize; + + Status = EFI_SUCCESS; + *Mask = 0; + + *Selector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR) + 2 * sizeof (EFI_IP_ADDRESS_INFO)); + ASSERT (*Selector != NULL); + + (*Selector)->LocalAddress = (EFI_IP_ADDRESS_INFO *) (*Selector + 1); + (*Selector)->RemoteAddress = (*Selector)->LocalAddress + 1; + + ReturnStatus = CreateSpdSelector (*Selector, ParamPackage, Mask); + + // + // SPD DATA + // NOTE: Allocate enough memory and add padding for different arch. + // + DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SPD_DATA)); + DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_PROCESS_POLICY)); + DataSize += sizeof (EFI_IPSEC_TUNNEL_OPTION); + + *Data = AllocateZeroPool (DataSize); + ASSERT (*Data != NULL); + + (*Data)->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER ( + (*Data + 1), + sizeof (UINTN) + ); + (*Data)->ProcessingPolicy->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ( + ((*Data)->ProcessingPolicy + 1), + sizeof (UINTN) + ); + + + // + // Convert user imput from string to integer, and fill in the Name in EFI_IPSEC_SPD_DATA. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--name"); + if (ValueStr != NULL) { + UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) (*Data)->Name); + *Mask |= NAME; + } + + // + // Convert user imput from string to integer, and fill in the PackageFlag in EFI_IPSEC_SPD_DATA. + // + Status = GetNumber ( + L"--packet-flag", + (UINT8) -1, + &(*Data)->PackageFlag, + sizeof (UINT32), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= PACKET_FLAG; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the Action in EFI_IPSEC_SPD_DATA. + // + Status = GetNumber ( + L"--action", + (UINT8) -1, + &(*Data)->Action, + sizeof (UINT32), + mMapIpSecAction, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= ACTION; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the ExtSeqNum in EFI_IPSEC_SPD_DATA. + // + if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence")) { + (*Data)->ProcessingPolicy->ExtSeqNum = TRUE; + *Mask |= EXT_SEQUENCE; + } else if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence-")) { + (*Data)->ProcessingPolicy->ExtSeqNum = FALSE; + *Mask |= EXT_SEQUENCE; + } + + // + // Convert user imput from string to integer, and fill in the SeqOverflow in EFI_IPSEC_SPD_DATA. + // + if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow")) { + (*Data)->ProcessingPolicy->SeqOverflow = TRUE; + *Mask |= SEQUENCE_OVERFLOW; + } else if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow-")) { + (*Data)->ProcessingPolicy->SeqOverflow = FALSE; + *Mask |= SEQUENCE_OVERFLOW; + } + + // + // Convert user imput from string to integer, and fill in the FragCheck in EFI_IPSEC_SPD_DATA. + // + if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check")) { + (*Data)->ProcessingPolicy->FragCheck = TRUE; + *Mask |= FRAGMENT_CHECK; + } else if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check-")) { + (*Data)->ProcessingPolicy->FragCheck = FALSE; + *Mask |= FRAGMENT_CHECK; + } + + // + // Convert user imput from string to integer, and fill in the ProcessingPolicy in EFI_IPSEC_SPD_DATA. + // + Status = GetNumber ( + L"--lifebyte", + (UINT64) -1, + &(*Data)->ProcessingPolicy->SaLifetime.ByteCount, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFEBYTE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime", + (UINT64) -1, + &(*Data)->ProcessingPolicy->SaLifetime.HardLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME; + } + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime-soft", + (UINT64) -1, + &(*Data)->ProcessingPolicy->SaLifetime.SoftLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME_SOFT; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + (*Data)->ProcessingPolicy->Mode = EfiIPsecTransport; + Status = GetNumber ( + L"--mode", + 0, + &(*Data)->ProcessingPolicy->Mode, + sizeof (UINT32), + mMapIpSecMode, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= MODE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-local"); + if (ValueStr != NULL) { + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->LocalTunnelAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--tunnel-local", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= TUNNEL_LOCAL; + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-remote"); + if (ValueStr != NULL) { + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->RemoteTunnelAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--tunnel-remote", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= TUNNEL_REMOTE; + } + } + + (*Data)->ProcessingPolicy->TunnelOption->DF = EfiIPsecTunnelCopyDf; + Status = GetNumber ( + L"--dont-fragment", + 0, + &(*Data)->ProcessingPolicy->TunnelOption->DF, + sizeof (UINT32), + mMapDfOption, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= DONT_FRAGMENT; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + (*Data)->ProcessingPolicy->Proto = EfiIPsecESP; + Status = GetNumber ( + L"--ipsec-proto", + 0, + &(*Data)->ProcessingPolicy->Proto, + sizeof (UINT32), + mMapIpSecProtocol, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= IPSEC_PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--encrypt-algo", + 0, + &(*Data)->ProcessingPolicy->EncAlgoId, + sizeof (UINT8), + mMapEncAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= ENCRYPT_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--auth-algo", + 0, + &(*Data)->ProcessingPolicy->AuthAlgoId, + sizeof (UINT8), + mMapAuthAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Cannot check Mode against EfiIPsecTunnel, because user may want to change tunnel_remote only so the Mode is not set. + // + if ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE | DONT_FRAGMENT)) == 0) { + (*Data)->ProcessingPolicy->TunnelOption = NULL; + } + + if ((*Mask & (EXT_SEQUENCE | SEQUENCE_OVERFLOW | FRAGMENT_CHECK | LIFEBYTE | + LIFETIME_SOFT | LIFETIME | MODE | TUNNEL_LOCAL | TUNNEL_REMOTE | + DONT_FRAGMENT | IPSEC_PROTO | AUTH_ALGO | ENCRYPT_ALGO)) == 0) { + if ((*Data)->Action != EfiIPsecActionProtect) { + // + // User may not provide additional parameter for Protect action, so we cannot simply set ProcessingPolicy to NULL. + // + (*Data)->ProcessingPolicy = NULL; + } + } + + if (CreateNew) { + if ((*Mask & (LOCAL | REMOTE | PROTO | ACTION)) != (LOCAL | REMOTE | PROTO | ACTION)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--local --remote --proto --action" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if (((*Data)->Action == EfiIPsecActionProtect) && + ((*Data)->ProcessingPolicy->Mode == EfiIPsecTunnel) && + ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE))) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--tunnel-local --tunnel-remote" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } + + return ReturnStatus; +} + +/** + Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA through ParamPackage list. + + @param[out] SaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[out] Data The pointer to the EFI_IPSEC_SA_DATA structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreateSadEntry ( + OUT EFI_IPSEC_SA_ID **SaId, + OUT EFI_IPSEC_SA_DATA **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINTN AuthKeyLength; + UINTN EncKeyLength; + CONST CHAR16 *ValueStr; + UINTN DataSize; + + Status = EFI_SUCCESS; + ReturnStatus = EFI_SUCCESS; + *Mask = 0; + AuthKeyLength = 0; + EncKeyLength = 0; + + *SaId = AllocateZeroPool (sizeof (EFI_IPSEC_SA_ID)); + ASSERT (*SaId != NULL); + + // + // Convert user imput from string to integer, and fill in the Spi in EFI_IPSEC_SA_ID. + // + Status = GetNumber (L"--spi", (UINT32) -1, &(*SaId)->Spi, sizeof (UINT32), NULL, ParamPackage, FORMAT_NUMBER); + if (!EFI_ERROR (Status)) { + *Mask |= SPI; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the Proto in EFI_IPSEC_SA_ID. + // + Status = GetNumber ( + L"--ipsec-proto", + 0, + &(*SaId)->Proto, + sizeof (EFI_IPSEC_PROTOCOL_TYPE), + mMapIpSecProtocol, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= IPSEC_PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--dest"); + if (ValueStr != NULL) { + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*SaId)->DestAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--dest", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= DEST; + } + } + + // + // Convert user imput from string to integer, and fill in EFI_IPSEC_SA_DATA. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key"); + if (ValueStr != NULL) { + AuthKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16); + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key"); + if (ValueStr != NULL) { + EncKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16); + } + + // + // EFI_IPSEC_SA_DATA: + // +------------ + // | EFI_IPSEC_SA_DATA + // +----------------------- + // | AuthKey + // +------------------------- + // | EncKey + // +------------------------- + // | SpdSelector + // + // Notes: To make sure the address alignment add padding after each data if needed. + // + DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA)); + DataSize = ALIGN_VARIABLE (DataSize + AuthKeyLength); + DataSize = ALIGN_VARIABLE (DataSize + EncKeyLength); + DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_SPD_SELECTOR)); + DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IP_ADDRESS_INFO)); + DataSize += sizeof (EFI_IP_ADDRESS_INFO); + + + + *Data = AllocateZeroPool (DataSize); + ASSERT (*Data != NULL); + + (*Data)->ManualSet = TRUE; + (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER (((*Data) + 1), sizeof (UINTN)); + (*Data)->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.AuthKey + AuthKeyLength), + sizeof (UINTN) + ); + (*Data)->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ( + ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.EncKey + EncKeyLength), + sizeof (UINTN) + ); + (*Data)->SpdSelector->LocalAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER ( + ((UINT8 *) (*Data)->SpdSelector + sizeof (EFI_IPSEC_SPD_SELECTOR)), + sizeof (UINTN)); + (*Data)->SpdSelector->RemoteAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER ( + (*Data)->SpdSelector->LocalAddress + 1, + sizeof (UINTN) + ); + + (*Data)->Mode = EfiIPsecTransport; + Status = GetNumber ( + L"--mode", + 0, + &(*Data)->Mode, + sizeof (EFI_IPSEC_MODE), + mMapIpSecMode, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= MODE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // According to RFC 4303-3.3.3. The first packet sent using a given SA + // will contain a sequence number of 1. + // + (*Data)->SNCount = 1; + Status = GetNumber ( + L"--sequence-number", + (UINT64) -1, + &(*Data)->SNCount, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= SEQUENCE_NUMBER; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + (*Data)->AntiReplayWindows = 0; + Status = GetNumber ( + L"--antireplay-window", + (UINT8) -1, + &(*Data)->AntiReplayWindows, + sizeof (UINT8), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= SEQUENCE_NUMBER; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--encrypt-algo", + 0, + &(*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId, + sizeof (UINT8), + mMapEncAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= ENCRYPT_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key"); + if (ValueStr != NULL ) { + (*Data)->AlgoInfo.EspAlgoInfo.EncKeyLength = EncKeyLength; + CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.EncKey, ValueStr, EncKeyLength); + *Mask |= ENCRYPT_KEY; + } else { + (*Data)->AlgoInfo.EspAlgoInfo.EncKey = NULL; + } + + Status = GetNumber ( + L"--auth-algo", + 0, + &(*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId, + sizeof (UINT8), + mMapAuthAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key"); + if (ValueStr != NULL) { + (*Data)->AlgoInfo.EspAlgoInfo.AuthKeyLength = AuthKeyLength; + CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.AuthKey, ValueStr, AuthKeyLength); + *Mask |= AUTH_KEY; + } else { + (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = NULL; + } + + Status = GetNumber ( + L"--lifebyte", + (UINT64) -1, + &(*Data)->SaLifetime.ByteCount, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFEBYTE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime", + (UINT64) -1, + &(*Data)->SaLifetime.HardLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime-soft", + (UINT64) -1, + &(*Data)->SaLifetime.SoftLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME_SOFT; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--path-mtu", + (UINT32) -1, + &(*Data)->PathMTU, + sizeof (UINT32), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= PATH_MTU; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + ReturnStatus = CreateSpdSelector ((*Data)->SpdSelector, ParamPackage, Mask); + + if (CreateNew) { + if ((*Mask & (SPI | IPSEC_PROTO | DEST)) != (SPI | IPSEC_PROTO | DEST)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--spi --ipsec-proto --dest" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + if ((*SaId)->Proto == EfiIPsecAH) { + if ((*Mask & AUTH_ALGO) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--auth-algo" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != EFI_IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--auth-key" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } else { + if ((*Mask & ENCRYPT_ALGO) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if ((*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (*Mask & ENCRYPT_KEY) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-key" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } + } + } + + return ReturnStatus; +} + +/** + Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA through ParamPackage list. + + @param[out] PadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[out] Data The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreatePadEntry ( + OUT EFI_IPSEC_PAD_ID **PadId, + OUT EFI_IPSEC_PAD_DATA **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + EFI_FILE_HANDLE FileHandle; + UINT64 FileSize; + UINTN AuthDataLength; + UINTN RevocationDataLength; + UINTN DataLength; + UINTN Index; + CONST CHAR16 *ValueStr; + UINTN DataSize; + + Status = EFI_SUCCESS; + ReturnStatus = EFI_SUCCESS; + *Mask = 0; + AuthDataLength = 0; + RevocationDataLength = 0; + + *PadId = AllocateZeroPool (sizeof (EFI_IPSEC_PAD_ID)); + ASSERT (*PadId != NULL); + + // + // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_ID. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-address"); + if (ValueStr != NULL) { + (*PadId)->PeerIdValid = FALSE; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &(*PadId)->Id.IpAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--peer-address", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= PEER_ADDRESS; + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-id"); + if (ValueStr != NULL) { + (*PadId)->PeerIdValid = TRUE; + StrnCpy ((CHAR16 *) (*PadId)->Id.PeerId, ValueStr, ARRAY_SIZE ((*PadId)->Id.PeerId) - 1); + *Mask |= PEER_ID; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data"); + if (ValueStr != NULL) { + if (ValueStr[0] == L'@') { + // + // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat" + // + Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + Status = ShellGetFileSize (FileHandle, &FileSize); + ShellCloseFile (&FileHandle); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + AuthDataLength = (UINTN) FileSize; + } + } + } else { + AuthDataLength = StrLen (ValueStr); + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data"); + if (ValueStr != NULL) { + RevocationDataLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16); + } + + // + // Allocate Buffer for Data. Add padding after each struct to make sure the alignment + // in different Arch. + // + DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA)); + DataSize = ALIGN_VARIABLE (DataSize + AuthDataLength); + DataSize += RevocationDataLength; + + *Data = AllocateZeroPool (DataSize); + ASSERT (*Data != NULL); + + (*Data)->AuthData = (VOID *) ALIGN_POINTER ((*Data + 1), sizeof (UINTN)); + (*Data)->RevocationData = (VOID *) ALIGN_POINTER (((UINT8 *) (*Data + 1) + AuthDataLength), sizeof (UINTN)); + (*Data)->AuthProtocol = EfiIPsecAuthProtocolIKEv1; + + // + // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_DATA. + // + Status = GetNumber ( + L"--auth-proto", + 0, + &(*Data)->AuthProtocol, + sizeof (EFI_IPSEC_AUTH_PROTOCOL_TYPE), + mMapAuthProto, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--auth-method", + 0, + &(*Data)->AuthMethod, + sizeof (EFI_IPSEC_AUTH_METHOD), + mMapAuthMethod, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_METHOD; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id")) { + (*Data)->IkeIdFlag = TRUE; + *Mask |= IKE_ID; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id-")) { + (*Data)->IkeIdFlag = FALSE; + *Mask |= IKE_ID; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data"); + if (ValueStr != NULL) { + if (ValueStr[0] == L'@') { + // + // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat" + // + + Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + (*Data)->AuthData = NULL; + } else { + DataLength = AuthDataLength; + Status = ShellReadFile (FileHandle, &DataLength, (*Data)->AuthData); + ShellCloseFile (&FileHandle); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + (*Data)->AuthData = NULL; + } else { + ASSERT (DataLength == AuthDataLength); + *Mask |= AUTH_DATA; + } + } + } else { + for (Index = 0; Index < AuthDataLength; Index++) { + ((CHAR8 *) (*Data)->AuthData)[Index] = (CHAR8) ValueStr[Index]; + } + (*Data)->AuthDataSize = AuthDataLength; + *Mask |= AUTH_DATA; + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data"); + if (ValueStr != NULL) { + CopyMem ((*Data)->RevocationData, ValueStr, RevocationDataLength); + (*Data)->RevocationDataSize = RevocationDataLength; + *Mask |= REVOCATION_DATA; + } else { + (*Data)->RevocationData = NULL; + } + + if (CreateNew) { + if ((*Mask & (PEER_ID | PEER_ADDRESS)) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--peer-id --peer-address" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if ((*Mask & (AUTH_METHOD | AUTH_DATA)) != (AUTH_METHOD | AUTH_DATA)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--auth-method --auth-data" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } + + return ReturnStatus; +} + +CREATE_POLICY_ENTRY mCreatePolicyEntry[] = { + (CREATE_POLICY_ENTRY) CreateSpdEntry, + (CREATE_POLICY_ENTRY) CreateSadEntry, + (CREATE_POLICY_ENTRY) CreatePadEntry +}; + +/** + Combine old SPD entry with new SPD entry. + + @param[in, out] OldSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in, out] OldData The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] NewSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] NewData The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CombineSpdEntry ( + IN OUT EFI_IPSEC_SPD_SELECTOR *OldSelector, + IN OUT EFI_IPSEC_SPD_DATA *OldData, + IN EFI_IPSEC_SPD_SELECTOR *NewSelector, + IN EFI_IPSEC_SPD_DATA *NewData, + IN UINT32 Mask, + OUT BOOLEAN *CreateNew + ) +{ + + // + // Process Selector + // + *CreateNew = FALSE; + if ((Mask & LOCAL) == 0) { + NewSelector->LocalAddressCount = OldSelector->LocalAddressCount; + NewSelector->LocalAddress = OldSelector->LocalAddress; + } else if ((NewSelector->LocalAddressCount != OldSelector->LocalAddressCount) || + (CompareMem (NewSelector->LocalAddress, OldSelector->LocalAddress, NewSelector->LocalAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) { + *CreateNew = TRUE; + } + + if ((Mask & REMOTE) == 0) { + NewSelector->RemoteAddressCount = OldSelector->RemoteAddressCount; + NewSelector->RemoteAddress = OldSelector->RemoteAddress; + } else if ((NewSelector->RemoteAddressCount != OldSelector->RemoteAddressCount) || + (CompareMem (NewSelector->RemoteAddress, OldSelector->RemoteAddress, NewSelector->RemoteAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) { + *CreateNew = TRUE; + } + + if ((Mask & PROTO) == 0) { + NewSelector->NextLayerProtocol = OldSelector->NextLayerProtocol; + } else if (NewSelector->NextLayerProtocol != OldSelector->NextLayerProtocol) { + *CreateNew = TRUE; + } + + switch (NewSelector->NextLayerProtocol) { + case EFI_IP4_PROTO_TCP: + case EFI_IP4_PROTO_UDP: + if ((Mask & LOCAL_PORT) == 0) { + NewSelector->LocalPort = OldSelector->LocalPort; + NewSelector->LocalPortRange = OldSelector->LocalPortRange; + } else if ((NewSelector->LocalPort != OldSelector->LocalPort) || + (NewSelector->LocalPortRange != OldSelector->LocalPortRange)) { + *CreateNew = TRUE; + } + + if ((Mask & REMOTE_PORT) == 0) { + NewSelector->RemotePort = OldSelector->RemotePort; + NewSelector->RemotePortRange = OldSelector->RemotePortRange; + } else if ((NewSelector->RemotePort != OldSelector->RemotePort) || + (NewSelector->RemotePortRange != OldSelector->RemotePortRange)) { + *CreateNew = TRUE; + } + break; + + case EFI_IP4_PROTO_ICMP: + if ((Mask & ICMP_TYPE) == 0) { + NewSelector->LocalPort = OldSelector->LocalPort; + } else if (NewSelector->LocalPort != OldSelector->LocalPort) { + *CreateNew = TRUE; + } + + if ((Mask & ICMP_CODE) == 0) { + NewSelector->RemotePort = OldSelector->RemotePort; + } else if (NewSelector->RemotePort != OldSelector->RemotePort) { + *CreateNew = TRUE; + } + break; + } + // + // Process Data + // + if ((Mask & NAME) != 0) { + AsciiStrCpy ((CHAR8 *) OldData->Name, (CHAR8 *) NewData->Name); + } + + if ((Mask & PACKET_FLAG) != 0) { + OldData->PackageFlag = NewData->PackageFlag; + } + + if ((Mask & ACTION) != 0) { + OldData->Action = NewData->Action; + } + + if (OldData->Action != EfiIPsecActionProtect) { + OldData->ProcessingPolicy = NULL; + } else { + // + // Protect + // + if (OldData->ProcessingPolicy == NULL) { + // + // Just point to new data if originally NULL. + // + OldData->ProcessingPolicy = NewData->ProcessingPolicy; + if (OldData->ProcessingPolicy->Mode == EfiIPsecTunnel && + (Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE) + ) { + // + // Change to Protect action and Tunnel mode, but without providing local/remote tunnel address. + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--tunnel-local --tunnel-remote" + ); + return EFI_INVALID_PARAMETER; + } + } else { + // + // Modify some of the data. + // + if ((Mask & EXT_SEQUENCE) != 0) { + OldData->ProcessingPolicy->ExtSeqNum = NewData->ProcessingPolicy->ExtSeqNum; + } + + if ((Mask & SEQUENCE_OVERFLOW) != 0) { + OldData->ProcessingPolicy->SeqOverflow = NewData->ProcessingPolicy->SeqOverflow; + } + + if ((Mask & FRAGMENT_CHECK) != 0) { + OldData->ProcessingPolicy->FragCheck = NewData->ProcessingPolicy->FragCheck; + } + + if ((Mask & LIFEBYTE) != 0) { + OldData->ProcessingPolicy->SaLifetime.ByteCount = NewData->ProcessingPolicy->SaLifetime.ByteCount; + } + + if ((Mask & LIFETIME_SOFT) != 0) { + OldData->ProcessingPolicy->SaLifetime.SoftLifetime = NewData->ProcessingPolicy->SaLifetime.SoftLifetime; + } + + if ((Mask & LIFETIME) != 0) { + OldData->ProcessingPolicy->SaLifetime.HardLifetime = NewData->ProcessingPolicy->SaLifetime.HardLifetime; + } + + if ((Mask & MODE) != 0) { + OldData->ProcessingPolicy->Mode = NewData->ProcessingPolicy->Mode; + } + + if ((Mask & IPSEC_PROTO) != 0) { + OldData->ProcessingPolicy->Proto = NewData->ProcessingPolicy->Proto; + } + + if ((Mask & AUTH_ALGO) != 0) { + OldData->ProcessingPolicy->AuthAlgoId = NewData->ProcessingPolicy->AuthAlgoId; + } + + if ((Mask & ENCRYPT_ALGO) != 0) { + OldData->ProcessingPolicy->EncAlgoId = NewData->ProcessingPolicy->EncAlgoId; + } + + if (OldData->ProcessingPolicy->Mode != EfiIPsecTunnel) { + OldData->ProcessingPolicy->TunnelOption = NULL; + } else { + if (OldData->ProcessingPolicy->TunnelOption == NULL) { + // + // Set from Transport mode to Tunnel mode, should ensure TUNNEL_LOCAL & TUNNEL_REMOTE both exists. + // + if ((Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--tunnel-local --tunnel-remote" + ); + return EFI_INVALID_PARAMETER; + } + + OldData->ProcessingPolicy->TunnelOption = NewData->ProcessingPolicy->TunnelOption; + } else { + if ((Mask & TUNNEL_LOCAL) != 0) { + CopyMem ( + &OldData->ProcessingPolicy->TunnelOption->LocalTunnelAddress, + &NewData->ProcessingPolicy->TunnelOption->LocalTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + + if ((Mask & TUNNEL_REMOTE) != 0) { + CopyMem ( + &OldData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, + &NewData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + + if ((Mask & DONT_FRAGMENT) != 0) { + OldData->ProcessingPolicy->TunnelOption->DF = NewData->ProcessingPolicy->TunnelOption->DF; + } + } + } + } + } + + return EFI_SUCCESS; +} + +/** + Combine old SAD entry with new SAD entry. + + @param[in, out] OldSaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in, out] OldData The pointer to the EFI_IPSEC_SA_DATA structure. + @param[in] NewSaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in] NewData The pointer to the EFI_IPSEC_SA_DATA structure. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CombineSadEntry ( + IN OUT EFI_IPSEC_SA_ID *OldSaId, + IN OUT EFI_IPSEC_SA_DATA *OldData, + IN EFI_IPSEC_SA_ID *NewSaId, + IN EFI_IPSEC_SA_DATA *NewData, + IN UINT32 Mask, + OUT BOOLEAN *CreateNew + ) +{ + + *CreateNew = FALSE; + + if ((Mask & SPI) == 0) { + NewSaId->Spi = OldSaId->Spi; + } else if (NewSaId->Spi != OldSaId->Spi) { + *CreateNew = TRUE; + } + + if ((Mask & IPSEC_PROTO) == 0) { + NewSaId->Proto = OldSaId->Proto; + } else if (NewSaId->Proto != OldSaId->Proto) { + *CreateNew = TRUE; + } + + if ((Mask & DEST) == 0) { + CopyMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS)); + } else if (CompareMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS)) != 0) { + *CreateNew = TRUE; + } + + // + // Process SA_DATA. + // + if ((Mask & MODE) != 0) { + OldData->Mode = NewData->Mode; + } + + if ((Mask & SEQUENCE_NUMBER) != 0) { + OldData->SNCount = NewData->SNCount; + } + + if ((Mask & ANTIREPLAY_WINDOW) != 0) { + OldData->AntiReplayWindows = NewData->AntiReplayWindows; + } + + if ((Mask & AUTH_ALGO) != 0) { + OldData->AlgoInfo.EspAlgoInfo.AuthAlgoId = NewData->AlgoInfo.EspAlgoInfo.AuthAlgoId; + } + + if ((Mask & AUTH_KEY) != 0) { + OldData->AlgoInfo.EspAlgoInfo.AuthKey = NewData->AlgoInfo.EspAlgoInfo.AuthKey; + OldData->AlgoInfo.EspAlgoInfo.AuthKeyLength = NewData->AlgoInfo.EspAlgoInfo.AuthKeyLength; + } + + if ((Mask & ENCRYPT_ALGO) != 0) { + OldData->AlgoInfo.EspAlgoInfo.EncAlgoId = NewData->AlgoInfo.EspAlgoInfo.EncAlgoId; + } + + if ((Mask & ENCRYPT_KEY) != 0) { + OldData->AlgoInfo.EspAlgoInfo.EncKey = NewData->AlgoInfo.EspAlgoInfo.EncKey; + OldData->AlgoInfo.EspAlgoInfo.EncKeyLength = NewData->AlgoInfo.EspAlgoInfo.EncKeyLength; + } + + if (NewSaId->Proto == EfiIPsecAH) { + if ((Mask & (ENCRYPT_ALGO | ENCRYPT_KEY)) != 0) { + // + // Should not provide encrypt_* if AH. + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_UNWANTED_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo --encrypt-key" + ); + return EFI_INVALID_PARAMETER; + } + } + + if (NewSaId->Proto == EfiIPsecESP && OldSaId->Proto == EfiIPsecAH) { + // + // AH -> ESP + // Should provide encrypt_algo at least. + // + if ((Mask & ENCRYPT_ALGO) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo" + ); + return EFI_INVALID_PARAMETER; + } + + // + // Encrypt_key should be provided if algorithm is not NONE. + // + if (NewData->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (Mask & ENCRYPT_KEY) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo" + ); + return EFI_INVALID_PARAMETER; + } + } + + if ((Mask & LIFEBYTE) != 0) { + OldData->SaLifetime.ByteCount = NewData->SaLifetime.ByteCount; + } + + if ((Mask & LIFETIME_SOFT) != 0) { + OldData->SaLifetime.SoftLifetime = NewData->SaLifetime.SoftLifetime; + } + + if ((Mask & LIFETIME) != 0) { + OldData->SaLifetime.HardLifetime = NewData->SaLifetime.HardLifetime; + } + + if ((Mask & PATH_MTU) != 0) { + OldData->PathMTU = NewData->PathMTU; + } + // + // Process SpdSelector. + // + if (OldData->SpdSelector == NULL) { + if ((Mask & (LOCAL | REMOTE | PROTO | LOCAL_PORT | REMOTE_PORT | ICMP_TYPE | ICMP_CODE)) != 0) { + if ((Mask & (LOCAL | REMOTE | PROTO)) != (LOCAL | REMOTE | PROTO)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--local --remote --proto" + ); + return EFI_INVALID_PARAMETER; + } + + OldData->SpdSelector = NewData->SpdSelector; + } + } else { + if ((Mask & LOCAL) != 0) { + OldData->SpdSelector->LocalAddressCount = NewData->SpdSelector->LocalAddressCount; + OldData->SpdSelector->LocalAddress = NewData->SpdSelector->LocalAddress; + } + + if ((Mask & REMOTE) != 0) { + OldData->SpdSelector->RemoteAddressCount = NewData->SpdSelector->RemoteAddressCount; + OldData->SpdSelector->RemoteAddress = NewData->SpdSelector->RemoteAddress; + } + + if ((Mask & PROTO) != 0) { + OldData->SpdSelector->NextLayerProtocol = NewData->SpdSelector->NextLayerProtocol; + } + + if (OldData->SpdSelector != NULL) { + switch (OldData->SpdSelector->NextLayerProtocol) { + case EFI_IP4_PROTO_TCP: + case EFI_IP4_PROTO_UDP: + if ((Mask & LOCAL_PORT) != 0) { + OldData->SpdSelector->LocalPort = NewData->SpdSelector->LocalPort; + } + + if ((Mask & REMOTE_PORT) != 0) { + OldData->SpdSelector->RemotePort = NewData->SpdSelector->RemotePort; + } + break; + + case EFI_IP4_PROTO_ICMP: + if ((Mask & ICMP_TYPE) != 0) { + OldData->SpdSelector->LocalPort = (UINT8) NewData->SpdSelector->LocalPort; + } + + if ((Mask & ICMP_CODE) != 0) { + OldData->SpdSelector->RemotePort = (UINT8) NewData->SpdSelector->RemotePort; + } + break; + } + } + } + + return EFI_SUCCESS; +} + +/** + Combine old PAD entry with new PAD entry. + + @param[in, out] OldPadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in, out] OldData The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] NewPadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in] NewData The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CombinePadEntry ( + IN OUT EFI_IPSEC_PAD_ID *OldPadId, + IN OUT EFI_IPSEC_PAD_DATA *OldData, + IN EFI_IPSEC_PAD_ID *NewPadId, + IN EFI_IPSEC_PAD_DATA *NewData, + IN UINT32 Mask, + OUT BOOLEAN *CreateNew + ) +{ + + *CreateNew = FALSE; + + if ((Mask & (PEER_ID | PEER_ADDRESS)) == 0) { + CopyMem (NewPadId, OldPadId, sizeof (EFI_IPSEC_PAD_ID)); + } else { + if ((Mask & PEER_ID) != 0) { + if (OldPadId->PeerIdValid) { + if (StrCmp ((CONST CHAR16 *) OldPadId->Id.PeerId, (CONST CHAR16 *) NewPadId->Id.PeerId) != 0) { + *CreateNew = TRUE; + } + } else { + *CreateNew = TRUE; + } + } else { + // + // MASK & PEER_ADDRESS + // + if (OldPadId->PeerIdValid) { + *CreateNew = TRUE; + } else { + if ((CompareMem (&OldPadId->Id.IpAddress.Address, &NewPadId->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) || + (OldPadId->Id.IpAddress.PrefixLength != NewPadId->Id.IpAddress.PrefixLength)) { + *CreateNew = TRUE; + } + } + } + } + + if ((Mask & AUTH_PROTO) != 0) { + OldData->AuthProtocol = NewData->AuthProtocol; + } + + if ((Mask & AUTH_METHOD) != 0) { + OldData->AuthMethod = NewData->AuthMethod; + } + + if ((Mask & IKE_ID) != 0) { + OldData->IkeIdFlag = NewData->IkeIdFlag; + } + + if ((Mask & AUTH_DATA) != 0) { + OldData->AuthDataSize = NewData->AuthDataSize; + OldData->AuthData = NewData->AuthData; + } + + if ((Mask & REVOCATION_DATA) != 0) { + OldData->RevocationDataSize = NewData->RevocationDataSize; + OldData->RevocationData = NewData->RevocationData; + } + + return EFI_SUCCESS; +} + +COMBINE_POLICY_ENTRY mCombinePolicyEntry[] = { + (COMBINE_POLICY_ENTRY) CombineSpdEntry, + (COMBINE_POLICY_ENTRY) CombineSadEntry, + (COMBINE_POLICY_ENTRY) CombinePadEntry +}; + +/** + Edit entry information in the database. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure. + @param[in] Data The pointer to the data. + @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure. + + @retval EFI_SUCCESS Continue the iteration. + @retval EFI_ABORTED Abort the iteration. +**/ +EFI_STATUS +EditOperatePolicyEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN EDIT_POLICY_ENTRY_CONTEXT *Context + ) +{ + EFI_STATUS Status; + BOOLEAN CreateNew; + + if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) { + ASSERT (Context->DataType < 3); + + Status = mCombinePolicyEntry[Context->DataType] ( + Selector, + Data, + Context->Selector, + Context->Data, + Context->Mask, + &CreateNew + ); + if (!EFI_ERROR (Status)) { + if (CreateNew) { + // + // Insert new entry before old entry + // + Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Context->Selector, + Data, + Selector + ); + ASSERT_EFI_ERROR (Status); + // + // Delete old entry + // + Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Selector, + NULL, + NULL + ); + ASSERT_EFI_ERROR (Status); + } else { + Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Context->Selector, + Data, + NULL + ); + } + } + + Context->Status = Status; + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Edit entry information in database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Edit entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +EditPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + EDIT_POLICY_ENTRY_CONTEXT Context; + CONST CHAR16 *ValueStr; + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr); + return EFI_NOT_FOUND; + } + + Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage); + if (!EFI_ERROR (Status)) { + Context.DataType = DataType; + Context.Status = EFI_NOT_FOUND; + Status = mCreatePolicyEntry[DataType] (&Context.Selector, &Context.Data, ParamPackage, &Context.Mask, FALSE); + if (!EFI_ERROR (Status)) { + ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) EditOperatePolicyEntry, &Context); + Status = Context.Status; + } + + if (Context.Selector != NULL) { + gBS->FreePool (Context.Selector); + } + + if (Context.Data != NULL) { + gBS->FreePool (Context.Data); + } + } + + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr); + } else if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_EDIT_FAILED), mHiiHandle, mAppName); + } + + return Status; + +} + +/** + Insert entry information in database. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure. + @param[in] Data The pointer to the data. + @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure. + + @retval EFI_SUCCESS Continue the iteration. + @retval EFI_ABORTED Abort the iteration. +**/ +EFI_STATUS +InsertPolicyEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN INSERT_POLICY_ENTRY_CONTEXT *Context + ) +{ + // + // Found the entry which we want to insert before. + // + if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) { + + Context->Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Context->Selector, + Context->Data, + Selector + ); + // + // Abort the iteration after the insertion. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Insert or add entry information in database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Insert or add entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval EFI_BUFFER_TOO_SMALL The entry already existed. + @retval EFI_UNSUPPORTED The operation is not supported. + @retval Others Some mistaken case. +**/ +EFI_STATUS +AddOrInsertPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + INSERT_POLICY_ENTRY_CONTEXT Context; + UINT32 Mask; + UINTN DataSize; + CONST CHAR16 *ValueStr; + + Status = mCreatePolicyEntry[DataType] (&Selector, &Data, ParamPackage, &Mask, TRUE); + if (!EFI_ERROR (Status)) { + // + // Find if the Selector to be inserted already exists. + // + DataSize = 0; + Status = mIpSecConfig->GetData ( + mIpSecConfig, + DataType, + Selector, + &DataSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_EXISTS), mHiiHandle, mAppName); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-a")) { + Status = mIpSecConfig->SetData ( + mIpSecConfig, + DataType, + Selector, + Data, + NULL + ); + } else { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr); + return EFI_NOT_FOUND; + } + + Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage); + if (!EFI_ERROR (Status)) { + Context.DataType = DataType; + Context.Status = EFI_NOT_FOUND; + Context.Selector = Selector; + Context.Data = Data; + + ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) InsertPolicyEntry, &Context); + Status = Context.Status; + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr); + } + } + } + + gBS->FreePool (Selector); + gBS->FreePool (Data); + } + + if (Status == EFI_UNSUPPORTED) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_UNSUPPORT), mHiiHandle, mAppName); + } else if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_FAILED), mHiiHandle, mAppName); + } + + return Status; +} diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h new file mode 100644 index 0000000000..5161bacccb --- /dev/null +++ b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h @@ -0,0 +1,158 @@ +/** @file + The function declaration of policy entry operation in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _POLICY_ENTRY_OPERATION_H_ +#define _POLICY_ENTRY_OPERATION_H_ + +#define LOCAL BIT(0) +#define REMOTE BIT(1) +#define PROTO BIT(2) +#define LOCAL_PORT BIT(3) +#define REMOTE_PORT BIT(4) +#define ICMP_TYPE BIT(5) +#define ICMP_CODE BIT(6) +#define NAME BIT(7) +#define PACKET_FLAG BIT(8) +#define ACTION BIT(9) +#define EXT_SEQUENCE BIT(10) +#define SEQUENCE_OVERFLOW BIT(11) +#define FRAGMENT_CHECK BIT(12) +#define LIFEBYTE BIT(13) +#define LIFETIME_SOFT BIT(14) +#define LIFETIME BIT(15) +#define MODE BIT(16) +#define TUNNEL_LOCAL BIT(17) +#define TUNNEL_REMOTE BIT(18) +#define DONT_FRAGMENT BIT(19) +#define IPSEC_PROTO BIT(20) +#define AUTH_ALGO BIT(21) +#define ENCRYPT_ALGO BIT(22) +#define SPI BIT(23) +#define DEST BIT(24) +#define SEQUENCE_NUMBER BIT(25) +#define ANTIREPLAY_WINDOW BIT(26) +#define AUTH_KEY BIT(27) +#define ENCRYPT_KEY BIT(28) +#define PATH_MTU BIT(29) + +#define PEER_ID BIT(0) +#define PEER_ADDRESS BIT(1) +#define AUTH_PROTO BIT(2) +#define AUTH_METHOD BIT(3) +#define IKE_ID BIT(4) +#define AUTH_DATA BIT(5) +#define REVOCATION_DATA BIT(6) + +typedef struct { + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted. + VOID *Data; + UINT32 Mask; + POLICY_ENTRY_INDEXER Indexer; + EFI_STATUS Status; // Indicate whether insertion succeeds. +} EDIT_POLICY_ENTRY_CONTEXT; + +typedef struct { + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted. + VOID *Data; + POLICY_ENTRY_INDEXER Indexer; + EFI_STATUS Status; // Indicate whether insertion succeeds. +} INSERT_POLICY_ENTRY_CONTEXT; + +/** + The prototype for the CreateSpdEntry()/CreateSadEntry()/CreatePadEntry(). + Fill in EFI_IPSEC_CONFIG_SELECTOR and corresponding data thru ParamPackage list. + + @param[out] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union. + @param[out] Data The pointer to corresponding data. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Filled in EFI_IPSEC_CONFIG_SELECTOR and corresponding data successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +typedef +EFI_STATUS +(*CREATE_POLICY_ENTRY) ( + OUT EFI_IPSEC_CONFIG_SELECTOR **Selector, + OUT VOID **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ); + +/** + The prototype for the CombineSpdEntry()/CombineSadEntry()/CombinePadEntry(). + Combine old SPD/SAD/PAD entry with new SPD/SAD/PAD entry. + + @param[in, out] OldSelector The pointer to the old EFI_IPSEC_CONFIG_SELECTOR union. + @param[in, out] OldData The pointer to the corresponding old data. + @param[in] NewSelector The pointer to the new EFI_IPSEC_CONFIG_SELECTOR union. + @param[in] NewData The pointer to the corresponding new data. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +typedef +EFI_STATUS +(* COMBINE_POLICY_ENTRY) ( + EFI_IPSEC_CONFIG_SELECTOR *OldSelector, + VOID *OldData, + EFI_IPSEC_CONFIG_SELECTOR *NewSelector, + VOID *NewData, + UINT32 Mask, + BOOLEAN *CreateNew + ); + +/** + Insert or add entry information in database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Insert or add entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval EFI_BUFFER_TOO_SMALL The entry already existed. + @retval EFI_UNSUPPORTED The operation is not supported./ + @retval Others Some mistaken case. +**/ +EFI_STATUS +AddOrInsertPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); + +/** + Edit entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Edit entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +EditPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); +#endif diff --git a/NetworkPkg/Application/Ping6/Ia32/Tsc.c b/NetworkPkg/Application/Ping6/Ia32/Tsc.c new file mode 100644 index 0000000000..e2eae99077 --- /dev/null +++ b/NetworkPkg/Application/Ping6/Ia32/Tsc.c @@ -0,0 +1,28 @@ +/** @file + The implement to read TSC in IA32 platform. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 + +/** + Reads and returns the current value of the Time Stamp Counter (TSC). + + @return The current value of TSC. + +**/ +UINT64 +ReadTime () +{ + return AsmReadTsc (); +} diff --git a/NetworkPkg/Application/Ping6/Ipf/Itc.c b/NetworkPkg/Application/Ping6/Ipf/Itc.c new file mode 100644 index 0000000000..131e5c0e30 --- /dev/null +++ b/NetworkPkg/Application/Ping6/Ipf/Itc.c @@ -0,0 +1,28 @@ +/** @file + The implement to read ITC in IA64 platform. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 + +/** + Reads and returns the current value of the Interval Timer Counter Register (ITC). + + @return The current value of ITC. + +**/ +UINT64 +ReadTime () +{ + return AsmReadItc (); +} diff --git a/NetworkPkg/Application/Ping6/Ping6.c b/NetworkPkg/Application/Ping6/Ping6.c new file mode 100644 index 0000000000..b783c5a027 --- /dev/null +++ b/NetworkPkg/Application/Ping6/Ping6.c @@ -0,0 +1,1179 @@ +/** @file + The implementation for Ping6 application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Ping6.h" + +SHELL_PARAM_ITEM Ping6ParamList[] = { + { + L"-l", + TypeValue + }, + { + L"-n", + TypeValue + }, + { + L"-s", + TypeValue + }, + { + L"-?", + TypeFlag + }, + { + NULL, + TypeMax + }, +}; + +// +// Global Variables in Ping6 application. +// +EFI_HII_HANDLE mHiiHandle; +CONST CHAR16 *mIp6DstString; +CONST CHAR16 *mIp6SrcString; +EFI_GUID mEfiPing6Guid = EFI_PING6_GUID; +UINT32 mFrequency = 0; +/** + Get and caculate the frequency in tick/ms. + The result is saved in the globle variable mFrequency + + @retval EFI_SUCCESS Caculated the frequency successfully. + @retval Others Failed to caculate the frequency. + +**/ +EFI_STATUS +Ping6GetFrequency ( + VOID + ) +{ + EFI_STATUS Status; + EFI_CPU_ARCH_PROTOCOL *Cpu; + UINT64 CurrentTick; + UINT32 TimerPeriod; + + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, (UINT64 *) &TimerPeriod); + + if (EFI_ERROR (Status)) { + // + // For NT32 Simulator only. 358049 is a similar value to keep timer granularity. + // Set the timer period by ourselves. + // + TimerPeriod = NTTIMERPERIOD; + } + // + // The timer period is in femtosecond (1 femtosecond is 1e-15 second). + // So 1e+12 is divided by timer period to produce the freq in tick/ms. + // + mFrequency = (UINT32) DivU64x32 (1000000000000ULL, TimerPeriod); + + return EFI_SUCCESS; +} + +/** + Get and caculate the duration in ms. + + @param[in] Begin The start point of time. + @param[in] End The end point of time. + + @return The duration in ms. + +**/ +UINT32 +Ping6CalculateTick ( + IN UINT64 Begin, + IN UINT64 End + ) +{ + ASSERT (End > Begin); + return (UINT32) DivU64x32 (End - Begin, mFrequency); +} + +/** + Destroy IPING6_ICMP6_TX_INFO, and recollect the memory. + + @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO. + +**/ +VOID +Ping6DestroyTxInfo ( + IN PING6_ICMP6_TX_INFO *TxInfo + ) +{ + EFI_IP6_TRANSMIT_DATA *TxData; + EFI_IP6_FRAGMENT_DATA *FragData; + UINTN Index; + + ASSERT (TxInfo != NULL); + + if (TxInfo->Token != NULL) { + + if (TxInfo->Token->Event != NULL) { + gBS->CloseEvent (TxInfo->Token->Event); + } + + TxData = TxInfo->Token->Packet.TxData; + if (TxData != NULL) { + + if (TxData->OverrideData != NULL) { + FreePool (TxData->OverrideData); + } + + if (TxData->ExtHdrs != NULL) { + FreePool (TxData->ExtHdrs); + } + + for (Index = 0; Index < TxData->FragmentCount; Index++) { + FragData = TxData->FragmentTable[Index].FragmentBuffer; + if (FragData != NULL) { + FreePool (FragData); + } + } + } + + FreePool (TxInfo->Token); + } + + FreePool (TxInfo); +} + +/** + Match the request, and reply with SequenceNum/TimeStamp. + + @param[in] Private The pointer to PING6_PRIVATE_DATA. + @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY. + + @retval EFI_SUCCESS The match is successful. + @retval EFI_NOT_FOUND The reply can't be matched with any request. + +**/ +EFI_STATUS +Ping6MatchEchoReply ( + IN PING6_PRIVATE_DATA *Private, + IN ICMP6_ECHO_REQUEST_REPLY *Packet + ) +{ + PING6_ICMP6_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); + + if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) { + Private->RxCount++; + RemoveEntryList (&TxInfo->Link); + Ping6DestroyTxInfo (TxInfo); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + The original intention is to send a request. + Currently, the application retransmits an icmp6 echo request packet + per second in sendnumber times that is specified by the user. + Because nothing can be done here, all things move to the timer rountine. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to Context. + +**/ +VOID +EFIAPI +Ping6OnEchoRequestSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +} + +/** + receive reply, match and print reply infomation. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to context. + +**/ +VOID +EFIAPI +Ping6OnEchoReplyReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + PING6_PRIVATE_DATA *Private; + EFI_IP6_COMPLETION_TOKEN *RxToken; + EFI_IP6_RECEIVE_DATA *RxData; + ICMP6_ECHO_REQUEST_REPLY *Reply; + UINT32 PayLoad; + UINT32 Rtt; + CHAR8 Near; + + Private = (PING6_PRIVATE_DATA *) Context; + + if (Private->Status == EFI_ABORTED) { + return; + } + + RxToken = &Private->RxToken; + RxData = RxToken->Packet.RxData; + Reply = RxData->FragmentTable[0].FragmentBuffer; + PayLoad = RxData->DataLength; + + if (RxData->Header->NextHeader != IP6_ICMP) { + goto ON_EXIT; + } + + if (!IP6_IS_MULTICAST (&Private->DstAddress) && + !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) { + goto ON_EXIT; + } + + if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) { + goto ON_EXIT; + } + + if (PayLoad != Private->BufferSize) { + goto ON_EXIT; + } + // + // Check whether the reply matches the sent request before. + // + Status = Ping6MatchEchoReply (Private, Reply); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + // + // Display statistics on this icmp6 echo reply packet. + // + Rtt = Ping6CalculateTick (Reply->TimeStamp, ReadTime ()); + if (Rtt != 0) { + Near = (CHAR8) '='; + } else { + Near = (CHAR8) '<'; + } + + Private->RttSum += Rtt; + Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin; + Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax; + + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING6_REPLY_INFO), + mHiiHandle, + PayLoad, + mIp6DstString, + Reply->SequenceNum, + RxData->Header->HopLimit, + Near, + Rtt + ); + +ON_EXIT: + + if (Private->RxCount < Private->SendNum) { + // + // Continue to receive icmp6 echo reply packets. + // + RxToken->Status = EFI_ABORTED; + + Status = Private->Ip6->Receive (Private->Ip6, RxToken); + + if (EFI_ERROR (Status)) { + Private->Status = EFI_ABORTED; + } + } else { + // + // All reply have already been received from the dest host. + // + Private->Status = EFI_SUCCESS; + } + // + // Singal to recycle the each rxdata here, not at the end of process. + // + gBS->SignalEvent (RxData->RecycleSignal); +} + +/** + Initial EFI_IP6_COMPLETION_TOKEN. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + @param[in] TimeStamp The TimeStamp of request. + @param[in] SequenceNum The SequenceNum of request. + + @return The pointer of EFI_IP6_COMPLETION_TOKEN. + +**/ +EFI_IP6_COMPLETION_TOKEN * +Ping6GenerateToken ( + IN PING6_PRIVATE_DATA *Private, + IN UINT64 TimeStamp, + IN UINT16 SequenceNum + ) +{ + EFI_STATUS Status; + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_TRANSMIT_DATA *TxData; + ICMP6_ECHO_REQUEST_REPLY *Request; + + Request = AllocateZeroPool (Private->BufferSize); + + if (Request == NULL) { + return NULL; + } + // + // Assembly icmp6 echo request packet. + // + Request->Type = ICMP_V6_ECHO_REQUEST; + Request->Code = 0; + Request->SequenceNum = SequenceNum; + Request->TimeStamp = TimeStamp; + Request->Identifier = 0; + // + // Leave check sum to ip6 layer, since it has no idea of source address + // selection. + // + Request->Checksum = 0; + + TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA)); + + if (TxData == NULL) { + FreePool (Request); + return NULL; + } + // + // Assembly ipv6 token for transmit. + // + TxData->OverrideData = 0; + TxData->ExtHdrsLength = 0; + TxData->ExtHdrs = NULL; + TxData->DataLength = Private->BufferSize; + TxData->FragmentCount = 1; + TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request; + TxData->FragmentTable[0].FragmentLength = Private->BufferSize; + + Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN)); + + if (Token == NULL) { + FreePool (Request); + FreePool (TxData); + return NULL; + } + + Token->Status = EFI_ABORTED; + Token->Packet.TxData = TxData; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnEchoRequestSent, + Private, + &Token->Event + ); + + if (EFI_ERROR (Status)) { + FreePool (Request); + FreePool (TxData); + FreePool (Token); + return NULL; + } + + return Token; +} + +/** + Transmit the EFI_IP6_COMPLETION_TOKEN. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + + @retval EFI_SUCCESS Transmitted successfully. + @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. + @retval others Transmitted unsuccessfully. + +**/ +EFI_STATUS +Ping6SendEchoRequest ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + PING6_ICMP6_TX_INFO *TxInfo; + + TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO)); + + if (TxInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxInfo->TimeStamp = ReadTime (); + TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1); + + TxInfo->Token = Ping6GenerateToken ( + Private, + TxInfo->TimeStamp, + TxInfo->SequenceNum + ); + + if (TxInfo->Token == NULL) { + Ping6DestroyTxInfo (TxInfo); + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token); + + if (EFI_ERROR (Status)) { + Ping6DestroyTxInfo (TxInfo); + return Status; + } + + InsertTailList (&Private->TxList, &TxInfo->Link); + Private->TxCount++; + + return EFI_SUCCESS; +} + +/** + Place a completion token into the receive packet queue to receive the echo reply. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + + @retval EFI_SUCCESS Put the token into the receive packet queue successfully. + @retval others Put the token into the receive packet queue unsuccessfully. + +**/ +EFI_STATUS +Ping6ReceiveEchoReply ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnEchoReplyReceived, + Private, + &Private->RxToken.Event + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Private->RxToken.Status = EFI_NOT_READY; + + return Private->Ip6->Receive (Private->Ip6, &Private->RxToken); +} + +/** + Remove the timeout request from the list. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to Context. + +**/ +VOID +EFIAPI +Ping6OnTimerRoutine ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + PING6_PRIVATE_DATA *Private; + PING6_ICMP6_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + UINT32 Time; + + Private = (PING6_PRIVATE_DATA *) Context; + + // + // Retransmit icmp6 echo request packets per second in sendnumber times. + // + if (Private->TxCount < Private->SendNum) { + + Status = Ping6SendEchoRequest (Private); + if (Private->TxCount != 0){ + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), mHiiHandle, Private->TxCount + 1); + } + } + } + // + // Check whether any icmp6 echo request in the list timeout. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); + Time = Ping6CalculateTick (TxInfo->TimeStamp, ReadTime ()); + + // + // Remove the timeout echo request from txlist. + // + if (Time > PING6_DEFAULT_TIMEOUT) { + + if (EFI_ERROR (TxInfo->Token->Status)) { + Private->Ip6->Cancel (Private->Ip6, TxInfo->Token); + } + // + // Remove the timeout icmp6 echo request from list. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), mHiiHandle, TxInfo->SequenceNum); + + RemoveEntryList (&TxInfo->Link); + Ping6DestroyTxInfo (TxInfo); + + if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) { + // + // All the left icmp6 echo request in the list timeout. + // + Private->Status = EFI_TIMEOUT; + } + } + } +} + +/** + Create a valid IP6 instance. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + + @retval EFI_SUCCESS Create a valid IP6 instance successfully. + @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully. + @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address. + @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. + @retval EFI_NOT_FOUND The source address is not found. +**/ +EFI_STATUS +Ping6CreateIp6Instance ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN HandleIndex; + UINTN HandleNum; + EFI_HANDLE *HandleBuffer; + EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_DATA Ip6Config; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + UINTN IfInfoSize; + EFI_IPv6_ADDRESS *Addr; + UINTN AddrIndex; + + HandleBuffer = NULL; + Ip6Sb = NULL; + IfInfo = NULL; + IfInfoSize = 0; + + // + // Locate all the handles with ip6 service binding protocol. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + &HandleNum, + &HandleBuffer + ); + if (EFI_ERROR (Status) || (HandleNum == 0)) { + return EFI_ABORTED; + } + // + // Source address is required when pinging a link-local address on multi- + // interfaces host. + // + if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && + NetIp6IsUnspecifiedAddr (&Private->SrcAddress) && + (HandleNum > 1)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), mHiiHandle); + Status = EFI_INVALID_PARAMETER; + goto ON_ERROR; + } + // + // For each ip6 protocol, check interface addresses list. + // + for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) { + + Ip6Sb = NULL; + IfInfo = NULL; + IfInfoSize = 0; + + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + &gEfiIp6ServiceBindingProtocolGuid, + (VOID **) &Ip6Sb + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) { + // + // No need to match interface address. + // + break; + } else { + // + // Ip6config protocol and ip6 service binding protocol are installed + // on the same handle. + // + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Ip6Cfg + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + // + // Get the interface information size. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &IfInfoSize, + NULL + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + IfInfo = AllocateZeroPool (IfInfoSize); + + if (IfInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + // + // Get the interface info. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &IfInfoSize, + IfInfo + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + // + // Check whether the source address is one of the interface addresses. + // + for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) { + + Addr = &(IfInfo->AddressInfo[AddrIndex].Address); + if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) { + // + // Match a certain interface address. + // + break; + } + } + + if (AddrIndex < IfInfo->AddressInfoCount) { + // + // Found a nic handle with right interface address. + // + break; + } + } + + FreePool (IfInfo); + IfInfo = NULL; + } + // + // No exact interface address matched. + // + + if (HandleIndex == HandleNum) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), mHiiHandle, mIp6SrcString); + Status = EFI_NOT_FOUND; + goto ON_ERROR; + } + + Private->NicHandle = HandleBuffer[HandleIndex]; + + ASSERT (Ip6Sb != NULL); + Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip6ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Private->Ip6, + Private->ImageHandle, + Private->Ip6ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA)); + + // + // Configure the ip6 instance for icmp6 packet exchange. + // + Ip6Config.DefaultProtocol = 58; + Ip6Config.AcceptAnyProtocol = FALSE; + Ip6Config.AcceptIcmpErrors = TRUE; + Ip6Config.AcceptPromiscuous = FALSE; + Ip6Config.TrafficClass = 0; + Ip6Config.HopLimit = 128; + Ip6Config.FlowLabel = 0; + Ip6Config.ReceiveTimeout = 0; + Ip6Config.TransmitTimeout = 0; + + IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress); + + IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress); + + Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), mHiiHandle, Status); + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + if (IfInfo != NULL) { + FreePool (IfInfo); + } + + if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) { + Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle); + } + + return Status; +} + +/** + Destory the IP6 instance. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + +**/ +VOID +Ping6DestoryIp6Instance ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb; + + gBS->CloseProtocol ( + Private->Ip6ChildHandle, + &gEfiIp6ProtocolGuid, + Private->ImageHandle, + Private->Ip6ChildHandle + ); + + Status = gBS->HandleProtocol ( + Private->NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + (VOID **) &Ip6Sb + ); + + if (!EFI_ERROR(Status)) { + Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle); + } +} + +/** + The Ping6 Process. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SendNumber The send request count. + @param[in] BufferSize The send buffer size. + @param[in] SrcAddress The source IPv6 address. + @param[in] DstAddress The destination IPv6 address. + + @retval EFI_SUCCESS The ping6 processed successfullly. + @retval others The ping6 processed unsuccessfully. + +**/ +EFI_STATUS +Ping6 ( + IN EFI_HANDLE ImageHandle, + IN UINT32 SendNumber, + IN UINT32 BufferSize, + IN EFI_IPv6_ADDRESS *SrcAddress, + IN EFI_IPv6_ADDRESS *DstAddress + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + PING6_PRIVATE_DATA *Private; + PING6_ICMP6_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA)); + + ASSERT (Private != NULL); + + Private->ImageHandle = ImageHandle; + Private->SendNum = SendNumber; + Private->BufferSize = BufferSize; + Private->RttMin = 0xFFFF; + Private->Status = EFI_NOT_READY; + + InitializeListHead (&Private->TxList); + + IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress); + IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress); + + // + // Open and configure a ip6 instance for ping6. + // + Status = Ping6CreateIp6Instance (Private); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Print the command line itself. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), mHiiHandle, mIp6DstString, Private->BufferSize); + // + // Create a ipv6 token to receive the first icmp6 echo reply packet. + // + Status = Ping6ReceiveEchoReply (Private); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Create and start timer to send icmp6 echo request packet per second. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnTimerRoutine, + Private, + &Private->Timer + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Create a ipv6 token to send the first icmp6 echo request packet. + // + Status = Ping6SendEchoRequest (Private); + // + // EFI_NOT_READY for IPsec is enable and IKE is not established. + // + if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) { + if(Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), mHiiHandle, mIp6DstString); + } + + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Private->Timer, + TimerPeriodic, + PING6_ONE_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Control the ping6 process by two factors: + // 1. Hot key + // 2. Private->Status + // 2.1. success means all icmp6 echo request packets get reply packets. + // 2.2. timeout means the last icmp6 echo reply request timeout to get reply. + // 2.3. noready means ping6 process is on-the-go. + // + while (Private->Status == EFI_NOT_READY) { + Private->Ip6->Poll (Private->Ip6); + + // + // Terminate the ping6 process by 'esc' or 'ctl-c'. + // + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + if (!EFI_ERROR(Status)) { + if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) || + ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) { + goto ON_STAT; + } + } + } + +ON_STAT: + // + // Display the statistics in all. + // + gBS->SetTimer (Private->Timer, TimerCancel, 0); + + if (Private->TxCount != 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING6_STAT), + mHiiHandle, + Private->TxCount, + Private->RxCount, + (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount, + Private->RttSum + ); + } + + if (Private->RxCount != 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING6_RTT), + mHiiHandle, + Private->RttMin, + Private->RttMax, + Private->RttSum / Private->RxCount + ); + } + +ON_EXIT: + + if (Private != NULL) { + Private->Status = EFI_ABORTED; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); + + Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token); + + RemoveEntryList (&TxInfo->Link); + Ping6DestroyTxInfo (TxInfo); + } + + if (Private->Timer != NULL) { + gBS->CloseEvent (Private->Timer); + } + + if (Private->Ip6 != NULL) { + Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken); + } + + if (Private->RxToken.Event != NULL) { + gBS->CloseEvent (Private->RxToken.Event); + } + + if (Private->Ip6ChildHandle != NULL) { + Ping6DestoryIp6Instance (Private); + } + + FreePool (Private); + } + + return Status; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for the Ping6 application that parses the command line input and calls the Ping6 process. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETETR Input parameters combination is invalid. + @retval Others Some errors occur. + +**/ +EFI_STATUS +EFIAPI +InitializePing6 ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS DstAddress; + EFI_IPv6_ADDRESS SrcAddress; + UINT64 BufferSize; + UINTN SendNumber; + LIST_ENTRY *ParamPackage; + CONST CHAR16 *ValueStr; + CONST CHAR16 *ValueStrPtr; + UINTN NonOptionCount; + + // + // Register our string package with HII and return the handle to it. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, Ping6Strings, NULL); + ASSERT (mHiiHandle != NULL); + + Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, NULL, TRUE, FALSE); + if (EFI_ERROR(Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle); + goto ON_EXIT; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"-?")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_HELP), mHiiHandle); + goto ON_EXIT; + } + + SendNumber = 10; + BufferSize = 16; + + // + // Parse the paramter of count number. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n"); + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + SendNumber = ShellStrToUintn (ValueStrPtr); + + // + // ShellStrToUintn will return 0 when input is 0 or an invalid input string. + // + if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + // + // Parse the paramter of buffer size. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l"); + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + BufferSize = ShellStrToUintn (ValueStrPtr); + + // + // ShellStrToUintn will return 0 when input is 0 or an invalid input string. + // + if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + + ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS)); + + // + // Parse the paramter of source ip address. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s"); + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + mIp6SrcString = ValueStr; + Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + // + // Parse the paramter of destination ip address. + // + NonOptionCount = ShellCommandLineGetCount(); + ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1)); + if (NonOptionCount != 2) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + mIp6DstString = ValueStr; + Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + // + // Get frequency to calculate the time from ticks. + // + Status = Ping6GetFrequency (); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + // + // Enter into ping6 process. + // + Status = Ping6 ( + ImageHandle, + (UINT32)SendNumber, + (UINT32)BufferSize, + &SrcAddress, + &DstAddress + ); + +ON_EXIT: + ShellCommandLineFreeVarList (ParamPackage); + HiiRemovePackages (mHiiHandle); + return Status; +} diff --git a/NetworkPkg/Application/Ping6/Ping6.h b/NetworkPkg/Application/Ping6/Ping6.h new file mode 100644 index 0000000000..02271e1b58 --- /dev/null +++ b/NetworkPkg/Application/Ping6/Ping6.h @@ -0,0 +1,92 @@ +/** @file + The interface function declaration of shell application Ping6 (Ping for v6 series). + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _PING6_H_ +#define _PING6_H_ + +#define EFI_PING6_GUID \ + { \ + 0x3f0b2478, 0x3619, 0x46c5, {0x81, 0x50, 0xa5, 0xab, 0xdd, 0xb6, 0x6b, 0xd9} \ + } + +#define PING6_DEFAULT_TIMEOUT 5000 +#define PING6_MAX_SEND_NUMBER 10000 +#define PING6_MAX_BUFFER_SIZE 32768 +#define PING6_ONE_SECOND 10000000 + +// +// A similar amount of time that passes in femtoseconds +// for each increment of TimerValue. It is for NT32 only. +// +#define NTTIMERPERIOD 358049 + +#pragma pack(1) + +typedef struct _ICMP6_ECHO_REQUEST_REPLY { + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + UINT16 Identifier; + UINT16 SequenceNum; + UINT64 TimeStamp; + UINT8 Data[1]; +} ICMP6_ECHO_REQUEST_REPLY; + +#pragma pack() + +typedef struct _PING6_ICMP6_TX_INFO { + LIST_ENTRY Link; + UINT16 SequenceNum; + UINT64 TimeStamp; + EFI_IP6_COMPLETION_TOKEN *Token; +} PING6_ICMP6_TX_INFO; + +typedef struct _PING6_PRIVATE_DATA { + EFI_HANDLE ImageHandle; + EFI_HANDLE NicHandle; + EFI_HANDLE Ip6ChildHandle; + EFI_IP6_PROTOCOL *Ip6; + EFI_EVENT Timer; + + EFI_STATUS Status; + LIST_ENTRY TxList; + EFI_IP6_COMPLETION_TOKEN RxToken; + UINT16 RxCount; + UINT16 TxCount; + UINT32 RttSum; + UINT32 RttMin; + UINT32 RttMax; + UINT32 SequenceNum; + + EFI_IPv6_ADDRESS SrcAddress; + EFI_IPv6_ADDRESS DstAddress; + UINT32 SendNum; + UINT32 BufferSize; +} PING6_PRIVATE_DATA; + +/** + Reads and returns the current value of register. + In IA64, the register is the Interval Timer Vector (ITV). + In X86(IA32/X64), the register is the Time Stamp Counter (TSC) + + @return The current value of the register. + +**/ +UINT64 +ReadTime ( + VOID + ); + +#endif diff --git a/NetworkPkg/Application/Ping6/Ping6.inf b/NetworkPkg/Application/Ping6/Ping6.inf new file mode 100644 index 0000000000..56b2163ead --- /dev/null +++ b/NetworkPkg/Application/Ping6/Ping6.inf @@ -0,0 +1,64 @@ +## @file +# Component description file for Ping6 application. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = Ping6 + FILE_GUID = F35F733F-5235-4d7b-83FA-97780CEBCB20 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = InitializePing6 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + Ping6.c + Ping6Strings.uni + Ping6.h + +[Sources.IA32] + Ia32/Tsc.c + +[Sources.X64] + X64/Tsc.c + +[Sources.IPF] + Ipf/Itc.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + BaseLib + UefiBootServicesTableLib + UefiApplicationEntryPoint + BaseMemoryLib + ShellLib + MemoryAllocationLib + DebugLib + HiiLib + NetLib + +[Protocols] + gEfiCpuArchProtocolGuid ## CONSUMS + gEfiIp6ProtocolGuid ## CONSUMS + gEfiIp6ServiceBindingProtocolGuid ## CONSUMS + gEfiIp6ConfigProtocolGuid ## CONSUMS diff --git a/NetworkPkg/Application/Ping6/Ping6Strings.uni b/NetworkPkg/Application/Ping6/Ping6Strings.uni new file mode 100644 index 0000000000..c8b919c4ce Binary files /dev/null and b/NetworkPkg/Application/Ping6/Ping6Strings.uni differ diff --git a/NetworkPkg/Application/Ping6/X64/Tsc.c b/NetworkPkg/Application/Ping6/X64/Tsc.c new file mode 100644 index 0000000000..b3e7bdbb96 --- /dev/null +++ b/NetworkPkg/Application/Ping6/X64/Tsc.c @@ -0,0 +1,28 @@ +/** @file + The implement to read TSC in X64 platform. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 + +/** + Reads and returns the current value of Time Stamp Counter (TSC). + + @return The current value of TSC + +**/ +UINT64 +ReadTime () +{ + return AsmReadTsc (); +} diff --git a/NetworkPkg/Application/VConfig/VConfig.c b/NetworkPkg/Application/VConfig/VConfig.c new file mode 100644 index 0000000000..c948131ffc --- /dev/null +++ b/NetworkPkg/Application/VConfig/VConfig.c @@ -0,0 +1,668 @@ +/** @file + Shell application for VLAN configuration. + + Copyright (C) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define INVALID_NIC_INDEX 0xffff +#define INVALID_VLAN_ID 0xffff + +// +// This is the generated String package data for all .UNI files. +// This data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 VConfigStrings[]; + +EFI_HANDLE mImageHandle = NULL; +EFI_HII_HANDLE mHiiHandle = NULL; + +SHELL_PARAM_ITEM mParamList[] = { + { + L"-l", + TypeValue + }, + { + L"-a", + TypeMaxValue + }, + { + L"-d", + TypeValue + }, + { + NULL, + TypeMax + } +}; + +/** + Locate the network interface handle buffer. + + @param[out] NumberOfHandles Pointer to the number of handles. + @param[out] HandleBuffer Pointer to the buffer to store the returned handles. + +**/ +VOID +LocateNicHandleBuffer ( + OUT UINTN *NumberOfHandles, + OUT EFI_HANDLE **HandleBuffer + ) +{ + EFI_STATUS Status; + + *NumberOfHandles = 0; + *HandleBuffer = NULL; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiVlanConfigProtocolGuid, + NULL, + NumberOfHandles, + HandleBuffer + ); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_LOCATE_FAIL), mHiiHandle, Status); + } +} + +/** + Extract the decimal index from the network interface name. + + @param[in] Name Name of the network interface. + + @retval INVALID_NIC_INDEX Failed to extract the network interface index. + @return others The network interface index. + +**/ +UINTN +NicNameToIndex ( + IN CHAR16 *Name + ) +{ + CHAR16 *Str; + + Str = Name + 3; + if ((StrnCmp (Name, L"eth", 3) != 0) || (*Str == 0)) { + return INVALID_NIC_INDEX; + } + + while (*Str != 0) { + if ((*Str < L'0') || (*Str > L'9')) { + return INVALID_NIC_INDEX; + } + + Str++; + } + + return (UINT16) StrDecimalToUintn (Name + 3); +} + +/** + Find network interface device handle by its name. + + @param[in] Name Name of the network interface. + + @retval NULL Cannot find the network interface. + @return others Handle of the network interface. + +**/ +EFI_HANDLE +NicNameToHandle ( + IN CHAR16 *Name + ) +{ + UINTN NumberOfHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_HANDLE Handle; + + // + // Find all NIC handles. + // + LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer); + if (NumberOfHandles == 0) { + return NULL; + } + + Index = NicNameToIndex (Name); + if (Index >= NumberOfHandles) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_IF), mHiiHandle, Name); + Handle = NULL; + } else { + Handle = HandleBuffer[Index]; + } + + FreePool (HandleBuffer); + return Handle; +} + +/** + Open VlanConfig protocol from a handle. + + @param[in] Handle The handle to open the VlanConfig protocol. + + @return The VlanConfig protocol interface. + +**/ +EFI_VLAN_CONFIG_PROTOCOL * +OpenVlanConfigProtocol ( + IN EFI_HANDLE Handle + ) +{ + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + + VlanConfig = NULL; + gBS->OpenProtocol ( + Handle, + &gEfiVlanConfigProtocolGuid, + (VOID **) &VlanConfig, + mImageHandle, + Handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + return VlanConfig; +} + +/** + Close VlanConfig protocol of a handle. + + @param[in] Handle The handle to close the VlanConfig protocol. + +**/ +VOID +CloseVlanConfigProtocol ( + IN EFI_HANDLE Handle + ) +{ + gBS->CloseProtocol ( + Handle, + &gEfiVlanConfigProtocolGuid, + mImageHandle, + Handle + ); +} + +/** + Display VLAN configuration of a network interface. + + @param[in] Handle Handle of the network interface. + @param[in] NicIndex Index of the network interface. + +**/ +VOID +ShowNicVlanInfo ( + IN EFI_HANDLE Handle, + IN UINTN NicIndex + ) +{ + CHAR16 *MacStr; + EFI_STATUS Status; + UINTN Index; + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + UINT16 NumberOfVlan; + EFI_VLAN_FIND_DATA *VlanData; + + VlanConfig = OpenVlanConfigProtocol (Handle); + if (VlanConfig == NULL) { + return ; + } + + MacStr = NULL; + Status = NetLibGetMacString (Handle, mImageHandle, &MacStr); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_MAC_FAIL), mHiiHandle, Status); + goto Exit; + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_ETH_MAC), mHiiHandle, NicIndex, MacStr); + + Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VLAN), mHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_FIND_FAIL), mHiiHandle, Status); + } + + goto Exit; + } + + for (Index = 0; Index < NumberOfVlan; Index++) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_VCONFIG_VLAN_DISPLAY), + mHiiHandle, + VlanData[Index].VlanId, + VlanData[Index].Priority + ); + } + + FreePool (VlanData); + +Exit: + CloseVlanConfigProtocol (Handle); + + if (MacStr != NULL) { + FreePool (MacStr); + } +} + +/** + Display the VLAN configuration of all, or a specified network interface. + + @param[in] Name Name of the network interface. If NULL, the VLAN + configuration of all network will be displayed. + +**/ +VOID +DisplayVlan ( + IN CHAR16 *Name OPTIONAL + ) +{ + UINTN NumberOfHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_HANDLE NicHandle; + + if (Name != NULL) { + // + // Display specified NIC + // + NicHandle = NicNameToHandle (Name); + if (NicHandle == NULL) { + return ; + } + + ShowNicVlanInfo (NicHandle, 0); + return ; + } + + // + // Find all NIC handles + // + LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer); + if (NumberOfHandles == 0) { + return ; + } + + for (Index = 0; Index < NumberOfHandles; Index++) { + ShowNicVlanInfo (HandleBuffer[Index], Index); + } + + FreePool (HandleBuffer); +} + +/** + Convert a NULL-terminated unicode decimal VLAN ID string to VLAN ID. + + @param[in] String Pointer to VLAN ID string from user input. + + @retval Value translated from String, or INVALID_VLAN_ID is string is invalid. + +**/ +UINT16 +StrToVlanId ( + IN CHAR16 *String + ) +{ + CHAR16 *Str; + + if (String == NULL) { + return INVALID_VLAN_ID; + } + + Str = String; + while ((*Str >= '0') && (*Str <= '9')) { + Str++; + } + + if (*Str != 0) { + return INVALID_VLAN_ID; + } + + return (UINT16) StrDecimalToUintn (String); +} + +/** + Add a VLAN device. + + @param[in] ParamStr Parameter string from user input. + +**/ +VOID +AddVlan ( + IN CHAR16 *ParamStr + ) +{ + CHAR16 *Name; + CHAR16 *VlanIdStr; + CHAR16 *PriorityStr; + CHAR16 *StrPtr; + BOOLEAN IsSpace; + UINTN VlanId; + UINTN Priority; + EFI_HANDLE Handle; + EFI_HANDLE VlanHandle; + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + EFI_STATUS Status; + + VlanConfig = NULL; + Priority = 0; + + if (ParamStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle); + return ; + } + + StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr); + if (StrPtr == NULL) { + return ; + } + + Name = StrPtr; + VlanIdStr = NULL; + PriorityStr = NULL; + IsSpace = FALSE; + while (*StrPtr != 0) { + if (*StrPtr == L' ') { + *StrPtr = 0; + IsSpace = TRUE; + } else { + if (IsSpace) { + // + // Start of a parameter. + // + if (VlanIdStr == NULL) { + // + // 2nd parameter is VLAN ID. + // + VlanIdStr = StrPtr; + } else if (PriorityStr == NULL) { + // + // 3rd parameter is Priority. + // + PriorityStr = StrPtr; + } else { + // + // Ignore else parameters. + // + break; + } + } + + IsSpace = FALSE; + } + + StrPtr++; + } + + Handle = NicNameToHandle (Name); + if (Handle == NULL) { + goto Exit; + } + + VlanConfig = OpenVlanConfigProtocol (Handle); + if (VlanConfig == NULL) { + goto Exit; + } + + // + // Check VLAN ID. + // + if ((VlanIdStr == NULL) || (*VlanIdStr == 0)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle); + goto Exit; + } + + VlanId = StrToVlanId (VlanIdStr); + if (VlanId > 4094) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr); + goto Exit; + } + + // + // Check Priority. + // + if ((PriorityStr != NULL) && (*PriorityStr != 0)) { + Priority = StrDecimalToUintn (PriorityStr); + if (Priority > 7) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_PRIORITY), mHiiHandle, PriorityStr); + goto Exit; + } + } + + // + // Set VLAN + // + Status = VlanConfig->Set (VlanConfig, (UINT16) VlanId, (UINT8) Priority); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_FAIL), mHiiHandle, Status); + goto Exit; + } + + // + // Connect the VLAN device. + // + VlanHandle = NetLibGetVlanHandle (Handle, (UINT16) VlanId); + if (VlanHandle != NULL) { + gBS->ConnectController (VlanHandle, NULL, NULL, TRUE); + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_SUCCESS), mHiiHandle); + +Exit: + if (VlanConfig != NULL) { + CloseVlanConfigProtocol (Handle); + } + + FreePool (Name); +} + +/** + Remove a VLAN device. + + @param[in] ParamStr Parameter string from user input. + +**/ +VOID +DeleteVlan ( + CHAR16 *ParamStr + ) +{ + CHAR16 *Name; + CHAR16 *VlanIdStr; + CHAR16 *StrPtr; + UINTN VlanId; + EFI_HANDLE Handle; + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + EFI_STATUS Status; + UINT16 NumberOfVlan; + EFI_VLAN_FIND_DATA *VlanData; + + VlanConfig = NULL; + + if (ParamStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle); + return ; + } + + StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr); + if (StrPtr == NULL) { + return ; + } + + Name = StrPtr; + VlanIdStr = NULL; + while (*StrPtr != 0) { + if (*StrPtr == L'.') { + *StrPtr = 0; + VlanIdStr = StrPtr + 1; + break; + } + + StrPtr++; + } + + Handle = NicNameToHandle (Name); + if (Handle == NULL) { + goto Exit; + } + + VlanConfig = OpenVlanConfigProtocol (Handle); + if (VlanConfig == NULL) { + goto Exit; + } + + // + // Check VLAN ID + // + if (VlanIdStr == NULL || *VlanIdStr == 0) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle); + goto Exit; + } + + VlanId = StrToVlanId (VlanIdStr); + if (VlanId > 4094) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr); + goto Exit; + } + + // + // Delete VLAN. + // + Status = VlanConfig->Remove (VlanConfig, (UINT16) VlanId); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NOT_FOUND), mHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_FAIL), mHiiHandle, Status); + } + + goto Exit; + } + + // + // Check whether this is the last VLAN to remove. + // + Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData); + if (EFI_ERROR (Status)) { + // + // This is the last VLAN to remove, try to connect the controller handle. + // + gBS->ConnectController (Handle, NULL, NULL, TRUE); + } else { + FreePool (VlanData); + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_SUCCESS), mHiiHandle); + +Exit: + if (VlanConfig != NULL) { + CloseVlanConfigProtocol (Handle); + } + + FreePool (Name); +} + +/** + The actual entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point executed successfully. + @retval other Some error occur when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +VlanConfigMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + LIST_ENTRY *List; + CONST CHAR16 *Str; + + mImageHandle = ImageHandle; + + // + // Register our string package to HII database. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, VConfigStrings, NULL); + if (mHiiHandle == NULL) { + return EFI_SUCCESS; + } + + List = NULL; + ShellCommandLineParseEx (mParamList, &List, NULL, FALSE, FALSE); + if (List == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-?")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_HELP), mHiiHandle); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-l")) { + Str = ShellCommandLineGetValue (List, L"-l"); + DisplayVlan ((CHAR16 *) Str); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-a")) { + Str = ShellCommandLineGetValue (List, L"-a"); + AddVlan ((CHAR16 *) Str); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-d")) { + Str = ShellCommandLineGetValue (List, L"-d"); + DeleteVlan ((CHAR16 *) Str); + goto Exit; + } + + // + // No valid argument till now. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle); + +Exit: + if (List != NULL) { + ShellCommandLineFreeVarList (List); + } + + // + // Remove our string package from HII database. + // + HiiRemovePackages (mHiiHandle); + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/Application/VConfig/VConfig.inf b/NetworkPkg/Application/VConfig/VConfig.inf new file mode 100644 index 0000000000..e69da02f7b --- /dev/null +++ b/NetworkPkg/Application/VConfig/VConfig.inf @@ -0,0 +1,47 @@ +## @file +# Component files for VLAN configuration shell application. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VConfig + FILE_GUID = 87E36301-0406-44db-AAF3-9E0E591F3725 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = VlanConfigMain + +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + VConfigStrings.uni + VConfig.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + UefiBootServicesTableLib + UefiLib + ShellLib + NetLib + MemoryAllocationLib + HiiLib + +[Protocols] + gEfiVlanConfigProtocolGuid diff --git a/NetworkPkg/Application/VConfig/VConfigStrings.uni b/NetworkPkg/Application/VConfig/VConfigStrings.uni new file mode 100644 index 0000000000..e8bdcefee8 Binary files /dev/null and b/NetworkPkg/Application/VConfig/VConfigStrings.uni differ diff --git a/NetworkPkg/Dhcp6Dxe/ComponentName.c b/NetworkPkg/Dhcp6Dxe/ComponentName.c new file mode 100644 index 0000000000..314ca37d16 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/ComponentName.c @@ -0,0 +1,312 @@ +/** @file + UEFI Component Name(2) protocol implementation for Dhcp6 driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Dhcp6Impl.h" + + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that attempt to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName = { + Dhcp6ComponentNameGetDriverName, + Dhcp6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Dhcp6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Dhcp6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDhcp6DriverNameTable[] = { + { + "eng;en", + L"DHCP6 Protocol Driver" + }, + { + NULL, + NULL + } +}; + + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDhcp6DriverNameTable, + DriverName, + (BOOLEAN)(This == &gDhcp6ComponentName) + ); +} + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in the + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c new file mode 100644 index 0000000000..d14f169f07 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c @@ -0,0 +1,782 @@ +/** @file + Driver Binding functions and Service Binding functions + implementationfor for Dhcp6 Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Dhcp6Impl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = { + Dhcp6DriverBindingSupported, + Dhcp6DriverBindingStart, + Dhcp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = { + Dhcp6ServiceBindingCreateChild, + Dhcp6ServiceBindingDestroyChild +}; + + +/** + Configure the default Udp6Io to receive all the DHCP6 traffic + on this network interface. + + @param[in] UdpIo The pointer to Udp6Io to be configured. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The Udp6Io is successfully configured. + @retval Others Failed to configure the Udp6Io. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ConfigureUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Config; + + Udp6 = UdpIo->Protocol.Udp6; + Config = &(UdpIo->Config.Udp6); + + ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA)); + + // + // Set Udp6 configure data for the Dhcp6 instance. + // + Config->AcceptPromiscuous = FALSE; + Config->AcceptAnyPort = FALSE; + Config->AllowDuplicatePort = FALSE; + Config->TrafficClass = 0; + Config->HopLimit = 128; + Config->ReceiveTimeout = 0; + Config->TransmitTimeout = 0; + + // + // Configure an endpoint of client(0, 546), server(0, 0), the addresses + // will be overridden later. Note that we MUST not limit RemotePort. + // More details, refer to RFC 3315 section 5.2. + // + Config->StationPort = DHCP6_PORT_CLIENT; + Config->RemotePort = 0; + + return Udp6->Configure (Udp6, Config);; +} + + +/** + Destory the Dhcp6 service. The Dhcp6 service may be partly initialized, + or partly destroyed. If a resource is destroyed, it is marked as such in + case the destroy failed and being called again later. + + @param[in, out] Service The pointer to Dhcp6 service to be destroyed. + +**/ +VOID +Dhcp6DestroyService ( + IN OUT DHCP6_SERVICE *Service + ) +{ + // + // All children instances should have been already destoryed here. + // + ASSERT (Service->NumOfChild == 0); + + if (Service->ClientId != NULL) { + FreePool (Service->ClientId); + } + + if (Service->UdpIo != NULL) { + UdpIoFreeIo (Service->UdpIo); + } + + FreePool (Service); +} + + +/** + Create a new Dhcp6 service for the Nic controller. + + @param[in] Controller The controller to be installed DHCP6 service + binding protocol. + @param[in] ImageHandle The image handle of the Dhcp6 driver. + @param[out] Service The return pointer of the new Dhcp6 service. + + @retval EFI_SUCCESS The Dhcp6 service is created successfully. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + +**/ +EFI_STATUS +Dhcp6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT DHCP6_SERVICE **Service + ) +{ + DHCP6_SERVICE *Dhcp6Srv; + + *Service = NULL; + Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE)); + + if (Dhcp6Srv == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Open the SNP protocol to get mode data later. + // + Dhcp6Srv->Snp = NULL; + NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp); + if (Dhcp6Srv->Snp == NULL) { + FreePool (Dhcp6Srv); + return EFI_DEVICE_ERROR; + } + + // + // Initialize the fields of the new Dhcp6 service. + // + Dhcp6Srv->Signature = DHCP6_SERVICE_SIGNATURE; + Dhcp6Srv->InDestory = FALSE; + Dhcp6Srv->Controller = Controller; + Dhcp6Srv->Image = ImageHandle; + Dhcp6Srv->Xid = (0xffffff & NET_RANDOM (NetRandomInitSeed ())); + + CopyMem ( + &Dhcp6Srv->ServiceBinding, + &gDhcp6ServiceBindingTemplate, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + // + // Generate client Duid in the format of Duid-llt. + // + Dhcp6Srv->ClientId = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode); + + if (Dhcp6Srv->ClientId == NULL) { + FreePool (Dhcp6Srv); + return EFI_DEVICE_ERROR; + } + + // + // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance. + // + Dhcp6Srv->UdpIo = UdpIoCreateIo ( + Controller, + ImageHandle, + Dhcp6ConfigureUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + + if (Dhcp6Srv->UdpIo == NULL) { + FreePool (Dhcp6Srv->ClientId); + FreePool (Dhcp6Srv); + return EFI_DEVICE_ERROR; + } + + InitializeListHead (&Dhcp6Srv->Child); + + *Service = Dhcp6Srv; + + return EFI_SUCCESS; +} + + +/** + Destroy the Dhcp6 instance and recycle the resources. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + +**/ +VOID +Dhcp6DestroyInstance ( + IN OUT DHCP6_INSTANCE *Instance + ) +{ + // + // Clean up the retry list first. + // + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL); + gBS->CloseEvent (Instance->Timer); + + // + // Clean up the current configure data. + // + if (Instance->Config != NULL) { + Dhcp6CleanupConfigData (Instance->Config); + FreePool (Instance->Config); + } + + // + // Clean up the current Ia. + // + if (Instance->IaCb.Ia != NULL) { + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + FreePool (Instance->IaCb.Ia); + } + + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + } + + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + FreePool (Instance); +} + + +/** + Create the Dhcp6 instance and initialize it. + + @param[in] Service The pointer to the Dhcp6 service. + @param[out] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS The Dhcp6 instance is created. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +Dhcp6CreateInstance ( + IN DHCP6_SERVICE *Service, + OUT DHCP6_INSTANCE **Instance + ) +{ + EFI_STATUS Status; + DHCP6_INSTANCE *Dhcp6Ins; + + *Instance = NULL; + Dhcp6Ins = AllocateZeroPool (sizeof (DHCP6_INSTANCE)); + + if (Dhcp6Ins == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the fields of the new Dhcp6 instance. + // + Dhcp6Ins->Signature = DHCP6_INSTANCE_SIGNATURE; + Dhcp6Ins->UdpSts = EFI_ALREADY_STARTED; + Dhcp6Ins->Service = Service; + Dhcp6Ins->InDestory = FALSE; + Dhcp6Ins->MediaPresent = TRUE; + + CopyMem ( + &Dhcp6Ins->Dhcp6, + &gDhcp6ProtocolTemplate, + sizeof (EFI_DHCP6_PROTOCOL) + ); + + InitializeListHead (&Dhcp6Ins->TxList); + InitializeListHead (&Dhcp6Ins->InfList); + + // + // There is a timer for each Dhcp6 instance, which is used to track the + // lease time of Ia and the retransmisson time of all sent packets. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Dhcp6OnTimerTick, + Dhcp6Ins, + &Dhcp6Ins->Timer + ); + + if (EFI_ERROR (Status)) { + FreePool (Dhcp6Ins); + return Status; + } + + *Instance = Dhcp6Ins; + + return EFI_SUCCESS; +} + + +/** + Entry point of the DHCP6 driver to install various protocols. + + @param[in] ImageHandle The handle of the UEFI image file. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others Unexpected error occurs. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDhcp6DriverBinding, + ImageHandle, + &gDhcp6ComponentName, + &gDhcp6ComponentName2 + ); +} + + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + DHCP6_SERVICE *Service; + + // + // Check the Dhcp6 serivce whether already started. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Create and initialize the Dhcp6 service. + // + Status = Dhcp6CreateService ( + ControllerHandle, + This->DriverBindingHandle, + &Service + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Service != NULL); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Service->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + Dhcp6DestroyService (Service); + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + + // + // Find and check the Nic handle by the controller handle. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding); + + if (Service->InDestory) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (NumberOfChildren == 0) { + // + // Destory the service itself if no child instance left. + // + Service->InDestory = TRUE; + + Status = gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + ServiceBinding + ); + + if (EFI_ERROR (Status)) { + Service->InDestory = FALSE; + goto ON_EXIT; + } + + Dhcp6DestroyService (Service); + + } else { + // + // Destory all the children instances before destory the service. + // + while (!IsListEmpty (&Service->Child)) { + Instance = NET_LIST_HEAD (&Service->Child, DHCP6_INSTANCE, Link); + ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); + } + // + // Any of child failed to be destroyed. + // + if (Service->NumOfChild != 0) { + Status = EFI_DEVICE_ERROR; + } + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + VOID *Udp6; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Service = DHCP6_SERVICE_FROM_THIS (This); + + Status = Dhcp6CreateInstance (Service, &Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Instance != NULL); + + // + // Start the timer when the instance is ready to use. + // + Status = gBS->SetTimer ( + Instance->Timer, + TimerPeriodic, + TICKS_PER_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the DHCP6 protocol onto ChildHandle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiDhcp6ProtocolGuid, + &Instance->Dhcp6, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->Handle = *ChildHandle; + + // + // Open the UDP6 protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + Service->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + gDhcp6DriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiDhcp6ProtocolGuid, + &Instance->Dhcp6, + NULL + ); + goto ON_ERROR; + } + + // + // Add into the children list of its parent service. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&Service->Child, &Instance->Link); + Service->NumOfChild++; + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +ON_ERROR: + + Dhcp6DestroyInstance (Instance); + return Status; +} + + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_DHCP6_PROTOCOL *Dhcp6; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Dhcp6, + gDhcp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6); + Service = DHCP6_SERVICE_FROM_THIS (This); + + if (Instance->Service != Service) { + return EFI_INVALID_PARAMETER; + } + + if (Instance->InDestory) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->InDestory = TRUE; + + Status = gBS->CloseProtocol ( + Service->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gDhcp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + if (EFI_ERROR (Status)) { + Instance->InDestory = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Uninstall the MTFTP6 protocol first to enable a top down destruction. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiDhcp6ProtocolGuid, + Dhcp6 + ); + + if (EFI_ERROR (Status)) { + Instance->InDestory = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Remove it from the children list of its parent service. + // + RemoveEntryList (&Instance->Link); + Service->NumOfChild--; + + Dhcp6DestroyInstance (Instance); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h new file mode 100644 index 0000000000..5a88be4d3b --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h @@ -0,0 +1,155 @@ +/** @file + Driver Binding functions and Service Binding functions + declaration for Dhcp6 Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_DHCP6_DRIVER_H__ +#define __EFI_DHCP6_DRIVER_H__ + +#include + +extern EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2; + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start(), it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf new file mode 100644 index 0000000000..f10b07ac3c --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf @@ -0,0 +1,69 @@ +## @file +# Component description file for Dhcp6 module. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Dhcp6Dxe + FILE_GUID = 95E3669D-34BE-4775-A651-7EA41B69D89E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Dhcp6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gDhcp6DriverBinding +# COMPONENT_NAME = gDhcp6ComponentName +# COMPONENT_NAME2 = gDhcp6ComponentName2 +# + +[Sources] + Dhcp6Driver.c + Dhcp6Driver.h + Dhcp6Impl.c + Dhcp6Impl.h + Dhcp6Io.c + Dhcp6Io.h + Dhcp6Utility.c + Dhcp6Utility.h + ComponentName.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + DebugLib + NetLib + UdpIoLib + + +[Protocols] + gEfiUdp6ServiceBindingProtocolGuid + gEfiUdp6ProtocolGuid + gEfiDhcp6ServiceBindingProtocolGuid + gEfiDhcp6ProtocolGuid + diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c new file mode 100644 index 0000000000..3e73976ddb --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c @@ -0,0 +1,1220 @@ +/** @file + This EFI_DHCP6_PROTOCOL interface implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Dhcp6Impl.h" + +// +// Well-known multi-cast address defined in section-24.1 of rfc-3315 +// +// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2 +// ALL_DHCP_Servers address: FF05::1:3 +// +EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}}; +EFI_IPv6_ADDRESS mAllDhcpServersAddress = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}}; + +EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = { + EfiDhcp6GetModeData, + EfiDhcp6Configure, + EfiDhcp6Start, + EfiDhcp6InfoRequest, + EfiDhcp6RenewRebind, + EfiDhcp6Decline, + EfiDhcp6Release, + EfiDhcp6Stop, + EfiDhcp6Parse +}; + +/** + Starts the DHCPv6 standard S.A.R.R. process. + + The Start() function starts the DHCPv6 standard process. This function can + be called only when the state of Dhcp6 instance is in the Dhcp6Init state. + If the DHCP process completes successfully, the state of the Dhcp6 instance + will be transferred through Dhcp6Selecting and Dhcp6Requesting to the + Dhcp6Bound state. + Refer to rfc-3315 for precise state transitions during this process. At the + time when each event occurs in this process, the callback function that was set + by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this + opportunity to control the process. + + @param[in] This The pointer to Dhcp6 protocol. + + @retval EFI_SUCCESS The DHCPv6 standard process has started, or it has + completed when CompletionEvent is NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no + response was received from the server within the + specified timeout value. + @retval EFI_ABORTED The user aborted the DHCPv6 process. + @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6 + standard process. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Start ( + IN EFI_DHCP6_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + // + // The instance has already been started. + // + if (Instance->IaCb.Ia->State != Dhcp6Init) { + return EFI_ALREADY_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Need to clear initial time to make sure that elapsed-time + // is set to 0 for first Solicit. + // + Instance->StartTime = 0; + + // + // Send the solicit message to start S.A.R.R process. + // + Status = Dhcp6SendSolicitMsg (Instance); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchronous call. + // + if (Instance->Config->IaInfoEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Stops the DHCPv6 standard S.A.R.R. process. + + The Stop() function is used to stop the DHCPv6 standard process. After this + function is called successfully, the state of Dhcp6 instance is transferred + into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called + before DHCPv6 standard process can be started again. This function can be + called when the Dhcp6 instance is in any state. + + @param[in] This The pointer to the Dhcp6 protocol. + + @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Stop ( + IN EFI_DHCP6_PROTOCOL *This + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + Udp6 = Service->UdpIo->Protocol.Udp6; + Status = EFI_SUCCESS; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return Status; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + // + // The instance has already been stopped. + // + if (Instance->IaCb.Ia->State == Dhcp6Init || + Instance->IaCb.Ia->State == Dhcp6Selecting || + Instance->IaCb.Ia->State == Dhcp6Requesting + ) { + return Status; + } + + // + // Release the current ready Ia. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->UdpSts = EFI_ALREADY_STARTED; + Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia); + + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + ASSERT (Udp6 != NULL); + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Udp6->Poll (Udp6); + } + Status = Instance->UdpSts; + } + + // + // Clean up the session data for the released Ia. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Dhcp6CleanupSession (Instance, EFI_SUCCESS); + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Returns the current operating mode data for the Dhcp6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the Dhcp6 instance. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data. + @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data. + + @retval EFI_SUCCESS The mode data was returned. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance was not + configured. +**/ +EFI_STATUS +EFIAPI +EfiDhcp6GetModeData ( + IN EFI_DHCP6_PROTOCOL *This, + OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL, + OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_DHCP6_IA *Ia; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + UINT32 IaSize; + UINT32 IdSize; + + if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + if (Instance->Config == NULL && Dhcp6ConfigData != NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Service->ClientId != NULL); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // User needs a copy of instance config data. + // + if (Dhcp6ConfigData != NULL) { + ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA)); + // + // Duplicate config data, including all reference buffers. + // + if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) { + goto ON_ERROR; + } + } + + // + // User need a copy of instance mode data. + // + if (Dhcp6ModeData != NULL) { + ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA)); + // + // Duplicate a copy of EFI_DHCP6_DUID for client Id. + // + IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length); + + Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize); + if (Dhcp6ModeData->ClientId == NULL) { + goto ON_ERROR; + } + + CopyMem ( + Dhcp6ModeData->ClientId, + Service->ClientId, + IdSize + ); + + Ia = Instance->IaCb.Ia; + if (Ia != NULL) { + // + // Duplicate a copy of EFI_DHCP6_IA for configured Ia. + // + IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS); + + Dhcp6ModeData->Ia = AllocateZeroPool (IaSize); + if (Dhcp6ModeData->Ia == NULL) { + goto ON_ERROR; + } + + CopyMem ( + Dhcp6ModeData->Ia, + Ia, + IaSize + ); + + // + // Duplicate a copy of reply packet if has. + // + if (Ia->ReplyPacket != NULL) { + Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size); + if (Dhcp6ModeData->Ia->ReplyPacket == NULL) { + goto ON_ERROR; + } + CopyMem ( + Dhcp6ModeData->Ia->ReplyPacket, + Ia->ReplyPacket, + Ia->ReplyPacket->Size + ); + } + } + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + if (Dhcp6ConfigData != NULL) { + Dhcp6CleanupConfigData (Dhcp6ConfigData); + } + if (Dhcp6ModeData != NULL) { + Dhcp6CleanupModeData (Dhcp6ModeData); + } + gBS->RestoreTPL (OldTpl); + + return EFI_OUT_OF_RESOURCES; +} + + +/** + Initializes, changes, or resets the operational settings for the Dhcp6 instance. + + The Configure() function is used to initialize or clean up the configuration + data of the Dhcp6 instance: + - When Dhcp6CfgData is not NULL and Configure() is called successfully, the + configuration data will be initialized in the Dhcp6 instance, and the state + of the configured IA will be transferred into Dhcp6Init. + - When Dhcp6CfgData is NULL and Configure() is called successfully, the + configuration data will be cleaned up and no IA will be associated with + the Dhcp6 instance. + To update the configuration data for an Dhcp6 instance, the original data + must be cleaned up before setting the new configuration data. + + @param[in] This The pointer to the Dhcp6 protocol + @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA. + + @retval EFI_SUCCESS The Dhcp6 is configured successfully with the + Dhcp6Init state, or cleaned up the original + configuration setting. + @retval EFI_ACCESS_DENIED The Dhcp6 instance was already configured. + The Dhcp6 instance has already started the + DHCPv6 S.A.R.R when Dhcp6CfgData is NULL. + @retval EFI_INVALID_PARAMETER Some of the parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Configure ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + LIST_ENTRY *Entry; + DHCP6_INSTANCE *Other; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + UINTN Index; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // Check the parameter of configure data. + // + if (Dhcp6CfgData != NULL) { + if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) { + return EFI_INVALID_PARAMETER; + } + if (Dhcp6CfgData->OptionList != NULL) { + for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) { + if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata + ) { + return EFI_INVALID_PARAMETER; + } + } + } + + if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA && + Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA + ) { + return EFI_INVALID_PARAMETER; + } + + if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Dhcp6CfgData->SolicitRetransmission != NULL && + Dhcp6CfgData->SolicitRetransmission->Mrc == 0 && + Dhcp6CfgData->SolicitRetransmission->Mrd == 0 + ) { + return EFI_INVALID_PARAMETER; + } + + // + // Make sure the (IaId, IaType) is unique over all the instances. + // + NET_LIST_FOR_EACH (Entry, &Service->Child) { + Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link); + if (Other->IaCb.Ia != NULL && + Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type && + Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId + ) { + return EFI_INVALID_PARAMETER; + } + } + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (Dhcp6CfgData != NULL) { + // + // It's not allowed to configure one instance twice without configure null. + // + if (Instance->Config != NULL) { + gBS->RestoreTPL (OldTpl); + return EFI_ACCESS_DENIED; + } + + // + // Duplicate config data including all reference buffers. + // + Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA)); + if (Instance->Config == NULL) { + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + + Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData); + if (EFI_ERROR(Status)) { + FreePool (Instance->Config); + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the Ia descriptor from the config data, and leave the other + // fields of the Ia as default value 0. + // + Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA)); + if (Instance->IaCb.Ia == NULL) { + Dhcp6CleanupConfigData (Instance->Config); + FreePool (Instance->Config); + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + CopyMem ( + &Instance->IaCb.Ia->Descriptor, + &Dhcp6CfgData->IaDescriptor, + sizeof(EFI_DHCP6_IA_DESCRIPTOR) + ); + + } else { + + if (Instance->Config == NULL) { + ASSERT (Instance->IaCb.Ia == NULL); + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + } + + // + // It's not allowed to configure a started instance as null. + // + if (Instance->IaCb.Ia->State != Dhcp6Init) { + gBS->RestoreTPL (OldTpl); + return EFI_ACCESS_DENIED; + } + + Dhcp6CleanupConfigData (Instance->Config); + FreePool (Instance->Config); + Instance->Config = NULL; + + FreePool (Instance->IaCb.Ia); + Instance->IaCb.Ia = NULL; + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Request configuration information without the assignment of any + Ia addresses of the client. + + The InfoRequest() function is used to request configuration information + without the assignment of any IPv6 address of the client. The client sends + out an Information Request packet to obtain the required configuration + information, and DHCPv6 server responds with a Reply packet containing + the information for the client. The received Reply packet will be passed + to the user by ReplyCallback function. If the user returns EFI_NOT_READY from + ReplyCallback, the Dhcp6 instance will continue to receive other Reply + packets unless timeout according to the Retransmission parameter. + Otherwise, the Information Request exchange process will be finished + successfully if user returns EFI_SUCCESS from ReplyCallback. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client + Identifier option and include it into Information Request + packet. Otherwise, Client Identifier option will not be included. + @param[in] OptionRequest The pointer to the buffer of option request options. + @param[in] OptionCount The option number in the OptionList. + @param[in] OptionList The list of appended options. + @param[in] Retransmission The pointer to the retransmission of the message. + @param[in] TimeoutEvent The event of timeout. + @param[in] ReplyCallback The callback function when the reply was received. + @param[in] CallbackContext The pointer to the parameter passed to the callback. + + @retval EFI_SUCCESS The DHCPv6 information request exchange process + completed when TimeoutEvent is NULL. Information + Request packet has been sent to DHCPv6 server when + TimeoutEvent is not NULL. + @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed + because of no response, or not all requested-options + are responded by DHCPv6 servers when Timeout happened. + @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted + by user. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6InfoRequest ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL, + IN EFI_DHCP6_RETRANSMISSION *Retransmission, + IN EFI_EVENT TimeoutEvent OPTIONAL, + IN EFI_DHCP6_INFO_CALLBACK ReplyCallback, + IN VOID *CallbackContext OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + DHCP6_INF_CB *InfCb; + UINTN Index; + + if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) { + return EFI_INVALID_PARAMETER; + } + + if (OptionCount > 0 && OptionList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (OptionList != NULL) { + for (Index = 0; Index < OptionCount; Index++) { + if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) { + return EFI_INVALID_PARAMETER; + } + } + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Create and initialize the control block for the info-request. + // + InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB)); + + if (InfCb == NULL) { + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + + InfCb->ReplyCallback = ReplyCallback; + InfCb->CallbackContext = CallbackContext; + InfCb->TimeoutEvent = TimeoutEvent; + + InsertTailList (&Instance->InfList, &InfCb->Link); + + // + // Send the info-request message to start exchange process. + // + Status = Dhcp6SendInfoRequestMsg ( + Instance, + InfCb, + SendClientId, + OptionRequest, + OptionCount, + OptionList, + Retransmission + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateless exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (TimeoutEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Manually extend the valid and preferred lifetimes for the IPv6 addresses + of the configured IA and update other configuration parameters by sending a + Renew or Rebind packet. + + The RenewRebind() function is used to manually extend the valid and preferred + lifetimes for the IPv6 addresses of the configured IA, and update other + configuration parameters by sending Renew or Rebind packet. + - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound, + it sends Renew packet to the previously DHCPv6 server and transfer the + state of the configured IA to Dhcp6Renewing. If valid Reply packet received, + the state transfers to Dhcp6Bound and the valid and preferred timer restarts. + If fails, the state transfers to Dhcp6Bound, but the timer continues. + - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound, + it will send a Rebind packet. If valid Reply packet is received, the state transfers + to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state + transfers to Dhcp6Init, and the IA can't be used. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state. + Otherwise, Renew packet will be sent and enter Dhcp6Renewing state. + + @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process has + completed and at least one IPv6 address of the + configured IA has been bound again when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL. + The EFI DHCPv6 Protocol instance has sent Renew + or Rebind packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ALREADY_STARTED The state of the configured IA has already entered + Dhcp6Renewing when RebindRequest is FALSE. + The state of the configured IA has already entered + Dhcp6Rebinding when RebindRequest is TRUE. + @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted + by the user. + @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed + because of no response. + @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured + IA after the DHCPv6 renew/rebind exchange process. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6RenewRebind ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN RebindRequest + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + // + // The instance has already entered renewing or rebinding state. + // + if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) || + (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest) + ) { + return EFI_ALREADY_STARTED; + } + + if (Instance->IaCb.Ia->State != Dhcp6Bound) { + return EFI_ACCESS_DENIED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Send renew/rebind message to start exchange process. + // + Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Inform that one or more addresses assigned by a server are already + in use by another node. + + The Decline() function is used to manually decline the assignment of + IPv6 addresses, which have been already used by another node. If all + IPv6 addresses of the configured IA are declined through this function, + the state of the IA will switch through Dhcp6Declining to Dhcp6Init. + Otherwise, the state of the IA will restore to Dhcp6Bound after the + declining process. The Decline() can only be called when the IA is in + Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, + this function is a blocking operation. It will return after the + declining process finishes, or aborted by user. + + @param[in] This The pointer to EFI_DHCP6_PROTOCOL. + @param[in] AddressCount The number of declining addresses. + @param[in] Addresses The pointer to the buffer stored the declining + addresses. + + @retval EFI_SUCCESS The DHCPv6 decline exchange process completed + when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL. + The Dhcp6 instance sent Decline packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 decline exchange process aborted by user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Decline ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_DHCP6_IA *DecIa; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL || AddressCount == 0 || Addresses == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + if (Instance->IaCb.Ia->State != Dhcp6Bound) { + return EFI_ACCESS_DENIED; + } + + // + // Check whether all the declined addresses belongs to the configured Ia. + // + Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (EFI_ERROR(Status)) { + return Status; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Deprive of all the declined addresses from the configured Ia, and create a + // DeclineIa used to create decline message. + // + DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (DecIa == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send the decline message to start exchange process. + // + Status = Dhcp6SendDeclineMsg (Instance, DecIa); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + FreePool (DecIa); + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (DecIa != NULL) { + FreePool (DecIa); + } + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Release one or more addresses associated with the configured Ia + for current instance. + + The Release() function is used to manually release one or more + IPv6 addresses. If AddressCount is zero, it will release all IPv6 + addresses of the configured IA. If all IPv6 addresses of the IA are + released through this function, the state of the IA will switch + through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the + IA will restore to Dhcp6Bound after the releasing process. + The Release() can only be called when the IA is in Dhcp6Bound state. + If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is + a blocking operation. It will return after the releasing process + finishes, or is aborted by user. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] AddressCount The number of releasing addresses. + @param[in] Addresses The pointer to the buffer stored the releasing + addresses. + + @retval EFI_SUCCESS The DHCPv6 release exchange process + completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + was NULL. The Dhcp6 instance was sent Release + packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + was not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 release exchange process aborted by user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Release ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_DHCP6_IA *RelIa; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL || (AddressCount != 0 && Addresses == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + if (Instance->IaCb.Ia->State != Dhcp6Bound) { + return EFI_ACCESS_DENIED; + } + + // + // Check whether all the released addresses belongs to the configured Ia. + // + Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (EFI_ERROR(Status)) { + return Status; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Deprive of all the released addresses from the configured Ia, and create a + // ReleaseIa used to create release message. + // + RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (RelIa == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send the release message to start exchange process. + // + Status = Dhcp6SendReleaseMsg (Instance, RelIa); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + FreePool (RelIa); + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (RelIa != NULL) { + FreePool (RelIa); + } + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Parse the option data in the Dhcp6 packet. + + The Parse() function is used to retrieve the option list in the DHCPv6 packet. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] Packet The pointer to the Dhcp6 packet. + @param[in, out] OptionCount The number of option in the packet. + @param[out] PacketOptionList The array of pointers to each option in the packet. + + @retval EFI_SUCCESS The packet was successfully parsed. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options + that were found in the Packet. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Parse ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_PACKET *Packet, + IN OUT UINT32 *OptionCount, + OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL + ) +{ + UINT32 OptCnt; + UINT32 OptLen; + UINT16 DataLen; + UINT8 *Start; + UINT8 *End; + + if (This == NULL || Packet == NULL || OptionCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*OptionCount != 0 && PacketOptionList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) { + return EFI_INVALID_PARAMETER; + } + + // + // The format of Dhcp6 option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-code | option-len (option data) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-data | + // | (option-len octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + OptCnt = 0; + OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER); + Start = Packet->Dhcp6.Option; + End = Start + OptLen; + + // + // Calculate the number of option in the packet. + // + while (Start < End) { + DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen; + Start += (NTOHS (DataLen) + 4); + OptCnt++; + } + + // + // It will return buffer too small if pass-in option count is smaller than the + // actual count of options in the packet. + // + if (OptCnt > *OptionCount) { + *OptionCount = OptCnt; + return EFI_BUFFER_TOO_SMALL; + } + + ZeroMem ( + PacketOptionList, + (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *)) + ); + + OptCnt = 0; + Start = Packet->Dhcp6.Option; + + while (Start < End) { + + PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start; + DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen; + Start += (NTOHS (DataLen) + 4); + OptCnt++; + } + + return EFI_SUCCESS; +} + diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h new file mode 100644 index 0000000000..8fb1dfa382 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h @@ -0,0 +1,597 @@ +/** @file + Dhcp6 internal data structure and definition declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_DHCP6_IMPL_H__ +#define __EFI_DHCP6_IMPL_H__ + + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct _DHCP6_IA_CB DHCP6_IA_CB; +typedef struct _DHCP6_INF_CB DHCP6_INF_CB; +typedef struct _DHCP6_TX_CB DHCP6_TX_CB; +typedef struct _DHCP6_SERVICE DHCP6_SERVICE; +typedef struct _DHCP6_INSTANCE DHCP6_INSTANCE; + +#include "Dhcp6Utility.h" +#include "Dhcp6Io.h" +#include "Dhcp6Driver.h" + +#define DHCP6_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'S') +#define DHCP6_INSTANCE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'I') + +// +// Transmit parameters of solicit message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_SOL_MAX_DELAY 1 +#define DHCP6_SOL_IRT 1 +#define DHCP6_SOL_MRC 0 +#define DHCP6_SOL_MRT 120 +#define DHCP6_SOL_MRD 0 +// +// Transmit parameters of request message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REQ_IRT 1 +#define DHCP6_REQ_MRC 10 +#define DHCP6_REQ_MRT 30 +#define DHCP6_REQ_MRD 0 +// +// Transmit parameters of confirm message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_CNF_MAX_DELAY 1 +#define DHCP6_CNF_IRT 1 +#define DHCP6_CNF_MRC 0 +#define DHCP6_CNF_MRT 4 +#define DHCP6_CNF_MRD 10 +// +// Transmit parameters of renew message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REN_IRT 10 +#define DHCP6_REN_MRC 0 +#define DHCP6_REN_MRT 600 +#define DHCP6_REN_MRD 0 +// +// Transmit parameters of rebind message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REB_IRT 10 +#define DHCP6_REB_MRC 0 +#define DHCP6_REB_MRT 600 +#define DHCP6_REB_MRD 0 +// +// Transmit parameters of information request message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_INF_MAX_DELAY 1 +#define DHCP6_INF_IRT 1 +#define DHCP6_INF_MRC 0 +#define DHCP6_INF_MRT 120 +#define DHCP6_INF_MRD 0 +// +// Transmit parameters of release message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REL_IRT 1 +#define DHCP6_REL_MRC 5 +#define DHCP6_REL_MRT 0 +#define DHCP6_REL_MRD 0 +// +// Transmit parameters of decline message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_DEC_IRT 1 +#define DHCP6_DEC_MRC 5 +#define DHCP6_DEC_MRT 0 +#define DHCP6_DEC_MRD 0 + +#define DHCP6_PACKET_ALL 0 +#define DHCP6_PACKET_STATEFUL 1 +#define DHCP6_PACKET_STATELESS 2 + +#define DHCP6_BASE_PACKET_SIZE 1024 + +#define DHCP6_PORT_CLIENT 546 +#define DHCP6_PORT_SERVER 547 + +#define DHCP6_INSTANCE_FROM_THIS(Instance) CR ((Instance), DHCP6_INSTANCE, Dhcp6, DHCP6_INSTANCE_SIGNATURE) +#define DHCP6_SERVICE_FROM_THIS(Service) CR ((Service), DHCP6_SERVICE, ServiceBinding, DHCP6_SERVICE_SIGNATURE) + +extern EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress; +extern EFI_IPv6_ADDRESS mAllDhcpServersAddress; +extern EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate; + +// +// Enumeration of Dhcp6 message type, refers to section-5.3 of rfc-3315. +// +typedef enum { + Dhcp6MsgSolicit = 1, + Dhcp6MsgAdvertise = 2, + Dhcp6MsgRequest = 3, + Dhcp6MsgConfirm = 4, + Dhcp6MsgRenew = 5, + Dhcp6MsgRebind = 6, + Dhcp6MsgReply = 7, + Dhcp6MsgRelease = 8, + Dhcp6MsgDecline = 9, + Dhcp6MsgReconfigure = 10, + Dhcp6MsgInfoRequest = 11 +} DHCP6_MSG_TYPE; + +// +// Enumeration of option code in Dhcp6 packet, refers to section-24.3 of rfc-3315. +// +typedef enum { + Dhcp6OptClientId = 1, + Dhcp6OptServerId = 2, + Dhcp6OptIana = 3, + Dhcp6OptIata = 4, + Dhcp6OptIaAddr = 5, + Dhcp6OptRequestOption = 6, + Dhcp6OptPreference = 7, + Dhcp6OptElapsedTime = 8, + Dhcp6OptReplayMessage = 9, + Dhcp6OptAuthentication = 11, + Dhcp6OptServerUnicast = 12, + Dhcp6OptStatusCode = 13, + Dhcp6OptRapidCommit = 14, + Dhcp6OptUserClass = 15, + Dhcp6OptVendorClass = 16, + Dhcp6OptVendorInfo = 17, + Dhcp6OptInterfaceId = 18, + Dhcp6OptReconfigMessage = 19, + Dhcp6OptReconfigureAccept = 20 +} DHCP6_OPT_CODE; + +// +// Enumeration of status code recorded by IANA, refers to section-24.4 of rfc-3315. +// +typedef enum { + Dhcp6StsSuccess = 0, + Dhcp6StsUnspecFail = 1, + Dhcp6StsNoAddrsAvail = 2, + Dhcp6StsNoBinding = 3, + Dhcp6StsNotOnLink = 4, + Dhcp6StsUseMulticast = 5 +} DHCP6_STS_CODE; + +// +// Enumeration of Duid type recorded by IANA, refers to section-24.5 of rfc-3315. +// +typedef enum { + Dhcp6DuidTypeLlt = 1, + Dhcp6DuidTypeEn = 2, + Dhcp6DuidTypeLl = 3 +} DHCP6_DUID_TYPE; + +// +// Control block for each IA. +// +struct _DHCP6_IA_CB { + EFI_DHCP6_IA *Ia; + UINT32 T1; + UINT32 T2; + UINT32 AllExpireTime; + UINT32 LeaseTime; +}; + +// +// Control block for each transmitted message. +// +struct _DHCP6_TX_CB { + LIST_ENTRY Link; + UINT32 Xid; + EFI_DHCP6_PACKET *TxPacket; + EFI_DHCP6_RETRANSMISSION RetryCtl; + UINT32 RetryCnt; + UINT32 RetryExp; + UINT32 RetryLos; + UINT32 TickTime; + UINT16 *Elapsed; +}; + +// +// Control block for each info-request message. +// +struct _DHCP6_INF_CB { + LIST_ENTRY Link; + UINT32 Xid; + EFI_DHCP6_INFO_CALLBACK ReplyCallback; + VOID *CallbackContext; + EFI_EVENT TimeoutEvent; +}; + +// +// Control block for Dhcp6 instance, it's per configuration data. +// +struct _DHCP6_INSTANCE { + UINT32 Signature; + EFI_HANDLE Handle; + DHCP6_SERVICE *Service; + LIST_ENTRY Link; + EFI_DHCP6_PROTOCOL Dhcp6; + EFI_EVENT Timer; + EFI_DHCP6_CONFIG_DATA *Config; + EFI_DHCP6_IA *CacheIa; + DHCP6_IA_CB IaCb; + LIST_ENTRY TxList; + LIST_ENTRY InfList; + EFI_DHCP6_PACKET *AdSelect; + UINT8 AdPref; + EFI_IPv6_ADDRESS *Unicast; + EFI_STATUS UdpSts; + BOOLEAN InDestory; + BOOLEAN MediaPresent; + UINT64 StartTime; +}; + +// +// Control block for Dhcp6 service, it's per Nic handle. +// +struct _DHCP6_SERVICE { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_HANDLE Image; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_DHCP6_DUID *ClientId; + UDP_IO *UdpIo; + UINT32 Xid; + LIST_ENTRY Child; + UINTN NumOfChild; + BOOLEAN InDestory; +}; + +/** + Starts the DHCPv6 standard S.A.R.R. process. + + The Start() function starts the DHCPv6 standard process. This function can + be called only when the state of Dhcp6 instance is in the Dhcp6Init state. + If the DHCP process completes successfully, the state of the Dhcp6 instance + will be transferred through Dhcp6Selecting and Dhcp6Requesting to the + Dhcp6Bound state. + Refer to rfc-3315 for precise state transitions during this process. At the + time when each event occurs in this process, the callback function that was set + by EFI_DHCP6_PROTOCOL.Configure() will be called and the user can take this + opportunity to control the process. + + @param[in] This The pointer to Dhcp6 protocol. + + @retval EFI_SUCCESS The DHCPv6 standard process has started, or it + completed when CompletionEvent was NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no + response was received from the server within the + specified timeout value. + @retval EFI_ABORTED The user aborted the DHCPv6 process. + @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6 + standard process. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Start ( + IN EFI_DHCP6_PROTOCOL *This + ); + +/** + Stops the DHCPv6 standard S.A.R.R. process. + + The Stop() function is used to stop the DHCPv6 standard process. After this + function is called successfully, the state of Dhcp6 instance is transferred + into the Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called + before DHCPv6 standard process can be started again. This function can be + called when the Dhcp6 instance is in any state. + + @param[in] This The pointer to the Dhcp6 protocol. + + @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Stop ( + IN EFI_DHCP6_PROTOCOL *This + ); + +/** + Returns the current operating mode data for the Dhcp6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the Dhcp6 instance. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data. + @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data. + + @retval EFI_SUCCESS The mode data was returned. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance has not + been configured when Dhcp6ConfigData is + not NULL. +**/ +EFI_STATUS +EFIAPI +EfiDhcp6GetModeData ( + IN EFI_DHCP6_PROTOCOL *This, + OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL, + OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL + ); + +/** + Initializes, changes, or resets the operational settings for the Dhcp6 instance. + + The Configure() function is used to initialize or clean up the configuration + data of the Dhcp6 instance: + - When Dhcp6CfgData is not NULL and Configure() is called successfully, the + configuration data will be initialized in the Dhcp6 instance and the state + of the configured IA will be transferred into Dhcp6Init. + - When Dhcp6CfgData is NULL and Configure() is called successfully, the + configuration data will be cleaned up and no IA will be associated with + the Dhcp6 instance. + To update the configuration data for an Dhcp6 instance, the original data + must be cleaned up before setting the new configuration data. + + @param[in] This The pointer to the Dhcp6 protocol + @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA. + + @retval EFI_SUCCESS The Dhcp6 is configured successfully with the + Dhcp6Init state, or cleaned up the original + configuration setting. + @retval EFI_ACCESS_DENIED The Dhcp6 instance has been already configured + when Dhcp6CfgData is not NULL. + The Dhcp6 instance has already started the + DHCPv6 S.A.R.R when Dhcp6CfgData is NULL. + @retval EFI_INVALID_PARAMETER Some of the parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Configure ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL + ); + +/** + Request configuration information without the assignment of any + Ia addresses of the client. + + The InfoRequest() function is used to request configuration information + without the assignment of any IPv6 address of the client. Client sends + out Information Request packet to obtain the required configuration + information, and DHCPv6 server responds with Reply packet containing + the information for the client. The received Reply packet will be passed + to the user by ReplyCallback function. If user returns EFI_NOT_READY from + ReplyCallback, the Dhcp6 instance will continue to receive other Reply + packets unless timeout according to the Retransmission parameter. + Otherwise, the Information Request exchange process will be finished + successfully if user returns EFI_SUCCESS from ReplyCallback. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client + Identifier option and include it into Information Request + packet. Otherwise, Client Identifier option will not be included. + @param[in] OptionRequest The pointer to the buffer of option request options. + @param[in] OptionCount The option number in the OptionList. + @param[in] OptionList The list of appended options. + @param[in] Retransmission The pointer to the retransmission of the message. + @param[in] TimeoutEvent The event of timeout. + @param[in] ReplyCallback The callback function when a reply was received. + @param[in] CallbackContext The pointer to the parameter passed to the callback. + + @retval EFI_SUCCESS The DHCPv6 information request exchange process + completed when TimeoutEvent is NULL. Information + Request packet has been sent to DHCPv6 server when + TimeoutEvent is not NULL. + @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed + because of no response, or not all requested-options + are responded to by DHCPv6 servers when Timeout happened. + @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted + by the user. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6InfoRequest ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL, + IN EFI_DHCP6_RETRANSMISSION *Retransmission, + IN EFI_EVENT TimeoutEvent OPTIONAL, + IN EFI_DHCP6_INFO_CALLBACK ReplyCallback, + IN VOID *CallbackContext OPTIONAL + ); + +/** + Manually extend the valid and preferred lifetimes for the IPv6 addresses + of the configured IA and update other configuration parameters by sending + Renew or Rebind packet. + + The RenewRebind() function is used to manually extend the valid and preferred + lifetimes for the IPv6 addresses of the configured IA and update other + configuration parameters by sending a Renew or Rebind packet. + - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound, + it will send Renew packet to the previously DHCPv6 server and transfer the + state of the configured IA to Dhcp6Renewing. If valid Reply packet received, + the state transfers to Dhcp6Bound and the valid and preferred timer restarts. + If fails, the state transfers to Dhcp6Bound but the timer continues. + - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound, + it will send a Rebind packet. If a valid Reply packet is received, the state transfers + to Dhcp6Bound, and the valid and preferred timer restarts. If it fails, the state + transfers to Dhcp6Init, and the IA can't be used. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state. + Otherwise, Renew packet will be sent and enter Dhcp6Renewing state. + + @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process + completed and at least one IPv6 address of the + configured IA was bound again when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL. + The EFI DHCPv6 Protocol instance has sent Renew + or Rebind packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ALREADY_STARTED The state of the configured IA has already entered + Dhcp6Renewing when RebindRequest is FALSE. + The state of the configured IA has already entered + Dhcp6Rebinding when RebindRequest is TRUE. + @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted + by user. + @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed + because of no response. + @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured + IA after the DHCPv6 renew/rebind exchange process. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6RenewRebind ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN RebindRequest + ); + +/** + Inform that one or more addresses assigned by a server are already + in use by another node. + + The Decline() function is used to manually decline the assignment of + IPv6 addresses, which have been already used by another node. If all + IPv6 addresses of the configured IA are declined through this function, + the state of the IA will switch through Dhcp6Declining to Dhcp6Init. + Otherwise, the state of the IA will restore to Dhcp6Bound after the + declining process. The Decline() can only be called when the IA is in + Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, + this function is a blocking operation. It will return after the + declining process finishes, or aborted by user. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] AddressCount The number of declining addresses. + @param[in] Addresses The pointer to the buffer stored the declining + addresses. + + @retval EFI_SUCCESS The DHCPv6 decline exchange process completed + when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL. + The Dhcp6 instance has sent Decline packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 decline exchange process was aborted by the user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Decline ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + Release one or more addresses associated with the configured Ia + for the current instance. + + The Release() function is used to manually release the one or more + IPv6 address. If AddressCount is zero, it will release all IPv6 + addresses of the configured IA. If all IPv6 addresses of the IA are + released through this function, the state of the IA will switch + through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the + IA will restore to Dhcp6Bound after the releasing process. + The Release() can only be called when the IA is in a Dhcp6Bound state. + If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is + a blocking operation. It will return after the releasing process + finishes, or aborted by user. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] AddressCount The number of releasing addresses. + @param[in] Addresses The pointer to the buffer stored the releasing + addresses. + @retval EFI_SUCCESS The DHCPv6 release exchange process has + completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + is NULL. The Dhcp6 instance has sent Release + packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 release exchange process was aborted by the user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Release ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + Parse the option data in the Dhcp6 packet. + + The Parse() function is used to retrieve the option list in the DHCPv6 packet. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] Packet The pointer to the Dhcp6 packet. + @param[in, out] OptionCount The number of option in the packet. + @param[out] PacketOptionList The array of pointers to the each option in the packet. + + @retval EFI_SUCCESS The packet was successfully parsed. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options + that were found in the Packet. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Parse ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_PACKET *Packet, + IN OUT UINT32 *OptionCount, + OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL + ); + +#endif diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c new file mode 100644 index 0000000000..761f9c2d36 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c @@ -0,0 +1,2965 @@ +/** @file + Dhcp6 internal functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Dhcp6Impl.h" + + +/** + Enqueue the packet into the retry list in case of timeout. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 packet to retry. + @param[in] Elapsed The pointer to the elapsed time value in the packet. + @param[in] RetryCtl The pointer to the transmission control of the packet. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS Successfully enqueued the packet into the retry list according + to its message type. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected message type. + +**/ +EFI_STATUS +Dhcp6EnqueueRetry ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + IN UINT16 *Elapsed, + IN EFI_DHCP6_RETRANSMISSION *RetryCtl OPTIONAL + ) +{ + DHCP6_TX_CB *TxCb; + DHCP6_IA_CB *IaCb; + + ASSERT (Packet != NULL); + + IaCb = &Instance->IaCb; + TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB)); + + if (TxCb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Save tx packet pointer, and it will be destoryed when reply received. + // + TxCb->TxPacket = Packet; + TxCb->Xid = Packet->Dhcp6.Header.TransactionId; + + // + // Save pointer to elapsed-time value so we can update it on retransmits. + // + TxCb->Elapsed = Elapsed; + + // + // Calculate the retransmission according to the the message type. + // + switch (Packet->Dhcp6.Header.MessageType) { + case Dhcp6MsgSolicit: + // + // Calculate the retransmission threshold value for solicit packet. + // Use the default value by rfc-3315 if user doesn't configure. + // + if (RetryCtl == NULL) { + TxCb->RetryCtl.Irt = DHCP6_SOL_IRT; + TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC; + TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT; + TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD; + } else { + TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT; + TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC; + TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT; + TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD; + } + + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + FALSE + ); + break; + + case Dhcp6MsgRequest: + // + // Calculate the retransmission threshold value for request packet. + // + TxCb->RetryCtl.Irt = DHCP6_REQ_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT; + TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgConfirm: + // + // Calculate the retransmission threshold value for confirm packet. + // + TxCb->RetryCtl.Irt = DHCP6_CNF_IRT; + TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC; + TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT; + TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRenew: + // + // Calculate the retransmission threshold value for renew packet. + // + TxCb->RetryCtl.Irt = DHCP6_REB_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REB_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REB_MRT; + TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRebind: + // + // Calculate the retransmission threshold value for rebind packet. + // + TxCb->RetryCtl.Irt = DHCP6_REN_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REN_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REN_MRT; + TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgDecline: + // + // Calculate the retransmission threshold value for decline packet. + // + TxCb->RetryCtl.Irt = DHCP6_DEC_IRT; + TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC; + TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT; + TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRelease: + // + // Calculate the retransmission threshold value for release packet. + // + TxCb->RetryCtl.Irt = DHCP6_REL_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REL_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REL_MRT; + TxCb->RetryCtl.Mrd = DHCP6_REL_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgInfoRequest: + // + // Calculate the retransmission threshold value for info-request packet. + // Use the default value by rfc-3315 if user doesn't configure. + // + if (RetryCtl == NULL) { + TxCb->RetryCtl.Irt = DHCP6_INF_IRT; + TxCb->RetryCtl.Mrc = DHCP6_INF_MRC; + TxCb->RetryCtl.Mrt = DHCP6_INF_MRT; + TxCb->RetryCtl.Mrd = DHCP6_INF_MRD; + } else { + TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT; + TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC; + TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT; + TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD; + } + + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + default: + // + // Unexpected message type. + // + return EFI_DEVICE_ERROR; + } + + // + // Insert into the retransmit list of the instance. + // + InsertTailList (&Instance->TxList, &TxCb->Link); + + return EFI_SUCCESS; +} + + +/** + Dequeue the packet from retry list if reply received or timeout at last. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] PacketXid The packet transaction id to match. + @param[in] NeedSignal If TRUE, then an timeout event need be signaled when it is existed. + Otherwise, this parameter is ignored. + + @retval EFI_SUCCESS Successfully dequeued the packet into retry list . + @retval EFI_NOT_FOUND There is no xid matched in retry list. + +**/ +EFI_STATUS +Dhcp6DequeueRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 PacketXid, + IN BOOLEAN NeedSignal + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_TX_CB *TxCb; + DHCP6_INF_CB *InfCb; + + // + // Seek the retransmit node in the retransmit list by packet xid. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->Xid == PacketXid) { + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + + // + // Seek the info-request node in the info-request list by packet xid. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) { + + InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link); + + if (InfCb->Xid == PacketXid) { + // + // Remove the info-request node, and signal the event if timeout. + // + if (InfCb->TimeoutEvent != NULL && NeedSignal) { + gBS->SignalEvent (InfCb->TimeoutEvent); + } + + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + } + } + } + // + // Remove the retransmit node. + // + RemoveEntryList (&TxCb->Link); + ASSERT(TxCb->TxPacket); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Clean up the specific nodes in the retry list. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Scope The scope of cleanup nodes. + +**/ +VOID +Dhcp6CleanupRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 Scope + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_TX_CB *TxCb; + DHCP6_INF_CB *InfCb; + + // + // Clean up all the stateful messages from the retransmit list. + // + if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) { + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) { + RemoveEntryList (&TxCb->Link); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + } + } + } + + // + // Clean up all the stateless messages from the retransmit list. + // + if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) { + + // + // Clean up all the retransmit list for stateless messages. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + RemoveEntryList (&TxCb->Link); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + } + } + + // + // Clean up all the info-request messages list. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) { + + InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link); + + if (InfCb->TimeoutEvent != NULL) { + gBS->SignalEvent (InfCb->TimeoutEvent); + } + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + } + } +} + + +/** + Clean up the session of the instance stateful exchange. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Status The return status from udp. + +**/ +VOID +Dhcp6CleanupSession ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_STATUS Status + ) +{ + UINTN Index; + EFI_DHCP6_IA *Ia; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + // + // Clean up the retransmit list for stateful messages. + // + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL); + + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + } + + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + + // + // Reinitialize the Ia fields of the instance. + // + Instance->UdpSts = Status; + Instance->AdSelect = NULL; + Instance->AdPref = 0; + Instance->Unicast = NULL; + Instance->IaCb.T1 = 0; + Instance->IaCb.T2 = 0; + Instance->IaCb.AllExpireTime = 0; + Instance->IaCb.LeaseTime = 0; + + // + // Clear start time + // + Instance->StartTime = 0; + + Ia = Instance->IaCb.Ia; + Ia->State = Dhcp6Init; + Ia->ReplyPacket = NULL; + + // + // Set the addresses as zero lifetime, and then the notify + // function in Ip6Config will remove these timeout address. + // + for (Index = 0; Index < Ia->IaAddressCount; Index++) { + Ia->IaAddress[Index].PreferredLifetime = 0; + Ia->IaAddress[Index].ValidLifetime = 0; + } + + // + // + // Signal the Ia information updated event to informal user. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } +} + + +/** + Callback to user when Dhcp6 transmit/receive occurs. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Event The current Dhcp6 event. + @param[in, out] Packet The pointer to the packet sending or received. + + @retval EFI_SUCCESS The user function returns success. + @retval EFI_NOT_READY Direct the caller to continue collecting the offer. + @retval EFI_ABORTED The user function ask it to abort. + +**/ +EFI_STATUS +EFIAPI +Dhcp6CallbackUser ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_EVENT Event, + IN OUT EFI_DHCP6_PACKET **Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *NewPacket; + EFI_DHCP6_CALLBACK Callback; + VOID *Context; + + ASSERT (Packet != NULL); + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + + NewPacket = NULL; + Status = EFI_SUCCESS; + Callback = Instance->Config->Dhcp6Callback; + Context = Instance->Config->CallbackContext; + + // + // Callback to user with the new message if has. + // + if (Callback != NULL) { + + Status = Callback ( + &Instance->Dhcp6, + Context, + Instance->IaCb.Ia->State, + Event, + *Packet, + &NewPacket + ); + // + // Updated the new packet from user to replace the original one. + // + if (NewPacket != NULL) { + ASSERT (*Packet != NULL); + FreePool (*Packet); + *Packet = NewPacket; + } + } + + return Status; +} + + +/** + Update Ia according to the new reply message. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to reply messages. + + @retval EFI_SUCCESS Updated the Ia information successfully. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6UpdateIaInfo ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_STATE State; + UINT8 *Option; + UINT8 *IaInnerOpt; + UINT16 IaInnerLen; + UINT16 StsCode; + UINT32 T1; + UINT32 T2; + + ASSERT (Instance->Config != NULL); + // + // If the reply was received in reponse to a solicit with rapid commit option, + // request, renew or rebind message, the client updates the information it has + // recorded about IAs from the IA options contained in the reply message: + // 1. record the T1 and T2 times + // 2. add any new addresses in the IA + // 3. discard any addresses from the IA, that have a valid lifetime of 0 + // 4. update lifetimes for any addresses that alread recorded + // 5. leave unchanged any information about addresses + // + // See details in the section-18.1.8 of rfc-3315. + // + State = Dhcp6Init; + Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length - sizeof (EFI_DHCP6_HEADER), + &Instance->Config->IaDescriptor + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // The format of the IA_NA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // The format of the IA_TA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_TA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len + IaId) = 8 + // sizeof (option-code + option-len + IaId + T1) = 12 + // sizeof (option-code + option-len + IaId + T1 + T2) = 16 + // + // The inner options still start with 2 bytes option-code and 2 bytes option-len. + // + if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) { + T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8))); + T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12))); + IaInnerOpt = Option + 16; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12); + } else { + T1 = 0; + T2 = 0; + IaInnerOpt = Option + 8; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4); + } + + // + // The format of the Status Code option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_STATUS_CODE | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | status-code | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + // . . + // . status-message . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len) = 4 + // + StsCode = Dhcp6StsSuccess; + Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode); + + if (Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Generate control block for the Ia. + // + Status = Dhcp6GenerateIaCb ( + Instance, + IaInnerOpt, + IaInnerLen, + T1, + T2 + ); + + return Status; +} + + + +/** + Seek StatusCode Option in package. A Status Code option may appear in the + options field of a DHCP message and/or in the options field of another option. + See details in section 22.13, RFC3315. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to reply messages. + @param[out] Option The pointer to status code option. + + @retval EFI_SUCCESS Seek status code option successfully. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6SeekStsOption ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + OUT UINT8 **Option + ) +{ + UINT8 *IaInnerOpt; + UINT16 IaInnerLen; + UINT16 StsCode; + + // + // Seek StatusCode option directly in DHCP message body. That is, search in + // non-encapsulated option fields. + // + *Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptStatusCode + ); + + if (*Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Seek in encapsulated options, IA_NA and IA_TA. + // + *Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length - sizeof (EFI_DHCP6_HEADER), + &Instance->Config->IaDescriptor + ); + if (*Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // The format of the IA_NA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // The format of the IA_TA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_TA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len + IaId) = 8 + // sizeof (option-code + option-len + IaId + T1) = 12 + // sizeof (option-code + option-len + IaId + T1 + T2) = 16 + // + // The inner options still start with 2 bytes option-code and 2 bytes option-len. + // + if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) { + IaInnerOpt = *Option + 16; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12); + } else { + IaInnerOpt = *Option + 8; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4); + } + + // + // The format of the Status Code option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_STATUS_CODE | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | status-code | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + // . . + // . status-message . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len) = 4 + // + *Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode); + if (*Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} + + +/** + Transmit Dhcp6 message by udpio. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to transmit message. + @param[in] Elapsed The pointer to the elapsed time value to fill in. + + @retval EFI_SUCCESS Successfully transmitted the packet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Dhcp6TransmitPacket ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + IN UINT16 *Elapsed + ) +{ + EFI_STATUS Status; + NET_BUF *Wrap; + NET_FRAGMENT Frag; + UDP_END_POINT EndPt; + DHCP6_SERVICE *Service; + + Service = Instance->Service; + + // + // Wrap it into a netbuf then send it. + // + Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header; + Frag.Len = Packet->Length; + + // + // Do not register free packet here, which will be handled in retry list. + // + Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Multicast the Dhcp6 message, unless get the unicast server address by option. + // + ZeroMem (&EndPt, sizeof (UDP_END_POINT)); + + if (Instance->Unicast != NULL) { + CopyMem ( + &EndPt.RemoteAddr, + Instance->Unicast, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + CopyMem ( + &EndPt.RemoteAddr, + &mAllDhcpRelayAndServersAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + EndPt.RemotePort = DHCP6_PORT_SERVER; + EndPt.LocalPort = DHCP6_PORT_CLIENT; + + // + // Update the elapsed time value. + // + if (Elapsed != NULL) { + SetElapsedTime (Elapsed, Instance); + } + + // + // Send out the message by the configured Udp6Io. + // + Status = UdpIoSendDatagram ( + Service->UdpIo, + Wrap, + &EndPt, + NULL, + Dhcp6OnTransmitted, + NULL + ); + + if (EFI_ERROR (Status)) { + NetbufFree (Wrap); + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Create the solicit message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6SendSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + Service = Instance->Service; + ClientId = Service->ClientId; + UserLen = 0; + + ASSERT (Service->ClientId != NULL); + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + + // + // Calculate the added length of customized option list. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgSolicit; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for solicit message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send solicit packet with the state transition from Dhcp6init to + // Dhcp6selecting. + // + Instance->IaCb.Ia->State = Dhcp6Selecting; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry ( + Instance, + Packet, + Elapsed, + Instance->Config->SolicitRetransmission + ); +} + +/** + Configure some parameter to initiate SolicitMsg. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6InitSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + Instance->IaCb.T1 = 0; + Instance->IaCb.T2 = 0; + Instance->IaCb.Ia->IaAddressCount = 0; + + return Dhcp6SendSolicitMsg (Instance); +} + + +/** + Create the request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the request message. + +**/ +EFI_STATUS +Dhcp6SendRequestMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(Instance->AdSelect != NULL); + ASSERT(Instance->Config != NULL); + ASSERT(Instance->IaCb.Ia != NULL); + ASSERT(Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + + ASSERT(ClientId != NULL); + + // + // Get the server Id from the selected advertisement message. + // + Option = Dhcp6SeekOption ( + Instance->AdSelect->Dhcp6.Option, + Instance->AdSelect->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgRequest; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for request message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send request packet with the state transition from Dhcp6selecting to + // Dhcp6requesting. + // + Instance->IaCb.Ia->State = Dhcp6Requesting; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the decline message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] DecIa The pointer to the decline Ia. + + @retval EFI_SUCCESS Created and sent the decline message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the decline message. + +**/ +EFI_STATUS +Dhcp6SendDeclineMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *DecIa + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT16 Length; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + LastReply = Instance->IaCb.Ia->ReplyPacket; + + ASSERT (ClientId != NULL); + ASSERT (LastReply != NULL); + + // + // Get the server Id from the last reply message. + // + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // EFI_DHCP6_DUID contains a length field of 2 bytes. + // + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgDecline; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0); + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send decline packet with the state transition from Dhcp6bound to + // Dhcp6declining. + // + Instance->IaCb.Ia->State = Dhcp6Declining; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the release message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RelIa The pointer to the release Ia. + + @retval EFI_SUCCESS Created and sent the release message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the release message. + +**/ +EFI_STATUS +Dhcp6SendReleaseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *RelIa + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT16 Length; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Service = Instance->Service; + ClientId = Service->ClientId; + LastReply = Instance->IaCb.Ia->ReplyPacket; + + ASSERT(ClientId); + ASSERT(LastReply); + + // + // Get the server Id from the last reply message. + // + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgRelease; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + // + // ServerId is extracted from packet, it's network order. + // + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0); + + // + // Determine the size/length of packet + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send release packet with the state transition from Dhcp6bound to + // Dhcp6releasing. + // + Instance->IaCb.Ia->State = Dhcp6Releasing; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the renew/rebind message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RebindRequest If TRUE, it is a Rebind type message. + Otherwise, it is a Renew type message. + + @retval EFI_SUCCESS Created and sent the renew/rebind message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the renew/rebind message. + +**/ +EFI_STATUS +Dhcp6SendRenewRebindMsg ( + IN DHCP6_INSTANCE *Instance, + IN BOOLEAN RebindRequest + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + EFI_DHCP6_STATE State; + EFI_DHCP6_EVENT Event; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Service = Instance->Service; + ClientId = Service->ClientId; + + ASSERT(ClientId); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + if (!RebindRequest) { + // + // Get the server Id from the last reply message and + // insert it for rebind request. + // + LastReply = Instance->IaCb.Ia->ReplyPacket; + ASSERT (LastReply); + + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + FreePool (Packet); + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + } + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + State = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing; + Event = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing; + + Status = Dhcp6CallbackUser (Instance, Event, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send renew/rebind packet with the state transition from Dhcp6bound to + // Dhcp6renew/rebind. + // And sync the lease time when send renew/rebind, in case that user send + // renew/rebind actively. + // + Instance->IaCb.Ia->State = State; + Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the information request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] InfCb The pointer to the information request control block. + @param[in] SendClientId If TRUE, the client identifier option will be included in + information request message. Otherwise, the client identifier + option will not be included. + @param[in] OptionRequest The pointer to the option request option. + @param[in] OptionCount The number options in the OptionList. + @param[in] OptionList The array pointers to the appended options. + @param[in] Retransmission The pointer to the retransmission control. + + @retval EFI_SUCCESS Created and sent the info-request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the info-request message. + +**/ +EFI_STATUS +Dhcp6SendInfoRequestMsg ( + IN DHCP6_INSTANCE *Instance, + IN DHCP6_INF_CB *InfCb, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[], + IN EFI_DHCP6_RETRANSMISSION *Retransmission + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(OptionRequest); + + Service = Instance->Service; + ClientId = Service->ClientId; + UserLen = NTOHS (OptionRequest->OpLen) + 4; + + ASSERT(ClientId); + + // + // Calculate the added length of customized option list. + // + for (Index = 0; Index < OptionCount; Index++) { + UserLen += (NTOHS (OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgInfoRequest; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + InfCb->Xid = Packet->Dhcp6.Header.TransactionId; + + // + // Assembly Dhcp6 options for info-request message. + // + Cursor = Packet->Dhcp6.Option; + + if (SendClientId) { + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + } + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + OptionRequest->OpCode, + OptionRequest->OpLen, + OptionRequest->Data + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < OptionCount; Index++) { + + UserOpt = OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Send info-request packet with no state. + // + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission); +} + + +/** + Create the Confirm message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the confirm message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the confirm message. + +**/ +EFI_STATUS +Dhcp6SendConfirmMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT8 *Cursor; + UINTN Index; + UINT16 Length; + UINT32 UserLen; + EFI_STATUS Status; + DHCP6_SERVICE *Service; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + UINT16 *Elapsed; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + ASSERT (ClientId != NULL); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize common fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgConfirm; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for solicit message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption ( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send confirm packet with the state transition from Dhcp6Bound to + // Dhcp6Confirming. + // + Instance->IaCb.Ia->State = Dhcp6Confirming; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + + +/** + Handle with the Dhcp6 reply message. + + @param[in] Instance The pointer to Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 reply message. + + @retval EFI_SUCCESS Processed the reply message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to process the reply message. + +**/ +EFI_STATUS +Dhcp6HandleReplyMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 *Option; + UINT16 StsCode; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Packet != NULL); + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + return EFI_DEVICE_ERROR; + } + + // + // If the client subsequently receives a valid reply message that includes a + // rapid commit option since send a solicit with rapid commit option before, + // preocess the reply message and discard any reply messages received in + // response to the request message. + // See details in the section-17.1.4 of rfc-3315. + // + Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptRapidCommit + ); + + if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) { + return EFI_DEVICE_ERROR; + } + + // + // As to a valid reply packet in response to a request/renew/rebind packet, + // ignore the packet if not contains the Ia option + // + if (Instance->IaCb.Ia->State == Dhcp6Requesting || + Instance->IaCb.Ia->State == Dhcp6Renewing || + Instance->IaCb.Ia->State == Dhcp6Rebinding + ) { + + Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length, + &Instance->Config->IaDescriptor + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Dequeue the sent packet from retransmit list since reply received. + // + Status = Dhcp6DequeueRetry ( + Instance, + Packet->Dhcp6.Header.TransactionId, + FALSE + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // When receive a valid reply packet in response to a decline/release packet, + // the client considers the decline/release event completed regardless of the + // status code. + // + if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) { + + if (Instance->IaCb.Ia->IaAddressCount != 0) { + Instance->IaCb.Ia->State = Dhcp6Bound; + } else { + ASSERT (Instance->IaCb.Ia->ReplyPacket); + FreePool (Instance->IaCb.Ia->ReplyPacket); + Instance->IaCb.Ia->ReplyPacket = NULL; + Instance->IaCb.Ia->State = Dhcp6Init; + } + + // + // For sync, set the success flag out of polling in decline/release. + // + Instance->UdpSts = EFI_SUCCESS; + + // + // For async, signal the Ia event to inform Ia infomation update. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } + + // + // Reset start time for next exchange. + // + Instance->StartTime = 0; + + return EFI_SUCCESS; + } + + // + // Upon the receipt of a valid reply packet in response to a solicit, request, + // confirm, renew and rebind, the behavior depends on the status code option. + // See the details in the section-18.1.8 of rfc-3315. + // + Option = NULL; + Status = Dhcp6SeekStsOption ( + Instance, + Packet, + &Option + ); + + if (!EFI_ERROR (Status)) { + // + // Reset start time for next exchange. + // + Instance->StartTime = 0; + + // + // No status code or no error status code means succeed to reply. + // + Status = Dhcp6UpdateIaInfo (Instance, Packet); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set bound state and store the reply packet. + // + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + + Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size); + + if (Instance->IaCb.Ia->ReplyPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size); + + Instance->IaCb.Ia->State = Dhcp6Bound; + + // + // For sync, set the success flag out of polling in start/renewrebind. + // + Instance->UdpSts = EFI_SUCCESS; + + // + // Maybe this is a new round DHCP process due to some reason, such as NotOnLink + // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that + // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP + // consumers can be notified to flush old address. + // + Dhcp6AppendCacheIa (Instance); + + // + // For async, signal the Ia event to inform Ia infomation update. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } + } else if (Option != NULL) { + // + // Any error status code option is found. + // + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + switch (StsCode) { + case Dhcp6StsUnspecFail: + // + // It indicates the server is unable to process the message due to an + // unspecified failure condition, so just retry if possible. + // + break; + + case Dhcp6StsUseMulticast: + // + // It indicates the server receives a message via unicast from a client + // to which the server has not sent a unicast option, so retry it by + // multi-cast address. + // + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + Instance->Unicast = NULL; + } + break; + + case Dhcp6StsNotOnLink: + if (Instance->IaCb.Ia->State == Dhcp6Confirming) { + // + // Before initiate new round DHCP, cache the current IA. + // + Status = Dhcp6CacheIa (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Restart S.A.R.R process to acquire new address. + // + Status = Dhcp6InitSolicitMsg (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + } + break; + + default: + // + // The other status code, just restart solicitation. + // + break; + } + } + + return EFI_SUCCESS; +} + + +/** + Select the appointed Dhcp6 advertisement message. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] AdSelect The pointer to the selected Dhcp6 advertisement message. + + @retval EFI_SUCCESS Selected the right advertisement message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to select the advertise message. + +**/ +EFI_STATUS +Dhcp6SelectAdvertiseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *AdSelect + ) +{ + EFI_STATUS Status; + UINT8 *Option; + + ASSERT (AdSelect != NULL); + + // + // Callback to user with the selected advertisement packet, and the user + // might overwrite it. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect); + + if (EFI_ERROR (Status)) { + return Status; + } + + Instance->AdSelect = AdSelect; + + // + // Dequeue the sent packet for the retransmission since advertisement selected. + // + Status = Dhcp6DequeueRetry ( + Instance, + AdSelect->Dhcp6.Header.TransactionId, + FALSE + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Check whether there is server unicast option in the selected advertise + // packet, and update it. + // + Option = Dhcp6SeekOption( + AdSelect->Dhcp6.Option, + AdSelect->Length - 4, + Dhcp6OptServerUnicast + ); + + if (Option != NULL) { + + Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS)); + + if (Instance->Unicast == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS)); + } + + // + // Update the information of the Ia by the selected advertisement message. + // + Status = Dhcp6UpdateIaInfo (Instance, AdSelect); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Send the request message to continue the S.A.R.R. process. + // + return Dhcp6SendRequestMsg (Instance); +} + + +/** + Handle with the Dhcp6 advertisement message. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 advertisement message. + + @retval EFI_SUCCESS Processed the advertisement message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to process the advertise message. + +**/ +EFI_STATUS +Dhcp6HandleAdvertiseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 *Option; + UINT16 StsCode; + BOOLEAN Timeout; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Timeout = FALSE; + StsCode = Dhcp6StsSuccess; + + // + // If the client does receives a valid reply message that includes a rapid + // commit option since a solicit with rapid commit optioin sent before, select + // this reply message. Or else, process the advertise messages as normal. + // See details in the section-17.1.4 of rfc-3315. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptRapidCommit + ); + + if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) { + + return Dhcp6HandleReplyMsg (Instance, Packet); + } + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) { + return EFI_DEVICE_ERROR; + } + + // + // Client must ignore any advertise message that includes a status code option + // containing the value noaddrsavail, with the exception that the client may + // display the associated status message to the user. + // See the details in the section-17.1.3 of rfc-3315. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptStatusCode + ); + + if (Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet); + + if (!EFI_ERROR (Status)) { + // + // Success means user choose the current advertisement packet. + // + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + // + // Store the selected advertisement packet and set a flag. + // + Instance->AdSelect = AllocateZeroPool (Packet->Size); + + if (Instance->AdSelect == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->AdSelect, Packet, Packet->Size); + + Instance->AdPref = 0xff; + + } else if (Status == EFI_NOT_READY) { + // + // Not_ready means user wants to continue to receive more advertise packets. + // + if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) { + // + // It's a tricky point. The timer routine set adpref as 0xff if the first + // rt timeout and no advertisement received, which means any advertisement + // received will be selected after the first rt. + // + Timeout = TRUE; + } + + // + // Check whether the current packet has a 255 preference option or not. + // Take non-preference option as 0 value. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptPreference + ); + + if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) { + // + // No advertisements received before or preference is more than other + // advertisements received before. Then store the new packet and the + // preference value. + // + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + Instance->AdSelect = AllocateZeroPool (Packet->Size); + + if (Instance->AdSelect == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->AdSelect, Packet, Packet->Size); + + if (Option != NULL) { + Instance->AdPref = *(Option + 4); + } + } else { + // + // Non-preference and other advertisements received before or current + // preference is less than other advertisements received before. + // Leave the packet alone. + } + + } else { + // + // Other error status means termination. + // + return Status; + } + + // + // Client must collect advertise messages as more as possible until the first + // RT has elapsed, or get a highest preference 255 advertise. + // See details in the section-17.1.2 of rfc-3315. + // + if (Instance->AdPref == 0xff || Timeout) { + Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect); + } + + return Status; +} + + +/** + The Dhcp6 stateful exchange process routine. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the received Dhcp6 message. + +**/ +VOID +Dhcp6HandleStateful ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Option; + + Service = Instance->Service; + ClientId = Service->ClientId; + Status = EFI_SUCCESS; + + if (Instance->InDestory || Instance->Config == NULL) { + goto ON_CONTINUE; + } + + ASSERT (ClientId); + ASSERT (Instance->Config); + ASSERT (Instance->IaCb.Ia); + + // + // Discard the packet if not advertisement or reply packet. + // + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + goto ON_CONTINUE; + } + + // + // Check whether include client Id or not. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptClientId + ); + + if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) { + goto ON_CONTINUE; + } + + // + // Check whether include server Id or not. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptServerId + ); + + if (Option == NULL) { + goto ON_CONTINUE; + } + + switch (Instance->IaCb.Ia->State) { + case Dhcp6Selecting: + // + // Handle the advertisement message when in the Dhcp6Selecting state. + // Do not need check return status, if failed, just continue to the next. + // + Dhcp6HandleAdvertiseMsg (Instance, Packet); + break; + + case Dhcp6Requesting: + case Dhcp6Confirming: + case Dhcp6Renewing: + case Dhcp6Rebinding: + case Dhcp6Releasing: + case Dhcp6Declining: + // + // Handle the reply message when in the Dhcp6Requesting, Dhcp6Renewing + // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state. + // If failed here, it should reset the current session. + // + Status = Dhcp6HandleReplyMsg (Instance, Packet); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + break; + default: + // + // Other state has not supported yet. + // + break; + } + +ON_CONTINUE: + // + // Continue to receive the following Dhcp6 message. + // + Status = UdpIoRecvDatagram ( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp6CleanupSession (Instance, Status); + } +} + + +/** + The Dhcp6 stateless exchange process routine. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the received Dhcp6 message. + +**/ +VOID +Dhcp6HandleStateless ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + DHCP6_SERVICE *Service; + DHCP6_INF_CB *InfCb; + UINT8 *Option; + BOOLEAN IsMatched; + + Service = Instance->Service; + Status = EFI_SUCCESS; + IsMatched = FALSE; + InfCb = NULL; + + if (Instance->InDestory) { + goto ON_EXIT; + } + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + goto ON_EXIT; + } + + // + // Check whether it's a desired Info-request message by Xid. + // + while (!IsListEmpty (&Instance->InfList)) { + InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link); + if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) { + IsMatched = TRUE; + break; + } + } + + if (!IsMatched) { + goto ON_EXIT; + } + + // + // Check whether include server Id or not. + // + Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptServerId + ); + + if (Option == NULL) { + goto ON_EXIT; + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = InfCb->ReplyCallback ( + &Instance->Dhcp6, + InfCb->CallbackContext, + Packet + ); + + if (Status == EFI_NOT_READY) { + // + // Success or aborted will both stop this info-request exchange process, + // but not ready means user wants to continue to receive reply. + // + goto ON_EXIT; + } + + // + // Dequeue the sent packet from the txlist if the xid matched, and ignore + // if no xid matched. + // + Dhcp6DequeueRetry ( + Instance, + Packet->Dhcp6.Header.TransactionId, + FALSE + ); + + // + // For sync, set the status out of polling for info-request. + // + Instance->UdpSts = Status; + +ON_EXIT: + + Status = UdpIoRecvDatagram ( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status)) { + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS); + } +} + + +/** + The receive callback function for Dhcp6 exchange process. + + @param[in] Udp6Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6ReceivePacket ( + IN NET_BUF *Udp6Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + EFI_DHCP6_HEADER *Head; + EFI_DHCP6_PACKET *Packet; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + DHCP6_TX_CB *TxCb; + UINT32 Size; + BOOLEAN IsDispatched; + BOOLEAN IsStateless; + LIST_ENTRY *Entry1; + LIST_ENTRY *Next1; + LIST_ENTRY *Entry2; + LIST_ENTRY *Next2; + + ASSERT (Udp6Wrap != NULL); + ASSERT (Context != NULL); + + Service = (DHCP6_SERVICE *) Context; + Instance = NULL; + Packet = NULL; + IsDispatched = FALSE; + IsStateless = FALSE; + + if (EFI_ERROR (IoStatus)) { + return ; + } + + // + // Copy the net buffer received from upd6 to a Dhcp6 packet. + // + Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize; + Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size); + + if (Packet == NULL) { + goto ON_CONTINUE; + } + + Packet->Size = Size; + Head = &Packet->Dhcp6.Header; + Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head); + + if (Packet->Length == 0) { + goto ON_CONTINUE; + } + + // + // Dispatch packet to right instance by transaction id. + // + NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) { + + Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link); + + NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link); + + if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) { + // + // Find the corresponding packet in tx list, and check it whether belongs + // to stateful exchange process. + // + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + IsStateless = TRUE; + } + IsDispatched = TRUE; + break; + } + } + + if (IsDispatched) { + break; + } + } + + // + // Skip this packet if not dispatched to any instance. + // + if (!IsDispatched) { + goto ON_CONTINUE; + } + + // + // Dispatch the received packet ot the right instance. + // + if (IsStateless) { + Dhcp6HandleStateless (Instance, Packet); + } else { + Dhcp6HandleStateful (Instance, Packet); + } + +ON_CONTINUE: + + NetbufFree (Udp6Wrap); + + if (Packet != NULL) { + FreePool (Packet); + } +} + +/** + Detect Link movement for specified network device. + + This routine will try to invoke Snp->GetStatus() to get the media status. + If media present status switches from unpresent to present, a link movement + is detected. Note that the underlying UNDI driver may not support reporting + media status from GET_STATUS command. If that, fail to detect link movement. + + @param[in] Instance The pointer to DHCP6_INSTANCE. + + @retval TRUE A link movement is detected. + @retval FALSE A link movement is not detected. + +**/ +BOOLEAN +Dhcp6LinkMovDetect ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT32 InterruptStatus; + BOOLEAN MediaPresent; + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + ASSERT (Instance != NULL); + Snp = Instance->Service->Snp; + MediaPresent = Instance->MediaPresent; + + // + // Check whether SNP support media detection + // + if (!Snp->Mode->MediaPresentSupported) { + return FALSE; + } + + // + // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data + // + Status = Snp->GetStatus (Snp, &InterruptStatus, NULL); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Instance->MediaPresent = Snp->Mode->MediaPresent; + // + // Media transimit Unpresent to Present means new link movement is detected. + // + if (!MediaPresent && Instance->MediaPresent) { + return TRUE; + } + return FALSE; +} + + +/** + The timer routine of the Dhcp6 instance for each second. + + @param[in] Event The timer event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_INSTANCE *Instance; + DHCP6_TX_CB *TxCb; + DHCP6_IA_CB *IaCb; + UINT32 LossTime; + + ASSERT (Context != NULL); + + Instance = (DHCP6_INSTANCE *) Context; + + // + // 1. Loop the tx list, count live time of every tx packet to check whether + // need re-transmit or not. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + + TxCb->TickTime++; + + if (TxCb->TickTime > TxCb->RetryExp) { + // + // Handle the first rt in the transmission of solicit specially. + // + if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) { + if (Instance->AdSelect == NULL) { + // + // Set adpref as 0xff here to indicate select any advertisement + // afterwards. + // + Instance->AdPref = 0xff; + } else { + // + // Select the advertisement received before. + // + Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect); + return; + } + } + // + // Increase the retry count for the packet and add up the total loss time. + // + TxCb->RetryCnt++; + TxCb->RetryLos += TxCb->RetryExp; + + // + // Check whether overflow the max retry count limit for this packet + // + if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) { + goto ON_CLOSE; + } + + // + // Check whether overflow the max retry duration for this packet + // + if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) { + goto ON_CLOSE; + } + + // + // Re-calculate retry expire timeout for the next time. + // + // Firstly, Check the new calculated time whether overflow the max retry + // expire time. + // + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryExp, + FALSE, + TRUE + ); + + if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) { + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Mrt, + TRUE, + TRUE + ); + } + + // + // Secondly, Check the new calculated time whether overflow the max retry + // duration time. + // + LossTime = TxCb->RetryLos + TxCb->RetryExp; + if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) { + TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos; + } + + // + // Reset the tick time for the next retransmission + // + TxCb->TickTime = 0; + + // + // Retransmit the last sent packet again. + // + Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed); + } + } + + // + // 2. Check the configured Ia, count lease time of every valid Ia to check + // whether need to renew or rebind this Ia. + // + IaCb = &Instance->IaCb; + + if (Instance->Config == NULL || IaCb->Ia == NULL) { + return; + } + + if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) { + + IaCb->LeaseTime++; + + if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) { + // + // Exceed t2, send rebind packet to extend the Ia lease. + // + Dhcp6SendRenewRebindMsg (Instance, TRUE); + + } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) { + + // + // Exceed t1, send renew packet to extend the Ia lease. + // + Dhcp6SendRenewRebindMsg (Instance, FALSE); + } + } + + // + // 3. In any situation when a client may have moved to a new link, the + // client MUST initiate a Confirm/Reply message exchange. + // + if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) { + Dhcp6SendConfirmMsg (Instance); + } + + return; + + ON_CLOSE: + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest || + TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew || + TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm + ) { + // + // The failure of renew/Confirm will still switch to the bound state. + // + if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) || + (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) { + ASSERT (Instance->IaCb.Ia); + Instance->IaCb.Ia->State = Dhcp6Bound; + } + // + // The failure of info-request will return no response. + // + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + Instance->UdpSts = EFI_NO_RESPONSE; + } + Dhcp6DequeueRetry ( + Instance, + TxCb->Xid, + TRUE + ); + } else { + // + // The failure of the others will terminate current state machine if timeout. + // + Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE); + } +} diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h new file mode 100644 index 0000000000..31459c96d3 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h @@ -0,0 +1,193 @@ +/** @file + Dhcp6 internal functions declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_DHCP6_IO_H__ +#define __EFI_DHCP6_IO_H__ + + +/** + Clean up the specific nodes in the retry list. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Scope The scope of cleanup nodes. + +**/ +VOID +Dhcp6CleanupRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 Scope + ); + +/** + Clean up the session of the instance stateful exchange. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Status The return status from udp. + +**/ +VOID +Dhcp6CleanupSession ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_STATUS Status + ); + +/** + Create the solicit message and send it. + + @param[in] Instance The pointer to Dhcp6 instance. + + @retval EFI_SUCCESS Create and send the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6SendSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ); + +/** + Create the request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Create and send the request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the request message. + +**/ +EFI_STATUS +Dhcp6SendRequestMsg ( + IN DHCP6_INSTANCE *Instance + ); + +/** + Create the renew/rebind message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RebindRequest If TRUE, it is a Rebind type message. + Otherwise, it is a Renew type message. + + @retval EFI_SUCCESS Create and send the renew/rebind message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the renew/rebind message. + +**/ +EFI_STATUS +Dhcp6SendRenewRebindMsg ( + IN DHCP6_INSTANCE *Instance, + IN BOOLEAN RebindRequest + ); + +/** + Create the decline message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] DecIa The pointer to the decline Ia. + + @retval EFI_SUCCESS Create and send the decline message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the decline message. + +**/ +EFI_STATUS +Dhcp6SendDeclineMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *DecIa + ); + +/** + Create the release message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RelIa The pointer to the release Ia. + + @retval EFI_SUCCESS Create and send the release message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the release message. + +**/ +EFI_STATUS +Dhcp6SendReleaseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *RelIa + ); + +/** + Create the information request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] InfCb The pointer to the information request control block. + @param[in] SendClientId If TRUE, the client identifier option will be included in + information request message. Otherwise, the client identifier + option will not be included. + @param[in] OptionRequest The pointer to the option request option. + @param[in] OptionCount The number options in the OptionList. + @param[in] OptionList The array pointers to the appended options. + @param[in] Retransmission The pointer to the retransmission control. + + @retval EFI_SUCCESS Create and send the info-request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the info-request message. + +**/ +EFI_STATUS +Dhcp6SendInfoRequestMsg ( + IN DHCP6_INSTANCE *Instance, + IN DHCP6_INF_CB *InfCb, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[], + IN EFI_DHCP6_RETRANSMISSION *Retransmission + ); + +/** + The receive callback function for the Dhcp6 exchange process. + + @param[in] Udp6Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6ReceivePacket ( + IN NET_BUF *Udp6Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + +/** + The timer routine of the Dhcp6 instance for each second. + + @param[in] Event The timer event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c new file mode 100644 index 0000000000..be7a985551 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c @@ -0,0 +1,1146 @@ +/** @file + Dhcp6 support functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Dhcp6Impl.h" + + +/** + Generate client Duid in the format of Duid-llt. + + @param[in] Mode The pointer to the mode of SNP. + + @retval NULL If it failed to generate a client Id. + @retval others The pointer to the new client id. + +**/ +EFI_DHCP6_DUID * +Dhcp6GenerateClientId ( + IN EFI_SIMPLE_NETWORK_MODE *Mode + ) +{ + EFI_STATUS Status; + EFI_DHCP6_DUID *Duid; + EFI_TIME Time; + UINT32 Stamp; + + // + // Attempt to get client Id from variable to keep it constant. + // See details in section-9 of rfc-3315. + // + Duid = GetVariable (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid); + if (Duid != NULL) { + return Duid; + } + + // + // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month. + // + gRT->GetTime (&Time, NULL); + Stamp = (UINT32) + ( + (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) * + 60 + + Time.Second + ); + + // + // The format of client identifier option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_CLIENTID | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // . . + // . DUID . + // . (variable length) . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // + // The format of DUID-LLT: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Duid type (1) | hardware type (16 bits) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time (32 bits) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // . . + // . link-layer address (variable length) . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes + // + Duid = AllocateZeroPool (10 + Mode->HwAddressSize); + if (Duid == NULL) { + return NULL; + } + + // + // sizeof (Duid-type + hardware-type + time) = 8 bytes + // + Duid->Length = (UINT16) (Mode->HwAddressSize + 8); + + // + // Set the Duid-type, hardware-type, time and copy the hardware address. + // + WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt)); + WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET)); + WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp)); + + CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize); + + Status = gRT->SetVariable ( + L"ClientId", + &gEfiDhcp6ServiceBindingProtocolGuid, + (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS), + Duid->Length + 2, + (VOID *) Duid + ); + ASSERT_EFI_ERROR (Status); + + return Duid; +} + + +/** + Copy the Dhcp6 configure data. + + @param[in] DstCfg The pointer to the destination configure data. + @param[in] SorCfg The pointer to the source configure data. + + @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CopyConfigData ( + IN EFI_DHCP6_CONFIG_DATA *DstCfg, + IN EFI_DHCP6_CONFIG_DATA *SorCfg + ) +{ + UINTN Index; + UINTN OptionListSize; + UINTN OptionSize; + + CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA)); + + // + // Allocate another buffer for solicitretransmission, and copy it. + // + if (SorCfg->SolicitRetransmission != NULL) { + + DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION)); + + if (DstCfg->SolicitRetransmission == NULL) { + // + // Error will be handled out of this function. + // + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + DstCfg->SolicitRetransmission, + SorCfg->SolicitRetransmission, + sizeof (EFI_DHCP6_RETRANSMISSION) + ); + } + + if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) { + + OptionListSize = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *); + DstCfg->OptionList = AllocateZeroPool (OptionListSize); + + if (DstCfg->OptionList == NULL) { + // + // Error will be handled out of this function. + // + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < SorCfg->OptionCount; Index++) { + + OptionSize = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4; + DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize); + + if (DstCfg->OptionList[Index] == NULL) { + // + // Error will be handled out of this function. + // + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + DstCfg->OptionList[Index], + SorCfg->OptionList[Index], + OptionSize + ); + } + } + + return EFI_SUCCESS; +} + + +/** + Clean up the configure data. + + @param[in, out] CfgData The pointer to the configure data. + +**/ +VOID +Dhcp6CleanupConfigData ( + IN OUT EFI_DHCP6_CONFIG_DATA *CfgData + ) +{ + UINTN Index; + + ASSERT (CfgData != NULL); + // + // Clean up all fields in config data including the reference buffers, but do + // not free the config data buffer itself. + // + if (CfgData->OptionList != NULL) { + for (Index = 0; Index < CfgData->OptionCount; Index++) { + if (CfgData->OptionList[Index] != NULL) { + FreePool (CfgData->OptionList[Index]); + } + } + FreePool (CfgData->OptionList); + } + + if (CfgData->SolicitRetransmission != NULL) { + FreePool (CfgData->SolicitRetransmission); + } + + ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA)); +} + + +/** + Clean up the mode data. + + @param[in, out] ModeData The pointer to the mode data. + +**/ +VOID +Dhcp6CleanupModeData ( + IN OUT EFI_DHCP6_MODE_DATA *ModeData + ) +{ + ASSERT (ModeData != NULL); + // + // Clean up all fields in mode data including the reference buffers, but do + // not free the mode data buffer itself. + // + if (ModeData->ClientId != NULL) { + FreePool (ModeData->ClientId); + } + + if (ModeData->Ia != NULL) { + + if (ModeData->Ia->ReplyPacket != NULL) { + FreePool (ModeData->Ia->ReplyPacket); + } + FreePool (ModeData->Ia); + } + + ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA)); +} + + +/** + Calculate the expire time by the algorithm defined in rfc. + + @param[in] Base The base value of the time. + @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time. + @param[in] NeedSigned If TRUE, the the signed factor is needed. + + @return Expire The calculated result for the new expire time. + +**/ +UINT32 +Dhcp6CalculateExpireTime ( + IN UINT32 Base, + IN BOOLEAN IsFirstRt, + IN BOOLEAN NeedSigned + ) +{ + EFI_TIME Time; + BOOLEAN Signed; + UINT32 Seed; + UINT32 Expire; + + // + // Take the 10bits of microsecond in system time as a uniform distribution. + // Take the 10th bit as a flag to determine it's signed or not. + // + gRT->GetTime (&Time, NULL); + Seed = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK); + Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE); + Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE); + + // + // Calculate expire by the following algo: + // 1. base + base * (-0.1 ~ 0) for the first solicit + // 2. base + base * (-0.1 ~ 0.1) for the first other messages + // 3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages + // 4. base + base * (-0.1 ~ 0) for the more than mrt timeout + // + // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1). + // + if (IsFirstRt && Signed) { + + Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + + } else if (IsFirstRt && !Signed) { + + Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + + } else if (!IsFirstRt && Signed) { + + Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + + } else { + + Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + } + + Expire = (Expire != 0) ? Expire : 1; + + return Expire; +} + + +/** + Calculate the lease time by the algorithm defined in rfc. + + @param[in] IaCb The pointer to the Ia control block. + +**/ +VOID +Dhcp6CalculateLeaseTime ( + IN DHCP6_IA_CB *IaCb + ) +{ + EFI_DHCP6_IA_ADDRESS *IaAddr; + UINT32 MinLt; + UINT32 MaxLt; + UINTN Index; + + ASSERT (IaCb->Ia->IaAddressCount > 0); + + MinLt = (UINT32) (-1); + MaxLt = 0; + + // + // Calculate minlt as min of all valid life time, and maxlt as max of all + // valid life time. + // + for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) { + IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS); + MinLt = MIN (MinLt, IaAddr->ValidLifetime); + MaxLt = MAX (MinLt, IaAddr->ValidLifetime); + } + + // + // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer + // such information. + // + IaCb->T1 = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10); + IaCb->T2 = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10); + IaCb->AllExpireTime = MaxLt; + IaCb->LeaseTime = 0; +} + + +/** + Check whether the addresses are all included by the configured Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval EFI_SUCCESS The addresses are all included by the configured IA. + @retval EFI_NOT_FOUND The addresses are not included by the configured IA. + +**/ +EFI_STATUS +Dhcp6CheckAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + UINTN Index1; + UINTN Index2; + BOOLEAN Found; + + // + // Check whether the addresses are all included by the configured IA. And it + // will return success if address count is zero, which means all addresses. + // + for (Index1 = 0; Index1 < AddressCount; Index1++) { + + Found = FALSE; + + for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) { + + if (CompareMem ( + &Addresses[Index1], + &Ia->IaAddress[Index2], + sizeof (EFI_IPv6_ADDRESS) + ) == 0) { + + Found = TRUE; + break; + } + } + + if (!Found) { + return EFI_NOT_FOUND; + } + } + + return EFI_SUCCESS; +} + + +/** + Deprive the addresses from current Ia, and generate another eliminated Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval NULL If it failed to generate the deprived Ia. + @retval others The pointer to the deprived Ia. + +**/ +EFI_DHCP6_IA * +Dhcp6DepriveAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + EFI_DHCP6_IA *IaCopy; + UINTN IaCopySize; + UINTN Index1; + UINTN Index2; + BOOLEAN Found; + + if (AddressCount == 0) { + // + // It means release all Ia addresses if address count is zero. + // + AddressCount = Ia->IaAddressCount; + } + + ASSERT (AddressCount != 0); + + IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + IaCopy = AllocateZeroPool (IaCopySize); + + if (IaCopy == NULL) { + return NULL; + } + + if (AddressCount == Ia->IaAddressCount) { + // + // If release all Ia addresses, just copy the configured Ia and then set + // its address count as zero. + // We may decline/release part of addresses at the begining. So it's a + // forwarding step to update address infor for decline/release, while the + // other infor such as Ia state will be updated when receiving reply. + // + CopyMem (IaCopy, Ia, IaCopySize); + Ia->IaAddressCount = 0; + return IaCopy; + } + + CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA)); + + // + // Move the addresses from the Ia of instance to the deprived Ia. + // + for (Index1 = 0; Index1 < AddressCount; Index1++) { + + Found = FALSE; + + for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) { + + if (CompareMem ( + &Addresses[Index1], + &Ia->IaAddress[Index2], + sizeof (EFI_IPv6_ADDRESS) + ) == 0) { + // + // Copy the deprived address to the copy of Ia + // + CopyMem ( + &IaCopy->IaAddress[Index1], + &Ia->IaAddress[Index2], + sizeof (EFI_DHCP6_IA_ADDRESS) + ); + // + // Delete the deprived address from the instance Ia + // + if (Index2 + 1 < Ia->IaAddressCount) { + CopyMem ( + &Ia->IaAddress[Index2], + &Ia->IaAddress[Index2 + 1], + (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS) + ); + } + Found = TRUE; + break; + } + } + ASSERT (Found == TRUE); + } + + Ia->IaAddressCount -= AddressCount; + IaCopy->IaAddressCount = AddressCount; + + return IaCopy; +} + + +/** + The dummy ext buffer free callback routine. + + @param[in] Arg The pointer to the parameter. + +**/ +VOID +EFIAPI +Dhcp6DummyExtFree ( + IN VOID *Arg + ) +{ +} + + +/** + The callback routine once message transmitted. + + @param[in] Udp6Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTransmitted ( + IN NET_BUF *Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + NetbufFree (Wrap); +} + + +/** + Append the option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the buffer. + @param[in] OptType The option type. + @param[in] OptLen The length of option contents. + @param[in] Data The pointer to the option content. + + @return Buf The position to append the next option. + +**/ +UINT8 * +Dhcp6AppendOption ( + IN OUT UINT8 *Buf, + IN UINT16 OptType, + IN UINT16 OptLen, + IN UINT8 *Data + ) +{ + // + // The format of Dhcp6 option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-code | option-len (option data) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-data | + // | (option-len octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + ASSERT (OptLen != 0); + + WriteUnaligned16 ((UINT16 *) Buf, OptType); + Buf += 2; + WriteUnaligned16 ((UINT16 *) Buf, OptLen); + Buf += 2; + CopyMem (Buf, Data, NTOHS (OptLen)); + Buf += NTOHS (OptLen); + + return Buf; +} + + +/** + Append the appointed Ia option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Ia The pointer to the Ia. + @param[in] T1 The time of T1. + @param[in] T2 The time of T2. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendIaOption ( + IN OUT UINT8 *Buf, + IN EFI_DHCP6_IA *Ia, + IN UINT32 T1, + IN UINT32 T2 + ) +{ + UINT8 *AddrOpt; + UINT16 *Len; + UINTN Index; + UINT16 Length; + + // + // The format of IA_NA and IA_TA option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 (only for IA_NA) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 (only for IA_NA) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options/IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Fill the value of Ia option type + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type)); + Buf += 2; + + // + // Fill the len of Ia option later, keep the pointer first + // + Len = (UINT16 *) Buf; + Buf += 2; + + // + // Fill the value of iaid + // + WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId)); + Buf += 4; + + // + // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified. + // + if (Ia->Descriptor.Type == Dhcp6OptIana) { + WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff)); + Buf += 4; + WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff)); + Buf += 4; + } + + // + // Fill all the addresses belong to the Ia + // + for (Index = 0; Index < Ia->IaAddressCount; Index++) { + + AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS); + Length = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS)); + Buf = Dhcp6AppendOption ( + Buf, + HTONS (Dhcp6OptIaAddr), + Length, + AddrOpt + ); + } + + // + // Fill the value of Ia option length + // + *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2)); + + return Buf; +} + +/** + Append the appointed Elapsed time option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. + @param[out] Elapsed The pointer to the elapsed time value in + the generated packet. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendETOption ( + IN OUT UINT8 *Buf, + IN DHCP6_INSTANCE *Instance, + OUT UINT16 **Elapsed + ) +{ + // + // The format of elapsed time option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_ELAPSED_TIME | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | elapsed-time | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Fill the value of elapsed-time option type. + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime)); + Buf += 2; + + // + // Fill the len of elapsed-time option, which is fixed. + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS(2)); + Buf += 2; + + // + // Fill in elapsed time value with 0 value for now. The actual value is + // filled in later just before the packet is transmitted. + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS(0)); + *Elapsed = (UINT16 *) Buf; + Buf += 2; + + return Buf; +} + +/** + Set the elapsed time based on the given instance and the pointer to the + elapsed time option. + + @param[in] Elapsed The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. + +**/ +VOID +SetElapsedTime ( + IN UINT16 *Elapsed, + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_TIME Time; + UINT64 CurrentStamp; + UINT64 ElapsedTimeValue; + + // + // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month. + // + gRT->GetTime (&Time, NULL); + CurrentStamp = (UINT64) + ( + ((((((Time.Year - 2000) * 360 + + (Time.Month - 1)) * 30 + + (Time.Day - 1)) * 24 + Time.Hour) * 60 + + Time.Minute) * 60 + Time.Second) * 100 + + DivU64x32(Time.Nanosecond, 10000000) + ); + + // + // Sentinel value of 0 means that this is the first DHCP packet that we are + // sending and that we need to initialize the value. First DHCP Solicit + // gets 0 elapsed-time. Otherwise, calculate based on StartTime. + // + if (Instance->StartTime == 0) { + ElapsedTimeValue = 0; + Instance->StartTime = CurrentStamp; + } else { + ElapsedTimeValue = CurrentStamp - Instance->StartTime; + + // + // If elapsed time cannot fit in two bytes, set it to 0xffff. + // + if (ElapsedTimeValue > 0xffff) { + ElapsedTimeValue = 0xffff; + } + } + WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue)); +} + + +/** + Seek the address of the first byte of the option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] OptType The option type. + + @retval NULL If it failed to seek the option. + @retval others The position to the option. + +**/ +UINT8 * +Dhcp6SeekOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN UINT16 OptType + ) +{ + UINT8 *Cursor; + UINT8 *Option; + UINT16 DataLen; + UINT16 OpCode; + + Option = NULL; + Cursor = Buf; + + // + // The format of Dhcp6 option refers to Dhcp6AppendOption(). + // + while (Cursor < Buf + SeekLen) { + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + if (OpCode == HTONS (OptType)) { + Option = Cursor; + break; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } + + return Option; +} + + +/** + Seek the address of the first byte of the Ia option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] IaDesc The pointer to the Ia descriptor. + + @retval NULL If it failed to seek the Ia option. + @retval others The position to the Ia option. + +**/ +UINT8 * +Dhcp6SeekIaOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc + ) +{ + UINT8 *Cursor; + UINT8 *Option; + UINT16 DataLen; + UINT16 OpCode; + UINT32 IaId; + + // + // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption(). + // + Option = NULL; + Cursor = Buf; + + while (Cursor < Buf + SeekLen) { + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + IaId = ReadUnaligned32 ((UINT32 *) (Cursor + 4)); + if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) { + Option = Cursor; + break; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } + + return Option; +} + + +/** + Parse the address option and update the address infomation. + + @param[in] IaInnerOpt The pointer to the buffer. + @param[in] IaInnerLen The length to parse. + @param[out] AddrNum The number of addresses. + @param[in, out] AddrBuf The pointer to the address buffer. + +**/ +VOID +Dhcp6ParseAddrOption ( + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + OUT UINT32 *AddrNum, + IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf + ) +{ + UINT8 *Cursor; + UINT16 DataLen; + UINT16 OpCode; + UINT32 ValidLt; + + // + // The format of the IA Address option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IAADDR | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // | IPv6 address | + // | | + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | preferred-lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | valid-lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // . . + // . IAaddr-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Two usage model: + // + // 1. Pass addrbuf == null, to get the addrnum over the Ia inner options. + // 2. Pass addrbuf != null, to resolve the addresses over the Ia inner + // options to the addrbuf. + // + + Cursor = IaInnerOpt; + *AddrNum = 0; + + while (Cursor < IaInnerOpt + IaInnerLen) { + // + // Count the Ia address option with non-0 valid time. + // + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24)); + if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) { + + if (AddrBuf != NULL) { + CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS)); + AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime); + AddrBuf->ValidLifetime = NTOHL (AddrBuf->ValidLifetime); + AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS)); + } + + (*AddrNum)++; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } +} + + +/** + Create a control blcok for the Ia according to the corresponding options. + + @param[in] Instance The pointer to DHCP6 Instance. + @param[in] IaInnerOpt The pointer to the inner options in the Ia option. + @param[in] IaInnerLen The length of all the inner options in the Ia option. + @param[in] T1 T1 time in the Ia option. + @param[in] T2 T2 time in the Ia option. + + @retval EFI_NOT_FOUND No valid IA option is found. + @retval EFI_SUCCESS Create an IA control block successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6GenerateIaCb ( + IN DHCP6_INSTANCE *Instance, + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + IN UINT32 T1, + IN UINT32 T2 + ) +{ + UINT32 AddrNum; + UINT32 IaSize; + EFI_DHCP6_IA *Ia; + + if (Instance->IaCb.Ia == NULL) { + return EFI_NOT_FOUND; + } + + // + // Calculate the number of addresses for this Ia, excluding the addresses with + // the value 0 of valid lifetime. + // + Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL); + + if (AddrNum == 0) { + return EFI_NOT_FOUND; + } + + // + // Allocate for new IA. + // + IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + Ia = AllocateZeroPool (IaSize); + + if (Ia == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill up this new IA fields. + // + Ia->State = Instance->IaCb.Ia->State; + Ia->IaAddressCount = AddrNum; + CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR)); + Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress); + + // + // Free original IA resource. + // + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + FreePool (Instance->IaCb.Ia); + + + ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB)); + + // + // Update IaCb to use new IA. + // + Instance->IaCb.Ia = Ia; + + // + + // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime. + // + Instance->IaCb.T1 = T1; + Instance->IaCb.T2 = T2; + Dhcp6CalculateLeaseTime (&Instance->IaCb); + + return EFI_SUCCESS; +} + + +/** + Cache the current IA configuration information. + + @param[in] Instance The pointer to DHCP6 Instance. + + @retval EFI_SUCCESS Cache the current IA successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CacheIa ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINTN IaSize; + EFI_DHCP6_IA *Ia; + + Ia = Instance->IaCb.Ia; + + if ((Instance->CacheIa == NULL) && (Ia != NULL)) { + // + // Cache the current IA. + // + IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + + Instance->CacheIa = AllocateZeroPool (IaSize); + if (Instance->CacheIa == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Instance->CacheIa, Ia, IaSize); + } + return EFI_SUCCESS; +} + +/** + Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0. + + @param[in] Instance The pointer to DHCP6 instance. + +**/ +VOID +Dhcp6AppendCacheIa ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT8 *Ptr; + UINTN Index; + UINTN IaSize; + UINTN NewIaSize; + EFI_DHCP6_IA *Ia; + EFI_DHCP6_IA *NewIa; + EFI_DHCP6_IA *CacheIa; + + Ia = Instance->IaCb.Ia; + CacheIa = Instance->CacheIa; + + if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) { + // + // There are old addresses existing. Merge with current addresses. + // + NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + NewIa = AllocateZeroPool (NewIaSize); + if (NewIa == NULL) { + return; + } + + IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + CopyMem (NewIa, Ia, IaSize); + + // + // Clear old address.ValidLifetime + // + for (Index = 0; Index < CacheIa->IaAddressCount; Index++) { + CacheIa->IaAddress[Index].ValidLifetime = 0; + } + + NewIa->IaAddressCount += CacheIa->IaAddressCount; + Ptr = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount]; + CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS)); + + // + // Migrate to the NewIa and free previous. + // + FreePool (Instance->CacheIa); + FreePool (Instance->IaCb.Ia); + Instance->CacheIa = NULL; + Instance->IaCb.Ia = NewIa; + } +} diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h new file mode 100644 index 0000000000..62985a3d72 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h @@ -0,0 +1,340 @@ +/** @file + Dhcp6 support functions declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_DHCP6_UTILITY_H__ +#define __EFI_DHCP6_UTILITY_H__ + + +#define DHCP6_10_BIT_MASK 0x3ff + +/** + Generate client Duid in the format of Duid-llt. + + @param[in] Mode The pointer to the mode of SNP. + + @retval NULL if failed to generate client Id. + @retval Others The pointer to the new client id. + +**/ +EFI_DHCP6_DUID * +Dhcp6GenerateClientId ( + IN EFI_SIMPLE_NETWORK_MODE *Mode + ); + +/** + Copy the Dhcp6 configure data. + + @param[in] DstCfg The pointer to the destination configure data. + @param[in] SorCfg The pointer to the source configure data. + + @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CopyConfigData ( + IN EFI_DHCP6_CONFIG_DATA *DstCfg, + IN EFI_DHCP6_CONFIG_DATA *SorCfg + ); + +/** + Clean up the configure data. + + @param[in, out] CfgData The pointer to the configure data. + +**/ +VOID +Dhcp6CleanupConfigData ( + IN OUT EFI_DHCP6_CONFIG_DATA *CfgData + ); + +/** + Clean up the mode data. + + @param[in, out] ModeData The pointer to the mode data. + +**/ +VOID +Dhcp6CleanupModeData ( + IN OUT EFI_DHCP6_MODE_DATA *ModeData + ); + +/** + Calculate the expire time by the algorithm defined in rfc. + + @param[in] Base The base value of the time. + @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time. + @param[in] NeedSigned If TRUE, the the signed factor is needed. + + @return Expire The calculated result for the new expire time. + +**/ +UINT32 +Dhcp6CalculateExpireTime ( + IN UINT32 Base, + IN BOOLEAN IsFirstRt, + IN BOOLEAN NeedSigned + ); + +/** + Calculate the lease time by the algorithm defined in rfc. + + @param[in] IaCb The pointer to the Ia control block. + +**/ +VOID +Dhcp6CalculateLeaseTime ( + IN DHCP6_IA_CB *IaCb + ); + +/** + Check whether the addresses are all included by the configured Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval EFI_SUCCESS The addresses are all included by the configured IA. + @retval EFI_NOT_FOUND The addresses are not included by the configured IA. + +**/ +EFI_STATUS +Dhcp6CheckAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + Deprive the addresses from current Ia, and generate another eliminated Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval NULL If failed to generate the deprived Ia. + @retval others The pointer to the deprived Ia. + +**/ +EFI_DHCP6_IA * +Dhcp6DepriveAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + The dummy ext buffer free callback routine. + + @param[in] Arg The pointer to the parameter. + +**/ +VOID +EFIAPI +Dhcp6DummyExtFree ( + IN VOID *Arg + ); + +/** + The callback routine once message transmitted. + + @param[in] Udp6Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTransmitted ( + IN NET_BUF *Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + +/** + Append the appointed option to the buf, and move the buf to the end. + + @param[in, out] Buf The pointer to buffer. + @param[in] OptType The option type. + @param[in] OptLen The lenght of option content.s + @param[in] Data The pointer to the option content. + + @return Buf The position to append the next option. + +**/ +UINT8 * +Dhcp6AppendOption ( + IN OUT UINT8 *Buf, + IN UINT16 OptType, + IN UINT16 OptLen, + IN UINT8 *Data + ); + +/** + Append the Ia option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Ia The pointer to the Ia. + @param[in] T1 The time of T1. + @param[in] T2 The time of T2. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendIaOption ( + IN OUT UINT8 *Buf, + IN EFI_DHCP6_IA *Ia, + IN UINT32 T1, + IN UINT32 T2 + ); + +/** + Append the appointed Elapsed time option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. + @param[out] Elapsed The pointer to the elapsed time value in + the generated packet. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendETOption ( + IN OUT UINT8 *Buf, + IN DHCP6_INSTANCE *Instance, + OUT UINT16 **Elapsed + ); + +/** + Set the elapsed time based on the given instance and the pointer to the + elapsed time option. + + @param[in] Elapsed The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. +**/ +VOID +SetElapsedTime ( + IN UINT16 *Elapsed, + IN DHCP6_INSTANCE *Instance + ); + +/** + Seek the address of the first byte of the option header. + + @param[in] Buf The pointer to buffer. + @param[in] SeekLen The length to seek. + @param[in] OptType The option type. + + @retval NULL If failed to seek the option. + @retval others The position to the option. + +**/ +UINT8 * +Dhcp6SeekOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN UINT16 OptType + ); + +/** + Seek the address of the first byte of the Ia option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] IaDesc The pointer to the Ia descriptor. + + @retval NULL If failed to seek the Ia option. + @retval others The position to the Ia option. + +**/ +UINT8 * +Dhcp6SeekIaOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc + ); + +/** + Parse the address option and update the address info. + + @param[in] IaInnerOpt The pointer to the buffer. + @param[in] IaInnerLen The length to parse. + @param[out] AddrNum The number of addresses. + @param[in, out] AddrBuf The pointer to the address buffer. + +**/ +VOID +Dhcp6ParseAddrOption ( + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + OUT UINT32 *AddrNum, + IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf + ); + +/** + Create a control blcok for the Ia according to the corresponding options. + + @param[in] Instance The pointer to DHCP6 Instance. + @param[in] IaInnerOpt The pointer to the inner options in the Ia option. + @param[in] IaInnerLen The length of all the inner options in the Ia option. + @param[in] T1 T1 time in the Ia option. + @param[in] T2 T2 time in the Ia option. + + @retval EFI_NOT_FOUND No valid IA option is found. + @retval EFI_SUCCESS Create an IA control block successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6GenerateIaCb ( + IN DHCP6_INSTANCE *Instance, + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + IN UINT32 T1, + IN UINT32 T2 + ); + + +/** + Cache the current IA configuration information. + + @param[in] Instance The pointer to DHCP6 Instance. + + @retval EFI_SUCCESS Cache the current IA successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CacheIa ( + IN DHCP6_INSTANCE *Instance + ); + + +/** + Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0. + + @param[in] Instance The pointer to DHCP6 instance. + +**/ +VOID +Dhcp6AppendCacheIa ( + IN DHCP6_INSTANCE *Instance + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/ComponentName.c b/NetworkPkg/Ip6Dxe/ComponentName.c new file mode 100644 index 0000000000..c8382f74a9 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/ComponentName.c @@ -0,0 +1,313 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL protocol. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName = { + Ip6ComponentNameGetDriverName, + Ip6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIp6DriverNameTable[] = { + { + "eng;en", + L"IP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIp6DriverNameTable, + DriverName, + (BOOLEAN) (This == &gIp6ComponentName) + ); + +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.c b/NetworkPkg/Ip6Dxe/Ip6Common.c new file mode 100644 index 0000000000..2ae14a952c --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Common.c @@ -0,0 +1,796 @@ +/** @file + The implementation of common functions shared by IP6 driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL, + only the address count is returned. + + @param[in] IpSb The IP6 service binding instance. + @param[out] AddressCount The number of returned addresses. + @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO. + This is an optional parameter. + + + @retval EFI_SUCCESS The address array successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6BuildEfiAddressList ( + IN IP6_SERVICE *IpSb, + OUT UINT32 *AddressCount, + OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL + ) +{ + UINT32 Count; + LIST_ENTRY *Entry; + EFI_IP6_ADDRESS_INFO *EfiAddrInfo; + IP6_ADDRESS_INFO *AddrInfo; + + if (AddressCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IpSb->LinkLocalOk) { + Count = 1 + IpSb->DefaultInterface->AddressCount; + } else { + Count = 0; + } + + *AddressCount = Count; + + if ((AddressList == NULL) || (Count == 0)) { + return EFI_SUCCESS; + } + + if (*AddressList == NULL) { + *AddressList = AllocatePool (sizeof (EFI_IP6_ADDRESS_INFO) * Count); + if (*AddressList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + EfiAddrInfo = *AddressList; + + IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &IpSb->LinkLocalAddr); + EfiAddrInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + + EfiAddrInfo++; + Count = 1; + + NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &AddrInfo->Address); + EfiAddrInfo->PrefixLength = AddrInfo->PrefixLength; + + EfiAddrInfo++; + Count++; + } + + ASSERT (Count == *AddressCount); + + return EFI_SUCCESS; +} + +/** + Generate the multicast addresses identify the group of all IPv6 nodes or IPv6 + routers defined in RFC4291. + + All Nodes Addresses: FF01::1, FF02::1. + All Router Addresses: FF01::2, FF02::2, FF05::2. + + @param[in] Router If TRUE, generate all routers addresses, + else generate all node addresses. + @param[in] Scope interface-local(1), link-local(2), or site-local(5) + @param[out] Ip6Addr The generated multicast address. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The address is generated. + +**/ +EFI_STATUS +Ip6SetToAllNodeMulticast ( + IN BOOLEAN Router, + IN UINT8 Scope, + OUT EFI_IPv6_ADDRESS *Ip6Addr + ) +{ + if (Ip6Addr == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Router && Scope == IP6_SITE_LOCAL_SCOPE) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (Ip6Addr, sizeof (EFI_IPv6_ADDRESS)); + Ip6Addr->Addr[0] = 0xFF; + Ip6Addr->Addr[1] = Scope; + + if (!Router) { + Ip6Addr->Addr[15] = 0x1; + } else { + Ip6Addr->Addr[15] = 0x2; + } + + return EFI_SUCCESS; +} + +/** + This function converts MAC address to 64 bits interface ID according to RFC4291 + and returns the interface ID. Currently only 48-bit MAC address is supported by + this function. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL The operation fails. + @return Pointer to the generated interface ID. + +**/ +UINT8 * +Ip6CreateInterfaceID ( + IN OUT IP6_SERVICE *IpSb + ) +{ + UINT8 InterfaceId[8]; + UINT8 Byte; + EFI_MAC_ADDRESS *MacAddr; + UINT32 AddrLen; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + AddrLen = IpSb->SnpMode.HwAddressSize; + + // + // Currently only IEEE 802 48-bit MACs are supported to create link local address. + // + if (AddrLen != IP6_MAC_LEN || IpSb->InterfaceIdLen != IP6_IF_ID_LEN) { + return NULL; + } + + MacAddr = &IpSb->SnpMode.CurrentAddress; + + // + // Convert MAC address to 64 bits interface ID according to Appendix A of RFC4291: + // 1. Insert 0xFFFE to the middle + // 2. Invert the universal/local bit - bit 6 in network order + // + CopyMem (InterfaceId, MacAddr, 3); + InterfaceId[3] = 0xFF; + InterfaceId[4] = 0xFE; + CopyMem (&InterfaceId[5], &MacAddr->Addr[3], 3); + + Byte = (UINT8) (InterfaceId[0] & IP6_U_BIT); + if (Byte == IP6_U_BIT) { + InterfaceId[0] &= ~IP6_U_BIT; + } else { + InterfaceId[0] |= IP6_U_BIT; + } + + // + // Return the interface ID. + // + return AllocateCopyPool (IpSb->InterfaceIdLen, InterfaceId); +} + +/** + This function creates link-local address from interface identifier. The + interface identifier is normally created from MAC address. It might be manually + configured by administrator if the link-local address created from MAC address + is a duplicate address. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL If the operation fails. + @return The generated Link Local address, in network order. + +**/ +EFI_IPv6_ADDRESS * +Ip6CreateLinkLocalAddr ( + IN OUT IP6_SERVICE *IpSb + ) +{ + EFI_IPv6_ADDRESS *Ip6Addr; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + UINTN DataSize; + EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + if (IpSb->InterfaceId != NULL) { + FreePool (IpSb->InterfaceId); + } + + // + // Get the interface id if it is manully configured. + // + Ip6Config = &IpSb->Ip6ConfigInstance.Ip6Config; + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + ZeroMem (&InterfaceId, DataSize); + + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + &InterfaceId + ); + if (Status == EFI_NOT_FOUND) { + // + // Since the interface id is not configured, generate the interface id from + // MAC address. + // + IpSb->InterfaceId = Ip6CreateInterfaceID (IpSb); + if (IpSb->InterfaceId == NULL) { + return NULL; + } + + CopyMem (&InterfaceId, IpSb->InterfaceId, IpSb->InterfaceIdLen); + // + // Record the interface id. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + DataSize, + &InterfaceId + ); + if (EFI_ERROR (Status)) { + FreePool (IpSb->InterfaceId); + IpSb->InterfaceId = NULL; + return NULL; + } + } else if (!EFI_ERROR (Status)) { + IpSb->InterfaceId = AllocateCopyPool (DataSize, &InterfaceId); + if (IpSb->InterfaceId == NULL) { + return NULL; + } + } else { + return NULL; + } + + // + // Append FE80::/64 to the left of IPv6 address then return. + // + Ip6Addr = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS)); + if (Ip6Addr == NULL) { + FreePool (IpSb->InterfaceId); + IpSb->InterfaceId = NULL; + return NULL; + } + + CopyMem (&Ip6Addr->Addr[8], IpSb->InterfaceId, IpSb->InterfaceIdLen); + Ip6Addr->Addr[1] = 0x80; + Ip6Addr->Addr[0] = 0xFE; + + return Ip6Addr; +} + +/** + Compute the solicited-node multicast address for an unicast or anycast address, + by taking the low-order 24 bits of this address, and appending those bits to + the prefix FF02:0:0:0:0:1:FF00::/104. + + @param[in] Ip6Addr The unicast or anycast address, in network order. + @param[out] MulticastAddr The generated solicited-node multicast address, + in network order. + +**/ +VOID +Ip6CreateSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6Addr, + OUT EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + ASSERT (Ip6Addr != NULL && MulticastAddr != NULL); + + ZeroMem (MulticastAddr, sizeof (EFI_IPv6_ADDRESS)); + + MulticastAddr->Addr[0] = 0xFF; + MulticastAddr->Addr[1] = 0x02; + MulticastAddr->Addr[11] = 0x1; + MulticastAddr->Addr[12] = 0xFF; + + CopyMem (&MulticastAddr->Addr[13], &Ip6Addr->Addr[13], 3); +} + +/** + Insert a node IP6_ADDRESS_INFO to an IP6 interface. + + @param[in, out] IpIf Points to an IP6 interface. + @param[in] AddrInfo Points to IP6_ADDRESS_INFO + +**/ +VOID +Ip6AddAddr ( + IN OUT IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddrInfo + ) +{ + InsertHeadList (&IpIf->AddressList, &AddrInfo->Link); + IpIf->AddressCount++; +} + +/** + Destroy the IP instance if its StationAddress is removed. It is the help function + for Ip6RemoveAddr(). + + @param[in, out] IpSb Points to an IP6 service binding instance. + @param[in] Address The to be removed address + +**/ +VOID +Ip6DestroyInstanceByAddress ( + IN OUT IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ) +{ + BOOLEAN OneDestroyed; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + LIST_ENTRY *Entry; + IP6_PROTOCOL *Instance; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + ServiceBinding = &IpSb->ServiceBinding; + + // + // Upper layer IP protocol consumers may have tight relationship between several + // IP protocol instances, in other words, calling ServiceBinding->DestroyChild to + // destroy one IP child may cause other related IP children destroyed too. This + // will probably leave hole in the children list when we iterate it. So everytime + // we just destroy one child then back to the start point to iterate the list. + // + do { + OneDestroyed = FALSE; + + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + + if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) { + ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); + OneDestroyed = TRUE; + break; + } + } + } while (OneDestroyed); +} + +/** + Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO. + + This function removes the matching IPv6 addresses from the address list and + adjusts the address count of the address list. If IpSb is not NULL, this function + calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the + its solicited-node multicast MAC address from the filter list and sends out + a Multicast Listener Done. If Prefix is NULL, all address in the address list + will be removed. If Prefix is not NULL, the address that matching the Prefix + with PrefixLength in the address list will be removed. + + @param[in] IpSb NULL or points to IP6 service binding instance. + @param[in, out] AddressList Address list array. + @param[in, out] AddressCount The count of addresses in address list array. + @param[in] Prefix NULL or an IPv6 address prefix. + @param[in] PrefixLength The length of Prefix. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength + cannot be found in the address list. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6RemoveAddr ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN OUT LIST_ENTRY *AddressList, + IN OUT UINT32 *AddressCount, + IN EFI_IPv6_ADDRESS *Prefix OPTIONAL, + IN UINT8 PrefixLength + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ADDRESS_INFO *AddrInfo; + EFI_IPv6_ADDRESS SnMCastAddr; + + if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_NUM) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_NOT_FOUND; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (Prefix == NULL || + (PrefixLength == 128 && EFI_IP6_EQUAL (Prefix, &AddrInfo->Address)) || + (PrefixLength == AddrInfo->PrefixLength && NetIp6IsNetEqual (Prefix, &AddrInfo->Address, PrefixLength)) + ) { + if (IpSb != NULL) { + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + Ip6CreateSNMulticastAddr (&AddrInfo->Address, &SnMCastAddr); + Ip6LeaveGroup (IpSb, &SnMCastAddr); + + // + // Destroy any instance who is using the dying address as the source address. + // + Ip6DestroyInstanceByAddress (IpSb, &AddrInfo->Address); + } + + RemoveEntryList (Entry); + FreePool (AddrInfo); + (*AddressCount)--; + + Status = EFI_SUCCESS; + } + } + + return Status; +} + +/** + Check whether the incoming Ipv6 address is a solicited-node multicast address. + + @param[in] Ip6 Ip6 address, in network order. + + @retval TRUE Yes, solicited-node multicast address + @retval FALSE No + +**/ +BOOLEAN +Ip6IsSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ) +{ + EFI_IPv6_ADDRESS Sn; + BOOLEAN Flag; + + Ip6CreateSNMulticastAddr (Ip6, &Sn); + Flag = FALSE; + + if (CompareMem (Sn.Addr, Ip6->Addr, 13) == 0) { + Flag = TRUE; + } + + return Flag; +} + +/** + Check whether the incoming IPv6 address is one of the maintained addresses in + the IP6 service binding instance. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Address The IP6 address to be checked. + @param[out] Interface If not NULL, output the IP6 interface which + maintains the Address. + @param[out] AddressInfo If not NULL, output the IP6 address information + of the Address. + + @retval TRUE Yes, it is one of the maintained address. + @retval FALSE No, it is not one of the maintained address. + +**/ +BOOLEAN +Ip6IsOneOfSetAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address, + OUT IP6_INTERFACE **Interface OPTIONAL, + OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_ADDRESS_INFO *TmpAddressInfo; + + // + // Check link-local address first + // + if (IpSb->LinkLocalOk && EFI_IP6_EQUAL (&IpSb->LinkLocalAddr, Address)) { + if (Interface != NULL) { + *Interface = IpSb->DefaultInterface; + } + + if (AddressInfo != NULL) { + *AddressInfo = NULL; + } + + return TRUE; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + TmpAddressInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (EFI_IP6_EQUAL (&TmpAddressInfo->Address, Address)) { + if (Interface != NULL) { + *Interface = IpIf; + } + + if (AddressInfo != NULL) { + *AddressInfo = TmpAddressInfo; + } + + return TRUE; + } + } + } + + return FALSE; +} + +/** + Check whether the incoming MAC address is valid. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] LinkAddress The MAC address. + + @retval TRUE Yes, it is valid. + @retval FALSE No, it is not valid. + +**/ +BOOLEAN +Ip6IsValidLinkAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_MAC_ADDRESS *LinkAddress + ) +{ + UINT32 Index; + + // + // TODO: might be updated later to be more acceptable. + // + for (Index = IpSb->SnpMode.HwAddressSize; Index < sizeof (EFI_MAC_ADDRESS); Index++) { + if (LinkAddress->Addr[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Copy the PrefixLength bits from Src to Dest. + + @param[out] Dest A pointer to the buffer to copy to. + @param[in] Src A pointer to the buffer to copy from. + @param[in] PrefixLength The number of bits to copy. + +**/ +VOID +Ip6CopyAddressByPrefix ( + OUT EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src, + IN UINT8 PrefixLength + ) +{ + UINT8 Byte; + UINT8 Bit; + UINT8 Mask; + + ASSERT (Dest != NULL && Src != NULL); + ASSERT (PrefixLength < IP6_PREFIX_NUM); + + Byte = (UINT8) (PrefixLength / 8); + Bit = (UINT8) (PrefixLength % 8); + + ZeroMem (Dest, sizeof (EFI_IPv6_ADDRESS)); + + CopyMem (Dest, Src, Byte); + + if (Bit > 0) { + Mask = (UINT8) (0xFF << (8 - Bit)); + ASSERT (Byte < 16); + Dest->Addr[Byte] = (UINT8) (Src->Addr[Byte] & Mask); + } +} + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address instead of + hard-coding the NIC to be Ethernet. + + @param[in] Mnp The Mnp instance to get the MAC address. + @param[in] Multicast The multicast IP address to translate. + @param[out] Mac The buffer to hold the translated address. + + @retval EFI_SUCCESS The multicast IP successfully + translated to a multicast MAC address. + @retval Other The address is not converted because an error occurred. + +**/ +EFI_STATUS +Ip6GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_IPv6_ADDRESS *Multicast, + OUT EFI_MAC_ADDRESS *Mac + ) +{ + EFI_IP_ADDRESS EfiIp; + + IP6_COPY_ADDRESS (&EfiIp.v6, Multicast); + + return Mnp->McastIpToMac (Mnp, TRUE, &EfiIp, Mac); +} + +/** + Set the Ip6 variable data. + + @param[in] IpSb Points to an IP6 service binding instance. + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +Ip6SetVariableData ( + IN IP6_SERVICE *IpSb + ) +{ + UINT32 NumConfiguredInstance; + LIST_ENTRY *Entry; + UINTN VariableDataSize; + EFI_IP6_VARIABLE_DATA *Ip6VariableData; + EFI_IP6_ADDRESS_PAIR *Ip6AddressPair; + IP6_PROTOCOL *IpInstance; + CHAR16 *NewMacString; + EFI_STATUS Status; + + NumConfiguredInstance = 0; + + // + // Go through the children list to count the configured children. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + + if (IpInstance->State == IP6_STATE_CONFIGED) { + NumConfiguredInstance++; + } + } + + // + // Calculate the size of the Ip6VariableData. As there may be no IP child, + // we should add extra buffer for the address paris only if the number of configured + // children is more than 1. + // + VariableDataSize = sizeof (EFI_IP6_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_IP6_ADDRESS_PAIR) * (NumConfiguredInstance - 1); + } + + Ip6VariableData = AllocatePool (VariableDataSize); + if (Ip6VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip6VariableData->DriverHandle = IpSb->Image; + Ip6VariableData->AddressCount = NumConfiguredInstance; + + Ip6AddressPair = &Ip6VariableData->AddressPairs[0]; + + // + // Go through the children list to fill the configured children's address pairs. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + + if (IpInstance->State == IP6_STATE_CONFIGED) { + Ip6AddressPair->InstanceHandle = IpInstance->Handle; + Ip6AddressPair->PrefixLength = IpInstance->PrefixLength; + IP6_COPY_ADDRESS (&Ip6AddressPair->Ip6Address, &IpInstance->ConfigData.StationAddress); + + Ip6AddressPair++; + } + } + + // + // Get the mac string. + // + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (IpSb->MacString != NULL) { + // + // The variable is set already, we're going to update it. + // + if (StrCmp (IpSb->MacString, NewMacString) != 0) { + // + // The mac address is changed, delete the previous variable first. + // + gRT->SetVariable ( + IpSb->MacString, + &gEfiIp6ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + } + + FreePool (IpSb->MacString); + } + + IpSb->MacString = NewMacString; + + Status = gRT->SetVariable ( + IpSb->MacString, + &gEfiIp6ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableDataSize, + (VOID *) Ip6VariableData + ); + +Exit: + FreePool (Ip6VariableData); + return Status; +} + +/** + Clear the variable and free the resource. + + @param[in] IpSb Ip6 service binding instance. + +**/ +VOID +Ip6ClearVariableData ( + IN IP6_SERVICE *IpSb + ) +{ + ASSERT (IpSb->MacString != NULL); + + gRT->SetVariable ( + IpSb->MacString, + &gEfiIp6ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + + FreePool (IpSb->MacString); + IpSb->MacString = NULL; +} + +/** + Convert the multibyte field in IP header's byter order. + In spite of its name, it can also be used to convert from + host to network byte order. + + @param[in, out] Head The IP head to convert. + + @return Point to the converted IP head. + +**/ +EFI_IP6_HEADER * +Ip6NtohHead ( + IN OUT EFI_IP6_HEADER *Head + ) +{ + Head->FlowLabelL = NTOHS (Head->FlowLabelL); + Head->PayloadLength = NTOHS (Head->PayloadLength); + + return Head; +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.h b/NetworkPkg/Ip6Dxe/Ip6Common.h new file mode 100644 index 0000000000..c3755f4859 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Common.h @@ -0,0 +1,338 @@ +/** @file + Common definition and functions for IP6 driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_COMMON_H__ +#define __EFI_IP6_COMMON_H__ + +#define IP6_LINK_EQUAL(Mac1, Mac2) (CompareMem ((Mac1), (Mac2), sizeof (EFI_MAC_ADDRESS)) == 0) + +// +// Convert the Microsecond to second. IP transmit/receive time is +// in the unit of microsecond. IP ticks once per second. +// +#define IP6_US_TO_SEC(Us) (((Us) + 999999) / 1000000) + +#define IP6_ETHER_PROTO 0x86DD + +#define IP6_MAC_LEN 6 +#define IP6_IF_ID_LEN 8 + +#define IP6_INTERFACE_LOCAL_SCOPE 1 +#define IP6_LINK_LOCAL_SCOPE 2 +#define IP6_SITE_LOCAL_SCOPE 5 + +#define IP6_INFINIT_LIFETIME 0xFFFFFFFF + +#define IP6_HOP_LIMIT 255 +// +// Make it to 64 since all 54 bits are zero. +// +#define IP6_LINK_LOCAL_PREFIX_LENGTH 64 + +#define IP6_TIMER_INTERVAL_IN_MS 100 +#define IP6_ONE_SECOND_IN_MS 1000 + +// +// The packet is received as link level broadcast/multicast/promiscuous. +// +#define IP6_LINK_BROADCAST 0x00000001 +#define IP6_LINK_MULTICAST 0x00000002 +#define IP6_LINK_PROMISC 0x00000004 + +#define IP6_U_BIT 0x02 + +typedef enum { + Ip6Promiscuous = 1, + Ip6Unicast, + Ip6Multicast, + Ip6AnyCast +} IP6_ADDRESS_TYPE; + +typedef struct _IP6_INTERFACE IP6_INTERFACE; +typedef struct _IP6_PROTOCOL IP6_PROTOCOL; +typedef struct _IP6_SERVICE IP6_SERVICE; +typedef struct _IP6_ADDRESS_INFO IP6_ADDRESS_INFO; + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL, + only the address count is returned. + + @param[in] IpSb The IP6 service binding instance. + @param[out] AddressCount The number of returned addresses. + @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO. + This is an optional parameter. + + + @retval EFI_SUCCESS The address array is successfully build + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6BuildEfiAddressList ( + IN IP6_SERVICE *IpSb, + OUT UINT32 *AddressCount, + OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL + ); + +/** + Generate the multicast addresses identify the group of all IPv6 nodes or IPv6 + routers defined in RFC4291. + + All Nodes Addresses: FF01::1, FF02::1. + All Router Addresses: FF01::2, FF02::2, FF05::2. + + @param[in] Router If TRUE, generate all routers addresses, + else generate all node addresses. + @param[in] Scope interface-local(1), link-local(2), or site-local(5) + @param[out] Ip6Addr The generated multicast address. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The address is generated. + +**/ +EFI_STATUS +Ip6SetToAllNodeMulticast ( + IN BOOLEAN Router, + IN UINT8 Scope, + OUT EFI_IPv6_ADDRESS *Ip6Addr + ); + +/** + This function converts MAC address to 64 bits interface ID according to RFC4291 + and returns the interface ID. Currently only 48-bit MAC address is supported by + this function. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL The operation fails. + @return Pointer to the generated interface ID. + +**/ +UINT8 * +Ip6CreateInterfaceID ( + IN OUT IP6_SERVICE *IpSb + ); + +/** + This function creates link-local address from interface identifier. The + interface identifier is normally created from MAC address. It might be manually + configured by administrator if the link-local address created from MAC address + is a duplicate address. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL If the operation fails. + @return The generated Link Local address, in network order. + +**/ +EFI_IPv6_ADDRESS * +Ip6CreateLinkLocalAddr ( + IN OUT IP6_SERVICE *IpSb + ); + +/** + Compute the solicited-node multicast address for an unicast or anycast address, + by taking the low-order 24 bits of this address, and appending those bits to + the prefix FF02:0:0:0:0:1:FF00::/104. + + @param Ip6Addr The unicast or anycast address, in network order. + @param MulticastAddr The generated solicited-node multicast address, + in network order. + +**/ +VOID +Ip6CreateSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6Addr, + OUT EFI_IPv6_ADDRESS *MulticastAddr + ); + +/** + Check whether the incoming Ipv6 address is a solicited-node multicast address. + + @param[in] Ip6 Ip6 address, in network order. + + @retval TRUE Yes, solicited-node multicast address + @retval FALSE No + +**/ +BOOLEAN +Ip6IsSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ); + +/** + Check whether the incoming IPv6 address is one of the maintained address in + the IP6 service binding instance. + + @param[in] IpSb Points to a IP6 service binding instance + @param[in] Address The IP6 address to be checked. + @param[out] Interface If not NULL, output the IP6 interface which + maintains the Address. + @param[out] AddressInfo If not NULL, output the IP6 address information + of the Address. + + @retval TRUE Yes, it is one of the maintained addresses. + @retval FALSE No, it is not one of the maintained addresses. + +**/ +BOOLEAN +Ip6IsOneOfSetAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address, + OUT IP6_INTERFACE **Interface OPTIONAL, + OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL + ); + +/** + Check whether the incoming MAC address is valid. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] LinkAddress The MAC address. + + @retval TRUE Yes, it is valid. + @retval FALSE No, it is not valid. + +**/ +BOOLEAN +Ip6IsValidLinkAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_MAC_ADDRESS *LinkAddress + ); + + +/** + Copy the PrefixLength bits from Src to Dest. + + @param[out] Dest A pointer to the buffer to copy to. + @param[in] Src A pointer to the buffer to copy from. + @param[in] PrefixLength The number of bits to copy. + +**/ +VOID +Ip6CopyAddressByPrefix ( + OUT EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src, + IN UINT8 PrefixLength + ); + +/** + Insert a node IP6_ADDRESS_INFO to an IP6 interface. + + @param[in, out] IpIf Points to an IP6 interface. + @param[in] AddrInfo Points to an IP6_ADDRESS_INFO. + +**/ +VOID +Ip6AddAddr ( + IN OUT IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddrInfo + ); + +/** + Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO. + + This function removes the matching IPv6 addresses from the address list and + adjusts the address count of the address list. If IpSb is not NULL, this function + calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the + its solicited-node multicast MAC address from the filter list and sends out + a Multicast Listener Done. If Prefix is NULL, all address in the address list + will be removed. If Prefix is not NULL, the address that matching the Prefix + with PrefixLength in the address list will be removed. + + @param[in] IpSb NULL or points to IP6 service binding instance. + @param[in, out] AddressList address list array + @param[in, out] AddressCount the count of addresses in address list array + @param[in] Prefix NULL or an IPv6 address prefix + @param[in] PrefixLength the length of Prefix + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength + cannot be found in address list. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6RemoveAddr ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN OUT LIST_ENTRY *AddressList, + IN OUT UINT32 *AddressCount, + IN EFI_IPv6_ADDRESS *Prefix OPTIONAL, + IN UINT8 PrefixLength + ); + +/** + Set the Ip6 variable data. + + @param[in] IpSb Points to an IP6 service binding instance + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +Ip6SetVariableData ( + IN IP6_SERVICE *IpSb + ); + +/** + Clear the variable and free the resource. + + @param[in] IpSb Ip6 service binding instance. + +**/ +VOID +Ip6ClearVariableData ( + IN IP6_SERVICE *IpSb + ); + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address instead of + hard-coding the NIC to be Ethernet. + + @param[in] Mnp The Mnp instance to get the MAC address. + @param[in] Multicast The multicast IP address to translate. + @param[out] Mac The buffer to hold the translated address. + + @retval EFI_SUCCESS The multicast IP is successfully + translated to a multicast MAC address. + @retval Other The address is not converted because an error occurred. + +**/ +EFI_STATUS +Ip6GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_IPv6_ADDRESS *Multicast, + OUT EFI_MAC_ADDRESS *Mac + ); + +/** + Convert the multibyte field in IP header's byter order. + In spite of its name, it can also be used to convert from + host to network byte order. + + @param[in, out] Head The IP head to convert. + + @return Point to the converted IP head. + +**/ +EFI_IP6_HEADER * +Ip6NtohHead ( + IN OUT EFI_IP6_HEADER *Head + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Config.vfr b/NetworkPkg/Ip6Dxe/Ip6Config.vfr new file mode 100644 index 0000000000..902cef6209 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Config.vfr @@ -0,0 +1,170 @@ +/** @file + VFR file used by the IP6 configuration component. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6NvData.h" + +#define EFI_NETWORK_DEVICE_CLASS 0x04 + +formset + guid = IP6_CONFIG_NVDATA_GUID, + title = STRING_TOKEN(STR_IP6_CONFIG_FORM_TITLE), + help = STRING_TOKEN(STR_IP6_CONFIG_FORM_HELP), + class = EFI_NETWORK_DEVICE_CLASS, + subclass = 0x03, + + varstore IP6_CONFIG_IFR_NVDATA, + name = IP6_CONFIG_IFR_NVDATA, + guid = IP6_CONFIG_NVDATA_GUID; + + form formid = FORMID_MAIN_FORM, + title = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE); + + text + help = STRING_TOKEN(STR_IP6_INTERFACE_NAME_HELP), + text = STRING_TOKEN(STR_IP6_INTERFACE_NAME), + text = STRING_TOKEN(STR_IP6_INTERFACE_NAME_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_HELP), + text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE), + text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_MAC_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_MAC_ADDRESS), + text = STRING_TOKEN(STR_IP6_MAC_ADDRESS_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_HOST_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_HOST_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label HOST_ADDRESS_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_ROUTE_TABLE_HELP), + text = STRING_TOKEN(STR_IP6_ROUTE_TABLE), + text = STRING_TOKEN(STR_NULL); + + label ROUTE_TABLE_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label GATEWAY_ADDRESS_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_DNS_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_DNS_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label DNS_ADDRESS_LABEL; + label LABEL_END; + + string varid = IP6_CONFIG_IFR_NVDATA.InterfaceId, + prompt = STRING_TOKEN(STR_IP6_INTERFACE_ID), + help = STRING_TOKEN(STR_IP6_INTERFACE_ID_HELP), + flags = INTERACTIVE, + key = KEY_INTERFACE_ID, + minsize = INTERFACE_ID_STR_MIN_SIZE, + maxsize = INTERFACE_ID_STR_MAX_SIZE, + endstring; + + numeric varid = IP6_CONFIG_IFR_NVDATA.DadTransmitCount, + prompt = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT), + help = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT_HELP), + flags = 0, + minimum = 0, + maximum = DAD_MAX_TRANSMIT_COUNT, + step = 0, + endnumeric; + + oneof varid = IP6_CONFIG_IFR_NVDATA.Policy, + prompt = STRING_TOKEN(STR_POLICY_TYPE_PROMPT), + help = STRING_TOKEN(STR_POLICY_TYPE_HELP), + option text = STRING_TOKEN(STR_POLICY_TYPE_AUTO), value = IP6_POLICY_AUTO, flags = DEFAULT; + option text = STRING_TOKEN(STR_POLICY_TYPE_MANUAL), value = IP6_POLICY_MANUAL, flags = 0; + endoneof; + + subtitle text = STRING_TOKEN(STR_NULL); + + suppressif ideqval IP6_CONFIG_IFR_NVDATA.Policy == IP6_POLICY_AUTO; + goto FORMID_MANUAL_CONFIG_FORM, + prompt = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM), + help = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM_HELP), + flags = 0; + subtitle text = STRING_TOKEN(STR_NULL); + endif; + + text + help = STRING_TOKEN (STR_SAVE_CHANGES_HELP), + text = STRING_TOKEN (STR_SAVE_CHANGES), + text = STRING_TOKEN (STR_NULL), + flags = INTERACTIVE, + key = KEY_SAVE_CHANGES; + + endform; + + form formid = FORMID_MANUAL_CONFIG_FORM, + title = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM); + + string varid = IP6_CONFIG_IFR_NVDATA.ManualAddress, + prompt = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS), + help = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_MANUAL_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + string varid = IP6_CONFIG_IFR_NVDATA.GatewayAddress, + prompt = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDRESS), + help = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDR_HELP), + flags = INTERACTIVE, + key = KEY_GATEWAY_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + string varid = IP6_CONFIG_IFR_NVDATA.DnsAddress, + prompt = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS), + help = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_DNS_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + goto FORMID_MAIN_FORM, + prompt = STRING_TOKEN (STR_SAVE_AND_EXIT), + help = STRING_TOKEN (STR_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_SAVE_CONFIG_CHANGES; + + goto FORMID_MAIN_FORM, + prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_IGNORE_CONFIG_CHANGES; + + endform; + +endformset; + diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c new file mode 100644 index 0000000000..3cfd1f2104 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c @@ -0,0 +1,2369 @@ +/** @file + The implementation of EFI IPv6 Configuration Protocol. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +LIST_ENTRY mIp6ConfigInstanceList = {&mIp6ConfigInstanceList, &mIp6ConfigInstanceList}; + +/** + The event process routine when the DHCPv6 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context Pointer to the IP6 config instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Update the current policy to NewPolicy. During the transition + period, the default router list, on-link prefix list, autonomous prefix list + and address list in all interfaces will be released. + + @param[in] IpSb The IP6 service binding instance. + @param[in] NewPolicy The new policy to be updated to. + +**/ +VOID +Ip6ConfigOnPolicyChanged ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_CONFIG_POLICY NewPolicy + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + LIST_ENTRY *Next; + IP6_INTERFACE *IpIf; + IP6_DAD_ENTRY *DadEntry; + + // + // Currently there are only two policies: Manual and Automatic. Regardless of + // what transition is going on, i.e., Manual -> Automatic and Automatic -> + // Manual, we have to free default router list, on-link prefix list, autonomous + // prefix list, address list in all the interfaces and destroy any IPv6 child + // instance whose local IP is neither 0 nor the link-local address. + // + Ip6CleanDefaultRouterList (IpSb); + Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); + Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); + + // + // It's tricky... If the LinkLocal address is O.K., add back the link-local + // prefix to the on-link prefix table. + // + if (IpSb->LinkLocalOk) { + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + IP6_LINK_LOCAL_PREFIX_LENGTH, + &IpSb->LinkLocalAddr + ); + } + + // + // All IPv6 children that use global unicast address as it's source address + // should be destryoed now. The survivers are those use the link-local address + // or the unspecified address as the source address. + // TODO: Conduct a check here. + Ip6RemoveAddr ( + IpSb, + &IpSb->DefaultInterface->AddressList, + &IpSb->DefaultInterface->AddressCount, + NULL, + 0 + ); + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + // + // remove all pending DAD entries for the global addresses. + // + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { + DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); + + if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) { + // + // Fail this DAD entry if the address is not link-local. + // + Ip6OnDADFinished (FALSE, IpIf, DadEntry); + } + } + } + + if (NewPolicy == Ip6ConfigPolicyAutomatic) { + // + // Set paramters to trigger router solicitation sending in timer handler. + // + IpSb->RouterAdvertiseReceived = FALSE; + IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS; + // + // delay 1 second + // + IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS); + } + +} + +/** + The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] OtherInfoOnly If FALSE, get stateful address and other information + via DHCPv6. Otherwise, only get the other information. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_UNSUPPORTED The DHCP6 driver is not available. + +**/ +EFI_STATUS +Ip6ConfigStartStatefulAutoConfig ( + IN IP6_CONFIG_INSTANCE *Instance, + IN BOOLEAN OtherInfoOnly + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + EFI_DHCP6_CONFIG_DATA Dhcp6CfgData; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_DHCP6_PACKET_OPTION *OptList[1]; + UINT16 OptBuf[4]; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + + // + // A host must not invoke stateful address configuration if it is already + // participating in the statuful protocol as a result of an earlier advertisement. + // + if (Instance->Dhcp6Handle != NULL) { + return EFI_SUCCESS; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + Instance->OtherInfoOnly = OtherInfoOnly; + + Status = NetLibCreateServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Instance->Dhcp6Handle + ); + + if (Status == EFI_UNSUPPORTED) { + // + // No DHCPv6 Service Binding protocol, register a notify. + // + if (Instance->Dhcp6SbNotifyEvent == NULL) { + Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent ( + &gEfiDhcp6ServiceBindingProtocolGuid, + TPL_CALLBACK, + Ip6ConfigOnDhcp6SbInstalled, + (VOID *) Instance, + &Instance->Registration + ); + } + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Instance->Dhcp6SbNotifyEvent != NULL) { + gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent); + } + + Status = gBS->OpenProtocol ( + Instance->Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Instance->Dhcp6, + IpSb->Image, + IpSb->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT_EFI_ERROR (Status); + + Dhcp6 = Instance->Dhcp6; + Dhcp6->Configure (Dhcp6, NULL); + + // + // Set the exta options to send. Here we only want the option request option + // with DNS SERVERS. + // + Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf; + Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO); + Oro->OpLen = HTONS (2); + *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS); + OptList[0] = Oro; + + Status = EFI_SUCCESS; + + if (!OtherInfoOnly) { + // + // Get stateful address and other information via DHCPv6. + // + Dhcp6CfgData.Dhcp6Callback = NULL; + Dhcp6CfgData.CallbackContext = NULL; + Dhcp6CfgData.OptionCount = 1; + Dhcp6CfgData.OptionList = &OptList[0]; + Dhcp6CfgData.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Dhcp6CfgData.IaDescriptor.IaId = Instance->IaId; + Dhcp6CfgData.IaInfoEvent = Instance->Dhcp6Event; + Dhcp6CfgData.ReconfigureAccept = FALSE; + Dhcp6CfgData.RapidCommit = FALSE; + Dhcp6CfgData.SolicitRetransmission = NULL; + + Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData); + + if (!EFI_ERROR (Status)) { + + if (IpSb->LinkLocalOk) { + Status = Dhcp6->Start (Dhcp6); + } else { + IpSb->Dhcp6NeedStart = TRUE; + } + + } + } else { + // + // Only get other information via DHCPv6, this doesn't require a config + // action. + // + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 64; + InfoReqReXmit.Mrt = 60; + InfoReqReXmit.Mrd = 0; + + if (IpSb->LinkLocalOk) { + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + Instance->Dhcp6Event, + Ip6ConfigOnDhcp6Reply, + Instance + ); + } else { + IpSb->Dhcp6NeedInfoRequest = TRUE; + } + + } + + return Status; +} + +/** + Signal the registered event. It is the callback routine for NetMapIterate. + + @param[in] Map Points to the list of registered event. + @param[in] Item The registered event. + @param[in] Arg Not used. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigSignalEvent ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg + ) +{ + gBS->SignalEvent ((EFI_EVENT) Item->Key); + + return EFI_SUCCESS; +} + +/** + Read the configuration data from variable storage according to the VarName and + gEfiIp6ConfigProtocolGuid. It checks the integrity of variable data. If the + data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the + configuration data to IP6_CONFIG_INSTANCE. + + @param[in] VarName The pointer to the variable name + @param[in, out] Instance The pointer to the IP6 config instance data. + + @retval EFI_NOT_FOUND The variable can not be found or already corrupted. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_SUCCESS The configuration data was retrieved successfully. + +**/ +EFI_STATUS +Ip6ConfigReadConfigData ( + IN CHAR16 *VarName, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + UINTN VarSize; + IP6_CONFIG_VARIABLE *Variable; + IP6_CONFIG_DATA_ITEM *DataItem; + UINTN Index; + IP6_CONFIG_DATA_RECORD DataRecord; + CHAR8 *Data; + + // + // Try to read the configuration variable. + // + VarSize = 0; + Status = gRT->GetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + NULL, + &VarSize, + NULL + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate buffer and read the config variable. + // + Variable = AllocatePool (VarSize); + if (Variable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + NULL, + &VarSize, + Variable + ); + if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) { + // + // GetVariable still error or the variable is corrupted. + // Fall back to the default value. + // + FreePool (Variable); + + // + // Remove the problematic variable and return EFI_NOT_FOUND, a new + // variable will be set again. + // + gRT->SetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + IP6_CONFIG_VARIABLE_ATTRIBUTE, + 0, + NULL + ); + + return EFI_NOT_FOUND; + } + + // + // Get the IAID we use. + // + Instance->IaId = Variable->IaId; + + for (Index = 0; Index < Variable->DataRecordCount; Index++) { + + CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord)); + + DataItem = &Instance->DataItem[DataRecord.DataType]; + if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) && + (DataItem->DataSize != DataRecord.DataSize) + ) { + // + // Perhaps a corrupted data record... + // + continue; + } + + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { + // + // This data item has variable length data. + // + DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize); + if (DataItem->Data.Ptr == NULL) { + // + // no memory resource + // + continue; + } + } + + Data = (CHAR8 *) Variable + DataRecord.Offset; + CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize); + + DataItem->DataSize = DataRecord.DataSize; + DataItem->Status = EFI_SUCCESS; + } + + FreePool (Variable); + return EFI_SUCCESS; + } + + return Status; +} + +/** + Write the configuration data from IP6_CONFIG_INSTANCE to variable storage. + + @param[in] VarName The pointer to the variable name. + @param[in] Instance The pointer to the IP6 configuration instance data. + + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_SUCCESS The configuration data is written successfully. + +**/ +EFI_STATUS +Ip6ConfigWriteConfigData ( + IN CHAR16 *VarName, + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + UINTN Index; + UINTN VarSize; + IP6_CONFIG_DATA_ITEM *DataItem; + IP6_CONFIG_VARIABLE *Variable; + IP6_CONFIG_DATA_RECORD *DataRecord; + CHAR8 *Heap; + EFI_STATUS Status; + + VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { + + VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize; + } + } + + Variable = AllocatePool (VarSize); + if (Variable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Variable->IaId = Instance->IaId; + Heap = (CHAR8 *) Variable + VarSize; + Variable->DataRecordCount = 0; + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { + + Heap -= DataItem->DataSize; + CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize); + + DataRecord = &Variable->DataRecord[Variable->DataRecordCount]; + DataRecord->DataType = (EFI_IP6_CONFIG_DATA_TYPE) Index; + DataRecord->DataSize = DataItem->DataSize; + DataRecord->Offset = (UINT16) (Heap - (CHAR8 *) Variable); + + Variable->DataRecordCount++; + } + } + + Variable->Checksum = 0; + Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize); + + Status = gRT->SetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + IP6_CONFIG_VARIABLE_ATTRIBUTE, + VarSize, + Variable + ); + + FreePool (Variable); + + return Status; +} + +/** + The work function for EfiIp6ConfigGetData() to get the interface information + of the communication device this IP6Config instance manages. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in + bytes, the size of buffer required to store the specified + configuration data. + @param[in] Data The data buffer in which the configuration data is returned. + Ignored if DataSize is ZERO. + + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified + configuration data, and the required size is + returned in DataSize. + @retval EFI_SUCCESS The specified configuration data was obtained. + +**/ +EFI_STATUS +Ip6ConfigGetIfInfo ( + IN IP6_CONFIG_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + UINTN Length; + IP6_CONFIG_DATA_ITEM *Item; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + UINT32 AddressCount; + UINT32 RouteCount; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO); + + // + // Calculate the required length, add the buffer size for AddressInfo and + // RouteTable + // + Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL); + Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL); + + Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE); + + if (*DataSize < Length) { + *DataSize = Length; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy the fixed size part of the interface info. + // + Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo]; + IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data; + CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO)); + + // + // AddressInfo + // + IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *) (IfInfo + 1); + Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo); + + // + // RouteTable + // + IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *) (IfInfo->AddressInfo + IfInfo->AddressInfoCount); + Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable); + + if (IfInfo->AddressInfoCount == 0) { + IfInfo->AddressInfo = NULL; + } + + if (IfInfo->RouteCount == 0) { + IfInfo->RouteTable = NULL; + } + + return EFI_SUCCESS; +} + +/** + The work function for EfiIp6ConfigSetData() to set the alternative inteface ID + for the communication device managed by this IP6Config instance, if the link local + IPv6 addresses generated from the interface ID based on the default source the + EFI IPv6 Protocol uses is a duplicate address. + + @param[in] Instance Pointer to the IP6 configuration instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type, + 8 bytes. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetAltIfId ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_INTERFACE_ID *OldIfId; + EFI_IP6_CONFIG_INTERFACE_ID *NewIfId; + IP6_CONFIG_DATA_ITEM *DataItem; + + if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) { + return EFI_BAD_BUFFER_SIZE; + } + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; + OldIfId = DataItem->Data.AltIfId; + NewIfId = (EFI_IP6_CONFIG_INTERFACE_ID *) Data; + + CopyMem (OldIfId, NewIfId, DataSize); + DataItem->Status = EFI_SUCCESS; + + return EFI_SUCCESS; +} + +/** + The work function for EfiIp6ConfigSetData() to set the general configuration + policy for the EFI IPv6 network stack that is running on the communication device + managed by this IP6Config instance. The policy will affect other configuration settings. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_INVALID_PARAMETER The to be set policy is invalid. + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_ABORTED The new policy equals the current policy. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetPolicy ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_POLICY NewPolicy; + IP6_CONFIG_DATA_ITEM *DataItem; + IP6_SERVICE *IpSb; + + if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) { + return EFI_BAD_BUFFER_SIZE; + } + + NewPolicy = *((EFI_IP6_CONFIG_POLICY *) Data); + + if (NewPolicy > Ip6ConfigPolicyAutomatic) { + return EFI_INVALID_PARAMETER; + } + + if (NewPolicy == Instance->Policy) { + + return EFI_ABORTED; + } else { + + if (NewPolicy == Ip6ConfigPolicyAutomatic) { + // + // Clean the ManualAddress, Gateway and DnsServers, shrink the variable + // data size, and fire up all the related events. + // + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + } else { + // + // The policy is changed from automatic to manual. Stop the DHCPv6 process + // and destroy the DHCPv6 child. + // + if (Instance->Dhcp6Handle != NULL) { + Ip6ConfigDestroyDhcp6 (Instance); + } + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Ip6ConfigOnPolicyChanged (IpSb, NewPolicy); + + Instance->Policy = NewPolicy; + + return EFI_SUCCESS; + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the number of consecutive + Neighbor Solicitation messages sent while performing Duplicate Address Detection + on a tentative address. A value of ZERO indicates that Duplicate Address Detection + will not be performed on a tentative address. + + @param[in] The Instance Pointer to the IP6 config instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_ABORTED The new transmit count equals the current configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetDadXmits ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *OldDadXmits; + + if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) { + return EFI_BAD_BUFFER_SIZE; + } + + OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits; + + if ((*(UINT32 *) Data) == OldDadXmits->DupAddrDetectTransmits) { + + return EFI_ABORTED; + } else { + + OldDadXmits->DupAddrDetectTransmits = *((UINT32 *) Data); + return EFI_SUCCESS; + } +} + +/** + The callback function for Ip6SetAddr. The prototype is defined + as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed + for the manual address set by Ip6ConfigSetMaunualAddress. + + @param[in] IsDadPassed If TRUE, Duplicate Address Detection passed. + @param[in] TargetAddress The tentative IPv6 address to be checked. + @param[in] Context Pointer to the IP6 configuration instance data. + +**/ +VOID +Ip6ManualAddrDadCallback ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + UINTN Index; + IP6_CONFIG_DATA_ITEM *Item; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddr; + EFI_IP6_CONFIG_MANUAL_ADDRESS *PassedAddr; + UINTN DadPassCount; + UINTN DadFailCount; + IP6_SERVICE *IpSb; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Item = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + ManualAddr = NULL; + + for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) { + // + // Find the original tag used to place into the NET_MAP. + // + ManualAddr = Item->Data.ManualAddress + Index; + if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) { + break; + } + } + + ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + + if (IsDadPassed) { + NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL); + } else { + NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL); + } + + DadPassCount = NetMapGetCount (&Instance->DadPassedMap); + DadFailCount = NetMapGetCount (&Instance->DadFailedMap); + + if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) { + // + // All addresses have finished the configuration process. + // + if (DadFailCount != 0) { + // + // There is at least one duplicate address. + // + FreePool (Item->Data.Ptr); + + Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + if (Item->DataSize == 0) { + // + // All failed, bad luck. + // + Item->Data.Ptr = NULL; + Item->Status = EFI_NOT_FOUND; + } else { + // + // Part of addresses are detected to be duplicates, so update the + // data with those passed. + // + PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AllocatePool (Item->DataSize); + ASSERT (PassedAddr != NULL); + + Item->Data.Ptr = PassedAddr; + Item->Status = EFI_SUCCESS; + + while (!NetMapIsEmpty (&Instance->DadPassedMap)) { + ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) NetMapRemoveHead (&Instance->DadPassedMap, NULL); + CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + + PassedAddr++; + } + + ASSERT ((UINTN) PassedAddr - (UINTN) Item->Data.Ptr == Item->DataSize); + } + } else { + // + // All addresses are valid. + // + Item->Status = EFI_SUCCESS; + } + + // + // Remove the tags we put in the NET_MAPs. + // + while (!NetMapIsEmpty (&Instance->DadFailedMap)) { + NetMapRemoveHead (&Instance->DadFailedMap, NULL); + } + + while (!NetMapIsEmpty (&Instance->DadPassedMap)) { + NetMapRemoveHead (&Instance->DadPassedMap, NULL); + } + + // + // Signal the waiting events. + // + NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the station addresses manually + for the EFI IPv6 network stack. It is only configurable when the policy is + Ip6ConfigPolicyManual. + + @param[in] Instance Pointer to the IP6 configuration instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_NOT_READY An asynchrous process is invoked to set the specified + configuration data, and the process is not finished. + @retval EFI_ABORTED The manual addresses to be set equal current + configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetMaunualAddress ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_MANUAL_ADDRESS *NewAddress; + EFI_IP6_CONFIG_MANUAL_ADDRESS *TmpAddress; + IP6_CONFIG_DATA_ITEM *DataItem; + UINTN NewAddressCount; + UINTN Index1; + UINTN Index2; + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *CurrentAddrInfo; + IP6_ADDRESS_INFO *Copy; + LIST_ENTRY CurrentSourceList; + UINT32 CurrentSourceCount; + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + EFI_STATUS Status; + BOOLEAN IsUpdated; + + ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY); + + if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + NewAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) Data; + + for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { + + if (NetIp6IsLinkLocalAddr (&NewAddress->Address) || + !NetIp6IsValidUnicast (&NewAddress->Address) || + (NewAddress->PrefixLength > 128) + ) { + // + // make sure the IPv6 address is unicast and not link-local address && + // the prefix length is valid. + // + return EFI_INVALID_PARAMETER; + } + + TmpAddress = NewAddress + 1; + for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) { + // + // Any two addresses in the array can't be equal. + // + if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) { + + return EFI_INVALID_PARAMETER; + } + } + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + // + // Build the current source address list. + // + InitializeListHead (&CurrentSourceList); + CurrentSourceCount = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo); + if (Copy == NULL) { + break; + } + + InsertTailList (&CurrentSourceList, &Copy->Link); + CurrentSourceCount++; + } + } + + // + // Update the value... a long journey starts + // + NewAddress = AllocateCopyPool (DataSize, Data); + if (NewAddress == NULL) { + Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Store the new data, and init the DataItem status to EFI_NOT_READY because + // we may have an asynchronous configuration process. + // + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NewAddress; + DataItem->DataSize = DataSize; + DataItem->Status = EFI_NOT_READY; + + // + // Trigger DAD, it's an asynchronous process. + // + IsUpdated = FALSE; + + for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { + if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) { + ASSERT (CurrentAddrInfo != NULL); + // + // Remove this already existing source address from the CurrentSourceList + // built before. + // + Ip6RemoveAddr ( + NULL, + &CurrentSourceList, + &CurrentSourceCount, + &CurrentAddrInfo->Address, + 128 + ); + + // + // This manual address is already in use, see whether prefix length is changed. + // + if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) { + // + // Remove the on-link prefix table, the route entry will be removed + // implicitly. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + CurrentAddrInfo->PrefixLength, + &CurrentAddrInfo->Address + ); + if (PrefixEntry != NULL) { + Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); + } + + // + // Save the prefix length. + // + CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength; + IsUpdated = TRUE; + } + + // + // create a new on-link prefix entry. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + NewAddress->PrefixLength, + &NewAddress->Address + ); + if (PrefixEntry == NULL) { + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + NewAddress->PrefixLength, + &NewAddress->Address + ); + } + + CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast; + // + // Artificially mark this address passed DAD be'coz it is already in use. + // + Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance); + } else { + // + // A new address. + // + IsUpdated = TRUE; + + // + // Set the new address, this will trigger DAD and activate the address if + // DAD succeeds. + // + Ip6SetAddress ( + IpSb->DefaultInterface, + &NewAddress->Address, + NewAddress->IsAnycast, + NewAddress->PrefixLength, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + Ip6ManualAddrDadCallback, + Instance + ); + } + } + + // + // Check the CurrentSourceList, it now contains those addresses currently in + // use and will be removed. + // + IpIf = IpSb->DefaultInterface; + + while (!IsListEmpty (&CurrentSourceList)) { + IsUpdated = TRUE; + + CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link); + + // + // This local address is going to be removed, the IP instances that are + // currently using it will be destroyed. + // + Ip6RemoveAddr ( + IpSb, + &IpIf->AddressList, + &IpIf->AddressCount, + &CurrentAddrInfo->Address, + 128 + ); + + // + // Remove the on-link prefix table, the route entry will be removed + // implicitly. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + CurrentAddrInfo->PrefixLength, + &CurrentAddrInfo->Address + ); + if (PrefixEntry != NULL) { + Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); + } + + RemoveEntryList (&CurrentAddrInfo->Link); + FreePool (CurrentAddrInfo); + } + + if (IsUpdated) { + if (DataItem->Status == EFI_NOT_READY) { + // + // If DAD is disabled on this interface, the configuration process is + // actually synchronous, and the data item's status will be changed to + // the final status before we reach here, just check it. + // + Status = EFI_NOT_READY; + } else { + Status = EFI_SUCCESS; + } + } else { + // + // No update is taken, reset the status to success and return EFI_ABORTED. + // + DataItem->Status = EFI_SUCCESS; + Status = EFI_ABORTED; + } + + return Status; +} + +/** + The work function for EfiIp6ConfigSetData() to set the gateway addresses manually + for the EFI IPv6 network stack that is running on the communication device that + this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is + Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses. + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in] DataSize The size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. This points to an array of + EFI_IPv6_ADDRESS instances. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to complete the operation. + @retval EFI_ABORTED The manual gateway addresses to be set equal the + current configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetGateway ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + UINTN Index1; + UINTN Index2; + EFI_IPv6_ADDRESS *OldGateway; + EFI_IPv6_ADDRESS *NewGateway; + UINTN OldGatewayCount; + UINTN NewGatewayCount; + IP6_CONFIG_DATA_ITEM *Item; + BOOLEAN OneRemoved; + BOOLEAN OneAdded; + IP6_SERVICE *IpSb; + IP6_DEFAULT_ROUTER *DefaultRouter; + VOID *Tmp; + + if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + NewGateway = (EFI_IPv6_ADDRESS *) Data; + NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { + + if (!NetIp6IsValidUnicast (NewGateway + Index1)) { + + return EFI_INVALID_PARAMETER; + } + + for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) { + if (EFI_IP6_EQUAL (NewGateway + Index1, NewGateway + Index2)) { + return EFI_INVALID_PARAMETER; + } + } + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Item = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + OldGateway = Item->Data.Gateway; + OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS); + OneRemoved = FALSE; + OneAdded = FALSE; + + if (NewGatewayCount != OldGatewayCount) { + Tmp = AllocatePool (DataSize); + if (Tmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Tmp = NULL; + } + + for (Index1 = 0; Index1 < OldGatewayCount; Index1++) { + // + // Find the gateways that are no long in the new setting and remove them. + // + for (Index2 = 0; Index2 < NewGatewayCount; Index2++) { + if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) { + OneRemoved = TRUE; + break; + } + } + + if (Index2 == NewGatewayCount) { + // + // Remove this default router. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + } + + for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { + + DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1); + if (DefaultRouter == NULL) { + Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME); + OneAdded = TRUE; + } + } + + if (!OneRemoved && !OneAdded) { + Item->Status = EFI_SUCCESS; + return EFI_ABORTED; + } else { + + if (Tmp != NULL) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + Item->Data.Ptr = Tmp; + } + + CopyMem (Item->Data.Ptr, Data, DataSize); + Item->DataSize = DataSize; + Item->Status = EFI_SUCCESS; + return EFI_SUCCESS; + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the DNS server list for the + EFI IPv6 network stack running on the communication device that this EFI IPv6 + Configuration Protocol manages. It is not configurable when the policy is + Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses. + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in] DataSize The size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set, points to an array of + EFI_IPv6_ADDRESS instances. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_ABORTED The DNS server addresses to be set equal the current + configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetDnsServer ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + UINTN OldIndex; + UINTN NewIndex; + UINTN Index1; + EFI_IPv6_ADDRESS *OldDns; + EFI_IPv6_ADDRESS *NewDns; + UINTN OldDnsCount; + UINTN NewDnsCount; + IP6_CONFIG_DATA_ITEM *Item; + BOOLEAN OneAdded; + VOID *Tmp; + + if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + NewDns = (EFI_IPv6_ADDRESS *) Data; + OldDns = Item->Data.DnsServers; + NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS); + OneAdded = FALSE; + + if (NewDnsCount != OldDnsCount) { + Tmp = AllocatePool (DataSize); + if (Tmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Tmp = NULL; + } + + for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) { + + if (!NetIp6IsValidUnicast (NewDns + NewIndex)) { + // + // The dns server address must be unicast. + // + FreePool (Tmp); + return EFI_INVALID_PARAMETER; + } + + for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) { + if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) { + FreePool (Tmp); + return EFI_INVALID_PARAMETER; + } + } + + if (OneAdded) { + // + // If any address in the new setting is not in the old settings, skip the + // comparision below. + // + continue; + } + + for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) { + if (EFI_IP6_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) { + // + // If found break out. + // + break; + } + } + + if (OldIndex == OldDnsCount) { + OneAdded = TRUE; + } + } + + if (!OneAdded && (DataSize == Item->DataSize)) { + // + // No new item is added and the size is the same. + // + Item->Status = EFI_SUCCESS; + return EFI_ABORTED; + } else { + if (Tmp != NULL) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + Item->Data.Ptr = Tmp; + } + + CopyMem (Item->Data.Ptr, Data, DataSize); + Item->DataSize = DataSize; + Item->Status = EFI_SUCCESS; + return EFI_SUCCESS; + } +} + +/** + Generate the operational state of the interface this IP6 config instance manages + and output in EFI_IP6_CONFIG_INTERFACE_INFO. + + @param[in] IpSb The pointer to the IP6 service binding instance. + @param[out] IfInfo The pointer to the IP6 configuration interface information structure. + +**/ +VOID +Ip6ConfigInitIfInfo ( + IN IP6_SERVICE *IpSb, + OUT EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo + ) +{ + IfInfo->Name[0] = L'e'; + IfInfo->Name[1] = L't'; + IfInfo->Name[2] = L'h'; + IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex); + IfInfo->Name[4] = 0; + + IfInfo->IfType = IpSb->SnpMode.IfType; + IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize; + CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize); +} + +/** + Parse DHCPv6 reply packet to get the DNS server list. + It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event. + + @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL instance. + @param[in, out] Instance The pointer to the IP6 configuration instance data. + @param[in] Reply The pointer to the DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +Ip6ConfigParseDhcpReply ( + IN EFI_DHCP6_PROTOCOL *Dhcp6, + IN OUT IP6_CONFIG_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Reply + ) +{ + EFI_STATUS Status; + UINT32 OptCount; + EFI_DHCP6_PACKET_OPTION **OptList; + UINT16 OpCode; + UINT16 Length; + UINTN Index; + UINTN Index2; + EFI_IPv6_ADDRESS *DnsServer; + IP6_CONFIG_DATA_ITEM *Item; + + // + // A DHCPv6 reply packet is received as the response to our InfoRequest + // packet. + // + OptCount = 0; + Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_READY; + } + + OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); + if (OptList == NULL) { + return EFI_NOT_READY; + } + + Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + Status = EFI_SUCCESS; + + for (Index = 0; Index < OptCount; Index++) { + // + // Go through all the options to check the ones we are interested in. + // The OpCode and Length are in network byte-order and may not be naturally + // aligned. + // + CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode)); + OpCode = NTOHS (OpCode); + + if (OpCode == IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS) { + CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length)); + Length = NTOHS (Length); + + if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) { + // + // The length should be a multiple of 16 bytes. + // + Status = EFI_NOT_READY; + break; + } + + // + // Validate the DnsServers: whether they are unicast addresses. + // + DnsServer = (EFI_IPv6_ADDRESS *) OptList[Index]->Data; + for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) { + if (!NetIp6IsValidUnicast (DnsServer)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + DnsServer++; + } + + Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + + if (Item->DataSize != Length) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + + Item->Data.Ptr = AllocatePool (Length); + ASSERT (Item->Data.Ptr != NULL); + } + + CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length); + Item->DataSize = Length; + Item->Status = EFI_SUCCESS; + + // + // Signal the waiting events. + // + NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); + + break; + } + } + +ON_EXIT: + + FreePool (OptList); + return Status; +} + +/** + The callback function for Ip6SetAddr. The prototype is defined + as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed + on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event(). + + @param[in] IsDadPassed If TRUE, Duplicate Address Detection passes. + @param[in] TargetAddress The tentative IPv6 address to be checked. + @param[in] Context Pointer to the IP6 configuration instance data. + +**/ +VOID +Ip6ConfigSetStatefulAddrCallback ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + // + // We should record the addresses that fail the DAD, and DECLINE them. + // + if (IsDadPassed) { + // + // Decrease the count, no interests in those passed DAD. + // + if (Instance->FailedIaAddressCount > 0 ) { + Instance->FailedIaAddressCount--; + } + } else { + // + // Record it. + // + IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress); + Instance->DeclineAddressCount++; + } + + if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) { + // + // The checking on all addresses are finished. + // + if (Instance->DeclineAddressCount != 0) { + // + // Decline those duplicates. + // + Instance->Dhcp6->Decline ( + Instance->Dhcp6, + Instance->DeclineAddressCount, + Instance->DeclineAddress + ); + } + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + Instance->DeclineAddress = NULL; + Instance->DeclineAddressCount = 0; + } +} + +/** + The event handle routine when DHCPv6 process is finished or is updated. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP6 configuration instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6Event ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_STATUS Status; + EFI_DHCP6_MODE_DATA Dhcp6ModeData; + EFI_DHCP6_IA *Ia; + EFI_DHCP6_IA_ADDRESS *IaAddr; + UINT32 Index; + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddrInfo; + IP6_INTERFACE *IpIf; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + + if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) { + // + // IPv6 is not operating in the automatic policy now or + // the DHCPv6 information request message exchange is aborted. + // + return ; + } + + // + // The stateful address autoconfiguration is done or updated. + // + Dhcp6 = Instance->Dhcp6; + + Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL); + if (EFI_ERROR (Status)) { + return ; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + IpIf = IpSb->DefaultInterface; + Ia = Dhcp6ModeData.Ia; + IaAddr = Ia->IaAddress; + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + + Instance->DeclineAddress = (EFI_IPv6_ADDRESS *) AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS)); + if (Instance->DeclineAddress == NULL) { + goto ON_EXIT; + } + + Instance->FailedIaAddressCount = Ia->IaAddressCount; + Instance->DeclineAddressCount = 0; + + for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) { + if (Ia->IaAddress[Index].ValidLifetime != 0 && Ia->State == Dhcp6Bound) { + // + // Set this address, either it's a new address or with updated lifetimes. + // An appropriate prefix length will be set. + // + Ip6SetAddress ( + IpIf, + &IaAddr->IpAddress, + FALSE, + 0, + IaAddr->ValidLifetime, + IaAddr->PreferredLifetime, + Ip6ConfigSetStatefulAddrCallback, + Instance + ); + } else { + // + // discard this address, artificially decrease the count as if this address + // passed DAD. + // + if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) { + ASSERT (AddrInfo != NULL); + Ip6RemoveAddr ( + IpSb, + &IpIf->AddressList, + &IpIf->AddressCount, + &AddrInfo->Address, + AddrInfo->PrefixLength + ); + } + + if (Instance->FailedIaAddressCount > 0) { + Instance->FailedIaAddressCount--; + } + } + } + + // + // Parse the Reply packet to get the options we need. + // + if (Dhcp6ModeData.Ia->ReplyPacket != NULL) { + Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket); + } + +ON_EXIT: + + FreePool (Dhcp6ModeData.ClientId); + FreePool (Dhcp6ModeData.Ia); +} + +/** + The event process routine when the DHCPv6 server is answered with a reply packet + for an information request. + + @param[in] This Points to the EFI_DHCP6_PROTOCOL. + @param[in] Context The pointer to the IP6 configuration instance data. + @param[in] Packet The DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigOnDhcp6Reply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ) +{ + return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *) Context, Packet); +} + +/** + The event process routine when the DHCPv6 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP6 config instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + + if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) { + // + // The DHCP6 child is already created or the policy is no longer AUTOMATIC. + // + return ; + } + + Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly); +} + +/** + Set the configuration for the EFI IPv6 network stack running on the communication + device this EFI IPv6 Configuration Protocol instance manages. + + This function is used to set the configuration data of type DataType for the EFI + IPv6 network stack that is running on the communication device that this EFI IPv6 + Configuration Protocol instance manages. + + DataSize is used to calculate the count of structure instances in the Data for + a DataType in which multiple structure instances are allowed. + + This function is always non-blocking. When setting some type of configuration data, + an asynchronous process is invoked to check the correctness of the data, such as + performing Duplicate Address Detection on the manually set local IPv6 addresses. + EFI_NOT_READY is returned immediately to indicate that such an asynchronous process + is invoked, and the process is not finished yet. The caller wanting to get the result + of the asynchronous process is required to call RegisterDataNotify() to register an + event on the specified configuration data. Once the event is signaled, the caller + can call GetData() to obtain the configuration data and know the result. + For other types of configuration data that do not require an asynchronous configuration + process, the result of the operation is immediately returned. + + @param[in] This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to set. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. The type of the data buffer is + associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Data is NULL. + - One or more fields in Data do not match the requirement of the + data type indicated by DataType. + @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified + configuration data cannot be set under the current policy. + @retval EFI_ACCESS_DENIED Another set operation on the specified configuration + data is already in process. + @retval EFI_NOT_READY An asynchronous process was invoked to set the specified + configuration data, and the process is not finished yet. + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type + indicated by DataType. + @retval EFI_UNSUPPORTED This DataType is not supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigSetData ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = Instance->DataItem[DataType].Status; + if (Status != EFI_NOT_READY) { + + if (Instance->DataItem[DataType].SetData == NULL) { + // + // This type of data is readonly. + // + Status = EFI_WRITE_PROTECTED; + } else { + + Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data); + if (!EFI_ERROR (Status)) { + // + // Fire up the events registered with this type of data. + // + NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL); + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } else if (Status == EFI_ABORTED) { + // + // The SetData is aborted because the data to set is the same with + // the one maintained. + // + Status = EFI_SUCCESS; + NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL); + } + } + } else { + // + // Another asynchornous process is on the way. + // + Status = EFI_ACCESS_DENIED; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Get the configuration data for the EFI IPv6 network stack running on the communication + device that this EFI IPv6 Configuration Protocol instance manages. + + This function returns the configuration data of type DataType for the EFI IPv6 network + stack running on the communication device that this EFI IPv6 Configuration Protocol instance + manages. + + The caller is responsible for allocating the buffer used to return the specified + configuration data. The required size will be returned to the caller if the size of + the buffer is too small. + + EFI_NOT_READY is returned if the specified configuration data is not ready due to an + asynchronous configuration process already in progress. The caller can call RegisterDataNotify() + to register an event on the specified configuration data. Once the asynchronous configuration + process is finished, the event will be signaled, and a subsequent GetData() call will return + the specified configuration data. + + @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to get. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in bytes, the + size of buffer required to store the specified configuration data. + @param[in] Data The data buffer in which the configuration data is returned. The + type of the data buffer is associated with the DataType. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - DataSize is NULL. + - Data is NULL if *DataSize is not zero. + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data, + and the required size is returned in DataSize. + @retval EFI_NOT_READY The specified configuration data is not ready due to an + asynchronous configuration process already in progress. + @retval EFI_NOT_FOUND The specified configuration data is not found. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigGetData ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + IP6_CONFIG_DATA_ITEM *DataItem; + + if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + DataItem = &Instance->DataItem[DataType]; + + Status = Instance->DataItem[DataType].Status; + if (!EFI_ERROR (Status)) { + + if (DataItem->GetData != NULL) { + + Status = DataItem->GetData (Instance, DataSize, Data); + } else if (*DataSize < Instance->DataItem[DataType].DataSize) { + // + // Update the buffer length. + // + *DataSize = Instance->DataItem[DataType].DataSize; + Status = EFI_BUFFER_TOO_SMALL; + } else { + + *DataSize = Instance->DataItem[DataType].DataSize; + CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize); + } + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Register an event that is signaled whenever a configuration process on the specified + configuration data is done. + + This function registers an event that is to be signaled whenever a configuration + process on the specified configuration data is performed. An event can be registered + for a different DataType simultaneously. The caller is responsible for determining + which type of configuration data causes the signaling of the event in such an event. + + @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to unregister the event for. + @param[in] Event The event to register. + + @retval EFI_SUCCESS The notification event for the specified configuration data is + registered. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The configuration data type specified by DataType is not + supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigRegisterDataNotify ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + NET_MAP *EventMap; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + EventMap = &Instance->DataItem[DataType].EventMap; + + // + // Check whether this event is already registered for this DataType. + // + Item = NetMapFindKey (EventMap, Event); + if (Item == NULL) { + + Status = NetMapInsertTail (EventMap, Event, NULL); + + if (EFI_ERROR (Status)) { + + Status = EFI_OUT_OF_RESOURCES; + } + + } else { + + Status = EFI_ACCESS_DENIED; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Remove a previously registered event for the specified configuration data. + + @param This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param DataType The type of data to remove from the previously + registered event. + @param Event The event to be unregistered. + + @retval EFI_SUCCESS The event registered for the specified + configuration data was removed. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_NOT_FOUND The Event has not been registered for the + specified DataType. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigUnregisterDataNotify ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + + Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event); + if (Item != NULL) { + + NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL); + Status = EFI_SUCCESS; + } else { + + Status = EFI_NOT_FOUND; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Initialize an IP6_CONFIG_INSTANCE. + + @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip6ConfigInitInstance ( + OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + IP6_CONFIG_INSTANCE *TmpInstance; + LIST_ENTRY *Entry; + EFI_STATUS Status; + UINTN Index; + UINT16 IfIndex; + IP6_CONFIG_DATA_ITEM *DataItem; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE; + + // + // Determine the index of this interface. + // + IfIndex = 0; + NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) { + TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_INSTANCE_SIGNATURE); + + if (TmpInstance->IfIndex > IfIndex) { + // + // There is a sequence hole because some interface is down. + // + break; + } + + IfIndex++; + } + + Instance->IfIndex = IfIndex; + NetListInsertBefore (Entry, &Instance->Link); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + // + // Initialize the event map for each data item. + // + NetMapInit (&Instance->DataItem[Index].EventMap); + } + + // + // Initialize the NET_MAPs used for DAD on manually configured source addresses. + // + NetMapInit (&Instance->DadFailedMap); + NetMapInit (&Instance->DadPassedMap); + + // + // Initialize each data type: associate storage and set data size for the + // fixed size data types, hook the SetData function, set the data attribute. + // + DataItem = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo]; + DataItem->GetData = Ip6ConfigGetIfInfo; + DataItem->Data.Ptr = &Instance->InterfaceInfo; + DataItem->DataSize = sizeof (Instance->InterfaceInfo); + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE); + Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; + DataItem->SetData = Ip6ConfigSetAltIfId; + DataItem->Data.Ptr = &Instance->AltIfId; + DataItem->DataSize = sizeof (Instance->AltIfId); + DataItem->Status = EFI_NOT_FOUND; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypePolicy]; + DataItem->SetData = Ip6ConfigSetPolicy; + DataItem->Data.Ptr = &Instance->Policy; + DataItem->DataSize = sizeof (Instance->Policy); + Instance->Policy = Ip6ConfigPolicyAutomatic; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits]; + DataItem->SetData = Ip6ConfigSetDadXmits; + DataItem->Data.Ptr = &Instance->DadXmits; + DataItem->DataSize = sizeof (Instance->DadXmits); + Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + DataItem->SetData = Ip6ConfigSetMaunualAddress; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + DataItem->SetData = Ip6ConfigSetGateway; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + DataItem->SetData = Ip6ConfigSetDnsServer; + DataItem->Status = EFI_NOT_FOUND; + + // + // Create the event used for DHCP. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip6ConfigOnDhcp6Event, + Instance, + &Instance->Dhcp6Event + ); + ASSERT_EFI_ERROR (Status); + + Instance->Configured = TRUE; + + // + // Try to read the config data from NV variable. + // + Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance); + if (Status == EFI_NOT_FOUND) { + // + // The NV variable is not set, so generate a random IAID, and write down the + // fresh new configuration as the NV variable now. + // + Instance->IaId = NET_RANDOM (NetRandomInitSeed ()); + + for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) { + Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31)); + } + + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } else if (EFI_ERROR (Status)) { + return Status; + } + + Instance->Ip6Config.SetData = EfiIp6ConfigSetData; + Instance->Ip6Config.GetData = EfiIp6ConfigGetData; + Instance->Ip6Config.RegisterDataNotify = EfiIp6ConfigRegisterDataNotify; + Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify; + + + // + // Publish the IP6 configuration form + // + return Ip6ConfigFormInit (Instance); +} + +/** + Release an IP6_CONFIG_INSTANCE. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + +**/ +VOID +Ip6ConfigCleanInstance ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + UINTN Index; + IP6_CONFIG_DATA_ITEM *DataItem; + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + + if (!Instance->Configured) { + return ; + } + + if (Instance->Dhcp6Handle != NULL) { + + Ip6ConfigDestroyDhcp6 (Instance); + } + + // + // Close the event. + // + if (Instance->Dhcp6Event != NULL) { + gBS->CloseEvent (Instance->Dhcp6Event); + } + + NetMapClean (&Instance->DadPassedMap); + NetMapClean (&Instance->DadFailedMap); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + } + + NetMapClean (&Instance->DataItem[Index].EventMap); + } + + Ip6ConfigFormUnload (Instance); + + RemoveEntryList (&Instance->Link); +} + +/** + Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destory the child. + +**/ +EFI_STATUS +Ip6ConfigDestroyDhcp6 ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + Dhcp6 = Instance->Dhcp6; + ASSERT (Dhcp6 != NULL); + + Dhcp6->Stop (Dhcp6); + Dhcp6->Configure (Dhcp6, NULL); + Instance->Dhcp6 = NULL; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + // + // Close DHCPv6 protocol and destroy the child. + // + Status = gBS->CloseProtocol ( + Instance->Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + Instance->Dhcp6Handle + ); + + Instance->Dhcp6Handle = NULL; + + return Status; +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h new file mode 100644 index 0000000000..5ae483931a --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h @@ -0,0 +1,295 @@ +/** @file + Definitions for EFI IPv6 Configuartion Protocol implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __IP6_CONFIG_IMPL_H__ +#define __IP6_CONFIG_IMPL_H__ + +#define IP6_CONFIG_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'C') +#define IP6_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I') +#define IP6_CONFIG_VARIABLE_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) + +#define IP6_CONFIG_DEFAULT_DAD_XMITS 1 +#define IP6_CONFIG_DHCP6_OPTION_ORO 6 +#define IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS 23 + +#define DATA_ATTRIB_SIZE_FIXED 0x1 +#define DATA_ATTRIB_VOLATILE 0x2 + +#define DATA_ATTRIB_SET(Attrib, Bits) (BOOLEAN)((Attrib) & (Bits)) +#define SET_DATA_ATTRIB(Attrib, Bits) ((Attrib) |= (Bits)) + +typedef struct _IP6_CONFIG_INSTANCE IP6_CONFIG_INSTANCE; + +#define IP6_CONFIG_INSTANCE_FROM_PROTOCOL(Proto) \ + CR ((Proto), \ + IP6_CONFIG_INSTANCE, \ + Ip6Config, \ + IP6_CONFIG_INSTANCE_SIGNATURE \ + ) + + +#define IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK(Callback) \ + CR ((Callback), \ + IP6_CONFIG_INSTANCE, \ + CallbackInfo, \ + IP6_CONFIG_INSTANCE_SIGNATURE \ + ) + +#define IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE(Instance) \ + CR ((Instance), \ + IP6_SERVICE, \ + Ip6ConfigInstance, \ + IP6_SERVICE_SIGNATURE \ + ) + +#define IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \ + CR ((ConfigAccess), \ + IP6_FORM_CALLBACK_INFO, \ + HiiConfigAccess, \ + IP6_FORM_CALLBACK_INFO_SIGNATURE \ + ) + +/** + The prototype of work function for EfiIp6ConfigSetData(). + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in] DataSize In bytes, the size of the buffer pointed to by Data. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type, + 8 bytes. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set successfully. + +**/ +typedef +EFI_STATUS +(*IP6_CONFIG_SET_DATA) ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + The prototype of work function for EfiIp6ConfigGetData(). + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in + bytes, the size of buffer required to store the specified + configuration data. + @param[in] Data The data buffer in which the configuration data is returned. + Ignored if DataSize is ZERO. + + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified + configuration data, and the required size is + returned in DataSize. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +typedef +EFI_STATUS +(*IP6_CONFIG_GET_DATA) ( + IN IP6_CONFIG_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ); + +typedef union { + VOID *Ptr; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + EFI_IP6_CONFIG_INTERFACE_ID *AltIfId; + EFI_IP6_CONFIG_POLICY *Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + EFI_IPv6_ADDRESS *Gateway; + EFI_IPv6_ADDRESS *DnsServers; +} IP6_CONFIG_DATA; + +typedef struct { + IP6_CONFIG_SET_DATA SetData; + IP6_CONFIG_GET_DATA GetData; + EFI_STATUS Status; + UINT8 Attribute; + NET_MAP EventMap; + IP6_CONFIG_DATA Data; + UINTN DataSize; +} IP6_CONFIG_DATA_ITEM; + +typedef struct { + UINT16 Offset; + UINTN DataSize; + EFI_IP6_CONFIG_DATA_TYPE DataType; +} IP6_CONFIG_DATA_RECORD; + +#pragma pack(1) + +// +// heap data that contains the data for each data record. +// +// BOOLEAN IsAltIfIdSet; +// EFI_IP6_CONFIG_POLICY Policy; +// EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; +// UINT32 ManualaddressCount; +// UINT32 GatewayCount; +// UINT32 DnsServersCount; +// EFI_IP6_CONFIG_INTERFACE_ID AltIfId; +// EFI_IP6_CONFIG_MANUAL_ADDRESS ManualAddress[]; +// EFI_IPv6_ADDRESS Gateway[]; +// EFI_IPv6_ADDRESS DnsServers[]; +// +typedef struct { + UINT32 IaId; + UINT16 Checksum; + UINT16 DataRecordCount; + IP6_CONFIG_DATA_RECORD DataRecord[1]; +} IP6_CONFIG_VARIABLE; + +#pragma pack() + +typedef struct { + LIST_ENTRY Link; + EFI_IP6_ADDRESS_INFO AddrInfo; +} IP6_ADDRESS_INFO_ENTRY; + +typedef struct { + EFI_IP6_CONFIG_POLICY Policy; ///< manual or automatic + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadTransmitCount; ///< dad transmits count + EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; ///< alternative interface id + LIST_ENTRY ManualAddress; ///< IP addresses + UINT32 ManualAddressCount; ///< IP addresses count + LIST_ENTRY GatewayAddress; ///< Gateway address + UINT32 GatewayAddressCount; ///< Gateway address count + LIST_ENTRY DnsAddress; ///< DNS server address + UINT32 DnsAddressCount; ///< DNS server address count +} IP6_CONFIG_NVDATA; + +typedef struct _IP6_FORM_CALLBACK_INFO { + UINT32 Signature; + EFI_HANDLE ChildHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL HiiConfigAccess; + EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath; + EFI_HII_HANDLE RegisteredHandle; +} IP6_FORM_CALLBACK_INFO; + +struct _IP6_CONFIG_INSTANCE { + UINT32 Signature; + BOOLEAN Configured; + LIST_ENTRY Link; + UINT16 IfIndex; + + EFI_IP6_CONFIG_INTERFACE_INFO InterfaceInfo; + EFI_IP6_CONFIG_INTERFACE_ID AltIfId; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + + IP6_CONFIG_DATA_ITEM DataItem[Ip6ConfigDataTypeMaximum]; + NET_MAP DadFailedMap; + NET_MAP DadPassedMap; + + EFI_IP6_CONFIG_PROTOCOL Ip6Config; + + EFI_EVENT Dhcp6SbNotifyEvent; + VOID *Registration; + EFI_HANDLE Dhcp6Handle; + EFI_DHCP6_PROTOCOL *Dhcp6; + BOOLEAN OtherInfoOnly; + UINT32 IaId; + EFI_EVENT Dhcp6Event; + UINT32 FailedIaAddressCount; + EFI_IPv6_ADDRESS *DeclineAddress; + UINT32 DeclineAddressCount; + + IP6_FORM_CALLBACK_INFO CallbackInfo; + IP6_CONFIG_NVDATA Ip6NvData; +}; + +/** + The event process routine when the DHCPv6 server is answered with a reply packet + for an information request. + + @param[in] This Points to the EFI_DHCP6_PROTOCOL. + @param[in] Context The pointer to the IP6 configuration instance data. + @param[in] Packet The DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigOnDhcp6Reply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ); + +/** + The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] OtherInfoOnly If FALSE, get stateful address and other information + via DHCPv6. Otherwise, only get the other information. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_UNSUPPORTED The DHCP6 driver is not available. + +**/ +EFI_STATUS +Ip6ConfigStartStatefulAutoConfig ( + IN IP6_CONFIG_INSTANCE *Instance, + IN BOOLEAN OtherInfoOnly + ); + +/** + Initialize an IP6_CONFIG_INSTANCE. + + @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip6ConfigInitInstance ( + OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Release an IP6_CONFIG_INSTANCE. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + +**/ +VOID +Ip6ConfigCleanInstance ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destory the child. + +**/ +EFI_STATUS +Ip6ConfigDestroyDhcp6 ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c new file mode 100644 index 0000000000..9ec4886726 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c @@ -0,0 +1,2116 @@ +/** @file + Helper functions for configuring or obtaining the parameters relating to IP6. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +EFI_GUID mIp6HiiVendorDevicePathGuid = IP6_HII_VENDOR_DEVICE_PATH_GUID; +EFI_GUID mIp6ConfigNvDataGuid = IP6_CONFIG_NVDATA_GUID; +CHAR16 mIp6ConfigStorageName[] = L"IP6_CONFIG_IFR_NVDATA"; + +/** + The notify function of create event when performing a manual configuration. + + @param[in] Event The pointer of Event. + @param[in] Context The pointer of Context. + +**/ +VOID +EFIAPI +Ip6ConfigManualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Get the configuration data for the EFI IPv6 network stack running on the + communication. It is a help function to the call EfiIp6ConfigGetData(). + + @param[in] Ip6Config The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to get. + @param[out] DataSize The size of buffer required in bytes. + @param[out] Data The data buffer in which the configuration data is returned. The + type of the data buffer associated with the DataType. + It is the caller's responsibility to free the resource. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - Ip6Config is NULL or invalid. + - DataSize is NULL. + - Data is NULL. + @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resources. + @retval EFI_NOT_READY The specified configuration data is not ready due to an + asynchronous configuration process already in progress. + @retval EFI_NOT_FOUND The specified configuration data was not found. + +**/ +EFI_STATUS +Ip6ConfigNvGetData ( + IN EFI_IP6_CONFIG_PROTOCOL *Ip6Config, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + OUT UINTN *DataSize, + OUT VOID **Data + ) +{ + UINTN BufferSize; + VOID *Buffer; + EFI_STATUS Status; + + if ((Ip6Config == NULL) || (Data == NULL) || (DataSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + Status = Ip6Config->GetData ( + Ip6Config, + DataType, + &BufferSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData ( + Ip6Config, + DataType, + &BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + *DataSize = BufferSize; + *Data = Buffer; + + return EFI_SUCCESS; +} + +/** + Free all nodes in IP6_ADDRESS_INFO_ENTRY in the list array specified + with ListHead. + + @param[in] ListHead The head of the list array in IP6_ADDRESS_INFO_ENTRY. + +**/ +VOID +Ip6FreeAddressInfoList ( + IN LIST_ENTRY *ListHead + ) +{ + IP6_ADDRESS_INFO_ENTRY *Node; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ListHead) { + Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link); + RemoveEntryList (&Node->Link); + FreePool (Node); + } +} + +/** + Convert the IPv6 address into a formatted string. + + @param[in] Ip6 The IPv6 address. + @param[out] Str The formatted IP string. + +**/ +VOID +Ip6ToStr ( + IN EFI_IPv6_ADDRESS *Ip6, + OUT CHAR16 *Str + ) +{ + UINTN Index; + BOOLEAN Short; + UINTN Number; + CHAR16 FormatString[8]; + + Short = FALSE; + + for (Index = 0; Index < 15; Index = Index + 2) { + if (!Short && + Index % 2 == 0 && + Ip6->Addr[Index] == 0 && + Ip6->Addr[Index + 1] == 0 + ) { + // + // Deal with the case of ::. + // + if (Index == 0) { + *Str = L':'; + *(Str + 1) = L':'; + Str = Str + 2; + } else { + *Str = L':'; + Str = Str + 1; + } + + while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) { + Index = Index + 2; + } + + Short = TRUE; + + if (Index == 16) { + // + // :: is at the end of the address. + // + *Str = L'\0'; + break; + } + } + + ASSERT (Index < 15); + + if (Ip6->Addr[Index] == 0) { + Number = UnicodeSPrint (Str, 2 * IP6_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]); + } else { + if (Ip6->Addr[Index + 1] < 0x10) { + CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:")); + } else { + CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:")); + } + + Number = UnicodeSPrint ( + Str, + 2 * IP6_STR_MAX_SIZE, + (CONST CHAR16 *) FormatString, + (UINTN) Ip6->Addr[Index], + (UINTN) Ip6->Addr[Index + 1] + ); + } + + Str = Str + Number; + + if (Index + 2 == 16) { + *Str = L'\0'; + if (*(Str - 1) == L':') { + *(Str - 1) = L'\0'; + } + } + } +} + +/** + Convert EFI_IP6_CONFIG_INTERFACE_ID to string format. + + @param[out] String The buffer to store the converted string. + @param[in] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The string converted successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6ConvertInterfaceIdToString ( + OUT CHAR16 *String, + IN EFI_IP6_CONFIG_INTERFACE_ID *IfId + ) +{ + UINT8 Index; + UINTN Number; + + if ((String == NULL) || (IfId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < 8; Index++) { + Number = UnicodeSPrint ( + String, + 2 * INTERFACE_ID_STR_STORAGE, + L"%x:", + (UINTN) IfId->Id[Index] + ); + String = String + Number; + } + + *(String - 1) = '\0'; + + return EFI_SUCCESS; +} + +/** + Parse InterfaceId in string format and convert it to EFI_IP6_CONFIG_INTERFACE_ID. + + @param[in] String The buffer of the string to be parsed. + @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6ParseInterfaceIdFromString ( + IN CONST CHAR16 *String, + OUT EFI_IP6_CONFIG_INTERFACE_ID *IfId + ) +{ + UINT8 Index; + CHAR16 *IfIdStr; + CHAR16 *TempStr; + UINTN NodeVal; + + if ((String == NULL) || (IfId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IfIdStr = (CHAR16 *) String; + + ZeroMem (IfId, sizeof (EFI_IP6_CONFIG_INTERFACE_ID)); + + for (Index = 0; Index < 8; Index++) { + TempStr = IfIdStr; + + while ((*IfIdStr != L'\0') && (*IfIdStr != L':')) { + IfIdStr++; + } + + // + // The InterfaceId format is X:X:X:X, the number of X should not exceed 8. + // If the number of X is less than 8, zero is appended to the InterfaceId. + // + if ((*IfIdStr == ':') && (Index == 7)) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the string to interface id. AsciiStrHexToUintn stops at the + // first character that is not a valid hex character, ':' or '\0' here. + // + NodeVal = StrHexToUintn (TempStr); + if (NodeVal > 0xFF) { + return EFI_INVALID_PARAMETER; + } + + IfId->Id[Index] = (UINT8) NodeVal; + + IfIdStr++; + } + + return EFI_SUCCESS; +} + +/** + Create Hii Extend Label OpCode as the start opcode and end opcode. It is + a help function. + + @param[in] StartLabelNumber The number of start label. + @param[out] StartOpCodeHandle Points to the start opcode handle. + @param[out] StartLabel Points to the created start opcode. + @param[out] EndOpCodeHandle Points to the end opcode handle. + @param[out] EndLabel Points to the created end opcode. + + @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this + operation. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +Ip6CreateOpCode ( + IN UINT16 StartLabelNumber, + OUT VOID **StartOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **StartLabel, + OUT VOID **EndOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **EndLabel + ) +{ + EFI_STATUS Status; + EFI_IFR_GUID_LABEL *InternalStartLabel; + EFI_IFR_GUID_LABEL *InternalEndLabel; + + if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) { + return EFI_INVALID_PARAMETER; + } + + *StartOpCodeHandle = NULL; + *EndOpCodeHandle = NULL; + Status = EFI_OUT_OF_RESOURCES; + + // + // Initialize the container for dynamic opcodes. + // + *StartOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*StartOpCodeHandle == NULL) { + return Status; + } + + *EndOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*EndOpCodeHandle == NULL) { + goto Exit; + } + + // + // Create Hii Extend Label OpCode as the start opcode. + // + InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *StartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalStartLabel == NULL) { + goto Exit; + } + + InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalStartLabel->Number = StartLabelNumber; + + // + // Create Hii Extend Label OpCode as the end opcode. + // + InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *EndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalEndLabel == NULL) { + goto Exit; + } + + InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalEndLabel->Number = LABEL_END; + + *StartLabel = InternalStartLabel; + *EndLabel = InternalEndLabel; + + return EFI_SUCCESS; + +Exit: + + if (*StartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*StartOpCodeHandle); + } + + if (*EndOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*EndOpCodeHandle); + } + + return Status; +} + +/** + This function converts the different format of address list to string format and + then generates the corresponding text opcode to illustarate the address info in + IP6 configuration page. Currently, the following formats are supported: + EFI_IP6_ADDRESS_INFO AddressType: Ip6ConfigNvHostAddress; + EFI_IPv6_ADDRESS AddressType: Ip6ConfigNvGatewayAddress and Ip6ConfigNvDnsAddress; + EFI_IP6_ROUTE_TABLE AddressType: Ip6ConfigNvRouteTable. + + @param[in, out] String The pointer to the buffer to store the converted + string. + @param[in] HiiHandle A handle that was previously registered in the + HII Database. + @param[in] AddressType The address type. + @param[in] AddressInfo Pointer to the address list. + @param[in] AddressCount The address count of the address list. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The AddressType is not supported. + + +**/ +EFI_STATUS +Ip6ConvertAddressListToString ( + IN OUT CHAR16 *String, + IN EFI_HII_HANDLE HiiHandle, + IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType, + IN VOID *AddressInfo, + IN UINTN AddressCount + ) +{ + UINTN Index; + UINTN Number; + CHAR16 *TempStr; + EFI_STATUS Status; + VOID *StartOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + UINT16 StartLabelNumber; + EFI_STRING_ID TextTwo; + UINT8 *AddressHead; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS *Address; + + if ((String == NULL) || (HiiHandle == NULL) || (AddressInfo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (AddressType == Ip6ConfigNvHostAddress) { + StartLabelNumber = HOST_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvGatewayAddress) { + StartLabelNumber = GATEWAY_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvDnsAddress) { + StartLabelNumber = DNS_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvRouteTable) { + StartLabelNumber = ROUTE_TABLE_LABEL; + } else { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Status = Ip6CreateOpCode ( + StartLabelNumber, + &StartOpCodeHandle, + &StartLabel, + &EndOpCodeHandle, + &EndLabel + ); + if (EFI_ERROR (Status)) { + return Status; + } + + AddressHead = (UINT8 *) AddressInfo; + + for (Index = 0; Index < AddressCount; Index++) { + if (AddressType == Ip6ConfigNvHostAddress) { + AddressInfo = AddressHead + sizeof (EFI_IP6_ADDRESS_INFO) * Index; + Address = &((EFI_IP6_ADDRESS_INFO *) AddressInfo)->Address; + } else if (AddressType == Ip6ConfigNvRouteTable) { + AddressInfo = AddressHead + sizeof (EFI_IP6_ROUTE_TABLE) * Index; + Address = &((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Destination; + } else { + AddressInfo = AddressHead + sizeof (EFI_IPv6_ADDRESS) * Index; + Address = AddressInfo; + } + + // + // Convert the IP address info to string. + // + Ip6ToStr (Address, String); + TempStr = String + StrLen (String); + + if ((AddressType == Ip6ConfigNvHostAddress) || (AddressType == Ip6ConfigNvRouteTable)) { + if (AddressType == Ip6ConfigNvHostAddress) { + PrefixLength = ((EFI_IP6_ADDRESS_INFO *) AddressInfo)->PrefixLength; + } else { + PrefixLength = ((EFI_IP6_ROUTE_TABLE *) AddressInfo)->PrefixLength; + } + + // + // Append the prefix length to the string. + // + *TempStr = L'/'; + TempStr++; + Number = UnicodeSPrint (TempStr, 6, L"%d", PrefixLength); + TempStr = TempStr + Number; + } + + if (AddressType == Ip6ConfigNvRouteTable) { + // + // Append " >> " to the string. + // + Number = UnicodeSPrint (TempStr, 8, L" >> "); + TempStr = TempStr + Number; + + // + // Append the gateway address to the string. + // + Ip6ToStr (&((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Gateway, TempStr); + TempStr = TempStr + StrLen (TempStr); + } + + // + // Generate a text opcode and update the UI. + // + TextTwo = HiiSetString (HiiHandle, 0, String, NULL); + if (TextTwo == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + HiiCreateTextOpCode (StartOpCodeHandle, STR_NULL, STR_NULL, TextTwo); + + String = TempStr; + *String = IP6_ADDRESS_DELIMITER; + String++; + } + + *(String - 1) = '\0'; + + Status = HiiUpdateForm ( + HiiHandle, // HII handle + &mIp6ConfigNvDataGuid, // Formset GUID + FORMID_MAIN_FORM, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + +Exit: + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); + + return Status; +} + +/** + Parse address list in string format and convert it to a list array of node in + IP6_ADDRESS_INFO_ENTRY. + + @param[in] String The buffer to string to be parsed. + @param[out] ListHead The list head of array. + @param[out] AddressCount The number of list nodes in the array. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of resource. + +**/ +EFI_STATUS +Ip6ParseAddressListFromString ( + IN CONST CHAR16 *String, + OUT LIST_ENTRY *ListHead, + OUT UINT32 *AddressCount + ) +{ + EFI_STATUS Status; + CHAR16 *LocalString; + CHAR16 *Temp; + CHAR16 *TempStr; + EFI_IP6_ADDRESS_INFO AddressInfo; + IP6_ADDRESS_INFO_ENTRY *Node; + BOOLEAN Last; + UINT32 Count; + + if ((String == NULL) || (ListHead == NULL) || (AddressCount == NULL)) { + return EFI_INVALID_PARAMETER; + } + + LocalString = (CHAR16 *) AllocateCopyPool (StrSize (String), String); + if (LocalString == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Clean the original address list. + // + Ip6FreeAddressInfoList (ListHead); + + Temp = LocalString; + Last = FALSE; + Count = 0; + + while (*LocalString != L'\0') { + TempStr = LocalString; + while ((*LocalString != L'\0') && (*LocalString != IP6_ADDRESS_DELIMITER)) { + LocalString++; + } + + if (*LocalString == L'\0') { + Last = TRUE; + } + + *LocalString = L'\0'; + + Status = NetLibStrToIp6andPrefix (TempStr, &AddressInfo.Address, &AddressInfo.PrefixLength); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (AddressInfo.PrefixLength == 0xFF) { + AddressInfo.PrefixLength = 0; + } + + if (!NetIp6IsValidUnicast (&AddressInfo.Address)) { + Status = EFI_INVALID_PARAMETER; + goto Error; + } + + Node = AllocatePool (sizeof (IP6_ADDRESS_INFO_ENTRY)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (&Node->AddrInfo, &AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO)); + InsertTailList (ListHead, &Node->Link); + Count++; + + if (Last) { + break; + } + + LocalString++; + } + + FreePool (Temp); + *AddressCount = Count; + return EFI_SUCCESS; + +Error: + Ip6FreeAddressInfoList (ListHead); + FreePool (Temp); + return Status; +} + +/** + This function converts the interface info to string and draws it to the IP6 UI. + The interface information includes interface name, interface type, hardware address, + address info, and route table information. The address information is also used as the + content of manual addresses in IP6 UI. + + @param[in] IfInfo The pointer of EFI_IP6_CONFIG_INTERFACE_INFO. + @param[in] HiiHandle The handle that was previously registered in the + HII Database. + @param[in, out] IfrNvData Points to IP6_CONFIG_IFR_NVDATA. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources. + +**/ +EFI_STATUS +Ip6ConvertInterfaceInfoToString ( + IN EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo, + IN EFI_HII_HANDLE HiiHandle, + IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + UINT32 Index; + UINTN Number; + CHAR16 *String; + CHAR16 *LinkLocalStr; + CHAR16 PortString[ADDRESS_STR_MAX_SIZE]; + CHAR16 FormatString[8]; + EFI_STRING_ID StringId; + EFI_STATUS Status; + + if ((IfInfo == NULL) || (HiiHandle == NULL) || (IfrNvData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Print the interface name. + // + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_INTERFACE_NAME_CONTENT), + IfInfo->Name, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Print the interface type. + // + if (IfInfo->IfType == Ip6InterfaceTypeEthernet) { + StrCpy (PortString, IP6_ETHERNET); + } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) { + StrCpy (PortString, IP6_EXPERIMENTAL_ETHERNET); + } else { + // + // Refer to RFC1700, chapter Number Hardware Type. + // + UnicodeSPrint (PortString, 6, L"%d", IfInfo->IfType); + } + + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_INTERFACE_TYPE_CONTENT), + PortString, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Convert the hardware address. + // + String = PortString; + ASSERT (IfInfo->HwAddressSize <= 32); + + for (Index = 0; Index < IfInfo->HwAddressSize; Index++) { + + if (IfInfo->HwAddress.Addr[Index] < 0x10) { + StrCpy (FormatString, L"0%x-"); + } else { + StrCpy (FormatString, L"%x-"); + } + + Number = UnicodeSPrint ( + String, + 8, + (CONST CHAR16 *) FormatString, + (UINTN) IfInfo->HwAddress.Addr[Index] + ); + String = String + Number; + } + + if (Index != 0) { + ASSERT (String > PortString); + String--; + *String = '\0'; + } + + // + // Print the hardware address. + // + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_MAC_ADDRESS_CONTENT), + PortString, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Print the host address Information. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvHostAddress, + IfInfo->AddressInfo, + IfInfo->AddressInfoCount + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Copy the Host Address Info to manual address field. + // Do not copy the link local address. + // + LinkLocalStr = StrStr (PortString, IP6_LINK_LOCAL_PREFIX); + if (LinkLocalStr != NULL) { + Number = LinkLocalStr - PortString; + if (Number > 0) { + CopyMem (IfrNvData->ManualAddress, PortString, Number * sizeof (CHAR16)); + } + + while ((*LinkLocalStr != L' ') && (*LinkLocalStr != L'\0')) { + LinkLocalStr++; + } + + if (*LinkLocalStr != L'\0') { + LinkLocalStr++; + StrCat (IfrNvData->ManualAddress, LinkLocalStr); + } + } else { + StrCpy (IfrNvData->ManualAddress, PortString); + } + + // + // Print the route table information. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvRouteTable, + IfInfo->RouteTable, + IfInfo->RouteCount + ); + return Status; +} + +/** + Build the address info list from list array of node in IP6_ADDRESS_INFO_ENTRY. + + @param[in] Instance Points to IP6 config instance data. + @param[in] AddressType The address type. + @param[out] AddressInfo The pointer to the buffer to store the address list. + @param[out] AddressSize The address size of the address list. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The AddressType is not supported. + +**/ +EFI_STATUS +Ip6BuildNvAddressInfo ( + IN IP6_CONFIG_INSTANCE *Instance, + IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType, + OUT VOID **AddressInfo, + OUT UINTN *AddressSize + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + LIST_ENTRY *Entry; + LIST_ENTRY *ListHead; + IP6_ADDRESS_INFO_ENTRY *Node; + VOID *AddressList; + VOID *TmpStr; + UINTN DataSize; + EFI_IPv6_ADDRESS *Ip6Address; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + + if ((Instance == NULL) || (AddressInfo == NULL) || (AddressSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + Ip6NvData = &Instance->Ip6NvData; + + if (AddressType == Ip6ConfigNvHostAddress) { + ListHead = &Ip6NvData->ManualAddress; + DataSize = sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS) * Ip6NvData->ManualAddressCount; + } else if (AddressType == Ip6ConfigNvGatewayAddress) { + ListHead = &Ip6NvData->GatewayAddress; + DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->GatewayAddressCount; + } else if (AddressType == Ip6ConfigNvDnsAddress) { + ListHead = &Ip6NvData->DnsAddress; + DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->DnsAddressCount; + } else { + return EFI_UNSUPPORTED; + } + + AddressList = AllocateZeroPool (DataSize); + if (AddressList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TmpStr = AddressList; + + NET_LIST_FOR_EACH (Entry, ListHead) { + Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link); + if (AddressType == Ip6ConfigNvHostAddress) { + ManualAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AddressList; + IP6_COPY_ADDRESS (&ManualAddress->Address, &Node->AddrInfo.Address); + ManualAddress->PrefixLength = Node->AddrInfo.PrefixLength; + AddressList = (UINT8 *) AddressList + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + } else { + Ip6Address = (EFI_IPv6_ADDRESS *) AddressList; + IP6_COPY_ADDRESS (Ip6Address, &Node->AddrInfo.Address); + AddressList = (UINT8 *) AddressList + sizeof (EFI_IPv6_ADDRESS); + } + } + + *AddressInfo = TmpStr; + *AddressSize = DataSize; + return EFI_SUCCESS; +} + +/** + Convert the IP6 configuration data into the IFR data. + + @param[in, out] IfrNvData The IFR NV data. + @param[in] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The policy is not supported in the current implementation. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertConfigNvDataToIfrNvData ( + IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + UINTN DataSize; + VOID *Data; + EFI_STATUS Status; + EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + EFI_HII_HANDLE HiiHandle; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + Ip6Config = &Instance->Ip6Config; + Data = NULL; + DataSize = 0; + HiiHandle = Instance->CallbackInfo.RegisteredHandle; + + // + // Get the current interface info. + // + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Convert the interface info to string and print. + // + Status = Ip6ConvertInterfaceInfoToString ( + (EFI_IP6_CONFIG_INTERFACE_INFO *) Data, + HiiHandle, + IfrNvData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get the interface id. + // + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + ZeroMem (&InterfaceId, DataSize); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + &InterfaceId + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &InterfaceId); + + // + // Get current policy. + // + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + &DataSize, + &Policy + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (Policy == Ip6ConfigPolicyManual) { + IfrNvData->Policy = IP6_POLICY_MANUAL; + } else if (Policy == Ip6ConfigPolicyAutomatic) { + IfrNvData->Policy = IP6_POLICY_AUTO; + } else { + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + IfrNvData->DadTransmitCount = DadXmits.DupAddrDetectTransmits; + + // + // Get DNS server list. + // + FreePool (Data); + Data = NULL; + DataSize = 0; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + &DataSize, + (VOID **) &Data + ); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + goto Exit; + } + + if (DataSize > 0) { + // + // Convert the DNS server address to string and draw it to UI. + // + Status = Ip6ConvertAddressListToString ( + IfrNvData->DnsAddress, + HiiHandle, + Ip6ConfigNvDnsAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + FreePool (Data); + Data = NULL; + } + + // + // Get gateway adderss list. + // + DataSize = 0; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + &DataSize, + (VOID **) &Data + ); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + goto Exit; + } + + if (DataSize > 0) { + // + // Convert the gateway address to string and draw it to UI. + // + Status = Ip6ConvertAddressListToString ( + IfrNvData->GatewayAddress, + HiiHandle, + Ip6ConfigNvGatewayAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Status = EFI_SUCCESS; + +Exit: + if (Data != NULL) { + FreePool (Data); + } + + return Status; +} + +/** + Convert IFR data into IP6 configuration data. The policy, alternative interface + ID, and DAD transmit counts, and will be saved. If under manual policy, the configured + manual address, gateway address, and DNS server address will be saved. + + @param[in] IfrNvData The IFR NV data. + @param[in, out] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertIfrNvDataToConfigNvData ( + IN IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + EFI_IPv6_ADDRESS *Address; + BOOLEAN IsAddressOk; + EFI_EVENT SetAddressEvent; + EFI_EVENT TimeoutEvent; + UINTN DataSize; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Ip6NvData = &Instance->Ip6NvData; + Ip6Config = &Instance->Ip6Config; + + // + // Update those fields which don't have INTERACTIVE attribute. + // + if (IfrNvData->Policy == IP6_POLICY_AUTO) { + Ip6NvData->Policy = Ip6ConfigPolicyAutomatic; + } else if (IfrNvData->Policy == IP6_POLICY_MANUAL) { + Ip6NvData->Policy = Ip6ConfigPolicyManual; + } + + Ip6NvData->DadTransmitCount.DupAddrDetectTransmits = IfrNvData->DadTransmitCount; + + // + // Set the configured policy. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Ip6NvData->Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the duplicate address detection transmits count. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDupAddrDetectTransmits, + sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS), + &Ip6NvData->DadTransmitCount + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the alternative interface ID + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + sizeof (EFI_IP6_CONFIG_INTERFACE_ID), + &Ip6NvData->InterfaceId + ); + if (EFI_ERROR (Status)) { + return Status; + } + + + if (Ip6NvData->Policy == Ip6ConfigPolicyAutomatic) { + return EFI_SUCCESS; + } + + // + // Create events & timers for asynchronous settings. + // + SetAddressEvent = NULL; + TimeoutEvent = NULL; + ManualAddress = NULL; + Address = NULL; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6ConfigManualAddressNotify, + &IsAddressOk, + &SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Set the manual address list. This is an asynchronous process. + // + if (!IsListEmpty (&Ip6NvData->ManualAddress) && (Ip6NvData->ManualAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvHostAddress, + (VOID **) &ManualAddress, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + IsAddressOk = FALSE; + + Status = Ip6Config->RegisterDataNotify ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + DataSize, + (VOID *) ManualAddress + ); + if (Status == EFI_NOT_READY) { + gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000); + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + if (IsAddressOk) { + Status = EFI_SUCCESS; + } + break; + } + } + + Status = Ip6Config->UnregisterDataNotify ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + // + // Set gateway address list. + // + if (!IsListEmpty (&Ip6NvData->GatewayAddress) && (Ip6NvData->GatewayAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvGatewayAddress, + (VOID **) &Address, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + DataSize, + (VOID *) Address + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + FreePool (Address); + Address = NULL; + } + + // + // Set DNS server address list. + // + if (!IsListEmpty (&Ip6NvData->DnsAddress) && (Ip6NvData->DnsAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvDnsAddress, + (VOID **) &Address, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + DataSize, + (VOID *) Address + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Status = EFI_SUCCESS; + +Exit: + if (SetAddressEvent != NULL) { + gBS->CloseEvent (SetAddressEvent); + } + + if (TimeoutEvent != NULL) { + gBS->CloseEvent (TimeoutEvent); + } + + if (ManualAddress != NULL) { + FreePool (ManualAddress); + } + + if (Address != NULL) { + FreePool (Address); + } + + return Status; +} + +/** + This function allows the caller to request the current + configuration for one or more named elements. The resulting + string is in format. Any and all alternative + configuration strings shall also be appended to the end of the + current configuration string. If they are, they must appear + after the current configuration. They must contain the same + routing (GUID, NAME, PATH) as the current configuration string. + They must have an additional description indicating the type of + alternative configuration the string represents, + "ALTCFG=". That (when + converted from Hex UNICODE to binary) is a reference to a + string in the associated string pack. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Request A null-terminated Unicode string in + format. Note that this + includes the routing information as well as + the configurable name / value pairs. It is + invalid for this string to be in + format. + @param[out] Progress On return, points to a character in the + Request string. Points to the string's null + terminator if request was successful. Points + to the most recent "&" before the first + failing name / value pair (or the beginning + of the string if the failure is in the first + name / value pair) if the request was not + successful. + @param[out] Results A null-terminated Unicode string in + format which has all values + filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results string is filled with the + values corresponding to all requested + names. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + @retval EFI_INVALID_PARAMETER For example, passing in a NULL + for the Request parameter + would result in this type of + error. In this case, the + Progress parameter would be + set to NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any + known driver. Progress set to the + first character in the routing header. + Note: There is no requirement that the + driver validate the routing data. It + must skip the in order to + process the names. + @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set + to most recent & before the + error or the beginning of the + string. + @retval EFI_INVALID_PARAMETER Unknown name. Progress points + to the & before the name in + question. Currently not implemented. +**/ +EFI_STATUS +EFIAPI +Ip6FormExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + + EFI_STATUS Status; + IP6_FORM_CALLBACK_INFO *Private; + IP6_CONFIG_INSTANCE *Ip6ConfigInstance; + IP6_CONFIG_IFR_NVDATA *IfrNvData; + EFI_STRING ConfigRequestHdr; + EFI_STRING ConfigRequest; + BOOLEAN AllocatedRequest; + UINTN Size; + UINTN BufferSize; + + if (This == NULL || Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Request; + if ((Request != NULL) && + !HiiIsConfigHdrMatch (Request, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) { + return EFI_NOT_FOUND; + } + + ConfigRequestHdr = NULL; + ConfigRequest = NULL; + AllocatedRequest = FALSE; + Size = 0; + + Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + Ip6ConfigInstance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private); + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + + IfrNvData = (IP6_CONFIG_IFR_NVDATA *) AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6ConvertConfigNvDataToIfrNvData (IfrNvData, Ip6ConfigInstance); + if (EFI_ERROR (Status)) { + goto Exit; + } + + ConfigRequest = Request; + if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { + // + // Request has no request element, construct full request string. + // Allocate and fill a buffer large enough to hold the template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator. + // + ConfigRequestHdr = HiiConstructConfigHdr ( + &mIp6ConfigNvDataGuid, + mIp6ConfigStorageName, + Private->ChildHandle + ); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + ASSERT (ConfigRequest != NULL); + AllocatedRequest = TRUE; + UnicodeSPrint ( + ConfigRequest, + Size, + L"%s&OFFSET=0&WIDTH=%016LX", + ConfigRequestHdr, + (UINT64) BufferSize + ); + FreePool (ConfigRequestHdr); + } + + // + // Convert buffer data to by helper function BlockToConfig() + // + Status = gHiiConfigRouting->BlockToConfig ( + gHiiConfigRouting, + ConfigRequest, + (UINT8 *) IfrNvData, + BufferSize, + Results, + Progress + ); + +Exit: + FreePool (IfrNvData); + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return Status; +} + +/** + This function applies changes in a driver's configuration. + Input is a Configuration, which has the routing data for this + driver followed by name / value configuration pairs. The driver + must apply those pairs to its configurable storage. If the + driver's configuration is stored in a linear block of data + and the driver's name / value pairs are in + format, it may use the ConfigToBlock helper function (above) to + simplify the job. Currently not implemented. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Configuration A null-terminated Unicode string in + format. + @param[out] Progress A pointer to a string filled in with the + offset of the most recent '&' before the + first failing name / value pair (or the + beginn ing of the string if the failure + is in the first name / value pair) or + the terminating NULL if all was + successful. + + @retval EFI_SUCCESS The results have been distributed or are + awaiting distribution. + @retval EFI_OUT_OF_MEMORY Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + @retval EFI_INVALID_PARAMETERS Passing in a NULL for the + Results parameter would result + in this type of error. + @retval EFI_NOT_FOUND Target for the specified routing data + was not found. +**/ +EFI_STATUS +EFIAPI +Ip6FormRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (This == NULL || Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check routing data in . + // Note: if only one Storage is used, then this checking could be skipped. + // + if (!HiiIsConfigHdrMatch (Configuration, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) { + *Progress = Configuration; + return EFI_NOT_FOUND; + } + + *Progress = Configuration + StrLen (Configuration); + + return EFI_SUCCESS; +} + +/** + This function is called to provide results data to the driver. + This data consists of a unique key that is used to identify + which data is either being passed back or being asked for. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Action Specifies the type of action taken by the browser. + @param[in] QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. The format of the data tends to + vary based on the opcode that generated the callback. + @param[in] Type The type of value for the question. + @param[in] Value A pointer to the data being sent to the original + exporting driver. + @param[out] ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. Currently not implemented. + @retval EFI_INVALID_PARAMETER Passed in the wrong parameter. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +Ip6FormCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + IP6_FORM_CALLBACK_INFO *Private; + UINTN BufferSize; + IP6_CONFIG_IFR_NVDATA *IfrNvData; + IP6_CONFIG_IFR_NVDATA OldIfrNvData; + EFI_STATUS Status; + EFI_INPUT_KEY Key; + IP6_CONFIG_INSTANCE *Instance; + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_IP6_CONFIG_INTERFACE_INFO *Data; + UINTN DataSize; + CHAR16 PortString[ADDRESS_STR_MAX_SIZE]; + EFI_HII_HANDLE HiiHandle; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + Instance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private); + Ip6NvData = &Instance->Ip6NvData; + + if (Action == EFI_BROWSER_ACTION_FORM_OPEN) { + // + // Update main Form when main Form is opened. + // This will be done only in FORM_OPEN CallBack of question with KEY_INTERFACE_ID from main Form. + // + if (QuestionId != KEY_INTERFACE_ID) { + return EFI_SUCCESS; + } + + Ip6Config = &Instance->Ip6Config; + HiiHandle = Instance->CallbackInfo.RegisteredHandle; + + // + // Get the current interface info. + // + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Generate the dynamic text opcode for host address and draw it. + // + IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data; + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvHostAddress, + IfInfo->AddressInfo, + IfInfo->AddressInfoCount + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Generate the dynamic text opcode for route table and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvRouteTable, + IfInfo->RouteTable, + IfInfo->RouteCount + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get DNS server list. + // + DataSize = 0; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + goto Exit; + } + + if (DataSize > 0) { + // + // Generate the dynamic text opcode for DNS server and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvDnsAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + // + // Get gateway adderss list. + // + DataSize = 0; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + goto Exit; + } + + if (DataSize > 0) { + // + // Generate the dynamic text opcode for gateway and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvGatewayAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + +Exit: + FreePool (Data); + return Status; + } + + if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) { + // + // Do nothing for UEFI FORM_CLOSE action + // + return EFI_SUCCESS; + } + + if ((Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve uncommitted data from Browser + // + + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + IfrNvData = AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_SUCCESS; + + ZeroMem (&OldIfrNvData, BufferSize); + + HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData); + + CopyMem (&OldIfrNvData, IfrNvData, BufferSize); + + switch (QuestionId) { + case KEY_INTERFACE_ID: + Status = Ip6ParseInterfaceIdFromString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Interface ID!", + NULL + ); + } + + break; + + case KEY_MANUAL_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->ManualAddress, + &Ip6NvData->ManualAddress, + &Ip6NvData->ManualAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Host Addresses!", + NULL + ); + } + + break; + + case KEY_GATEWAY_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->GatewayAddress, + &Ip6NvData->GatewayAddress, + &Ip6NvData->GatewayAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Gateway Addresses!", + NULL + ); + } + + break; + + case KEY_DNS_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->DnsAddress, + &Ip6NvData->DnsAddress, + &Ip6NvData->DnsAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid DNS Addresses!", + NULL + ); + } + + break; + + case KEY_SAVE_CONFIG_CHANGES: + CopyMem (&OldIfrNvData, IfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA)); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; + break; + + case KEY_IGNORE_CONFIG_CHANGES: + CopyMem (IfrNvData, &OldIfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA)); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; + break; + + case KEY_SAVE_CHANGES: + Status = Ip6ConvertIfrNvDataToConfigNvData (IfrNvData, Instance); + if (EFI_ERROR (Status)) { + break; + } + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + break; + + default: + break; + } + + if (!EFI_ERROR (Status)) { + // + // Pass changed uncommitted data back to Form Browser. + // + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL); + } + + FreePool (IfrNvData); + return Status; +} + +/** + Install HII Config Access protocol for network device and allocate resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form. + + @retval EFI_SUCCESS The HII Config Access protocol is installed. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConfigFormInit ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + IP6_FORM_CALLBACK_INFO *CallbackInfo; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; + VENDOR_DEVICE_PATH VendorDeviceNode; + EFI_SERVICE_BINDING_PROTOCOL *MnpSb; + CHAR16 *MacString; + CHAR16 MenuString[128]; + CHAR16 PortString[128]; + CHAR16 *OldMenuString; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + ASSERT (IpSb != NULL); + + Status = gBS->HandleProtocol ( + IpSb->Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CallbackInfo = &Instance->CallbackInfo; + CallbackInfo->Signature = IP6_FORM_CALLBACK_INFO_SIGNATURE; + + // + // Construct device path node for EFI HII Config Access protocol, + // which consists of controller physical device path and one hardware + // vendor guid node. + // + ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH)); + VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH; + VendorDeviceNode.Header.SubType = HW_VENDOR_DP; + + CopyGuid (&VendorDeviceNode.Guid, &mIp6HiiVendorDevicePathGuid); + + SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH)); + CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode + ); + if (CallbackInfo->HiiVendorDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + ConfigAccess = &CallbackInfo->HiiConfigAccess; + ConfigAccess->ExtractConfig = Ip6FormExtractConfig; + ConfigAccess->RouteConfig = Ip6FormRouteConfig; + ConfigAccess->Callback = Ip6FormCallback; + + // + // Install Device Path Protocol and Config Access protocol on new handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + ConfigAccess, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Open the Parent Handle for the child + // + Status = gBS->OpenProtocol ( + IpSb->Controller, + &gEfiManagedNetworkServiceBindingProtocolGuid, + (VOID **) &MnpSb, + IpSb->Image, + CallbackInfo->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Publish our HII data + // + CallbackInfo->RegisteredHandle = HiiAddPackages ( + &mIp6ConfigNvDataGuid, + CallbackInfo->ChildHandle, + Ip6DxeStrings, + Ip6ConfigBin, + NULL + ); + if (CallbackInfo->RegisteredHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Append MAC string in the menu string and tile string + // + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString); + if (!EFI_ERROR (Status)) { + OldMenuString = HiiGetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE), + NULL) + ; + UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE), + MenuString, + NULL + ); + UnicodeSPrint (PortString, 128, L"MAC:%s", MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_DEVICE_FORM_TITLE), + PortString, + NULL + ); + + FreePool (MacString); + FreePool (OldMenuString); + + InitializeListHead (&Instance->Ip6NvData.ManualAddress); + InitializeListHead (&Instance->Ip6NvData.GatewayAddress); + InitializeListHead (&Instance->Ip6NvData.DnsAddress); + + return EFI_SUCCESS; + } + +Error: + Ip6ConfigFormUnload (Instance); + return Status; +} + +/** + Uninstall the HII Config Access protocol for network devices and free up the resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form. + +**/ +VOID +Ip6ConfigFormUnload ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + IP6_FORM_CALLBACK_INFO *CallbackInfo; + IP6_CONFIG_NVDATA *Ip6NvData; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + ASSERT (IpSb != NULL); + + CallbackInfo = &Instance->CallbackInfo; + + if (CallbackInfo->ChildHandle != NULL) { + + // + // Close the child handle + // + gBS->CloseProtocol ( + IpSb->Controller, + &gEfiManagedNetworkServiceBindingProtocolGuid, + IpSb->Image, + CallbackInfo->ChildHandle + ); + // + // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL + // + gBS->UninstallMultipleProtocolInterfaces ( + CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &CallbackInfo->HiiConfigAccess, + NULL + ); + } + + if (CallbackInfo->HiiVendorDevicePath != NULL) { + FreePool (CallbackInfo->HiiVendorDevicePath); + } + + if (CallbackInfo->RegisteredHandle != NULL) { + // + // Remove HII package list + // + HiiRemovePackages (CallbackInfo->RegisteredHandle); + } + + Ip6NvData = &Instance->Ip6NvData; + + Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress); + Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress); + Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress); + + Ip6NvData->ManualAddressCount = 0; + Ip6NvData->GatewayAddressCount = 0; + Ip6NvData->DnsAddressCount = 0; +} diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h new file mode 100644 index 0000000000..d184776707 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h @@ -0,0 +1,73 @@ +/** @file + The header file of Ip6ConfigNv.c. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _IP6_CONFIGNV_H_ +#define _IP6_CONFIGNV_H_ + +#include "Ip6NvData.h" +#include "Ip6ConfigImpl.h" + +extern UINT8 Ip6ConfigBin[]; +extern UINT8 Ip6DxeStrings[]; + +#define IP6_HII_VENDOR_DEVICE_PATH_GUID \ + { \ + 0x13288098, 0xb11f, 0x45b9, { 0xbc, 0x4f, 0x91, 0xb5, 0x4b, 0xa3, 0x39, 0xb9 } \ + } + +#define IP6_ETHERNET L"Ethernet" +#define IP6_EXPERIMENTAL_ETHERNET L"Experimental Ethernet" +#define IP6_ADDRESS_DELIMITER L' ' +#define IP6_LINK_LOCAL_PREFIX L"FE80::" + +typedef enum { + Ip6InterfaceTypeEthernet = 1, + Ip6InterfaceTypeExperimentalEthernet +} IP6_INTERFACE_TYPE; + +typedef enum { + Ip6ConfigNvHostAddress, + Ip6ConfigNvGatewayAddress, + Ip6ConfigNvDnsAddress, + Ip6ConfigNvRouteTable +} IP6_CONFIG_NV_ADDRESS_TYPE; + +/** + Install HII Config Access protocol for network device and allocate resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form. + + @retval EFI_SUCCESS The HII Config Access protocol is installed. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConfigFormInit ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Uninstall HII Config Access protocol for network device and free resource. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form. + +**/ +VOID +Ip6ConfigFormUnload ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.c b/NetworkPkg/Ip6Dxe/Ip6Driver.c new file mode 100644 index 0000000000..388dadef8d --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Driver.c @@ -0,0 +1,930 @@ +/** @file + The driver binding and service binding protocol for IP6 driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = { + Ip6DriverBindingSupported, + Ip6DriverBindingStart, + Ip6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + The entry point for IP6 driver which installs the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIp6DriverBinding, + ImageHandle, + &gIp6ComponentName, + &gIp6ComponentName2 + ); +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + // + // Test for the MNP service binding Protocol + // + return gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + +/** + Clean up an IP6 service binding instance. It releases all + the resource allocated by the instance. The instance may be + partly initialized, or partly destroyed. If a resource is + destroyed, it is marked as that in case the destory failed and + being called again later. + + @param[in] IpSb The IP6 service binding instance to clean up. + + @retval EFI_SUCCESS The resource used by the instance are cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip6CleanService ( + IN IP6_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS AllNodes; + IP6_NEIGHBOR_ENTRY *NeighborCache; + + Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance); + + // + // Leave link-scope all-nodes multicast address (FF02::1) + // + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + + Status = Ip6LeaveGroup (IpSb, &AllNodes); + if (EFI_ERROR (Status)) { + return Status; + } + + if (IpSb->DefaultInterface != NULL) { + Ip6CleanInterface (IpSb->DefaultInterface, NULL); + IpSb->DefaultInterface = NULL; + } + + Ip6CleanDefaultRouterList (IpSb); + + Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); + Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); + + if (IpSb->RouteTable != NULL) { + Ip6CleanRouteTable (IpSb->RouteTable); + IpSb->RouteTable = NULL; + } + + if (IpSb->InterfaceId != NULL) { + FreePool (IpSb->InterfaceId); + } + + IpSb->InterfaceId = NULL; + + Ip6CleanAssembleTable (&IpSb->Assemble); + + if (IpSb->MnpChildHandle != NULL) { + if (IpSb->Mnp != NULL) { + IpSb->Mnp->Cancel (IpSb->Mnp, NULL); + IpSb->Mnp->Configure (IpSb->Mnp, NULL); + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + IpSb->Mnp = NULL; + } + + NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiManagedNetworkServiceBindingProtocolGuid, + IpSb->MnpChildHandle + ); + + IpSb->MnpChildHandle = NULL; + } + + if (IpSb->RecvRequest.MnpToken.Event != NULL) { + gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event); + } + + if (IpSb->Timer != NULL) { + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->CloseEvent (IpSb->Timer); + + IpSb->Timer = NULL; + } + + if (IpSb->FasterTimer != NULL) { + gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0); + gBS->CloseEvent (IpSb->FasterTimer); + + IpSb->FasterTimer = NULL; + } + // + // Free the Neighbor Discovery resources + // + while (!IsListEmpty (&IpSb->NeighborTable)) { + NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link); + Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL); + } + + return EFI_SUCCESS; +} + +/** + Create a new IP6 driver service binding protocol. + + @param[in] Controller The controller that has MNP service binding + installed. + @param[in] ImageHandle The IP6 driver's image handle. + @param[out] Service The variable to receive the newly created IP6 + service. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new IP6 service binding private is created. + +**/ +EFI_STATUS +Ip6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT IP6_SERVICE **Service + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_CONFIG_DATA *Config; + IP6_CONFIG_DATA_ITEM *DataItem; + + ASSERT (Service != NULL); + + *Service = NULL; + + // + // allocate a service private data then initialize all the filed to + // empty resources, so if any thing goes wrong when allocating + // resources, Ip6CleanService can be called to clean it up. + // + IpSb = AllocateZeroPool (sizeof (IP6_SERVICE)); + + if (IpSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IpSb->Signature = IP6_SERVICE_SIGNATURE; + IpSb->ServiceBinding.CreateChild = Ip6ServiceBindingCreateChild; + IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild; + IpSb->State = IP6_SERVICE_UNSTARTED; + IpSb->InDestroy = FALSE; + + IpSb->NumChildren = 0; + InitializeListHead (&IpSb->Children); + + InitializeListHead (&IpSb->Interfaces); + IpSb->DefaultInterface = NULL; + IpSb->RouteTable = NULL; + + IpSb->RecvRequest.Signature = IP6_LINK_RX_SIGNATURE; + IpSb->RecvRequest.CallBack = NULL; + IpSb->RecvRequest.Context = NULL; + MnpToken = &IpSb->RecvRequest.MnpToken; + MnpToken->Event = NULL; + MnpToken->Status = EFI_NOT_READY; + MnpToken->Packet.RxData = NULL; + + Ip6CreateAssembleTable (&IpSb->Assemble); + + IpSb->MldCtrl.Mldv1QuerySeen = 0; + InitializeListHead (&IpSb->MldCtrl.Groups); + + ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS)); + IpSb->LinkLocalOk = FALSE; + IpSb->LinkLocalDadFail = FALSE; + IpSb->Dhcp6NeedStart = FALSE; + IpSb->Dhcp6NeedInfoRequest = FALSE; + + IpSb->CurHopLimit = IP6_HOP_LIMIT; + IpSb->LinkMTU = IP6_MIN_LINK_MTU; + IpSb->BaseReachableTime = IP6_REACHABLE_TIME; + Ip6UpdateReachableTime (IpSb); + // + // RFC4861 RETRANS_TIMER: 1,000 milliseconds + // + IpSb->RetransTimer = IP6_RETRANS_TIMER; + + IpSb->RoundRobin = 0; + + InitializeListHead (&IpSb->NeighborTable); + InitializeListHead (&IpSb->DefaultRouterList); + InitializeListHead (&IpSb->OnlinkPrefix); + InitializeListHead (&IpSb->AutonomousPrefix); + + IpSb->InterfaceIdLen = IP6_IF_ID_LEN; + IpSb->InterfaceId = NULL; + + IpSb->RouterAdvertiseReceived = FALSE; + IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS; + IpSb->Ticks = 0; + + IpSb->Image = ImageHandle; + IpSb->Controller = Controller; + + IpSb->MnpChildHandle = NULL; + IpSb->Mnp = NULL; + + Config = &IpSb->MnpConfigData; + Config->ReceivedQueueTimeoutValue = 0; + Config->TransmitQueueTimeoutValue = 0; + Config->ProtocolTypeFilter = IP6_ETHER_PROTO; + Config->EnableUnicastReceive = TRUE; + Config->EnableMulticastReceive = TRUE; + Config->EnableBroadcastReceive = TRUE; + Config->EnablePromiscuousReceive = FALSE; + Config->FlushQueuesOnReset = TRUE; + Config->EnableReceiveTimestamps = FALSE; + Config->DisableBackgroundPolling = FALSE; + + ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE)); + + IpSb->Timer = NULL; + IpSb->FasterTimer = NULL; + + ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE)); + + IpSb->MacString = NULL; + + // + // Create various resources. First create the route table, timer + // event, MNP token event and MNP child. + // + + IpSb->RouteTable = Ip6CreateRouteTable (); + if (IpSb->RouteTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip6TimerTicking, + IpSb, + &IpSb->Timer + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip6NdFasterTimerTicking, + IpSb, + &IpSb->FasterTimer + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = NetLibCreateServiceChild ( + Controller, + ImageHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &IpSb->MnpChildHandle + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) (&IpSb->Mnp), + ImageHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6ServiceConfigMnp (IpSb, TRUE); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER); + if (NetLibGetVlanId (IpSb->Controller) != 0) { + // + // This is a VLAN device, reduce MTU by VLAN tag length + // + IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN; + } + IpSb->OldMaxPacketSize = IpSb->MaxPacketSize; + + // + // Currently only ETHERNET is supported in IPv6 stack, since + // link local address requires an IEEE 802 48-bit MACs for + // EUI-64 format interface identifier mapping. + // + if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + Status = Ip6InitMld (IpSb); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds. + // + Status = gBS->SetTimer (IpSb->FasterTimer, TimerPeriodic, TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds. + // + Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_MS * IP6_ONE_SECOND_IN_MS); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnFrameReceived, + &IpSb->RecvRequest, + &MnpToken->Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE); + if (IpSb->DefaultInterface == NULL) { + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + // + // If there is any manual address, set it. + // + DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + DataItem->SetData ( + &IpSb->Ip6ConfigInstance, + DataItem->DataSize, + DataItem->Data.Ptr + ); + } + + InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link); + + *Service = IpSb; + return EFI_SUCCESS; + +ON_ERROR: + Ip6CleanService (IpSb); + FreePool (IpSb); + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + + // + // Test for the Ip6 service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (IpSb != NULL); + + // + // Install the Ip6ServiceBinding Protocol onto ControlerHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &IpSb->ServiceBinding, + &gEfiIp6ConfigProtocolGuid, + &IpSb->Ip6ConfigInstance.Ip6Config, + NULL + ); + + if (EFI_ERROR (Status)) { + + Ip6CleanService (IpSb); + FreePool (IpSb); + } else { + // + // Initialize the IP6 ID + // + mIp6Id = NET_RANDOM (NetRandomInitSeed ()); + + Ip6SetVariableData (IpSb); + } + + return Status; +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + BOOLEAN IsDhcp6; + EFI_TPL OldTpl; + INTN State; + + IsDhcp6 = FALSE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + + if (NicHandle != NULL) { + // + // DriverBindingStop is triggered by the uninstallation of the EFI DHCPv6 + // Protocol used by Ip6Config. + // + IsDhcp6 = TRUE; + } else { + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid); + + if (NicHandle == NULL) { + return EFI_DEVICE_ERROR; + } + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpSb->InDestroy) { + Status = EFI_SUCCESS; + goto Exit; + } + + if (IsDhcp6) { + + Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance); + gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event); + IpSb->Ip6ConfigInstance.Dhcp6Event = NULL; + } else if (NumberOfChildren == 0) { + + IpSb->InDestroy = TRUE; + State = IpSb->State; + IpSb->State = IP6_SERVICE_DESTROY; + + // + // Clear the variable data. + // + Ip6ClearVariableData (IpSb); + + Status = Ip6CleanService (IpSb); + if (EFI_ERROR (Status)) { + IpSb->State = State; + goto Exit; + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + ServiceBinding, + &gEfiIp6ConfigProtocolGuid, + &IpSb->Ip6ConfigInstance.Ip6Config, + NULL + ); + ASSERT_EFI_ERROR (Status); + FreePool (IpSb); + } else { + // + // NumberOfChildren is not zero, destroy all IP6 children instances. + // + while (!IsListEmpty (&IpSb->Children)) { + IpInstance = NET_LIST_HEAD (&IpSb->Children, IP6_PROTOCOL, Link); + ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle); + } + + if (IpSb->NumChildren != 0) { + Status = EFI_DEVICE_ERROR; + } + } + +Exit: + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Creates a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O services. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_TPL OldTpl; + EFI_STATUS Status; + VOID *Mnp; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpSb = IP6_SERVICE_FROM_PROTOCOL (This); + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + IpInstance = AllocatePool (sizeof (IP6_PROTOCOL)); + + if (IpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip6InitProtocol (IpSb, IpInstance); + + // + // Install Ip6 onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpInstance->Handle = *ChildHandle; + + // + // Open the Managed Network protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &Mnp, + gIp6DriverBinding.DriverBindingHandle, + IpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto, + NULL + ); + + goto ON_ERROR; + } + + // + // Insert it into the service binding instance. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&IpSb->Children, &IpInstance->Link); + IpSb->NumChildren++; + + gBS->RestoreTPL (OldTpl); + +ON_ERROR: + + if (EFI_ERROR (Status)) { + + Ip6CleanProtocol (IpInstance); + + FreePool (IpInstance); + } + + return Status; +} + +/** + Destroys a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_IP6_PROTOCOL *Ip6; + EFI_TPL OldTpl; + INTN State; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + IpSb = IP6_SERVICE_FROM_PROTOCOL (This); + + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Ip6, + gIp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6); + + if (IpInstance->Service != IpSb) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // A child can be destroyed more than once. For example, + // Ip6DriverBindingStop will destory all of its children. + // when UDP driver is being stopped, it will destory all + // the IP child it opens. + // + if (IpInstance->State == IP6_STATE_DESTROY) { + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + } + + State = IpInstance->State; + IpInstance->State = IP6_STATE_DESTROY; + + // + // Close the Managed Network protocol. + // + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + gIp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the IP6 protocol first. Many thing happens during + // this: + // 1. The consumer of the IP6 protocol will be stopped if it + // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is + // stopped, IP driver's stop function will be called, and uninstall + // EFI_IP6_PROTOCOL will trigger the UDP's stop function. This + // makes it possible to create the network stack bottom up, and + // stop it top down. + // 2. the upper layer will recycle the received packet. The recycle + // event's TPL is higher than this function. The recycle events + // will be called back before preceeding. If any packets not recycled, + // that means there is a resource leak. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6CleanProtocol (IpInstance); + + Ip6SetVariableData (IpSb); + + if (EFI_ERROR (Status)) { + gBS->InstallMultipleProtocolInterfaces ( + &ChildHandle, + &gEfiIp6ProtocolGuid, + Ip6, + NULL + ); + + goto ON_ERROR; + } + + RemoveEntryList (&IpInstance->Link); + ASSERT (IpSb->NumChildren > 0); + IpSb->NumChildren--; + + gBS->RestoreTPL (OldTpl); + + FreePool (IpInstance); + return EFI_SUCCESS; + +ON_ERROR: + IpInstance->State = State; + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.h b/NetworkPkg/Ip6Dxe/Ip6Driver.h new file mode 100644 index 0000000000..fcb92ab946 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Driver.h @@ -0,0 +1,185 @@ +/** @file + The driver binding and service binding protocol for IP6 driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_DRIVER_H__ +#define __EFI_IP6_DRIVER_H__ + +extern EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2; + +/** + Clean up an IP6 service binding instance. It releases all + the resource allocated by the instance. The instance may be + partly initialized, or partly destroyed. If a resource is + destroyed, it is marked as that in case the destory failed and + being called again later. + + @param[in] IpSb The IP6 service binding instance to clean up. + + @retval EFI_SUCCESS The resource used by the instance are cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip6CleanService ( + IN IP6_SERVICE *IpSb + ); + +// +// Function prototype for the driver's entry point +// + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + The entry point for IP6 driver which installs the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Drivr Binding Protocol +// + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// + +/** + Creates a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O services. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Dxe.inf b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf new file mode 100644 index 0000000000..aeb341cdc3 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf @@ -0,0 +1,100 @@ +## @file +# Component description file for Ip6 module. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Ip6Dxe + FILE_GUID = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Ip6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gIp6DriverBinding +# COMPONENT_NAME = gIp6ComponentName +# COMPONENT_NAME2 = gIp6ComponentName2 +# + +[Sources] + Ip6Output.h + Ip6Option.h + Ip6Input.h + Ip6Nd.h + Ip6Mld.h + Ip6Impl.c + Ip6Driver.c + ComponentName.c + Ip6Nd.c + Ip6Input.c + Ip6ConfigImpl.c + Ip6ConfigImpl.h + Ip6Impl.h + Ip6Option.c + Ip6If.h + Ip6Icmp.h + Ip6Mld.c + Ip6Common.c + Ip6Route.c + Ip6If.c + Ip6Driver.h + Ip6Output.c + Ip6Icmp.c + Ip6Common.h + Ip6Route.h + Ip6DxeStrings.uni + Ip6NvData.h + Ip6ConfigNv.c + Ip6ConfigNv.h + Ip6Config.vfr + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + BaseMemoryLib + DevicePathLib + HiiLib + UefiHiiServicesLib + PrintLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + UefiLib + DebugLib + NetLib + DpcLib + +[Protocols] + gEfiManagedNetworkServiceBindingProtocolGuid + gEfiManagedNetworkProtocolGuid + gEfiIp6ServiceBindingProtocolGuid + gEfiIp6ProtocolGuid + gEfiIp6ConfigProtocolGuid + gEfiDhcp6ServiceBindingProtocolGuid + gEfiDhcp6ProtocolGuid + gEfiIpSecProtocolGuid + gEfiHiiConfigAccessProtocolGuid + +[Guids] + gEfiIfrTianoGuid ## CONSUMES ## GUID diff --git a/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni new file mode 100644 index 0000000000..cf85e08795 Binary files /dev/null and b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni differ diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/NetworkPkg/Ip6Dxe/Ip6Icmp.c new file mode 100644 index 0000000000..db40b81d5e --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.c @@ -0,0 +1,684 @@ +/** @file + The ICMPv6 handle routines to process the ICMPv6 control messages. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = { + + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_NO_ROUTE_TO_DEST + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_COMM_PROHIBITED + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_BEYOND_SCOPE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ADDR_UNREACHABLE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_PORT_UNREACHABLE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_SOURCE_ADDR_FAILED + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ROUTE_REJECTED + }, + + { + ICMP_V6_PACKET_TOO_BIG, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_HOP_LIMIT + }, + { + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_REASSEMBLE + }, + + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_ERRONEOUS_HEADER + }, + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_UNRECOGNIZE_NEXT_HDR + }, + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_UNRECOGNIZE_OPTION + }, + + { + ICMP_V6_ECHO_REQUEST, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_ECHO_REPLY, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_LISTENER_QUERY, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_REPORT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_REPORT_2, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_DONE, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_ROUTER_SOLICIT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_ROUTER_ADVERTISE, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_NEIGHBOR_SOLICIT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_NEIGHBOR_ADVERTISE, + ICMP_V6_DEFAULT_CODE + }, +}; + +/** + Reply an ICMPv6 echo request. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 informational message. + @param[in] Packet The content of the ICMPv6 message with the IP head + removed. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS Successfully answered the ICMPv6 Echo request. + @retval Others Failed to answer the ICMPv6 Echo request. + +**/ +EFI_STATUS +Ip6IcmpReplyEcho ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD *Icmp; + NET_BUF *Data; + EFI_STATUS Status; + EFI_IP6_HEADER ReplyHead; + + Status = EFI_OUT_OF_RESOURCES; + // + // make a copy the packet, it is really a bad idea to + // send the MNP's buffer back to MNP. + // + Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN); + if (Data == NULL) { + goto Exit; + } + + // + // Change the ICMP type to echo reply, exchange the source + // and destination, then send it. The source is updated to + // use specific destination. See RFC1122. SRR/RR option + // update is omitted. + // + Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL); + if (Icmp == NULL) { + NetbufFree (Data); + goto Exit; + } + + Icmp->Head.Type = ICMP_V6_ECHO_REPLY; + Icmp->Head.Checksum = 0; + + // + // Generate the IPv6 basic header + // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address, + // the Source address of the Echo Reply must be the same address. + // + ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER)); + + ReplyHead.PayloadLength = HTONS ((UINT16) (Packet->TotalSize)); + ReplyHead.NextHeader = IP6_ICMP; + ReplyHead.HopLimit = IpSb->CurHopLimit; + IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress); + + if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { + IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress); + } + + // + // If source is unspecified, Ip6Output will select a source for us + // + Status = Ip6Output ( + IpSb, + NULL, + NULL, + Data, + &ReplyHead, + NULL, + 0, + Ip6SysPacketSent, + NULL + ); + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process Packet Too Big message sent by a router in response to a packet that + it cannot forward because the packet is larger than the MTU of outgoing link. + Since this driver already uses IPv6 minimum link MTU as the maximum packet size, + if Packet Too Big message is still received, do not reduce the packet size, but + rather include a Fragment header in the subsequent packets. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 error packet. + @param[in] Packet The content of the ICMPv6 error with the IP head + removed. + + @retval EFI_SUCCESS The ICMPv6 error processed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resource. + @retval EFI_NOT_FOUND The packet too big message is not sent to us. + +**/ +EFI_STATUS +Ip6ProcessPacketTooBig ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + UINT32 Mtu; + IP6_ROUTE_ENTRY *RouteEntry; + EFI_IPv6_ADDRESS *DestAddress; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Mtu = NTOHL (Icmp.Fourth); + DestAddress = &Icmp.IpHead.DestinationAddress; + + if (Mtu < IP6_MIN_LINK_MTU) { + // + // Normally the multicast address is considered to be on-link and not recorded + // in route table. Here it is added into the table since the MTU information + // need be recorded. + // + if (IP6_IS_MULTICAST (DestAddress)) { + RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL); + if (RouteEntry == NULL) { + NetbufFree (Packet); + return EFI_OUT_OF_RESOURCES; + } + + RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG; + InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link); + IpSb->RouteTable->TotalNum++; + } else { + RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL); + if (RouteEntry == NULL) { + NetbufFree (Packet); + return EFI_NOT_FOUND; + } + + RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG; + + Ip6FreeRouteEntry (RouteEntry); + } + } + + NetbufFree (Packet); + return EFI_SUCCESS; +} + +/** + Process the ICMPv6 error packet, and deliver the packet to upper layer. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 error packet. + @param[in] Packet The content of the ICMPv6 error with the IP head + removed. + + @retval EFI_SUCCESS The ICMPv6 error processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessIcmpError ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + + // + // Check the validity of the packet + // + if (Packet->TotalSize < sizeof (Icmp)) { + goto DROP; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) { + return Ip6ProcessPacketTooBig (IpSb, Head, Packet); + } + + // + // Notify the upper-layer process that an ICMPv6 eror message is received. + // + IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR; + return Ip6Demultiplex (IpSb, Head, Packet); + +DROP: + NetbufFree (Packet); + Packet = NULL; + return EFI_INVALID_PARAMETER; +} + +/** + Process the ICMPv6 informational messages. If it is an ICMPv6 echo + request, answer it. If it is a MLD message, trigger MLD routines to + process it. If it is a ND message, trigger ND routines to process it. + Otherwise, deliver it to upper layer. + + @param[in] IpSb The IP service that receivd the packet. + @param[in] Head The IP head of the ICMPv6 informational packet. + @param[in] Packet The content of the ICMPv6 informational packet + with IP head removed. + + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_SUCCESS The ICMPv6 informational message processed. + @retval Others Failed to process ICMPv6 informational message. + +**/ +EFI_STATUS +Ip6ProcessIcmpInformation ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE); + ASSERT (Head != NULL); + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Status = EFI_INVALID_PARAMETER; + + switch (Icmp.Head.Type) { + case ICMP_V6_ECHO_REQUEST: + // + // If ICMPv6 echo, reply it + // + if (Icmp.Head.Code == 0) { + Status = Ip6IcmpReplyEcho (IpSb, Head, Packet); + } + break; + case ICMP_V6_LISTENER_QUERY: + Status = Ip6ProcessMldQuery (IpSb, Head, Packet); + break; + case ICMP_V6_LISTENER_REPORT: + case ICMP_V6_LISTENER_REPORT_2: + Status = Ip6ProcessMldReport (IpSb, Head, Packet); + break; + case ICMP_V6_NEIGHBOR_SOLICIT: + Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet); + break; + case ICMP_V6_NEIGHBOR_ADVERTISE: + Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet); + break; + case ICMP_V6_ROUTER_ADVERTISE: + Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet); + break; + case ICMP_V6_REDIRECT: + Status = Ip6ProcessRedirect (IpSb, Head, Packet); + break; + case ICMP_V6_ECHO_REPLY: + Status = Ip6Demultiplex (IpSb, Head, Packet); + break; + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + return Status; +} + +/** + Handle the ICMPv6 packet. First validate the message format, + then, according to the message types, process it as an informational packet or + an error packet. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 packet with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_SUCCESS The ICMPv6 message successfully processed. + @retval Others Failed to handle the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6IcmpHandle ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_HEAD Icmp; + UINT16 PseudoCheckSum; + UINT16 CheckSum; + + // + // Check the validity of the incoming packet. + // + if (Packet->TotalSize < sizeof (Icmp)) { + goto DROP; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + // + // Make sure checksum is valid. + // + PseudoCheckSum = NetIp6PseudoHeadChecksum ( + &Head->SourceAddress, + &Head->DestinationAddress, + IP6_ICMP, + Packet->TotalSize + ); + CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet)); + if (CheckSum != 0) { + goto DROP; + } + + // + // According to the packet type, call corresponding process + // + if (Icmp.Type <= ICMP_V6_ERROR_MAX) { + return Ip6ProcessIcmpError (IpSb, Head, Packet); + } else { + return Ip6ProcessIcmpInformation (IpSb, Head, Packet); + } + +DROP: + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; +} + +/** + Retrieve the Prefix address according to the PrefixLength by clear the useless + bits. + + @param[in] PrefixLength The prefix length of the prefix. + @param[in, out] Prefix On input, points to the original prefix address + with dirty bits; on output, points to the updated + address with useless bit clear. + +**/ +VOID +Ip6GetPrefix ( + IN UINT8 PrefixLength, + IN OUT EFI_IPv6_ADDRESS *Prefix + ) +{ + UINT8 Byte; + UINT8 Bit; + UINT8 Mask; + UINT8 Value; + + ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_NUM)); + + if (PrefixLength == 0) { + ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS)); + return ; + } + + if (PrefixLength == IP6_PREFIX_NUM - 1) { + return ; + } + + Byte = (UINT8) (PrefixLength / 8); + Bit = (UINT8) (PrefixLength % 8); + Value = Prefix->Addr[Byte]; + + if ((Byte > 0) && (Byte < 16)) { + ZeroMem (Prefix->Addr + Byte, 16 - Byte); + } + + if (Bit > 0) { + Mask = (UINT8) (0xFF << (8 - Bit)); + Prefix->Addr[Byte] = (UINT8) (Value & Mask); + } + +} + +/** + Check whether the DestinationAddress is an anycast address. + + @param[in] IpSb The IP service that received the packet. + @param[in] DestinationAddress Points to the Destination Address of the packet. + + @retval TRUE The DestinationAddress is anycast address. + @retval FALSE The DestinationAddress is not anycast address. + +**/ +BOOLEAN +Ip6IsAnycast ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *DestinationAddress + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + EFI_IPv6_ADDRESS Prefix; + BOOLEAN Flag; + + ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS)); + + Flag = FALSE; + + // + // If the address is known as on-link or autonomous prefix, record it as + // anycast address. + // + do { + PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress); + if (PrefixEntry != NULL) { + IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix); + Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix); + if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) { + return TRUE; + } + } + + Flag = (BOOLEAN) !Flag; + } while (Flag); + + return FALSE; +} + +/** + Generate ICMPv6 error message and send it out to DestinationAddress. Currently + Destination Unreachable message, Time Exceeded message and Parameter Problem + message are supported. + + @param[in] IpSb The IP service that received the packet. + @param[in] Packet The packet which invoking ICMPv6 error. + @param[in] SourceAddress If not NULL, points to the SourceAddress. + Otherwise, the IP layer will select a source address + according to the DestinationAddress. + @param[in] DestinationAddress Points to the Destination Address of the ICMPv6 + error message. + @param[in] Type The type of the ICMPv6 message. + @param[in] Code The additional level of the ICMPv6 message. + @param[in] Pointer If not NULL, identifies the octet offset within + the invoking packet where the error was detected. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the + operation. + @retval EFI_SUCCESS The ICMPv6 message was successfully sent out. + @retval Others Failed to generate the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6SendIcmpError ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN UINT8 Type, + IN UINT8 Code, + IN UINT32 *Pointer OPTIONAL + ) +{ + UINT32 PacketLen; + NET_BUF *ErrorMsg; + UINT16 PayloadLen; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + UINT8 *ErrorBody; + + if (DestinationAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // An ICMPv6 error message must not be originated as a result of receiving + // a packet whose source address does not uniquely identify a single node -- + // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address + // known by the ICMP message originator to be an IPv6 anycast address. + // + if (NetIp6IsUnspecifiedAddr (DestinationAddress) || + IP6_IS_MULTICAST (DestinationAddress) || + Ip6IsAnycast (IpSb, DestinationAddress) + ) { + return EFI_INVALID_PARAMETER; + } + + switch (Type) { + case ICMP_V6_DEST_UNREACHABLE: + case ICMP_V6_TIME_EXCEEDED: + break; + + case ICMP_V6_PARAMETER_PROBLEM: + if (Pointer == NULL) { + return EFI_INVALID_PARAMETER; + } + + break; + + default: + return EFI_INVALID_PARAMETER; + } + + PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize; + + if (PacketLen > IpSb->MaxPacketSize) { + PacketLen = IpSb->MaxPacketSize; + } + + ErrorMsg = NetbufAlloc (PacketLen); + if (ErrorMsg == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER)); + + // + // Create the basic IPv6 header. + // + ZeroMem (&Head, sizeof (EFI_IP6_HEADER)); + + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IpSb->CurHopLimit; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP error message head + // + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + if (IcmpHead == NULL) { + NetbufFree (ErrorMsg); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = Type; + IcmpHead->Head.Code = Code; + + if (Pointer != NULL) { + IcmpHead->Fourth = HTONL (*Pointer); + } + + // + // Fill in the ICMP error message body + // + PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD); + ErrorBody = NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE); + if (ErrorBody != NULL) { + ZeroMem (ErrorBody, PayloadLen); + NetbufCopy (Packet, 0, PayloadLen, ErrorBody); + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.h b/NetworkPkg/Ip6Dxe/Ip6Icmp.h new file mode 100644 index 0000000000..6852ae5490 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.h @@ -0,0 +1,108 @@ +/** @file + Header file for ICMPv6 protocol. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_ICMP_H__ +#define __EFI_IP6_ICMP_H__ + +#define ICMP_V6_DEFAULT_CODE 0 + +#define ICMP_V6_ERROR_MAX 127 + +// +// ICMPv6 message classes, each class of ICMPv6 message shares +// a common message format. INVALID_MESSAGE is only a flag. +// +#define ICMP_V6_INVALID_MESSAGE 0 +#define ICMP_V6_ERROR_MESSAGE 1 +#define ICMP_V6_INFORMATION_MESSAGE 2 + + +extern EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[]; + +/** + Handle the ICMPv6 packet. First validate the message format, + then, according to the message types, process it as an informational packet or + an error packet. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 packet with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_SUCCESS The ICMPv6 message successfully processed. + @retval Others Failed to handle the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6IcmpHandle ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Check whether the DestinationAddress is an anycast address. + + @param[in] IpSb The IP service that received the packet. + @param[in] DestinationAddress Points to the Destination Address of the packet. + + @retval TRUE The DestinationAddress is anycast address. + @retval FALSE The DestinationAddress is not anycast address. + +**/ +BOOLEAN +Ip6IsAnycast ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *DestinationAddress + ); + +/** + Generate ICMPv6 error message and send it out to DestinationAddress. Currently + Destination Unreachable message, Time Exceeded message and Parameter Problem + message are supported. + + @param[in] IpSb The IP service that received the packet. + @param[in] Packet The packet which invoking ICMPv6 error. + @param[in] SourceAddress If not NULL, points to the SourceAddress. + Otherwise, the IP layer will select a source address + according to the DestinationAddress. + @param[in] DestinationAddress Points to the Destination Address of the ICMPv6 + error message. + @param[in] Type The type of the ICMPv6 message. + @param[in] Code The additional level of the ICMPv6 message. + @param[in] Pointer If not NULL, identifies the octet offset within + the invoking packet where the error was detected. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the + operation. + @retval EFI_SUCCESS The ICMPv6 message was successfully sent out. + @retval Others Failed to generate the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6SendIcmpError ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN UINT8 Type, + IN UINT8 Code, + IN UINT32 *Pointer OPTIONAL + ); + +#endif + diff --git a/NetworkPkg/Ip6Dxe/Ip6If.c b/NetworkPkg/Ip6Dxe/Ip6If.c new file mode 100644 index 0000000000..198b547ed2 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6If.c @@ -0,0 +1,802 @@ +/** @file + Implement IP6 pesudo interface. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +/** + Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context The Context which is pointed to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Fileter function to cancel all the frame related to an IP instance. + + @param[in] Frame The transmit request to test whether to cancel. + @param[in] Context The context which is the Ip instance that issued + the transmit. + + @retval TRUE The frame belongs to this instance and is to be + removed. + @retval FALSE The frame doesn't belong to this instance. + +**/ +BOOLEAN +Ip6CancelInstanceFrame ( + IN IP6_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if (Frame->IpInstance == (IP6_PROTOCOL *) Context) { + return TRUE; + } + + return FALSE; +} + +/** + Set the interface's address. This will trigger the DAD process for the + address to set. To set an already set address, the lifetimes wil be + updated to the new value passed in. + + @param[in] Interface The interface to set the address. + @param[in] Ip6Addr The interface's to be assigned IPv6 address. + @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast. + Otherwise, it is not anycast. + @param[in] PrefixLength The prefix length of the Ip6Addr. + @param[in] ValidLifetime The valid lifetime for this address. + @param[in] PreferredLifetime The preferred lifetime for this address. + @param[in] DadCallback The caller's callback to trigger when DAD finishes. + This is an optional parameter that may be NULL. + @param[in] Context The context that will be passed to DadCallback. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The interface is scheduled to be configured with + the specified address. + @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to + lack of resources. + +**/ +EFI_STATUS +Ip6SetAddress ( + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Ip6Addr, + IN BOOLEAN IsAnycast, + IN UINT8 PrefixLength, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN IP6_DAD_CALLBACK DadCallback OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddressInfo; + LIST_ENTRY *Entry; + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + UINT64 Delay; + IP6_DELAY_JOIN_LIST *DelayNode; + + NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE); + + IpSb = Interface->Service; + + if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) { + ASSERT (AddressInfo != NULL); + // + // Update the lifetime. + // + AddressInfo->ValidLifetime = ValidLifetime; + AddressInfo->PreferredLifetime = PreferredLifetime; + + if (DadCallback != NULL) { + DadCallback (TRUE, Ip6Addr, Context); + } + + return EFI_SUCCESS; + } + + AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO)); + if (AddressInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AddressInfo->Signature = IP6_ADDR_INFO_SIGNATURE; + IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr); + AddressInfo->IsAnycast = IsAnycast; + AddressInfo->PrefixLength = PrefixLength; + AddressInfo->ValidLifetime = ValidLifetime; + AddressInfo->PreferredLifetime = PreferredLifetime; + + if (AddressInfo->PrefixLength == 0) { + // + // Find an appropriate prefix from on-link prefixes and update the prefixlength. + // Longest prefix match is used here. + // + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) { + AddressInfo->PrefixLength = PrefixEntry->PrefixLength; + break; + } + } + } + + if (AddressInfo->PrefixLength == 0) { + // + // If the prefix length is still zero, try the autonomous prefixes. + // Longest prefix match is used here. + // + NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) { + PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) { + AddressInfo->PrefixLength = PrefixEntry->PrefixLength; + break; + } + } + } + + if (AddressInfo->PrefixLength == 0) { + // + // BUGBUG: Stil fail, use 64 as the default prefix length. + // + AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + } + + + // + // Node should delay joining the solicited-node mulitcast address by a random delay + // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second). + // Thus queue the address to be processed in Duplicate Address Detection module + // after the delay time (in milliseconds). + // + Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ()); + Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS); + Delay = RShiftU64 (Delay, 32); + + DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST)); + if (DelayNode == NULL) { + FreePool (AddressInfo); + return EFI_OUT_OF_RESOURCES; + } + + DelayNode->DelayTime = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS)); + DelayNode->Interface = Interface; + DelayNode->AddressInfo = AddressInfo; + DelayNode->DadCallback = DadCallback; + DelayNode->Context = Context; + + InsertTailList (&Interface->DelayJoinList, &DelayNode->Link); + return EFI_SUCCESS; +} + +/** + Create an IP6_INTERFACE. + + @param[in] IpSb The IP6 service binding instance. + @param[in] LinkLocal If TRUE, the instance is created for link-local address. + Otherwise, it is not for a link-local address. + + @return Point to the created IP6_INTERFACE, otherwise NULL. + +**/ +IP6_INTERFACE * +Ip6CreateInterface ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN LinkLocal + ) +{ + EFI_STATUS Status; + IP6_INTERFACE *Interface; + EFI_IPv6_ADDRESS *Ip6Addr; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Interface = AllocatePool (sizeof (IP6_INTERFACE)); + if (Interface == NULL) { + return NULL; + } + + Interface->Signature = IP6_INTERFACE_SIGNATURE; + Interface->RefCnt = 1; + + InitializeListHead (&Interface->AddressList); + Interface->AddressCount = 0; + Interface->Configured = FALSE; + + Interface->Service = IpSb; + Interface->Controller = IpSb->Controller; + Interface->Image = IpSb->Image; + + InitializeListHead (&Interface->ArpQues); + InitializeListHead (&Interface->SentFrames); + + Interface->DupAddrDetect = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits; + InitializeListHead (&Interface->DupAddrDetectList); + + InitializeListHead (&Interface->DelayJoinList); + + InitializeListHead (&Interface->IpInstances); + Interface->PromiscRecv = FALSE; + + if (!LinkLocal) { + return Interface; + } + + // + // Get the link local addr + // + Ip6Addr = Ip6CreateLinkLocalAddr (IpSb); + if (Ip6Addr == NULL) { + goto ON_ERROR; + } + + // + // Perform DAD - Duplicate Address Detection. + // + Status = Ip6SetAddress ( + Interface, + Ip6Addr, + FALSE, + IP6_LINK_LOCAL_PREFIX_LENGTH, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + NULL, + NULL + ); + + FreePool (Ip6Addr); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return Interface; + +ON_ERROR: + + FreePool (Interface); + return NULL; +} + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/prefix pair share the same interface. It is reference + counted. All the frames that haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list. + + @param[in] Interface The interface used by the IpInstance. + @param[in] IpInstance The IP instance that free the interface. NULL if + the IP driver is releasing the default interface. + +**/ +VOID +Ip6CleanInterface ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL + ) +{ + IP6_DAD_ENTRY *Duplicate; + IP6_DELAY_JOIN_LIST *Delay; + + NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE); + ASSERT (Interface->RefCnt > 0); + + // + // Remove all the pending transmit token related to this IP instance. + // + Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance); + + if (--Interface->RefCnt > 0) { + return; + } + + // + // Destory the interface if this is the last IP instance. + // Remove all the system transmitted packets + // from this interface, cancel the receive request if exists. + // + Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL); + + ASSERT (IsListEmpty (&Interface->IpInstances)); + ASSERT (IsListEmpty (&Interface->ArpQues)); + ASSERT (IsListEmpty (&Interface->SentFrames)); + + while (!IsListEmpty (&Interface->DupAddrDetectList)) { + Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link); + NetListRemoveHead (&Interface->DupAddrDetectList); + FreePool (Duplicate); + } + + while (!IsListEmpty (&Interface->DelayJoinList)) { + Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link); + NetListRemoveHead (&Interface->DelayJoinList); + FreePool (Delay); + } + + Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0); + + RemoveEntryList (&Interface->Link); + FreePool (Interface); +} + +/** + Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN. + + @param[in] Interface The interface to send out from. + @param[in] IpInstance The IpInstance that transmit the packet. NULL if + the packet is sent by the IP6 driver itself. + @param[in] Packet The packet to transmit + @param[in] CallBack Call back function to execute if transmission + finished. + @param[in] Context Opaque parameter to the callback. + + @return The wrapped token if succeed or NULL. + +**/ +IP6_LINK_TX_TOKEN * +Ip6CreateLinkTxToken ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + UINT32 Count; + + Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA)); + + if (Token == NULL) { + return NULL; + } + + Token->Signature = IP6_LINK_TX_SIGNATURE; + InitializeListHead (&Token->Link); + + Token->IpInstance = IpInstance; + Token->CallBack = CallBack; + Token->Packet = Packet; + Token->Context = Context; + ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS)); + IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress); + + MnpToken = &(Token->MnpToken); + MnpToken->Status = EFI_NOT_READY; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnFrameSent, + Token, + &MnpToken->Event + ); + + if (EFI_ERROR (Status)) { + FreePool (Token); + return NULL; + } + + MnpTxData = &Token->MnpTxData; + MnpToken->Packet.TxData = MnpTxData; + + MnpTxData->DestinationAddress = &Token->DstMac; + MnpTxData->SourceAddress = &Token->SrcMac; + MnpTxData->ProtocolType = IP6_ETHER_PROTO; + MnpTxData->DataLength = Packet->TotalSize; + MnpTxData->HeaderLength = 0; + + Count = Packet->BlockOpNum; + + NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count); + MnpTxData->FragmentCount = (UINT16)Count; + + return Token; +} + +/** + Free the link layer transmit token. It will close the event, + then free the memory used. + + @param[in] Token Token to free. + +**/ +VOID +Ip6FreeLinkTxToken ( + IN IP6_LINK_TX_TOKEN *Token + ) +{ + NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE); + + gBS->CloseEvent (Token->MnpToken.Event); + FreePool (Token); +} + +/** + Callback function when the received packet is freed. + Check Ip6OnFrameReceived for information. + + @param[in] Context Points to EFI_MANAGED_NETWORK_RECEIVE_DATA. + +**/ +VOID +EFIAPI +Ip6RecycleFrame ( + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData; + + RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context; + + gBS->SignalEvent (RxData->RecycleEvent); +} + +/** + Received a frame from MNP. Wrap it in net buffer then deliver + it to IP's input function. The ownship of the packet also + is transferred to IP. When Ip is finished with this packet, it + will call NetbufFree to release the packet, NetbufFree will + again call the Ip6RecycleFrame to signal MNP's event and free + the token used. + + @param[in] Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceivedDpc ( + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData; + IP6_LINK_RX_TOKEN *Token; + NET_FRAGMENT Netfrag; + NET_BUF *Packet; + UINT32 Flag; + IP6_SERVICE *IpSb; + + Token = (IP6_LINK_RX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE); + + // + // First clear the interface's receive request in case the + // caller wants to call Ip6ReceiveFrame in the callback. + // + IpSb = (IP6_SERVICE *) Token->Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + + MnpToken = &Token->MnpToken; + MnpRxData = MnpToken->Packet.RxData; + + if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) { + Token->CallBack (NULL, MnpToken->Status, 0, Token->Context); + return ; + } + + // + // Wrap the frame in a net buffer then deliever it to IP input. + // IP will reassemble the packet, and deliver it to upper layer + // + Netfrag.Len = MnpRxData->DataLength; + Netfrag.Bulk = MnpRxData->PacketData; + + Packet = NetbufFromExt (&Netfrag, 1, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData); + + if (Packet == NULL) { + gBS->SignalEvent (MnpRxData->RecycleEvent); + + Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context); + + return ; + } + + Flag = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0); + Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0); + Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0); + + Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context); +} + +/** + Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context); +} + +/** + Request to receive the packet from the interface. + + @param[in] CallBack Function to call when receive finished. + @param[in] IpSb Points to IP6 service binding instance. + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive. + @retval EFI_SUCCESS The recieve request has been started. + +**/ +EFI_STATUS +Ip6ReceiveFrame ( + IN IP6_FRAME_CALLBACK CallBack, + IN IP6_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + IP6_LINK_RX_TOKEN *Token; + + if (IpSb->InDestroy) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Token = &IpSb->RecvRequest; + Token->CallBack = CallBack; + Token->Context = (VOID *) IpSb; + + Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Callback funtion when frame transmission is finished. It will + call the frame owner's callback function to tell it the result. + + @param[in] Context Context which points to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSentDpc ( + IN VOID *Context + ) +{ + IP6_LINK_TX_TOKEN *Token; + + Token = (IP6_LINK_TX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE); + + RemoveEntryList (&Token->Link); + + Token->CallBack ( + Token->Packet, + Token->MnpToken.Status, + 0, + Token->Context + ); + + Ip6FreeLinkTxToken (Token); +} + +/** + Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context Context which points to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context); +} + +/** + Send a frame from the interface. If the next hop is a multicast address, + it is transmitted immediately. If the next hop is a unicast, + and the NextHop's MAC is not known, it will perform address resolution. + If an error occurred, the CallBack won't be called. So, the caller + must test the return value, and take action when there is an error. + + @param[in] Interface The interface to send the frame from + @param[in] IpInstance The IP child that request the transmission. + NULL if it is the IP6 driver itself. + @param[in] Packet The packet to transmit. + @param[in] NextHop The immediate destination to transmit the packet to. + @param[in] CallBack Function to call back when transmit finished. + @param[in] Context Opaque parameter to the callback. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame. + @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop. + @retval EFI_SUCCESS The packet successfully transmitted. + +**/ +EFI_STATUS +Ip6SendFrame ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *NextHop, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + IP6_NEIGHBOR_ENTRY *NeighborCache; + LIST_ENTRY *Entry; + IP6_NEIGHBOR_ENTRY *ArpQue; + + IpSb = Interface->Service; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // Only when link local address is performing DAD, the interface could be used in unconfigured. + // + if (IpSb->LinkLocalOk) { + ASSERT (Interface->Configured); + } + + Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context); + + if (Token == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (IP6_IS_MULTICAST (NextHop)) { + Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac); + if (EFI_ERROR (Status)) { + goto Error; + } + + goto SendNow; + } + + // + // If send to itself, directly send out + // + if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) { + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress); + goto SendNow; + } + + // + // If unicast, check the neighbor state. + // + + NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop); + ASSERT (NeighborCache != NULL); + + if (NeighborCache->Interface == NULL) { + NeighborCache->Interface = Interface; + } + + switch (NeighborCache->State) { + case EfiNeighborStale: + NeighborCache->State = EfiNeighborDelay; + NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME); + // + // Fall through + // + case EfiNeighborReachable: + case EfiNeighborDelay: + case EfiNeighborProbe: + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress); + goto SendNow; + break; + + default: + break; + } + + // + // Have to do asynchronous ARP resolution. First check whether there is + // already a pending request. + // + NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList); + if (ArpQue == NeighborCache) { + InsertTailList (&NeighborCache->Frames, &Token->Link); + NeighborCache->ArpFree = TRUE; + return EFI_SUCCESS; + } + } + + // + // First frame requires ARP. + // + InsertTailList (&NeighborCache->Frames, &Token->Link); + InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList); + + NeighborCache->ArpFree = TRUE; + + return EFI_SUCCESS; + +SendNow: + // + // Insert the tx token into the SentFrames list before calling Mnp->Transmit. + // Remove it if the returned status is not EFI_SUCCESS. + // + InsertTailList (&Interface->SentFrames, &Token->Link); + Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + goto Error; + } + + return EFI_SUCCESS; + +Error: + Ip6FreeLinkTxToken (Token); + return Status; +} + +/** + The heartbeat timer of IP6 service instance. It times out + all of its IP6 children's received-but-not-delivered and + transmitted-but-not-recycle packets. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Ip6PacketTimerTicking (IpSb); + Ip6NdTimerTicking (IpSb); + Ip6MldTimerTicking (IpSb); +} diff --git a/NetworkPkg/Ip6Dxe/Ip6If.h b/NetworkPkg/Ip6Dxe/Ip6If.h new file mode 100644 index 0000000000..736035ec39 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6If.h @@ -0,0 +1,267 @@ +/** @file + Definition for IP6 pesudo interface structure. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_IF_H__ +#define __EFI_IP6_IF_H__ + +#define IP6_LINK_RX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'R') +#define IP6_LINK_TX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'T') +#define IP6_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'I') +#define IP6_ADDR_INFO_SIGNATURE SIGNATURE_32 ('I', 'P', 'A', 'I') + +// +// This prototype is used by both receive and transmission. +// When receiving Netbuf is allocated by IP6_INTERFACE, and +// released by IP6. Flag shows whether the frame is received +// as unicast/multicast/anycast... +// +// When transmitting, the Netbuf is from IP6, and provided +// to the callback as a reference. Flag isn't used. +// +// IpInstance can be NULL which means that it is the IP6 driver +// itself sending the packets. IP6 driver may send packets that +// don't belong to any instance, such as ICMP errors, ICMP +// informational packets. IpInstance is used as a tag in +// this module. +// +typedef +VOID +(*IP6_FRAME_CALLBACK) ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +// +// Each receive request is wrapped in an IP6_LINK_RX_TOKEN. +// Upon completion, the Callback will be called. Only one +// receive request is send to MNP. IpInstance is always NULL. +// Reference MNP's spec for information. +// +typedef struct { + UINT32 Signature; + IP6_FRAME_CALLBACK CallBack; + VOID *Context; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; +} IP6_LINK_RX_TOKEN; + +// +// Each transmit request is wrapped in an IP6_LINK_TX_TOKEN. +// Upon completion, the Callback will be called. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + IP6_PROTOCOL *IpInstance; + IP6_FRAME_CALLBACK CallBack; + NET_BUF *Packet; + VOID *Context; + + EFI_MAC_ADDRESS DstMac; + EFI_MAC_ADDRESS SrcMac; + + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData; +} IP6_LINK_TX_TOKEN; + +struct _IP6_ADDRESS_INFO { + UINT32 Signature; + LIST_ENTRY Link; + EFI_IPv6_ADDRESS Address; + BOOLEAN IsAnycast; + UINT8 PrefixLength; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; +}; + +// +// Callback to select which frame to cancel. Caller can cancel a +// single frame, or all the frame from an IP instance. +// +typedef +BOOLEAN +(*IP6_FRAME_TO_CANCEL) ( + IP6_LINK_TX_TOKEN *Frame, + VOID *Context + ); + +struct _IP6_INTERFACE { + UINT32 Signature; + LIST_ENTRY Link; + INTN RefCnt; + + // + // IP address and prefix length of the interface. The fileds + // are invalid if (Configured == FALSE) + // + LIST_ENTRY AddressList; + UINT32 AddressCount; + BOOLEAN Configured; + + IP6_SERVICE *Service; + + EFI_HANDLE Controller; + EFI_HANDLE Image; + + + // + // Queues to keep the frames sent and waiting ARP request. + // + LIST_ENTRY ArpQues; + LIST_ENTRY SentFrames; + + + // + // The interface's configuration variables + // + UINT32 DupAddrDetect; + LIST_ENTRY DupAddrDetectList; + LIST_ENTRY DelayJoinList; + + // + // All the IP instances that have the same IP/SubnetMask are linked + // together through IpInstances. If any of the instance enables + // promiscuous receive, PromiscRecv is true. + // + LIST_ENTRY IpInstances; + BOOLEAN PromiscRecv; +}; + +/** + Create an IP6_INTERFACE. + + @param[in] IpSb The IP6 service binding instance. + @param[in] LinkLocal If TRUE, the instance is created for link-local address. + Otherwise, it is not for a link-local address. + + @return Point to the created IP6_INTERFACE, otherwise NULL. + +**/ +IP6_INTERFACE * +Ip6CreateInterface ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN LinkLocal + ); + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/prefix pair share the same interface. It is reference + counted. All the frames that haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list. + + @param[in] Interface The interface used by the IpInstance. + @param[in] IpInstance The IP instance that free the interface. NULL if + the IP driver is releasing the default interface. + +**/ +VOID +Ip6CleanInterface ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL + ); + +/** + Free the link layer transmit token. It will close the event + then free the memory used. + + @param[in] Token Token to free. + +**/ +VOID +Ip6FreeLinkTxToken ( + IN IP6_LINK_TX_TOKEN *Token + ); + +/** + Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Request to receive the packet from the interface. + + @param[in] CallBack Function to call when the receive finished. + @param[in] IpSb Points to the IP6 service binding instance. + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to receive. + @retval EFI_SUCCESS The recieve request has been started. + +**/ +EFI_STATUS +Ip6ReceiveFrame ( + IN IP6_FRAME_CALLBACK CallBack, + IN IP6_SERVICE *IpSb + ); + +/** + Send a frame from the interface. If the next hop is multicast address, + it is transmitted immediately. If the next hop is a unicast, + and the NextHop's MAC is not known, it will perform address resolution. + If some error happened, the CallBack won't be called. So, the caller + must test the return value, and take action when there is an error. + + @param[in] Interface The interface to send the frame from + @param[in] IpInstance The IP child that request the transmission. + NULL if it is the IP6 driver itself. + @param[in] Packet The packet to transmit. + @param[in] NextHop The immediate destination to transmit the packet to. + @param[in] CallBack Function to call back when transmit finished. + @param[in] Context Opaque parameter to the call back. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame. + @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop. + @retval EFI_SUCCESS The packet successfully transmitted. + +**/ +EFI_STATUS +Ip6SendFrame ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *NextHop, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ); + +/** + The heartbeat timer of IP6 service instance. It times out + all of its IP6 children's received-but-not-delivered and + transmitted-but-not-recycle packets. + + @param[in] Event The IP6 service instance's heart beat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.c b/NetworkPkg/Ip6Dxe/Ip6Impl.c new file mode 100644 index 0000000000..9b34eceeb7 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Impl.c @@ -0,0 +1,1857 @@ +/** @file + Implementation of EFI_IP6_PROTOCOL protocol interfaces. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +EFI_IPSEC_PROTOCOL *mIpSec = NULL; + +EFI_IP6_PROTOCOL mEfiIp6ProtocolTemplete = { + EfiIp6GetModeData, + EfiIp6Configure, + EfiIp6Groups, + EfiIp6Routes, + EfiIp6Neighbors, + EfiIp6Transmit, + EfiIp6Receive, + EfiIp6Cancel, + EfiIp6Poll +}; + +/** + Gets the current operational settings for this instance of the EFI IPv6 Protocol driver. + + The GetModeData() function returns the current operational mode data for this driver instance. + The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to + retrieve the operational mode data of underlying networks or drivers. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[out] Ip6ModeData Pointer to the EFI IPv6 Protocol mode data structure. + @param[out] MnpConfigData Pointer to the managed network configuration data structure. + @param[out] SnpModeData Pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIp6GetModeData ( + IN EFI_IP6_PROTOCOL *This, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + IP6_INTERFACE *IpIf; + EFI_IP6_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + IpIf = IpInstance->Interface; + + if (IpSb->LinkLocalDadFail) { + return EFI_INVALID_PARAMETER; + } + + if (Ip6ModeData != NULL) { + // + // IsStarted is "whether the EfiIp6Configure has been called". + // IsConfigured is "whether the station address has been configured" + // + Ip6ModeData->IsStarted = (BOOLEAN) (IpInstance->State == IP6_STATE_CONFIGED); + Ip6ModeData->MaxPacketSize = IpSb->MaxPacketSize; + CopyMem (&Ip6ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP6_CONFIG_DATA)); + Ip6ModeData->IsConfigured = FALSE; + + Ip6ModeData->AddressCount = 0; + Ip6ModeData->AddressList = NULL; + + Ip6ModeData->GroupCount = IpInstance->GroupCount; + Ip6ModeData->GroupTable = NULL; + + Ip6ModeData->RouteCount = 0; + Ip6ModeData->RouteTable = NULL; + + Ip6ModeData->NeighborCount = 0; + Ip6ModeData->NeighborCache = NULL; + + Ip6ModeData->PrefixCount = 0; + Ip6ModeData->PrefixTable = NULL; + + Ip6ModeData->IcmpTypeCount = 23; + Ip6ModeData->IcmpTypeList = AllocateCopyPool ( + Ip6ModeData->IcmpTypeCount * sizeof (EFI_IP6_ICMP_TYPE), + mIp6SupportedIcmp + ); + if (Ip6ModeData->IcmpTypeList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Return the currently configured IPv6 addresses and corresponding prefix lengths. + // + Status = Ip6BuildEfiAddressList ( + IpSb, + &Ip6ModeData->AddressCount, + &Ip6ModeData->AddressList + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Return the current station address for this IP child. + // If UseAnyStationAddress is set to TRUE, IP6 driver will + // select a source address from its address list. Otherwise use the + // StationAddress in config data. + // + if (Ip6ModeData->IsStarted) { + Config = &Ip6ModeData->ConfigData; + + if (IpIf->Configured || NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + Ip6ModeData->IsConfigured = TRUE; + } else { + Ip6ModeData->IsConfigured = FALSE; + } + + // + // Build a EFI route table for user from the internal route table. + // + Status = Ip6BuildEfiRouteTable ( + IpSb->RouteTable, + &Ip6ModeData->RouteCount, + &Ip6ModeData->RouteTable + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + } + + if (Ip6ModeData->IsConfigured) { + // + // Return the joined multicast group addresses. + // + if (IpInstance->GroupCount != 0) { + Ip6ModeData->GroupTable = AllocateCopyPool ( + IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS), + IpInstance->GroupList + ); + if (Ip6ModeData->GroupTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + } + // + // Return the neighbor cache entries + // + Status = Ip6BuildEfiNeighborCache ( + IpInstance, + &Ip6ModeData->NeighborCount, + &Ip6ModeData->NeighborCache + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Return the prefix table entries + // + Status = Ip6BuildPrefixTable ( + IpInstance, + &Ip6ModeData->PrefixCount, + &Ip6ModeData->PrefixTable + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + } + } + + // + // Get fresh mode data from MNP, since underlying media status may change + // + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData); + + goto Exit; + +Error: + if (Ip6ModeData != NULL) { + if (Ip6ModeData->AddressList != NULL) { + FreePool (Ip6ModeData->AddressList); + } + + if (Ip6ModeData->GroupTable != NULL) { + FreePool (Ip6ModeData->GroupTable); + } + + if (Ip6ModeData->RouteTable != NULL) { + FreePool (Ip6ModeData->RouteTable); + } + + if (Ip6ModeData->NeighborCache != NULL) { + FreePool (Ip6ModeData->NeighborCache); + } + + if (Ip6ModeData->PrefixTable != NULL) { + FreePool (Ip6ModeData->PrefixTable); + } + + if (Ip6ModeData->IcmpTypeList != NULL) { + FreePool (Ip6ModeData->IcmpTypeList); + } + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Validate that Ipv6 address is OK to be used as station address or next hop address/ neighbor. + + @param[in] IpSb The IP6 service instance. + @param[in] Ip The IPv6 address to validate. + @param[in] Flag If TRUE, validate if the address is OK to be used + as station address. If FALSE, validate if the + address is OK to be used as the next hop address/ + neighbor. + + @retval TRUE The Ip address is valid and could be used. + @retval FALSE Invalid Ip address. + +**/ +BOOLEAN +Ip6IsValidAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip, + IN BOOLEAN Flag + ) +{ + if (!NetIp6IsUnspecifiedAddr (Ip)) { + if (!NetIp6IsValidUnicast(Ip)) { + return FALSE; + } + if (Ip6IsOneOfSetAddress (IpSb, Ip, NULL, NULL)) { + return Flag; + } + } else { + return Flag; + } + + return (BOOLEAN) !Flag; +} + +/** + Validate whether the value of protocol is illegal or not. Protocol is the 'Next Header' field + in the last IPv6 extension header, or basic IPv6 header is there's no extension header. + + @param[in] Protocol Default value of 'Next Header' + + @retval TRUE The protocol is illegal. + @retval FALSE The protocol is legal. + +**/ +BOOLEAN +Ip6IsIllegalProtocol ( + IN UINT8 Protocol + ) +{ + if (Protocol == IP6_HOP_BY_HOP || Protocol == EFI_IP_PROTO_ICMP || Protocol == IP4_PROTO_IGMP) { + return TRUE; + } + + if (Protocol == 41 || Protocol == 43 || Protocol == 44 || Protocol == 59 || Protocol == 60 || Protocol == 124) { + return TRUE; + } + + return FALSE; +} + +/** + Intiialize the IP6_PROTOCOL structure to the unconfigured states. + + @param[in] IpSb The IP6 service instance. + @param[in, out] IpInstance The IP6 child instance. + +**/ +VOID +Ip6InitProtocol ( + IN IP6_SERVICE *IpSb, + IN OUT IP6_PROTOCOL *IpInstance + ) +{ + ASSERT ((IpSb != NULL) && (IpInstance != NULL)); + + ZeroMem (IpInstance, sizeof (IP6_PROTOCOL)); + + IpInstance->Signature = IP6_PROTOCOL_SIGNATURE; + IpInstance->State = IP6_STATE_UNCONFIGED; + IpInstance->Service = IpSb; + IpInstance->GroupList = NULL; + CopyMem (&IpInstance->Ip6Proto, &mEfiIp6ProtocolTemplete, sizeof (EFI_IP6_PROTOCOL)); + + NetMapInit (&IpInstance->RxTokens); + NetMapInit (&IpInstance->TxTokens); + InitializeListHead (&IpInstance->Received); + InitializeListHead (&IpInstance->Delivered); + + EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY); +} + +/** + Configure the IP6 child. If the child is already configured, + change the configuration parameter. Otherwise, configure it + for the first time. The caller should validate the configuration + before deliver them to it. It also don't do configure NULL. + + @param[in, out] IpInstance The IP6 child to configure. + @param[in] Config The configure data. + + @retval EFI_SUCCESS The IP6 child is successfully configured. + @retval EFI_DEVICE_ERROR Failed to free the pending transive or to + configure underlying MNP, or other errors. + @retval EFI_NO_MAPPING The IP6 child is configured to use the default + address, but the default address hasn't been + configured. The IP6 child doesn't need to be + reconfigured when the default address is configured. + @retval EFI_OUT_OF_RESOURCES No more memory space is available. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip6ConfigProtocol ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IP6_CONFIG_DATA *Config + ) +{ + IP6_SERVICE *IpSb; + IP6_INTERFACE *IpIf; + EFI_STATUS Status; + EFI_IP6_CONFIG_DATA *Current; + IP6_ADDRESS_INFO *AddressInfo; + BOOLEAN StationZero; + BOOLEAN DestZero; + EFI_IPv6_ADDRESS Source; + BOOLEAN AddrOk; + + IpSb = IpInstance->Service; + Current = &IpInstance->ConfigData; + + // + // User is changing packet filters. It must be stopped + // before the station address can be changed. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + // + // Cancel all the pending transmit/receive from upper layer + // + Status = Ip6Cancel (IpInstance, NULL); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + return EFI_SUCCESS; + } + + // + // Set up the interface. + // + StationZero = NetIp6IsUnspecifiedAddr (&Config->StationAddress); + DestZero = NetIp6IsUnspecifiedAddr (&Config->DestinationAddress); + + if (StationZero && DestZero) { + // + // StationAddress is still zero. + // + + NET_GET_REF (IpSb->DefaultInterface); + IpInstance->Interface = IpSb->DefaultInterface; + InsertTailList (&IpSb->DefaultInterface->IpInstances, &IpInstance->AddrLink); + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + IpInstance->State = IP6_STATE_CONFIGED; + + return EFI_SUCCESS; + } + + if (StationZero && !DestZero) { + Status = Ip6SelectSourceAddress (IpSb, &Config->DestinationAddress, &Source); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + IP6_COPY_ADDRESS (&Source, &Config->StationAddress); + } + + AddrOk = Ip6IsOneOfSetAddress (IpSb, &Source, &IpIf, &AddressInfo); + if (AddrOk) { + if (AddressInfo != NULL) { + IpInstance->PrefixLength = AddressInfo->PrefixLength; + } else { + IpInstance->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + } + } else { + // + // The specified source address is not one of the addresses IPv6 maintains. + // + return EFI_INVALID_PARAMETER; + } + + + NET_GET_REF (IpIf); + IpInstance->Interface = IpIf; + InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink); + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + IP6_COPY_ADDRESS (&Current->StationAddress, &Source); + IpInstance->State = IP6_STATE_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Clean up the IP6 child, and release all the resources used by it. + + @param[in, out] IpInstance The IP6 child to clean up. + + @retval EFI_SUCCESS The IP6 child is cleaned up. + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip6CleanProtocol ( + IN OUT IP6_PROTOCOL *IpInstance + ) +{ + if (EFI_ERROR (Ip6Cancel (IpInstance, NULL))) { + return EFI_DEVICE_ERROR; + } + + if (EFI_ERROR (Ip6Groups (IpInstance, FALSE, NULL))) { + return EFI_DEVICE_ERROR; + } + + // + // Some packets haven't been recycled. It is because either the + // user forgets to recycle the packets, or because the callback + // hasn't been called. Just leave it alone. + // + if (!IsListEmpty (&IpInstance->Delivered)) { + ; + } + + if (IpInstance->Interface != NULL) { + RemoveEntryList (&IpInstance->AddrLink); + Ip6CleanInterface (IpInstance->Interface, IpInstance); + IpInstance->Interface = NULL; + } + + if (IpInstance->GroupList != NULL) { + FreePool (IpInstance->GroupList); + IpInstance->GroupList = NULL; + IpInstance->GroupCount = 0; + } + + NetMapClean (&IpInstance->TxTokens); + + NetMapClean (&IpInstance->RxTokens); + + return EFI_SUCCESS; +} + +/** + Configure the MNP parameter used by IP. The IP driver uses one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. Also, it will enable/disable + the promiscuous receive according to whether there is IP child + enable that or not. If Force is FALSE, it will iterate through + all the IP children to check whether the promiscuous receive + setting has been changed. If it hasn't been changed, it won't + reconfigure the MNP. If Force is TRUE, the MNP is configured + whether that is changed or not. + + @param[in] IpSb The IP6 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP successfully configured/reconfigured. + @retval Others Configuration failed. + +**/ +EFI_STATUS +Ip6ServiceConfigMnp ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN Force + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *ProtoEntry; + IP6_INTERFACE *IpIf; + IP6_PROTOCOL *IpInstance; + BOOLEAN Reconfig; + BOOLEAN PromiscReceive; + EFI_STATUS Status; + + Reconfig = FALSE; + PromiscReceive = FALSE; + + if (!Force) { + // + // Iterate through the IP children to check whether promiscuous + // receive setting has been changed. Update the interface's receive + // filter also. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + IpIf->PromiscRecv = FALSE; + + NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP6_PROTOCOL, AddrLink); + + if (IpInstance->ConfigData.AcceptPromiscuous) { + IpIf->PromiscRecv = TRUE; + PromiscReceive = TRUE; + } + } + } + + // + // If promiscuous receive isn't changed, it isn't necessary to reconfigure. + // + if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) { + return EFI_SUCCESS; + } + + Reconfig = TRUE; + IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive; + } + + Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData); + + // + // recover the original configuration if failed to set the configure. + // + if (EFI_ERROR (Status) && Reconfig) { + IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive; + } + + return Status; +} + +/** + Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational parameters and filter + settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic + can be sent or received by this instance. Once the parameters have been reset (by calling this + function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped + independently of each other by enabling or disabling their receive filter settings with the + Configure() function. + + If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required + to be one of the currently configured IPv6 addresses listed in the EFI IPv6 drivers, or else + EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is + unspecified, the IPv6 driver will bind a source address according to the source address selection + algorithm. Clients could frequently call GetModeData() to check get currently configured IPv6 + address list in the EFI IPv6 driver. If both Ip6ConfigData.StationAddress and + Ip6ConfigData.Destination are unspecified, when transmitting the packet afterwards, the + source address filled in each outgoing IPv6 packet is decided based on the destination of this packet. + + If operational parameters are reset or changed, any pending transmit and receive requests will be + cancelled. Their completion token status will be set to EFI_ABORTED and their events will be + signaled. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Ip6ConfigData Pointer to the EFI IPv6 Protocol configuration data structure. + If NULL, reset the configuration data. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Ip6ConfigData.StationAddress is neither zero nor + a unicast IPv6 address. + - Ip6ConfigData.StationAddress is neither zero nor + one of the configured IP addresses in the EFI IPv6 driver. + - Ip6ConfigData.DefaultProtocol is illegal. + @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for + this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6 + address or prefix length can be changed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6 + Protocol driver instance was not opened. + @retval EFI_UNSUPPORTED Default protocol specified through + Ip6ConfigData.DefaulProtocol isn't supported. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Configure ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_IP6_CONFIG_DATA *Current; + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_SERVICE *IpSb; + + // + // First, validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = EFI_INVALID_PARAMETER; + + // + // Validate the configuration first. + // + if (Ip6ConfigData != NULL) { + // + // Check whether the station address is valid. + // + if (!Ip6IsValidAddress (IpSb, &Ip6ConfigData->StationAddress, TRUE)) { + goto Exit; + } + // + // Check whether the default protocol is valid. + // + if (Ip6IsIllegalProtocol (Ip6ConfigData->DefaultProtocol)) { + goto Exit; + } + + // + // User can only update packet filters when already configured. + // If it wants to change the station address, it must configure(NULL) + // the instance firstly. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + Current = &IpInstance->ConfigData; + + if (!EFI_IP6_EQUAL (&Current->StationAddress, &Ip6ConfigData->StationAddress)) { + Status = EFI_ALREADY_STARTED; + goto Exit; + } + + if (NetIp6IsUnspecifiedAddr (&Current->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + } + } + + // + // Configure the instance or clean it up. + // + if (Ip6ConfigData != NULL) { + Status = Ip6ConfigProtocol (IpInstance, Ip6ConfigData); + } else { + Status = Ip6CleanProtocol (IpInstance); + + // + // Don't change the state if it is DESTORY, consider the following + // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped, + // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED, + // the unload fails miserably. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + IpInstance->State = IP6_STATE_UNCONFIGED; + } + } + + // + // Update the MNP's configure data. Ip6ServiceConfigMnp will check + // whether it is necessary to reconfigure the MNP. + // + Ip6ServiceConfigMnp (IpInstance->Service, FALSE); + + // + // Update the variable data. + // + Ip6SetVariableData (IpInstance->Service); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Joins and leaves multicast groups. + + The Groups() function is used to join and leave multicast group sessions. Joining a group will + enable reception of matching multicast packets. Leaving a group will disable reception of matching + multicast packets. Source-Specific Multicast isn't required to be supported. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session, and FALSE to leave. + @param[in] GroupAddress Pointer to the IPv6 multicast address. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - JoinFlag is TRUE and GroupAddress is NULL. + - GroupAddress is not NULL and *GroupAddress is + not a multicast IPv6 address. + - GroupAddress is not NULL and *GroupAddress is in the + range of SSM destination address. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_OUT_OF_RESOURCES System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Groups ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (JoinFlag && GroupAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (GroupAddress != NULL && !IP6_IS_MULTICAST (GroupAddress)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + Status = Ip6Groups (IpInstance, JoinFlag, GroupAddress); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to, or deletes a route from, the routing table. + + Routes are determined by comparing the leftmost PrefixLength bits of Destination with + the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the + configured station address. + + The default route is added with Destination and PrefixLegth both set to all zeros. The + default route matches all destination IPv6 addresses that do not match any other routes. + + All EFI IPv6 Protocol instances share a routing table. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to + FALSE to add this route to the routing table. Destination, + PrefixLength and Gateway are used as the key to each + route entry. + @param[in] Destination The address prefix of the subnet that needs to be routed. + This is an optional parameter that may be NULL. + @param[in] PrefixLength The prefix length of Destination. Ignored if Destination + is NULL. + @param[in] GatewayAddress The unicast gateway IPv6 address for this route. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - When DeleteRoute is TRUE, both Destination and + GatewayAddress are NULL. + - When DeleteRoute is FALSE, either Destination or + GatewayAddress is NULL. + - *GatewayAddress is not a valid unicast IPv6 address. + - *GatewayAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when + DeleteRoute is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Routes ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (PrefixLength >= IP6_PREFIX_NUM)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (IpInstance->State != IP6_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (DeleteRoute && (Destination == NULL) && (GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!DeleteRoute && (Destination == NULL || GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (GatewayAddress != NULL) { + if (!Ip6IsValidAddress (IpSb, GatewayAddress, FALSE)) { + return EFI_INVALID_PARAMETER; + } + + if (!NetIp6IsUnspecifiedAddr (GatewayAddress) && + !NetIp6IsNetEqual (GatewayAddress, &IpInstance->ConfigData.StationAddress, PrefixLength) + ) { + return EFI_INVALID_PARAMETER; + } + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Update the route table + // + if (DeleteRoute) { + Status = Ip6DelRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress); + } else { + Status = Ip6AddRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress); + } + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Add or delete Neighbor cache entries. + + The Neighbors() function is used to add, update, or delete an entry from neighbor cache. + IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as + network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network + traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not + timeout) or dynamic (will timeout). + + The implementation should follow the neighbor cache timeout mechanism which is defined in + RFC4861. The default neighbor cache timeout value should be tuned for the expected network + environment + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteFlag Set to TRUE to delete the specified cache entry, set to FALSE to + add (or update, if it already exists and Override is TRUE) the + specified cache entry. TargetIp6Address is used as the key + to find the requested cache entry. + @param[in] TargetIp6Address Pointer to the Target IPv6 address. + @param[in] TargetLinkAddress Pointer to the link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache, it will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, EFI_ACCESS_DENIED + will be returned if a corresponding cache entry already existed. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - TargetIpAddress is NULL. + - *TargetLinkAddress is invalid when not NULL. + - *TargetIpAddress is not a valid unicast IPv6 address. + - *TargetIpAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is + TRUE or when DeleteFlag is FALSE while + TargetLinkAddress is NULL.). + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when Override + is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Neighbors ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + + if (This == NULL || TargetIp6Address == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (NetIp6IsUnspecifiedAddr (TargetIp6Address)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (!Ip6IsValidAddress (IpSb, TargetIp6Address, FALSE)) { + return EFI_INVALID_PARAMETER; + } + + if (TargetLinkAddress != NULL) { + if (!Ip6IsValidLinkAddress (IpSb, TargetLinkAddress)) { + return EFI_INVALID_PARAMETER; + } + } + + if (Ip6IsOneOfSetAddress (IpSb, TargetIp6Address, NULL, NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (DeleteFlag) { + Status = Ip6DelNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override); + } else { + Status = Ip6AddNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override); + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Check whether the user's token or event has already + been enqueue on IP6's list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +Ip6TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_IP6_COMPLETION_TOKEN *) Context; + TokenInItem = (EFI_IP6_COMPLETION_TOKEN *) Item->Key; + + if (Token == TokenInItem || Token->Event == TokenInItem->Event) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + +/** + Validate the user's token against the current station address. + + @param[in] Token User's token to validate. + + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long. + @retval EFI_SUCCESS The token is OK. + +**/ +EFI_STATUS +Ip6TxTokenValid ( + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + EFI_IP6_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 DataLength; + + if (Token == NULL || Token->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + TxData = Token->Packet.TxData; + + if (TxData == NULL || (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (TxData->FragmentCount == 0 || TxData->DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + for (DataLength = 0, Index = 0; Index < TxData->FragmentCount; Index++) { + if (TxData->FragmentTable[Index].FragmentLength == 0 || TxData->FragmentTable[Index].FragmentBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + DataLength += TxData->FragmentTable[Index].FragmentLength; + } + + if (TxData->DataLength != DataLength) { + return EFI_INVALID_PARAMETER; + } + + // + // TODO: Token.Packet.TxData.DataLength is too short to transmit. + // return EFI_BUFFER_TOO_SMALL; + // + + // + // If Token.Packet.TxData.DataLength is beyond the maximum that which can be + // described through the Fragment Offset field in Fragment header when performing + // fragmentation. + // + if (TxData->DataLength > 64 * 1024) { + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although this function seems simple, there + are some subtle aspects. + When user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data + is wrapped in an net buffer. The net buffer's Free function is + set to Ip6FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip6Output for + transmission. If an error happened before that, the buffer + is freed, which in turn frees the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be fired because we are still in the EfiIp6Transmit. If + the buffer has been sent by Ip6Output, it should be removed from + the TxToken map and user's event signaled. The token wrap and buffer + are bound together. Check the comments in Ip6Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip6FreeTxToken ( + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + + Wrap = (IP6_TXTOKEN_WRAP *) Context; + + // + // Signal IpSecRecycleEvent to inform IPsec free the memory + // + if (Wrap->IpSecRecycleSignal != NULL) { + gBS->SignalEvent (Wrap->IpSecRecycleSignal); + } + + // + // Find the token in the instance's map. EfiIp6Transmit put the + // token to the map. If that failed, NetMapFindKey will return NULL. + // + Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token); + + if (Item != NULL) { + NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL); + } + + if (Wrap->Sent) { + gBS->SignalEvent (Wrap->Token->Event); + + // + // Dispatch the DPC queued by the NotifyFunction of Token->Event. + // + DispatchDpc (); + } + + FreePool (Wrap); +} + + +/** + The callback function to Ip6Output to update the transmit status. + + @param[in] Packet The user's transmit packet. + @param[in] IoStatus The result of the transmission. + @param[in] Flag Not used during transmission. + @param[in] Context The token's wrap. + +**/ +VOID +Ip6OnPacketSent ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + + // + // This is the transmission request from upper layer, + // not the IP6 driver itself. + // + Wrap = (IP6_TXTOKEN_WRAP *) Context; + Wrap->Token->Status = IoStatus; + + NetbufFree (Wrap->Packet); +} + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some + errors occur, the event in the token will be signaled, and the status is updated. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token Pointer to the transmit token. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing + a source address for this transmission, + but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + - Token.Packet.TxData is NULL. + - Token.Packet.ExtHdrsLength is not zero and + Token.Packet.ExtHdrs is NULL. + - Token.Packet.FragmentCount is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentLength fields is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentBuffer fields is NULL. + - Token.Packet.TxData.DataLength is zero or not + equal to the sum of fragment lengths. + - Token.Packet.TxData.DestinationAddress is non + zero when DestinationAddress is configured as + non-zero when doing Configure() for this + EFI IPv6 protocol instance. + - Token.Packet.TxData.DestinationAddress is + unspecified when DestinationAddress is unspecified + when doing Configure() for this EFI IPv6 protocol + instance. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token. + Event was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_NOT_FOUND Not route is found to destination address. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too + short to transmit. + @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the + maximum that which can be described through the + Fragment Offset field in Fragment header when + performing fragmentation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Transmit ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_IP6_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_IP6_HEADER Head; + EFI_IP6_TRANSMIT_DATA *TxData; + EFI_IP6_OVERRIDE_DATA *Override; + IP6_TXTOKEN_WRAP *Wrap; + UINT8 *ExtHdrs; + + // + // Check input parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + ExtHdrs = NULL; + + Status = Ip6TxTokenValid (Token); + if (EFI_ERROR (Status)) { + return Status; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Config = &IpInstance->ConfigData; + + // + // Check whether the token or signal already existed. + // + if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip6TokenExist, Token))) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // Build the IP header, fill in the information from ConfigData or OverrideData + // + ZeroMem (&Head, sizeof(EFI_IP6_HEADER)); + TxData = Token->Packet.TxData; + IP6_COPY_ADDRESS (&Head.SourceAddress, &Config->StationAddress); + IP6_COPY_ADDRESS (&Head.DestinationAddress, &Config->DestinationAddress); + + Status = EFI_INVALID_PARAMETER; + + if (NetIp6IsUnspecifiedAddr (&TxData->DestinationAddress)) { + if (NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + goto Exit; + } + + ASSERT (!NetIp6IsUnspecifiedAddr (&Config->StationAddress)); + + } else { + // + // StationAddress is unspecified only when ConfigData.Dest is unspecified. + // Use TxData.Dest to override the DestinationAddress. + // + if (!NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + goto Exit; + } + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress)) { + Status = Ip6SelectSourceAddress ( + IpSb, + &TxData->DestinationAddress, + &Head.SourceAddress + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, &TxData->DestinationAddress); + } + + // + // Fill in Head infos. + // + Head.NextHeader = Config->DefaultProtocol; + if (TxData->ExtHdrsLength != 0) { + Head.NextHeader = TxData->NextHeader; + } + + if (TxData->OverrideData != NULL) { + Override = TxData->OverrideData; + Head.NextHeader = Override->Protocol; + Head.HopLimit = Override->HopLimit; + Head.FlowLabelL = HTONS ((UINT16) Override->FlowLabel); + Head.FlowLabelH = (UINT8) ((Override->FlowLabel >> 16) & 0x0F); + } else { + Head.HopLimit = Config->HopLimit; + Head.FlowLabelL = HTONS ((UINT16) Config->FlowLabel); + Head.FlowLabelH = (UINT8) ((Config->FlowLabel >> 16) & 0x0F); + } + + Head.PayloadLength = HTONS ((UINT16) (TxData->ExtHdrsLength + TxData->DataLength)); + + // + // OK, it survives all the validation check. Wrap the token in + // a IP6_TXTOKEN_WRAP and the data in a netbuf + // + Status = EFI_OUT_OF_RESOURCES; + Wrap = AllocateZeroPool (sizeof (IP6_TXTOKEN_WRAP)); + if (Wrap == NULL) { + goto Exit; + } + + Wrap->IpInstance = IpInstance; + Wrap->Token = Token; + Wrap->Sent = FALSE; + Wrap->Life = IP6_US_TO_SEC (Config->TransmitTimeout); + Wrap->Packet = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + IP6_MAX_HEADLEN, + 0, + Ip6FreeTxToken, + Wrap + ); + + if (Wrap->Packet == NULL) { + FreePool (Wrap); + goto Exit; + } + + Token->Status = EFI_NOT_READY; + + Status = NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + // + // NetbufFree will call Ip6FreeTxToken, which in turn will + // free the IP6_TXTOKEN_WRAP. Now, the token wrap hasn't been + // enqueued. + // + NetbufFree (Wrap->Packet); + goto Exit; + } + + // + // Allocate a new buffer to store IPv6 extension headers to avoid updating + // the original data in EFI_IP6_COMPLETION_TOKEN. + // + if (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs != NULL) { + ExtHdrs = (UINT8 *) AllocateCopyPool (TxData->ExtHdrsLength, TxData->ExtHdrs); + if (ExtHdrs == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + + // + // Mark the packet sent before output it. Mark it not sent again if the + // returned status is not EFI_SUCCESS; + // + Wrap->Sent = TRUE; + + Status = Ip6Output ( + IpSb, + NULL, + IpInstance, + Wrap->Packet, + &Head, + ExtHdrs, + TxData->ExtHdrsLength, + Ip6OnPacketSent, + Wrap + ); + if (EFI_ERROR (Status)) { + Wrap->Sent = FALSE; + NetbufFree (Wrap->Packet); + } + +Exit: + gBS->RestoreTPL (OldTpl); + + if (ExtHdrs != NULL) { + FreePool (ExtHdrs); + } + + return Status; +} + +/** + Places a receiving request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + + The Token.Event field in the completion token must be filled in by the caller + and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + Current Udp implementation creates an IP child for each Udp child. + It initates a asynchronous receive immediately no matter whether + there is no mapping or not. Therefore, disable the returning EFI_NO_MAPPING for now. + To enable it, the following check must be performed: + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance, + while no source address is available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system + resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI IPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already + in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Receive ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP6_SERVICE *IpSb; + + if (This == NULL || Token == NULL || Token->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + // + // Check whether the toke is already on the receive queue. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip6TokenExist, Token); + + if (EFI_ERROR (Status)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // Queue the token then check whether there is pending received packet. + // + Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6InstanceDeliverPacket (IpInstance); + + // + // Dispatch the DPC queued by the NotifyFunction of this instane's receive + // event. + // + DispatchDpc (); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Cancel the transmitted but not recycled packet. If a matching + token is found, it will call Ip6CancelPacket to cancel the + packet. Ip6CancelPacket cancels all the fragments of the + packet. When all the fragments are freed, the IP6_TXTOKEN_WRAP + is deleted from the Map, and user's event is signalled. + Because Ip6CancelPacket and other functions are all called in + line, after Ip6CancelPacket returns, the Item has been freed. + + @param[in] Map The IP6 child's transmit queue. + @param[in] Item The current transmitted packet to test. + @param[in] Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next Item. + @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled. + +**/ +EFI_STATUS +EFIAPI +Ip6CancelTxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + IP6_TXTOKEN_WRAP *Wrap; + + Token = (EFI_IP6_COMPLETION_TOKEN *) Context; + + // + // Return EFI_SUCCESS to check the next item in the map if + // this one doesn't match. + // + if ((Token != NULL) && (Token != Item->Key)) { + return EFI_SUCCESS; + } + + Wrap = (IP6_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + // + // Don't access the Item, Wrap and Token's members after this point. + // Item and wrap has been freed. And we no longer own the Token. + // + Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + + // + // If only one item is to be cancel, return EFI_ABORTED to stop + // iterating the map any more. + // + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the receive request. This is simple, because + it is only enqueued in our local receive map. + + @param[in] Map The IP6 child's receive queue. + @param[in] Item Current receive request to cancel. + @param[in] Context The user's token to cancel. + + + @retval EFI_SUCCESS Continue to check the next receive request on the + queue. + @retval EFI_ABORTED The user's token (token != NULL) has been + cancelled. + +**/ +EFI_STATUS +EFIAPI +Ip6CancelRxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_COMPLETION_TOKEN *This; + + Token = (EFI_IP6_COMPLETION_TOKEN *) Context; + This = Item->Key; + + if ((Token != NULL) && (Token != This)) { + return EFI_SUCCESS; + } + + NetMapRemoveItem (Map, Item, NULL); + + This->Status = EFI_ABORTED; + This->Packet.RxData = NULL; + gBS->SignalEvent (This->Event); + + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiIp6Cancel API. + + @param[in] IpInstance The IP6 child. + @param[in] Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled. + @retval EFI_NOT_FOUND The token isn't found on either the + transmit/receive queue. + @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip6Cancel ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // First check the transmitted packet. Ip6CancelTxTokens returns + // EFI_ABORTED to mean that the token has been cancelled when + // token != NULL. So, return EFI_SUCCESS for this condition. + // + Status = NetMapIterate (&IpInstance->TxTokens, Ip6CancelTxTokens, Token); + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // Check the receive queue. Ip6CancelRxTokens also returns EFI_ABORT + // for Token!=NULL and it is cancelled. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip6CancelRxTokens, Token); + // + // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's + // events. + // + DispatchDpc (); + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // OK, if the Token is found when Token != NULL, the NetMapIterate + // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS. + // + if (Token != NULL) { + return EFI_NOT_FOUND; + } + + // + // If Token == NULL, cancel all the tokens. return error if not + // all of them are cancelled. + // + if (!NetMapIsEmpty (&IpInstance->TxTokens) || !NetMapIsEmpty (&IpInstance->RxTokens)) { + + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Abort an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, Token->Status will be set to EFI_ABORTED, and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means the + asynchronous operation has completed, this function will not signal the token, + and EFI_NOT_FOUND is returned. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_IP6_PROTOCOL.Transmit() or + EFI_IP6_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is + defined in EFI_IP6_PROTOCOL.Transmit(). + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token->Event was signaled. When Token is NULL, all + pending requests were aborted, and their events were signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Cancel ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Ip6Cancel (IpInstance, Token); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Polls for incoming data packets, and processes outgoing data packets. + + The Poll() function polls for incoming data packets and processes outgoing data + packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll() + function to increase the rate that data packets are moved between the communications + device and the transmit and receive queues. + + In some systems the periodic timer event may not poll the underlying communications + device fast enough to transmit and/or receive all data packets without missing + incoming packets or dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function + more often. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Poll ( + IN EFI_IP6_PROTOCOL *This + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (IpInstance->State == IP6_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } + + Mnp = IpInstance->Service->Mnp; + + // + // Don't lock the Poll function to enable the deliver of + // the packet polled up. + // + return Mnp->Poll (Mnp); + +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.h b/NetworkPkg/Ip6Dxe/Ip6Impl.h new file mode 100644 index 0000000000..524de5e256 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Impl.h @@ -0,0 +1,751 @@ +/** @file + Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_IMPL_H__ +#define __EFI_IP6_IMPL_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Ip6Common.h" +#include "Ip6Driver.h" +#include "Ip6Icmp.h" +#include "Ip6If.h" +#include "Ip6Input.h" +#include "Ip6Mld.h" +#include "Ip6Nd.h" +#include "Ip6Option.h" +#include "Ip6Output.h" +#include "Ip6Route.h" +#include "Ip6ConfigNv.h" +#include "Ip6ConfigImpl.h" + +#define IP6_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'P') +#define IP6_SERVICE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'S') + +// +// The state of IP6 protocol. It starts from UNCONFIGED. if it is +// successfully configured, it goes to CONFIGED. if configure NULL +// is called, it becomes UNCONFIGED again. If (partly) destroyed, it +// becomes DESTROY. +// +#define IP6_STATE_UNCONFIGED 0 +#define IP6_STATE_CONFIGED 1 +#define IP6_STATE_DESTROY 2 + +// +// The state of IP6 service. It starts from UNSTARTED. It transits +// to STARTED if autoconfigure is started. If default address is +// configured, it becomes CONFIGED. and if partly destroyed, it goes +// to DESTROY. +// +#define IP6_SERVICE_UNSTARTED 0 +#define IP6_SERVICE_STARTED 1 +#define IP6_SERVICE_CONFIGED 2 +#define IP6_SERVICE_DESTROY 3 + +#define IP6_INSTANCE_FROM_PROTOCOL(Ip6) \ + CR ((Ip6), IP6_PROTOCOL, Ip6Proto, IP6_PROTOCOL_SIGNATURE) + +#define IP6_SERVICE_FROM_PROTOCOL(Sb) \ + CR ((Sb), IP6_SERVICE, ServiceBinding, IP6_SERVICE_SIGNATURE) + +#define IP6_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured) + +extern EFI_IPSEC_PROTOCOL *mIpSec; + +// +// IP6_TXTOKEN_WRAP wraps the upper layer's transmit token. +// The user's data is kept in the Packet. When fragment is +// needed, each fragment of the Packet has a reference to the +// Packet, no data is actually copied. The Packet will be +// released when all the fragments of it have been recycled by +// MNP. Upon then, the IP6_TXTOKEN_WRAP will be released, and +// user's event signalled. +// +typedef struct { + IP6_PROTOCOL *IpInstance; + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; + BOOLEAN Sent; + INTN Life; +} IP6_TXTOKEN_WRAP; + +typedef struct { + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; +} IP6_IPSEC_WRAP; + +// +// IP6_RXDATA_WRAP wraps the data IP6 child delivers to the +// upper layers. The received packet is kept in the Packet. +// The Packet itself may be constructured from some fragments. +// All the fragments of the Packet is organized by a +// IP6_ASSEMBLE_ENTRY structure. If the Packet is recycled by +// the upper layer, the assemble entry and its associated +// fragments will be freed at last. +// +typedef struct { + LIST_ENTRY Link; + IP6_PROTOCOL *IpInstance; + NET_BUF *Packet; + EFI_IP6_RECEIVE_DATA RxData; +} IP6_RXDATA_WRAP; + +struct _IP6_PROTOCOL { + UINT32 Signature; + + EFI_IP6_PROTOCOL Ip6Proto; + EFI_HANDLE Handle; + INTN State; + + IP6_SERVICE *Service; + LIST_ENTRY Link; // Link to all the IP protocol from the service + + UINT8 PrefixLength; // PrefixLength of the configured station address. + // + // User's transmit/receive tokens, and received/deliverd packets + // + NET_MAP RxTokens; + NET_MAP TxTokens; // map between (User's Token, IP6_TXTOKE_WRAP) + LIST_ENTRY Received; // Received but not delivered packet + LIST_ENTRY Delivered; // Delivered and to be recycled packets + EFI_LOCK RecycleLock; + + IP6_INTERFACE *Interface; + LIST_ENTRY AddrLink; // Ip instances with the same IP address. + + EFI_IPv6_ADDRESS *GroupList; // stored in network order. + UINT32 GroupCount; + + EFI_IP6_CONFIG_DATA ConfigData; +}; + +struct _IP6_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + INTN State; + BOOLEAN InDestroy; + + // + // List of all the IP instances and interfaces, and default + // interface and route table and caches. + // + UINTN NumChildren; + LIST_ENTRY Children; + + LIST_ENTRY Interfaces; + + IP6_INTERFACE *DefaultInterface; + IP6_ROUTE_TABLE *RouteTable; + + IP6_LINK_RX_TOKEN RecvRequest; + + // + // Ip reassemble utilities and MLD data + // + IP6_ASSEMBLE_TABLE Assemble; + IP6_MLD_SERVICE_DATA MldCtrl; + + EFI_IPv6_ADDRESS LinkLocalAddr; + BOOLEAN LinkLocalOk; + BOOLEAN LinkLocalDadFail; + BOOLEAN Dhcp6NeedStart; + BOOLEAN Dhcp6NeedInfoRequest; + + // + // ND data + // + UINT8 CurHopLimit; + UINT32 LinkMTU; + UINT32 BaseReachableTime; + UINT32 ReachableTime; + UINT32 RetransTimer; + LIST_ENTRY NeighborTable; + + LIST_ENTRY OnlinkPrefix; + LIST_ENTRY AutonomousPrefix; + + LIST_ENTRY DefaultRouterList; + UINT32 RoundRobin; + + UINT8 InterfaceIdLen; + UINT8 *InterfaceId; + + BOOLEAN RouterAdvertiseReceived; + UINT8 SolicitTimer; + UINT32 Ticks; + + // + // Low level protocol used by this service instance + // + EFI_HANDLE Image; + EFI_HANDLE Controller; + + EFI_HANDLE MnpChildHandle; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + EFI_EVENT Timer; + EFI_EVENT FasterTimer; + + // + // IPv6 Configuration Protocol instance + // + IP6_CONFIG_INSTANCE Ip6ConfigInstance; + + // + // The string representation of the current mac address of the + // NIC this IP6_SERVICE works on. + // + CHAR16 *MacString; + UINT32 MaxPacketSize; + UINT32 OldMaxPacketSize; +}; + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although this function seems simple, + there are some subtle aspects. + When a user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data + is wrapped in a net buffer. The net buffer's Free function is + set to Ip6FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip6Output for + transmission. If an error occurs before that, the buffer + is freed, which in turn frees the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be signaled because we are still in the EfiIp6Transmit. If + the buffer has been sent by Ip6Output, it should be removed from + the TxToken map and the user's event signaled. The token wrap and buffer + are bound together. Refer to the comments in Ip6Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip6FreeTxToken ( + IN VOID *Context + ); + +/** + Config the MNP parameter used by IP. The IP driver use one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. And it will enable/disable + the promiscuous receive according to whether there is IP child + enable that or not. If Force is FALSE, it will iterate through + all the IP children to check whether the promiscuous receive + setting has been changed. If it hasn't been changed, it won't + reconfigure the MNP. If Force is TRUE, the MNP is configured + whether that is changed or not. + + @param[in] IpSb The IP6 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP successfully configured/reconfigured. + @retval Others The configuration failed. + +**/ +EFI_STATUS +Ip6ServiceConfigMnp ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN Force + ); + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiIp6Cancel API. + + @param[in] IpInstance The IP6 child. + @param[in] Token The token to cancel. If NULL, all tokens will be + cancelled. + + @retval EFI_SUCCESS The token was cancelled. + @retval EFI_NOT_FOUND The token isn't found on either the + transmit or receive queue. + @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip6Cancel ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Initialize the IP6_PROTOCOL structure to the unconfigured states. + + @param[in] IpSb The IP6 service instance. + @param[in, out] IpInstance The IP6 child instance. + +**/ +VOID +Ip6InitProtocol ( + IN IP6_SERVICE *IpSb, + IN OUT IP6_PROTOCOL *IpInstance + ); + +/** + Clean up the IP6 child, release all the resources used by it. + + @param[in, out] IpInstance The IP6 child to clean up. + + @retval EFI_SUCCESS The IP6 child was cleaned up + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip6CleanProtocol ( + IN OUT IP6_PROTOCOL *IpInstance + ); + +// +// EFI_IP6_PROTOCOL interface prototypes +// + +/** + Gets the current operational settings for this instance of the EFI IPv6 Protocol driver. + + The GetModeData() function returns the current operational mode data for this driver instance. + The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to + retrieve the operational mode data of underlying networks or drivers. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[out] Ip6ModeData The pointer to the EFI IPv6 Protocol mode data structure. + @param[out] MnpConfigData The pointer to the managed network configuration data structure. + @param[out] SnpModeData The pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIp6GetModeData ( + IN EFI_IP6_PROTOCOL *This, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational parameters and filter + settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic + can be sent or received by this instance. Once the parameters have been reset (by calling this + function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped + independently of each other by enabling or disabling their receive filter settings with the + Configure() function. + + If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required + to be one of the currently configured IPv6 addresses list in the EFI IPv6 drivers, or else + EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is + unspecified, the IPv6 driver will bind a source address according to the source address selection + algorithm. Clients could frequently call GetModeData() to check get a currently configured IPv6. + If both Ip6ConfigData.StationAddress and Ip6ConfigData.Destination are unspecified, when + transmitting the packet afterwards, the source address filled in each outgoing IPv6 packet + is decided based on the destination of this packet. + + If operational parameters are reset or changed, any pending transmit and receive requests will be + cancelled. Their completion token status will be set to EFI_ABORTED, and their events will be + signaled. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Ip6ConfigData The pointer to the EFI IPv6 Protocol configuration data structure. + If NULL, reset the configuration data. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Ip6ConfigData.StationAddress is neither zero nor + a unicast IPv6 address. + - Ip6ConfigData.StationAddress is neither zero nor + one of the configured IP addresses in the EFI IPv6 driver. + - Ip6ConfigData.DefaultProtocol is illegal. + @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for + this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6 + address or prefix length can be changed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6 + Protocol driver instance was not opened. + @retval EFI_UNSUPPORTED Default protocol specified through + Ip6ConfigData.DefaulProtocol isn't supported. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Configure ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL + ); + +/** + Joins and leaves multicast groups. + + The Groups() function is used to join and leave multicast group sessions. Joining a group will + enable reception of matching multicast packets. Leaving a group will disable reception of matching + multicast packets. Source-Specific Multicast isn't required to be supported. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave. + @param[in] GroupAddress The pointer to the IPv6 multicast address. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - JoinFlag is TRUE and GroupAddress is NULL. + - GroupAddress is not NULL and *GroupAddress is + not a multicast IPv6 address. + - GroupAddress is not NULL and *GroupAddress is in the + range of SSM destination address. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_OUT_OF_RESOURCES System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Groups ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ); + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to or deletes a route from the routing table. + + Routes are determined by comparing the leftmost PrefixLength bits of Destination with + the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the + configured station address. + + The default route is added with Destination and PrefixLegth both set to all zeros. The + default route matches all destination IPv6 addresses that do not match any other routes. + + All EFI IPv6 Protocol instances share a routing table. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to + FALSE to add this route to the routing table. Destination, + PrefixLength and Gateway are used as the key to each + route entry. + @param[in] Destination The address prefix of the subnet that needs to be routed. + This is an optional parameter that may be NULL. + @param[in] PrefixLength The prefix length of Destination. Ignored if Destination + is NULL. + @param[in] GatewayAddress The unicast gateway IPv6 address for this route. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - When DeleteRoute is TRUE, both Destination and + GatewayAddress are NULL. + - When DeleteRoute is FALSE, either Destination or + GatewayAddress is NULL. + - *GatewayAddress is not a valid unicast IPv6 address. + - *GatewayAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when + DeleteRoute is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Routes ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ); + +/** + Add or delete Neighbor cache entries. + + The Neighbors() function is used to add, update, or delete an entry from a neighbor cache. + IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as + network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network + traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not + timeout) or dynamic (will timeout). + + The implementation should follow the neighbor cache timeout mechanism defined in + RFC4861. The default neighbor cache timeout value should be tuned for the expected network + environment. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteFlag Set to TRUE to delete the specified cache entry. Set to FALSE to + add (or update, if it already exists and Override is TRUE) the + specified cache entry. TargetIp6Address is used as the key + to find the requested cache entry. + @param[in] TargetIp6Address The pointer to the Target IPv6 address. + @param[in] TargetLinkAddress The pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache, it will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, EFI_ACCESS_DENIED + will be returned if a corresponding cache entry already exists. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - TargetIpAddress is NULL. + - *TargetLinkAddress is invalid when not NULL. + - *TargetIpAddress is not a valid unicast IPv6 address. + - *TargetIpAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is + TRUE or when DeleteFlag is FALSE while + TargetLinkAddress is NULL.). + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when Override + is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Neighbors ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some + errors occur, the event in the token will be signaled and the status is updated. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The pointer to the transmit token. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing + a source address for this transmission, + but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + - Token.Packet.TxData is NULL. + - Token.Packet.ExtHdrsLength is not zero and + Token.Packet.ExtHdrs is NULL. + - Token.Packet.FragmentCount is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentLength fields is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentBuffer fields is NULL. + - Token.Packet.TxData.DataLength is zero or not + equal to the sum of fragment lengths. + - Token.Packet.TxData.DestinationAddress is non- + zero when DestinationAddress is configured as + non-zero when doing Configure() for this + EFI IPv6 protocol instance. + - Token.Packet.TxData.DestinationAddress is + unspecified when DestinationAddress is unspecified + when doing Configure() for this EFI IPv6 protocol + instance. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token. + The event was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_NOT_FOUND Not route is found to the destination address. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too + short to transmit. + @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the + maximum that which can be described through the + Fragment Offset field in Fragment header when + performing fragmentation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Transmit ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ); + +/** + Places a receiving request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + + The Token.Event field in the completion token must be filled in by the caller + and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + Current Udp implementation creates an IP child for each Udp child. + It initates a asynchronous receive immediately whether or not + there is no mapping. Therefore, disable the returning EFI_NO_MAPPING for now. + To enable it, the following check must be performed: + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance, + while no source address is available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system + resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI IPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already + in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Receive ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ); + +/** + Abort an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, Token->Status will be set to EFI_ABORTED, and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means the + asynchronous operation has completed, this function will not signal the token, + and EFI_NOT_FOUND is returned. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The pointer to a token that has been issued by + EFI_IP6_PROTOCOL.Transmit() or + EFI_IP6_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is + defined in EFI_IP6_PROTOCOL.Transmit(). + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token->Event was signaled. When Token is NULL, all + pending requests were aborted, and their events were signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Cancel ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function polls for incoming data packets and processes outgoing data + packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll() + function to increase the rate that data packets are moved between the communications + device and the transmit and receive queues. + + In some systems the periodic timer event may not poll the underlying communications + device fast enough to transmit and/or receive all data packets without missing + incoming packets or dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function + more often. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Poll ( + IN EFI_IP6_PROTOCOL *This + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.c b/NetworkPkg/Ip6Dxe/Ip6Input.c new file mode 100644 index 0000000000..c18811b611 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Input.c @@ -0,0 +1,1710 @@ +/** @file + IP6 internal functions to process the incoming packets. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +/** + Create an empty assemble entry for the packet identified by + (Dst, Src, Id). The default life for the packet is 60 seconds. + + @param[in] Dst The destination address. + @param[in] Src The source address. + @param[in] Id The ID field in the IP header. + + @return NULL if failed to allocate memory for the entry. Otherwise, + the pointer to the just created reassemble entry. + +**/ +IP6_ASSEMBLE_ENTRY * +Ip6CreateAssembleEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN UINT32 Id + ) +{ + IP6_ASSEMBLE_ENTRY *Assemble; + + Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY)); + if (Assemble == NULL) { + return NULL; + } + + IP6_COPY_ADDRESS (&Assemble->Dst, Dst); + IP6_COPY_ADDRESS (&Assemble->Src, Src); + InitializeListHead (&Assemble->Fragments); + + Assemble->Id = Id; + Assemble->Life = IP6_FRAGMENT_LIFE + 1; + + Assemble->TotalLen = 0; + Assemble->CurLen = 0; + Assemble->Head = NULL; + Assemble->Info = NULL; + Assemble->Packet = NULL; + + return Assemble; +} + +/** + Release all the fragments of a packet, then free the assemble entry. + + @param[in] Assemble The assemble entry to free. + +**/ +VOID +Ip6FreeAssembleEntry ( + IN IP6_ASSEMBLE_ENTRY *Assemble + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + NET_BUF *Fragment; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) { + Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + RemoveEntryList (Entry); + NetbufFree (Fragment); + } + + if (Assemble->Packet != NULL) { + NetbufFree (Assemble->Packet); + } + + FreePool (Assemble); +} + +/** + Release all the fragments of the packet. This is the callback for + the assembled packet's OnFree. It will free the assemble entry, + which in turn frees all the fragments of the packet. + + @param[in] Arg The assemble entry to free. + +**/ +VOID +EFIAPI +Ip6OnFreeFragments ( + IN VOID *Arg + ) +{ + Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg); +} + +/** + Trim the packet to fit in [Start, End), and update per the + packet information. + + @param[in, out] Packet Packet to trim. + @param[in] Start The sequence of the first byte to fit in. + @param[in] End One beyond the sequence of last byte to fit in. + +**/ +VOID +Ip6TrimPacket ( + IN OUT NET_BUF *Packet, + IN INTN Start, + IN INTN End + ) +{ + IP6_CLIP_INFO *Info; + INTN Len; + + Info = IP6_GET_CLIP_INFO (Packet); + + ASSERT (Info->Start + Info->Length == Info->End); + ASSERT ((Info->Start < End) && (Start < Info->End)); + + if (Info->Start < Start) { + Len = Start - Info->Start; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD); + Info->Start = (UINT32) Start; + Info->Length -= (UINT32) Len; + } + + if (End < Info->End) { + Len = End - Info->End; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL); + Info->End = (UINT32) End; + Info->Length -= (UINT32) Len; + } +} + +/** + Reassemble the IP fragments. If all the fragments of the packet + have been received, it will wrap the packet in a net buffer then + return it to caller. If the packet can't be assembled, NULL is + returned. + + @param[in, out] Table The assemble table used. A new assemble entry will be created + if the Packet is from a new chain of fragments. + @param[in] Packet The fragment to assemble. It might be freed if the fragment + can't be re-assembled. + + @return NULL if the packet can't be reassembled. The pointer to the just assembled + packet if all the fragments of the packet have arrived. + +**/ +NET_BUF * +Ip6Reassemble ( + IN OUT IP6_ASSEMBLE_TABLE *Table, + IN NET_BUF *Packet + ) +{ + EFI_IP6_HEADER *Head; + IP6_CLIP_INFO *This; + IP6_CLIP_INFO *Node; + IP6_ASSEMBLE_ENTRY *Assemble; + IP6_ASSEMBLE_ENTRY *Entry; + LIST_ENTRY *ListHead; + LIST_ENTRY *Prev; + LIST_ENTRY *Cur; + NET_BUF *Fragment; + NET_BUF *TmpPacket; + NET_BUF *NewPacket; + NET_BUF *Duplicate; + UINT8 *DupHead; + INTN Index; + UINT16 UnFragmentLen; + UINT8 *NextHeader; + + Head = Packet->Ip.Ip6; + This = IP6_GET_CLIP_INFO (Packet); + + ASSERT (Head != NULL); + + // + // Find the corresponding assemble entry by (Dst, Src, Id) + // + Assemble = NULL; + Index = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id); + + NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) { + Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link); + + if (Entry->Id == This->Id && + EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) && + EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress) + ) { + Assemble = Entry; + break; + } + } + + // + // Create a new entry if can not find an existing one, insert it to assemble table + // + if (Assemble == NULL) { + Assemble = Ip6CreateAssembleEntry ( + &Head->DestinationAddress, + &Head->SourceAddress, + This->Id + ); + + if (Assemble == NULL) { + goto Error; + } + + InsertHeadList (&Table->Bucket[Index], &Assemble->Link); + } + + // + // Find the point to insert the packet: before the first + // fragment with THIS.Start < CUR.Start. the previous one + // has PREV.Start <= THIS.Start < CUR.Start. + // + ListHead = &Assemble->Fragments; + + NET_LIST_FOR_EACH (Cur, ListHead) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) { + break; + } + } + + // + // Check whether the current fragment overlaps with the previous one. + // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to + // check whether THIS.Start < PREV.End for overlap. If two fragments + // overlaps, trim the overlapped part off THIS fragment. + // + if ((Cur != ListHead) && ((Prev = Cur->BackLink) != ListHead)) { + Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + Node = IP6_GET_CLIP_INFO (Fragment); + + if (This->Start < Node->End) { + if (This->End <= Node->End) { + goto Error; + } + + // + // Trim the previous fragment from tail. + // + Ip6TrimPacket (Fragment, Node->Start, This->Start); + } + } + + // + // Insert the fragment into the packet. The fragment may be removed + // from the list by the following checks. + // + NetListInsertBefore (Cur, &Packet->List); + + // + // Check the packets after the insert point. It holds that: + // THIS.Start <= NODE.Start < NODE.End. The equality holds + // if PREV and NEXT are continuous. THIS fragment may fill + // several holes. Remove the completely overlapped fragments + // + while (Cur != ListHead) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Node = IP6_GET_CLIP_INFO (Fragment); + + // + // Remove fragments completely overlapped by this fragment + // + if (Node->End <= This->End) { + Cur = Cur->ForwardLink; + + RemoveEntryList (&Fragment->List); + Assemble->CurLen -= Node->Length; + + NetbufFree (Fragment); + continue; + } + + // + // The conditions are: THIS.Start <= NODE.Start, and THIS.End < + // NODE.End. Two fragments overlaps if NODE.Start < THIS.End. + // If two fragments start at the same offset, remove THIS fragment + // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)). + // + if (Node->Start < This->End) { + if (This->Start == Node->Start) { + RemoveEntryList (&Packet->List); + goto Error; + } + + Ip6TrimPacket (Packet, This->Start, Node->Start); + } + + break; + } + + // + // Update the assemble info: increase the current length. If it is + // the frist fragment, update the packet's IP head and per packet + // info. If it is the last fragment, update the total length. + // + Assemble->CurLen += This->Length; + + if (This->Start == 0) { + // + // Once the first fragment is enqueued, it can't be removed + // from the fragment list. So, Assemble->Head always point + // to valid memory area. + // + if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) { + goto Error; + } + + // + // Backup the first fragment in case the reasembly of that packet fail. + // + Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER)); + if (Duplicate == NULL) { + goto Error; + } + + // + // Revert IP head to network order. + // + DupHead = NetbufGetByte (Duplicate, 0, NULL); + ASSERT (DupHead != NULL); + Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead); + Assemble->Packet = Duplicate; + + // + // Adjust the unfragmentable part in first fragment + // + UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER)); + if (UnFragmentLen == 0) { + // + // There is not any unfragmentable extension header. + // + ASSERT (Head->NextHeader == IP6_FRAGMENT); + Head->NextHeader = This->NextHeader; + } else { + NextHeader = NetbufGetByte ( + Packet, + This->FormerNextHeader + sizeof (EFI_IP6_HEADER), + 0 + ); + if (NextHeader == NULL) { + goto Error; + } + + *NextHeader = This->NextHeader; + } + + Assemble->Head = Head; + Assemble->Info = IP6_GET_CLIP_INFO (Packet); + } + + // + // Don't update the length more than once. + // + if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) { + Assemble->TotalLen = This->End; + } + + // + // Deliver the whole packet if all the fragments received. + // All fragments received if: + // 1. received the last one, so, the totoal length is know + // 2. received all the data. If the last fragment on the + // queue ends at the total length, all data is received. + // + if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) { + + RemoveEntryList (&Assemble->Link); + + // + // If the packet is properly formated, the last fragment's End + // equals to the packet's total length. Otherwise, the packet + // is a fake, drop it now. + // + Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List); + if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) { + Ip6FreeAssembleEntry (Assemble); + goto Error; + } + + Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List); + This = Assemble->Info; + + // + // This TmpPacket is used to hold the unfragmentable part, i.e., + // the IPv6 header and the unfragmentable extension headers. Be noted that + // the Fragment Header is exluded. + // + TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0); + ASSERT (TmpPacket != NULL); + + NET_LIST_FOR_EACH (Cur, ListHead) { + // + // Trim off the unfragment part plus the fragment header from all fragments. + // + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE); + } + + InsertHeadList (ListHead, &TmpPacket->List); + + // + // Wrap the packet in a net buffer then deliver it up + // + NewPacket = NetbufFromBufList ( + &Assemble->Fragments, + 0, + 0, + Ip6OnFreeFragments, + Assemble + ); + + if (NewPacket == NULL) { + Ip6FreeAssembleEntry (Assemble); + goto Error; + } + + NewPacket->Ip.Ip6 = Assemble->Head; + + CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO)); + + return NewPacket; + } + + return NULL; + +Error: + NetbufFree (Packet); + return NULL; +} + + +/** + The callback function for the net buffer that wraps the packet processed by + IPsec. It releases the wrap packet and also signals IPsec to free the resources. + + @param[in] Arg The wrap context. + +**/ +VOID +EFIAPI +Ip6IpSecFree ( + IN VOID *Arg + ) +{ + IP6_IPSEC_WRAP *Wrap; + + Wrap = (IP6_IPSEC_WRAP *) Arg; + + if (Wrap->IpSecRecycleSignal != NULL) { + gBS->SignalEvent (Wrap->IpSecRecycleSignal); + } + + NetbufFree (Wrap->Packet); + + FreePool (Wrap); + + return; +} + +/** + The work function to locate the IPsec protocol to process the inbound or + outbound IP packets. The process routine handles the packet with the following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP6 service instance. + @param[in] Head The caller-supplied IP6 header. + @param[in, out] LastHead The next header field of last IP header. + @param[in, out] Netbuf The IP6 packet to be processed by IPsec. + @param[in] ExtHdrs The caller-supplied options. + @param[in] ExtHdrsLen The length of the option. + @param[in] Direction The directionality in an SPD entry, + EfiIPsecInBound, or EfiIPsecOutBound. + @param[in] Context The token's wrap. + + @retval EFI_SUCCESS The IPsec protocol is not available or disabled. + @retval EFI_SUCCESS The packet was bypassed, and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the + number of input data blocks when building a fragment table. + +**/ +EFI_STATUS +Ip6IpSecProcessPacket ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN OUT UINT8 *LastHead, + IN OUT NET_BUF **Netbuf, + IN VOID *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN VOID *Context + ) +{ + NET_FRAGMENT *FragmentTable; + UINT32 FragmentCount; + EFI_EVENT RecycleEvent; + NET_BUF *Packet; + IP6_TXTOKEN_WRAP *TxWrap; + IP6_IPSEC_WRAP *IpSecWrap; + EFI_STATUS Status; + EFI_IP6_HEADER *PacketHead; + UINT8 *Buf; + + Status = EFI_SUCCESS; + Packet = *Netbuf; + RecycleEvent = NULL; + IpSecWrap = NULL; + FragmentTable = NULL; + PacketHead = NULL; + Buf = NULL; + TxWrap = (IP6_TXTOKEN_WRAP *) Context; + FragmentCount = Packet->BlockOpNum; + + if (mIpSec == NULL) { + gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &mIpSec); + + // + // Check whether the ipsec protocol is available. + // + if (mIpSec == NULL) { + goto ON_EXIT; + } + } + + // + // Check whether the ipsec enable variable is set. + // + if (mIpSec->DisabledFlag) { + // + // If IPsec is disabled, restore the original MTU + // + IpSb->MaxPacketSize = IpSb->OldMaxPacketSize; + goto ON_EXIT; + } else { + // + // If IPsec is enabled, use the MTU which reduce the IPsec header length. + // + IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN; + } + + + // + // Bypass all multicast inbound or outbound traffic. + // + if (IP6_IS_MULTICAST (&Head->DestinationAddress) || IP6_IS_MULTICAST (&Head->SourceAddress)) { + goto ON_EXIT; + } + + // + // Rebuild fragment table from netbuf to ease ipsec process. + // + FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT)); + + if (FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount); + + if (EFI_ERROR(Status)) { + FreePool (FragmentTable); + goto ON_EXIT; + } + + // + // Convert host byte order to network byte order + // + Ip6NtohHead (Head); + + Status = mIpSec->Process ( + mIpSec, + IpSb->Controller, + IP_VERSION_6, + (VOID *) Head, + LastHead, + NULL, + 0, + (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable), + &FragmentCount, + Direction, + &RecycleEvent + ); + // + // Convert back to host byte order + // + Ip6NtohHead (Head); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (Direction == EfiIPsecOutBound && TxWrap != NULL) { + + TxWrap->IpSecRecycleSignal = RecycleEvent; + TxWrap->Packet = NetbufFromExt ( + FragmentTable, + FragmentCount, + IP6_MAX_HEADLEN, + 0, + Ip6FreeTxToken, + TxWrap + ); + if (TxWrap->Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + *Netbuf = TxWrap->Packet; + + } else { + + IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP)); + + if (IpSecWrap == NULL) { + goto ON_EXIT; + } + + IpSecWrap->IpSecRecycleSignal = RecycleEvent; + IpSecWrap->Packet = Packet; + Packet = NetbufFromExt ( + FragmentTable, + FragmentCount, + IP6_MAX_HEADLEN, + 0, + Ip6IpSecFree, + IpSecWrap + ); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + if (Direction == EfiIPsecInBound) { + + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace ( + Packet, + sizeof (EFI_IP6_HEADER) + ExtHdrsLen, + NET_BUF_HEAD + ); + if (PacketHead == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); + Packet->Ip.Ip6 = PacketHead; + + if (ExtHdrs != NULL) { + Buf = (UINT8 *) (PacketHead + 1); + CopyMem (Buf, ExtHdrs, ExtHdrsLen); + } + + NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE); + CopyMem ( + IP6_GET_CLIP_INFO (Packet), + IP6_GET_CLIP_INFO (IpSecWrap->Packet), + sizeof (IP6_CLIP_INFO) + ); + } + + *Netbuf = Packet; + } + +ON_EXIT: + return Status; +} + +/** + The IP6 input routine. It is called by the IP6_INTERFACE when an + IP6 fragment is received from MNP. + + @param[in] Packet The IP6 packet received. + @param[in] IoStatus The return status of receive request. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[in] Context The IP6 service instance that owns the MNP. + +**/ +VOID +Ip6AcceptFrame ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + IP6_CLIP_INFO *Info; + EFI_IP6_HEADER *Head; + UINT16 PayloadLen; + UINT8 *Payload; + UINT16 TotalLen; + UINT8 *LastHead; + UINT32 FormerHeadOffset; + UINT32 UnFragmentLen; + UINT32 ExtHdrsLen; + UINT32 HeadLen; + BOOLEAN Fragmented; + IP6_FRAGMENT_HEADER *FragmentHead; + UINT16 FragmentOffset; + EFI_STATUS Status; + EFI_IPv6_ADDRESS Loopback; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Payload = NULL; + + // + // Check input parameters + // + if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) { + goto Drop; + } + + // + // Check whether the input packet is a valid packet + // + if (Packet->TotalSize < IP6_MIN_HEADLEN) { + goto Restart; + } + + // + // Get header information of the packet. + // + Head = (EFI_IP6_HEADER *) NetbufGetByte (Packet, 0, NULL); + if (Head == NULL) { + goto Restart; + } + + // + // Multicast addresses must not be used as source addresses in IPv6 packets. + // + if ((Head->Version != 6) || (IP6_IS_MULTICAST (&Head->SourceAddress))) { + goto Restart; + } + + // + // A packet with a destination address of loopback ::1/128 or unspecified must be dropped. + // + ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS)); + Loopback.Addr[15] = 0x1; + if ((CompareMem (&Loopback, &Head->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) || + (NetIp6IsUnspecifiedAddr (&Head->DestinationAddress))) { + goto Restart; + } + + // + // Convert the IP header to host byte order. + // + Packet->Ip.Ip6 = Ip6NtohHead (Head); + + // + // Get the per packet info. + // + Info = IP6_GET_CLIP_INFO (Packet); + Info->LinkFlag = Flag; + Info->CastType = 0; + + if (IpSb->MnpConfigData.EnablePromiscuousReceive) { + Info->CastType = Ip6Promiscuous; + } + + if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { + Info->CastType = Ip6Unicast; + } else if (IP6_IS_MULTICAST (&Head->DestinationAddress)) { + if (Ip6FindMldEntry (IpSb, &Head->DestinationAddress) != NULL) { + Info->CastType = Ip6Multicast; + } + } + + // + // Drop the packet that is not delivered to us. + // + if (Info->CastType == 0) { + goto Restart; + } + + + PayloadLen = Head->PayloadLength; + + Info->Start = 0; + Info->Length = PayloadLen; + Info->End = Info->Start + Info->Length; + Info->HeadLen = (UINT16) sizeof (EFI_IP6_HEADER); + Info->Status = EFI_SUCCESS; + Info->LastFrag = FALSE; + + TotalLen = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER)); + + // + // Mnp may deliver frame trailer sequence up, trim it off. + // + if (TotalLen < Packet->TotalSize) { + NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE); + } + + if (TotalLen != Packet->TotalSize) { + goto Restart; + } + + // + // Check the extension headers, if exist validate them + // + if (PayloadLen != 0) { + Payload = AllocatePool ((UINTN) PayloadLen); + if (Payload == NULL) { + goto Restart; + } + + NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload); + } + + LastHead = NULL; + if (!Ip6IsExtsValid ( + IpSb, + Packet, + &Head->NextHeader, + Payload, + (UINT32) PayloadLen, + TRUE, + &FormerHeadOffset, + &LastHead, + &ExtHdrsLen, + &UnFragmentLen, + &Fragmented + )) { + goto Restart; + } + + HeadLen = sizeof (EFI_IP6_HEADER) + UnFragmentLen; + + if (Fragmented) { + // + // Get the fragment offset from the Fragment header + // + FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (Packet, HeadLen, NULL); + if (FragmentHead == NULL) { + goto Restart; + } + + FragmentOffset = NTOHS (FragmentHead->FragmentOffset); + + if ((FragmentOffset & 0x1) == 0) { + Info->LastFrag = TRUE; + } + + FragmentOffset &= (~0x1); + + // + // This is the first fragment of the packet + // + if (FragmentOffset == 0) { + Info->NextHeader = FragmentHead->NextHeader; + } + + Info->HeadLen = (UINT16) HeadLen; + HeadLen += sizeof (IP6_FRAGMENT_HEADER); + Info->Start = FragmentOffset; + Info->Length = TotalLen - (UINT16) HeadLen; + Info->End = Info->Start + Info->Length; + Info->Id = FragmentHead->Identification; + Info->FormerNextHeader = FormerHeadOffset; + + // + // Fragments should in the unit of 8 octets long except the last one. + // + if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) { + goto Restart; + } + + // + // Reassemble the packet. + // + Packet = Ip6Reassemble (&IpSb->Assemble, Packet); + if (Packet == NULL) { + goto Restart; + } + + // + // Re-check the assembled packet to get the right values. + // + Head = Packet->Ip.Ip6; + PayloadLen = Head->PayloadLength; + if (PayloadLen != 0) { + if (Payload != NULL) { + FreePool (Payload); + } + + Payload = AllocatePool ((UINTN) PayloadLen); + if (Payload == NULL) { + goto Restart; + } + + NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload); + } + + if (!Ip6IsExtsValid ( + IpSb, + Packet, + &Head->NextHeader, + Payload, + (UINT32) PayloadLen, + TRUE, + NULL, + &LastHead, + &ExtHdrsLen, + &UnFragmentLen, + &Fragmented + )) { + goto Restart; + } + } + + // + // Trim the head off, after this point, the packet is headless. + // and Packet->TotalLen == Info->Length. + // + NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE); + + // + // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer, + // and no need consider any other ahead ext headers. + // + Status = Ip6IpSecProcessPacket ( + IpSb, + Head, + LastHead, // need get the lasthead value for input + &Packet, + NULL, + 0, + EfiIPsecInBound, + NULL + ); + + if (EFI_ERROR(Status)) { + goto Restart; + } + + // + // TODO: may check the last head again, the same as the output routine + // + + // + // Packet may have been changed. The ownership of the packet + // is transfered to the packet process logic. + // + Head = Packet->Ip.Ip6; + IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS; + + switch (*LastHead) { + case IP6_ICMP: + Ip6IcmpHandle (IpSb, Head, Packet); + break; + default: + Ip6Demultiplex (IpSb, Head, Packet); + } + + Packet = NULL; + + // + // Dispatch the DPCs queued by the NotifyFunction of the rx token's events + // which are signaled with received data. + // + DispatchDpc (); + +Restart: + if (Payload != NULL) { + FreePool (Payload); + } + + Ip6ReceiveFrame (Ip6AcceptFrame, IpSb); + +Drop: + if (Packet != NULL) { + NetbufFree (Packet); + } + + return ; +} + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP6 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip6CreateAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ) +{ + UINT32 Index; + + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + InitializeListHead (&Table->Bucket[Index]); + } +} + +/** + Clean up the assemble table by removing all of the fragments + and assemble entries. + + @param[in, out] Table The assemble table to clean up. + +**/ +VOID +Ip6CleanAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ASSEMBLE_ENTRY *Assemble; + UINT32 Index; + + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link); + + RemoveEntryList (Entry); + Ip6FreeAssembleEntry (Assemble); + } + } +} + + +/** + The signal handle of IP6's recycle event. It is called back + when the upper layer releases the packet. + + @param[in] Event The IP6's recycle event. + @param[in] Context The context of the handle, which is a IP6_RXDATA_WRAP. + +**/ +VOID +EFIAPI +Ip6OnRecyclePacket ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_RXDATA_WRAP *Wrap; + + Wrap = (IP6_RXDATA_WRAP *) Context; + + EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock); + RemoveEntryList (&Wrap->Link); + EfiReleaseLock (&Wrap->IpInstance->RecycleLock); + + ASSERT (!NET_BUF_SHARED (Wrap->Packet)); + NetbufFree (Wrap->Packet); + + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + FreePool (Wrap); +} + +/** + Wrap the received packet to a IP6_RXDATA_WRAP, which will be + delivered to the upper layer. Each IP6 child that accepts the + packet will get a not-shared copy of the packet which is wrapped + in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed + to the upper layer. The upper layer will signal the recycle event in + it when it is done with the packet. + + @param[in] IpInstance The IP6 child to receive the packet. + @param[in] Packet The packet to deliver up. + + @return NULL if it failed to wrap the packet; otherwise, the wrapper. + +**/ +IP6_RXDATA_WRAP * +Ip6WrapRxData ( + IN IP6_PROTOCOL *IpInstance, + IN NET_BUF *Packet + ) +{ + IP6_RXDATA_WRAP *Wrap; + EFI_IP6_RECEIVE_DATA *RxData; + EFI_STATUS Status; + + Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum)); + + if (Wrap == NULL) { + return NULL; + } + + InitializeListHead (&Wrap->Link); + + Wrap->IpInstance = IpInstance; + Wrap->Packet = Packet; + RxData = &Wrap->RxData; + + ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnRecyclePacket, + Wrap, + &RxData->RecycleSignal + ); + + if (EFI_ERROR (Status)) { + FreePool (Wrap); + return NULL; + } + + ASSERT (Packet->Ip.Ip6 != NULL); + + // + // The application expects a network byte order header. + // + RxData->HeaderLength = sizeof (EFI_IP6_HEADER); + RxData->Header = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6); + RxData->DataLength = Packet->TotalSize; + + // + // Build the fragment table to be delivered up. + // + RxData->FragmentCount = Packet->BlockOpNum; + NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount); + + return Wrap; +} + +/** + Check whether this IP child accepts the packet. + + @param[in] IpInstance The IP child to check. + @param[in] Head The IP header of the packet. + @param[in] Packet The data of the packet. + + @retval TRUE The child wants to receive the packet. + @retval FALSE The child does not want to receive the packet. + +**/ +BOOLEAN +Ip6InstanceFrameAcceptable ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + EFI_IP6_CONFIG_DATA *Config; + IP6_CLIP_INFO *Info; + UINT8 *Proto; + UINT32 Index; + UINT8 *ExtHdrs; + UINT16 ErrMsgPayloadLen; + UINT8 *ErrMsgPayload; + + Config = &IpInstance->ConfigData; + Proto = NULL; + + // + // Dirty trick for the Tiano UEFI network stack implmentation. If + // ReceiveTimeout == -1, the receive of the packet for this instance + // is disabled. The UEFI spec don't have such captibility. We add + // this to improve the performance because IP will make a copy of + // the received packet for each accepting instance. Some IP instances + // used by UDP/TCP only send packets, they don't wants to receive. + // + if (Config->ReceiveTimeout == (UINT32)(-1)) { + return FALSE; + } + + if (Config->AcceptPromiscuous) { + return TRUE; + } + + // + // Check whether the protocol is acceptable. + // + ExtHdrs = NetbufGetByte (Packet, 0, NULL); + + if (!Ip6IsExtsValid ( + IpInstance->Service, + Packet, + &Head->NextHeader, + ExtHdrs, + (UINT32) Head->PayloadLength, + TRUE, + NULL, + &Proto, + NULL, + NULL, + NULL + )) { + return FALSE; + } + + // + // The upper layer driver may want to receive the ICMPv6 error packet + // invoked by its packet, like UDP. + // + if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) { + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) { + if (!Config->AcceptIcmpErrors) { + return FALSE; + } + + // + // Get the protocol of the invoking packet of ICMPv6 error packet. + // + ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength); + ErrMsgPayload = NetbufGetByte (Packet, sizeof (Icmp), NULL); + + if (!Ip6IsExtsValid ( + NULL, + NULL, + &Icmp.IpHead.NextHeader, + ErrMsgPayload, + ErrMsgPayloadLen, + TRUE, + NULL, + &Proto, + NULL, + NULL, + NULL + )) { + return FALSE; + } + } + } + + // + // Match the protocol + // + if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) { + return FALSE; + } + + // + // Check for broadcast, the caller has computed the packet's + // cast type for this child's interface. + // + Info = IP6_GET_CLIP_INFO (Packet); + + // + // If it is a multicast packet, check whether we are in the group. + // + if (Info->CastType == Ip6Multicast) { + // + // Receive the multicast if the instance wants to receive all packets. + // + if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) { + return TRUE; + } + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) { + break; + } + } + + return (BOOLEAN)(Index < IpInstance->GroupCount); + } + + return TRUE; +} + +/** + Enqueue a shared copy of the packet to the IP6 child if the + packet is acceptable to it. Here the data of the packet is + shared, but the net buffer isn't. + + @param IpInstance The IP6 child to enqueue the packet to. + @param Head The IP header of the received packet. + @param Packet The data of the received packet. + + @retval EFI_NOT_STARTED The IP child hasn't been configured. + @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources + @retval EFI_SUCCESS A shared copy the packet is enqueued to the child. + +**/ +EFI_STATUS +Ip6InstanceEnquePacket ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_CLIP_INFO *Info; + NET_BUF *Clone; + + // + // Check whether the packet is acceptable to this instance. + // + if (IpInstance->State != IP6_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) { + return EFI_INVALID_PARAMETER; + } + + // + // Enque a shared copy of the packet. + // + Clone = NetbufClone (Packet); + + if (Clone == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the receive time out for the assembled packet. If it expires, + // packet will be removed from the queue. + // + Info = IP6_GET_CLIP_INFO (Clone); + Info->Life = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout); + + InsertTailList (&IpInstance->Received, &Clone->List); + return EFI_SUCCESS; +} + +/** + Deliver the received packets to the upper layer if there are both received + requests and enqueued packets. If the enqueued packet is shared, it will + duplicate it to a non-shared packet, release the shared packet, then + deliver the non-shared packet up. + + @param[in] IpInstance The IP child to deliver the packet up. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the + packets. + @retval EFI_SUCCESS All the enqueued packets that can be delivered + are delivered up. + +**/ +EFI_STATUS +Ip6InstanceDeliverPacket ( + IN IP6_PROTOCOL *IpInstance + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + IP6_RXDATA_WRAP *Wrap; + NET_BUF *Packet; + NET_BUF *Dup; + UINT8 *Head; + + // + // Deliver a packet if there are both a packet and a receive token. + // + while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) { + + Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List); + + if (!NET_BUF_SHARED (Packet)) { + // + // If this is the only instance that wants the packet, wrap it up. + // + Wrap = Ip6WrapRxData (IpInstance, Packet); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Packet->List); + + } else { + // + // Create a duplicated packet if this packet is shared + // + Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER)); + + if (Dup == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the IP head over. The packet to deliver up is + // headless. Trim the head off after copy. The IP head + // may be not continuous before the data. + // + Head = NetbufAllocSpace (Dup, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD); + ASSERT (Head != NULL); + Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head; + + CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER)); + NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE); + + Wrap = Ip6WrapRxData (IpInstance, Dup); + + if (Wrap == NULL) { + NetbufFree (Dup); + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Packet->List); + NetbufFree (Packet); + + Packet = Dup; + } + + // + // Insert it into the delivered packet, then get a user's + // receive token, pass the wrapped packet up. + // + EfiAcquireLockOrFail (&IpInstance->RecycleLock); + InsertHeadList (&IpInstance->Delivered, &Wrap->Link); + EfiReleaseLock (&IpInstance->RecycleLock); + + Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL); + Token->Status = IP6_GET_CLIP_INFO (Packet)->Status; + Token->Packet.RxData = &Wrap->RxData; + + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + +/** + Enqueue a received packet to all the IP children that share + the same interface. + + @param[in] IpSb The IP6 service instance that receive the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + @param[in] IpIf The interface to enqueue the packet to. + + @return The number of the IP6 children that accepts the packet. + +**/ +INTN +Ip6InterfaceEnquePacket ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet, + IN IP6_INTERFACE *IpIf + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_CLIP_INFO *Info; + LIST_ENTRY *Entry; + INTN Enqueued; + INTN LocalType; + INTN SavedType; + + // + // First, check that the packet is acceptable to this interface + // and find the local cast type for the interface. + // + LocalType = 0; + Info = IP6_GET_CLIP_INFO (Packet); + + if (IpIf->PromiscRecv) { + LocalType = Ip6Promiscuous; + } else { + LocalType = Info->CastType; + } + + // + // Iterate through the ip instances on the interface, enqueue + // the packet if filter passed. Save the original cast type, + // and pass the local cast type to the IP children on the + // interface. The global cast type will be restored later. + // + SavedType = Info->CastType; + Info->CastType = (UINT32) LocalType; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink); + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + + if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) { + Enqueued++; + } + } + + Info->CastType = (UINT32) SavedType; + return Enqueued; +} + +/** + Deliver the packet for each IP6 child on the interface. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] IpIf The IP6 interface to deliver the packet. + +**/ +VOID +Ip6InterfaceDeliverPacket ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *IpIf + ) +{ + IP6_PROTOCOL *IpInstance; + LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink); + Ip6InstanceDeliverPacket (IpInstance); + } +} + +/** + De-multiplex the packet. the packet delivery is processed in two + passes. The first pass will enqueue a shared copy of the packet + to each IP6 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP6 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet, because each IP child needs + its own copy of the packet to make changes. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + + @retval EFI_NOT_FOUND No IP child accepts the packet. + @retval EFI_SUCCESS The packet is enqueued or delivered to some IP + children. + +**/ +EFI_STATUS +Ip6Demultiplex ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + + LIST_ENTRY *Entry; + IP6_INTERFACE *IpIf; + INTN Enqueued; + + // + // Two pass delivery: first, enque a shared copy of the packet + // to each instance that accept the packet. + // + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + if (IpIf->Configured) { + Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf); + } + } + + // + // Second: deliver a duplicate of the packet to each instance. + // Release the local reference first, so that the last instance + // getting the packet will not copy the data. + // + NetbufFree (Packet); + Packet = NULL; + + if (Enqueued == 0) { + return EFI_NOT_FOUND; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + if (IpIf->Configured) { + Ip6InterfaceDeliverPacket (IpSb, IpIf); + } + } + + return EFI_SUCCESS; +} + +/** + Decrease the life of the transmitted packets. If it is + decreased to zero, cancel the packet. This function is + called by Ip6packetTimerTicking that provides timeout for both the + received-but-not-delivered and transmitted-but-not-recycle + packets. + + @param[in] Map The IP6 child's transmit map. + @param[in] Item Current transmitted packet. + @param[in] Context Not used. + + @retval EFI_SUCCESS Always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +Ip6SentPacketTicking ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + + Wrap = (IP6_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + if ((Wrap->Life > 0) && (--Wrap->Life == 0)) { + Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + } + + return EFI_SUCCESS; +} + +/** + Timeout the fragments, and the enqueued, and transmitted packets. + + @param[in] IpSb The IP6 service instance to timeout. + +**/ +VOID +Ip6PacketTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + LIST_ENTRY *InstanceEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_PROTOCOL *IpInstance; + IP6_ASSEMBLE_ENTRY *Assemble; + NET_BUF *Packet; + IP6_CLIP_INFO *Info; + UINT32 Index; + + // + // First, time out the fragments. The packet's life is counting down + // once the first-arriving fragment of that packet was received. + // + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link); + + if ((Assemble->Life > 0) && (--Assemble->Life == 0)) { + // + // If the first fragment (the one with a Fragment Offset of zero) + // has been received, an ICMP Time Exceeded - Fragment Reassembly + // Time Exceeded message should be sent to the source of that fragment. + // + if ((Assemble->Packet != NULL) && + !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Assemble->Packet, + NULL, + &Assemble->Head->SourceAddress, + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_REASSEMBLE, + NULL + ); + } + + // + // If reassembly of a packet is not completed within 60 seconds of + // the reception of the first-arriving fragment of that packet, the + // reassembly must be abandoned and all the fragments that have been + // received for that packet must be discarded. + // + RemoveEntryList (Entry); + Ip6FreeAssembleEntry (Assemble); + } + } + } + + NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link); + + // + // Second, time out the assembled packets enqueued on each IP child. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) { + Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Info = IP6_GET_CLIP_INFO (Packet); + + if ((Info->Life > 0) && (--Info->Life == 0)) { + RemoveEntryList (Entry); + NetbufFree (Packet); + } + } + + // + // Third: time out the transmitted packets. + // + NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL); + } +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.h b/NetworkPkg/Ip6Dxe/Ip6Input.h new file mode 100644 index 0000000000..8594896521 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Input.h @@ -0,0 +1,235 @@ +/** @file + IP6 internal functions and definitions to process the incoming packets. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_INPUT_H__ +#define __EFI_IP6_INPUT_H__ + +#define IP6_MIN_HEADLEN 40 +#define IP6_MAX_HEADLEN 120 +/// +/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54 +/// +#define IP6_MAX_IPSEC_HEADLEN 54 + + +#define IP6_ASSEMLE_HASH_SIZE 127 +/// +/// Lift time in seconds. +/// +#define IP6_FRAGMENT_LIFE 60 +#define IP6_MAX_PACKET_SIZE 65535 + + +#define IP6_GET_CLIP_INFO(Packet) ((IP6_CLIP_INFO *) ((Packet)->ProtoData)) + +#define IP6_ASSEMBLE_HASH(Dst, Src, Id) \ + ((*((UINT32 *) (Dst)) + *((UINT32 *) (Src)) + (Id)) % IP6_ASSEMLE_HASH_SIZE) + +#define IP6_RXDATA_WRAP_SIZE(NumFrag) \ + (sizeof (IP6_RXDATA_WRAP) + sizeof (EFI_IP6_FRAGMENT_DATA) * ((NumFrag) - 1)) + +// +// Per packet information for input process. LinkFlag specifies whether +// the packet is received as Link layer unicast, multicast or broadcast. +// The CastType is the IP layer cast type, such as IP multicast or unicast. +// Start, End and Length are staffs used to assemble the packets. Start +// is the sequence number of the first byte of data in the packet. Length +// is the number of bytes of data. End = Start + Length, that is, the +// sequence number of last byte + 1. Each assembled packet has a count down +// life. If it isn't consumed before Life reaches zero, the packet is released. +// +typedef struct { + UINT32 LinkFlag; + INT32 CastType; + INT32 Start; + INT32 End; + INT32 Length; + UINT32 Life; + EFI_STATUS Status; + UINT32 Id; + UINT16 HeadLen; + UINT8 NextHeader; + UINT8 LastFrag; + UINT32 FormerNextHeader; +} IP6_CLIP_INFO; + +// +// Structure used to assemble IP packets. +// +typedef struct { + LIST_ENTRY Link; + LIST_ENTRY Fragments; // List of all the fragments of this packet + + // + // Identity of one IP6 packet. Each fragment of a packet has + // the same (Dst, Src, Id). + // + EFI_IPv6_ADDRESS Dst; + EFI_IPv6_ADDRESS Src; + UINT32 Id; + + UINT32 TotalLen; + UINT32 CurLen; + UINT32 Life; // Count down life for the packet. + + EFI_IP6_HEADER *Head; // IP head of the first fragment + IP6_CLIP_INFO *Info; // Per packet information of the first fragment + NET_BUF *Packet; // The first fragment of the packet +} IP6_ASSEMBLE_ENTRY; + +// +// Each Ip service instance has an assemble table to reassemble +// the packets before delivery to its children. It is organized +// as hash table. +// +typedef struct { + LIST_ENTRY Bucket[IP6_ASSEMLE_HASH_SIZE]; +} IP6_ASSEMBLE_TABLE; + +/** + The IP6 input routine. It is called by the IP6_INTERFACE when an + IP6 fragment is received from MNP. + + @param[in] Packet The IP6 packet received. + @param[in] IoStatus The return status of receive request. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[in] Context The IP6 service instance that own the MNP. + +**/ +VOID +Ip6AcceptFrame ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ); + +/** + Deliver the received packets to upper layer if there are both received + requests and enqueued packets. If the enqueued packet is shared, it will + duplicate it to a non-shared packet, release the shared packet, then + deliver the non-shared packet up. + + @param[in] IpInstance The IP child to deliver the packet up. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the + packets. + @retval EFI_SUCCESS All the enqueued packets that can be delivered + are delivered up. + +**/ +EFI_STATUS +Ip6InstanceDeliverPacket ( + IN IP6_PROTOCOL *IpInstance + ); + +/** + The work function to locate IPsec protocol to process the inbound or + outbound IP packets. The process routine handls the packet with the following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP6 service instance. + @param[in] Head The caller supplied IP6 header. + @param[in, out] LastHead The next header field of last IP header. + @param[in, out] Netbuf The IP6 packet to be processed by IPsec. + @param[in] ExtHdrs The caller supplied options. + @param[in] ExtHdrsLen The length of the option. + @param[in] Direction The directionality in an SPD entry, + EfiIPsecInBound or EfiIPsecOutBound. + @param[in] Context The token's wrap. + + @retval EFI_SUCCESS The IPsec protocol is not available or disabled. + @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the + number of input data blocks when building a fragment table. + +**/ +EFI_STATUS +Ip6IpSecProcessPacket ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN OUT UINT8 *LastHead, + IN OUT NET_BUF **Netbuf, + IN VOID *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN VOID *Context + ); + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP6 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip6CreateAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ); + +/** + Clean up the assemble table: remove all the fragments + and assemble entries. + + @param[in, out] Table The assemble table to clean up. + +**/ +VOID +Ip6CleanAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ); + +/** + Demultiple the packet. the packet delivery is processed in two + passes. The first pass will enque a shared copy of the packet + to each IP6 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP6 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet bacause each IP child need + its own copy of the packet to make changes. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + + @retval EFI_NOT_FOUND No IP child accepts the packet. + @retval EFI_SUCCESS The packet is enqueued or delivered to some IP + children. + +**/ +EFI_STATUS +Ip6Demultiplex ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Timeout the fragmented, enqueued, and transmitted packets. + + @param[in] IpSb The IP6 service instance to timeout. + +**/ +VOID +Ip6PacketTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.c b/NetworkPkg/Ip6Dxe/Ip6Mld.c new file mode 100644 index 0000000000..4a418fade5 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Mld.c @@ -0,0 +1,908 @@ +/** @file + Multicast Listener Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +/** + Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data. + + @param[in, out] IpSb Points to IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be recorded. + @param[in] DelayTimer The maximum allowed delay before sending a responding + report, in units of milliseconds. + @return The created IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6CreateMldEntry ( + IN OUT IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr, + IN UINT32 DelayTimer + ) +{ + IP6_MLD_GROUP *Entry; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + Entry = AllocatePool (sizeof (IP6_MLD_GROUP)); + if (Entry != NULL) { + Entry->RefCnt = 1; + Entry->DelayTimer = DelayTimer; + Entry->SendByUs = FALSE; + IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr); + InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link); + } + + return Entry; +} + +/** + Search a IP6_MLD_GROUP list entry node from a list array. + + @param[in] IpSb Points to IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be searched. + + @return The found IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6FindMldEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + LIST_ENTRY *Entry; + IP6_MLD_GROUP *Group; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) { + return Group; + } + } + + return NULL; +} + +/** + Count the number of IP6 multicast groups that are mapped to the + same MAC address. Several IP6 multicast address may be mapped to + the same MAC address. + + @param[in] MldCtrl The MLD control block to search in. + @param[in] Mac The MAC address to search. + + @return The number of the IP6 multicast group that mapped to the same + multicast group Mac. + +**/ +INTN +Ip6FindMac ( + IN IP6_MLD_SERVICE_DATA *MldCtrl, + IN EFI_MAC_ADDRESS *Mac + ) +{ + LIST_ENTRY *Entry; + IP6_MLD_GROUP *Group; + INTN Count; + + Count = 0; + + NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + + if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) { + Count++; + } + } + + return Count; +} + +/** + Generate MLD report message and send it out to MulticastAddr. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface The IP interface to send the packet. + If NULL, a system interface will be selected. + @param[in] MulticastAddr The specific IPv6 multicast address to which + the message sender is listening. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD report message was successfully sent out. + +**/ +EFI_STATUS +Ip6SendMldReport ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + IP6_MLD_HEAD *MldHead; + NET_BUF *Packet; + EFI_IP6_HEADER Head; + UINT16 PayloadLen; + UINTN OptionLen; + UINT8 *Options; + EFI_STATUS Status; + UINT16 HeadChecksum; + UINT16 PseudoChecksum; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + // + // Generate the packet to be sent + // IPv6 basic header + Hop by Hop option + MLD message + // + + OptionLen = 0; + Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // RFC3590: Use link-local address as source address if it is available, + // otherwise use the unspecified address. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_HOP_BY_HOP; + Head.HopLimit = 1; + IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr); + + // + // If Link-Local address is not ready, we use unspecified address. + // + IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header + // + Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); + ASSERT (Options != NULL); + Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); + if (EFI_ERROR (Status)) { + NetbufFree (Packet); + Packet = NULL; + return Status; + } + + // + // Fill in MLD message - Report + // + MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); + ASSERT (MldHead != NULL); + ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); + MldHead->Head.Type = ICMP_V6_LISTENER_REPORT; + MldHead->Head.Code = 0; + IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); + + HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head.SourceAddress, + &Head.DestinationAddress, + IP6_ICMP, + sizeof (IP6_MLD_HEAD) + ); + + MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); + + // + // Transmit the packet + // + return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate MLD Done message and send it out to MulticastAddr. + + @param[in] IpSb The IP service to send the packet. + @param[in] MulticastAddr The specific IPv6 multicast address to which + the message sender is ceasing to listen. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD report message was successfully sent out. + +**/ +EFI_STATUS +Ip6SendMldDone ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + IP6_MLD_HEAD *MldHead; + NET_BUF *Packet; + EFI_IP6_HEADER Head; + UINT16 PayloadLen; + UINTN OptionLen; + UINT8 *Options; + EFI_STATUS Status; + EFI_IPv6_ADDRESS Destination; + UINT16 HeadChecksum; + UINT16 PseudoChecksum; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + // + // Generate the packet to be sent + // IPv6 basic header + Hop by Hop option + MLD message + // + + OptionLen = 0; + Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_HOP_BY_HOP; + Head.HopLimit = 1; + + // + // If Link-Local address is not ready, we use unspecified address. + // + IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); + + Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination); + IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header + // + Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); + ASSERT (Options != NULL); + Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); + if (EFI_ERROR (Status)) { + NetbufFree (Packet); + Packet = NULL; + return Status; + } + + // + // Fill in MLD message - Done + // + MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); + ASSERT (MldHead != NULL); + ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); + MldHead->Head.Type = ICMP_V6_LISTENER_DONE; + MldHead->Head.Code = 0; + IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); + + HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head.SourceAddress, + &Head.DestinationAddress, + IP6_ICMP, + sizeof (IP6_MLD_HEAD) + ); + + MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Init the MLD data of the IP6 service instance. Configure + MNP to receive ALL SYSTEM multicast. + + @param[in] IpSb The IP6 service whose MLD is to be initialized. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the + operation. + @retval EFI_SUCCESS The MLD module successfully initialized. + +**/ +EFI_STATUS +Ip6InitMld ( + IN IP6_SERVICE *IpSb + ) +{ + EFI_IPv6_ADDRESS AllNodes; + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + // + // Join the link-scope all-nodes multicast address (FF02::1). + // This address is started in Idle Listener state and never transitions to + // another state, and never sends a Report or Done for that address. + // + + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + + Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME); + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Configure MNP to receive all-nodes multicast + // + Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ERROR; + } + + return EFI_SUCCESS; + +ERROR: + RemoveEntryList (&Group->Link); + FreePool (Group); + return Status; +} + +/** + Add a group address to the array of group addresses. + The caller should make sure that no duplicated address + existed in the array. + + @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. + @param[in] Group The IP6 multicast address to add. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete + the operation. + @retval EFI_SUCESS The address is added to the group address array. + +**/ +EFI_STATUS +Ip6CombineGroups ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IPv6_ADDRESS *Group + ) +{ + EFI_IPv6_ADDRESS *GroupList; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (Group != NULL && IP6_IS_MULTICAST (Group)); + + IpInstance->GroupCount++; + + GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS)); + if (GroupList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (IpInstance->GroupCount > 1) { + ASSERT (IpInstance->GroupList != NULL); + + CopyMem ( + GroupList, + IpInstance->GroupList, + (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS) + ); + + FreePool (IpInstance->GroupList); + } + + IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group); + + IpInstance->GroupList = GroupList; + + return EFI_SUCCESS; +} + +/** + Remove a group address from the array of group addresses. + Although the function doesn't assume the byte order of Group, + the network byte order is used by the caller. + + @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. + @param[in] Group The IP6 multicast address to remove. + + @retval EFI_NOT_FOUND Cannot find the to be removed group address. + @retval EFI_SUCCESS The group address was successfully removed. + +**/ +EFI_STATUS +Ip6RemoveGroup ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IPv6_ADDRESS *Group + ) +{ + UINT32 Index; + UINT32 Count; + + Count = IpInstance->GroupCount; + + for (Index = 0; Index < Count; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) { + break; + } + } + + if (Index == Count) { + return EFI_NOT_FOUND; + } + + while (Index < Count - 1) { + IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1); + Index++; + } + + ASSERT (IpInstance->GroupCount > 0); + IpInstance->GroupCount--; + + return EFI_SUCCESS; +} + +/** + Join the multicast group on behalf of this IP6 service binding instance. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Interface Points to an IP6_INTERFACE structure. + @param[in] Address The group address to join. + + @retval EFI_SUCCESS Successfully join the multicast group. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip6JoinGroup ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Address + ) +{ + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Group = Ip6FindMldEntry (IpSb, Address); + if (Group != NULL) { + Group->RefCnt++; + return EFI_SUCCESS; + } + + // + // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s) + // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss. + // + Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL); + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Group->SendByUs = TRUE; + + Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ERROR; + } + + // + // Send unsolicited report when a node starts listening to a multicast address + // + Status = Ip6SendMldReport (IpSb, Interface, Address); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + return EFI_SUCCESS; + +ERROR: + RemoveEntryList (&Group->Link); + FreePool (Group); + return Status; +} + +/** + Leave the IP6 multicast group. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP6 service instance isn't in the group. + @retval EFI_SUCCESS Successfully leave the multicast group.. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip6LeaveGroup ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ) +{ + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Group = Ip6FindMldEntry (IpSb, Address); + if (Group == NULL) { + return EFI_NOT_FOUND; + } + + // + // If more than one instance is in the group, decrease + // the RefCnt then return. + // + if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) { + return EFI_SUCCESS; + } + + // + // If multiple IP6 group addresses are mapped to the same + // multicast MAC address, don't configure the MNP to leave + // the MAC. + // + if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) { + Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + return Status; + } + } + + // + // Send a leave report if we are the last node to report + // + if (Group->SendByUs) { + Status = Ip6SendMldDone (IpSb, Address); + if (EFI_ERROR (Status)) { + return Status; + } + } + + RemoveEntryList (&Group->Link); + FreePool (Group); + + return EFI_SUCCESS; +} + +/** + Worker function for EfiIp6Groups(). The caller + should make sure that the parameters are valid. + + @param[in] IpInstance The IP6 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it. + @param[in] GroupAddress The target group address. If NULL, leave all + the group addresses. + + @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it + @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient resources. + @retval EFI_DEVICE_ERROR Failed to set the group configuraton. + @retval EFI_SUCCESS Successfully updated the group setting. + @retval EFI_NOT_FOUND Try to leave the group which it isn't a member. + +**/ +EFI_STATUS +Ip6Groups ( + IN IP6_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + UINT32 Index; + EFI_IPv6_ADDRESS *Group; + + IpSb = IpInstance->Service; + + if (JoinFlag) { + ASSERT (GroupAddress != NULL); + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) { + return EFI_ALREADY_STARTED; + } + } + + Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress); + if (!EFI_ERROR (Status)) { + return Ip6CombineGroups (IpInstance, GroupAddress); + } + + return Status; + } + + // + // Leave the group. Leave all the groups if GroupAddress is NULL. + // + for (Index = IpInstance->GroupCount; Index > 0; Index--) { + Group = IpInstance->GroupList + (Index - 1); + + if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) { + Status = Ip6LeaveGroup (IpInstance->Service, Group); + if (EFI_ERROR (Status)) { + return Status; + } + + Ip6RemoveGroup (IpInstance, Group); + + if (IpInstance->GroupCount == 0) { + ASSERT (Index == 1); + FreePool (IpInstance->GroupList); + IpInstance->GroupList = NULL; + } + + if (GroupAddress != NULL) { + return EFI_SUCCESS; + } + } + } + + return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS); +} + +/** + Set a random value of the delay timer for the multicast address from the range + [0, Maximum Response Delay]. If a timer for any address is already + running, it is reset to the new random value only if the requested + Maximum Response Delay is less than the remaining value of the + running timer. If the Query packet specifies a Maximum Response + Delay of zero, each timer is effectively set to zero, and the action + specified below for timer expiration is performed immediately. + + @param[in] IpSb The IP6 service binding instance. + @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds. + @param[in] MulticastAddr The multicast address. + @param[in, out] Group Points to a IP6_MLD_GROUP list entry node. + + @retval EFI_SUCCESS The delay timer is successfully updated or + timer expiration is performed immediately. + @retval Others Failed to send out MLD report message. + +**/ +EFI_STATUS +Ip6UpdateDelayTimer ( + IN IP6_SERVICE *IpSb, + IN UINT16 MaxRespDelay, + IN EFI_IPv6_ADDRESS *MulticastAddr, + IN OUT IP6_MLD_GROUP *Group + ) +{ + UINT32 Delay; + + // + // If the Query packet specifies a Maximum Response Delay of zero, perform timer + // expiration immediately. + // + if (MaxRespDelay == 0) { + Group->DelayTimer = 0; + return Ip6SendMldReport (IpSb, NULL, MulticastAddr); + } + + Delay = (UINT32) (MaxRespDelay / 1000); + + // + // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay] + // If a timer is already running, resets it if the request Maximum Response Delay + // is less than the remaining value of the running timer. + // + if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) { + Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ()); + } + + return EFI_SUCCESS; +} + +/** + Process the Multicast Listener Query message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD query packet. + @param[in] Packet The content of the MLD query packet with IP head + removed. + + @retval EFI_SUCCESS The MLD query packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessMldQuery ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + EFI_IPv6_ADDRESS AllNodes; + IP6_MLD_GROUP *Group; + IP6_MLD_HEAD MldPacket; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + // + // Check the validity of the packet, generic query or specific query + // + if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { + goto Exit; + } + + // + // The Packet points to MLD report raw data without Hop-By-Hop option. + // + NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); + MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay); + + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) { + // + // Receives a Multicast-Address-Specific Query, check it firstly + // + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { + goto Exit; + } + // + // The node is not listening but it receives the specific query. Just return. + // + Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); + if (Group == NULL) { + Status = EFI_SUCCESS; + goto Exit; + } + + Status = Ip6UpdateDelayTimer ( + IpSb, + MldPacket.MaxRespDelay, + &MldPacket.Group, + Group + ); + goto Exit; + } + + // + // Receives a General Query, sets a delay timer for each multicast address it is listening + // + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Multicast Listener Report message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD report packet. + @param[in] Packet The content of the MLD report packet with IP head + removed. + + @retval EFI_SUCCESS The MLD report packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + +**/ +EFI_STATUS +Ip6ProcessMldReport ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_MLD_HEAD MldPacket; + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + // + // Validate the incoming message, if invalid, drop it. + // + if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { + goto Exit; + } + + // + // The Packet points to MLD report raw data without Hop-By-Hop option. + // + NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { + goto Exit; + } + + Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); + if (Group == NULL) { + goto Exit; + } + + // + // The report is sent by another node, stop its own timer relates to the multicast address and clear + // + + if (!Group->SendByUs) { + Group->DelayTimer = 0; + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + The heartbeat timer of MLD module. It sends out a solicited MLD report when + DelayTimer expires. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6MldTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + IP6_MLD_GROUP *Group; + LIST_ENTRY *Entry; + + // + // Send solicited report when timer expires + // + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) { + Ip6SendMldReport (IpSb, NULL, &Group->Address); + } + } +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.h b/NetworkPkg/Ip6Dxe/Ip6Mld.h new file mode 100644 index 0000000000..e69f5d56b5 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Mld.h @@ -0,0 +1,198 @@ +/** @file + Multicast Listener Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_MLD_H__ +#define __EFI_IP6_MLD_H__ + +#define IP6_UNSOLICITED_REPORT_INTERVAL 10 + +#pragma pack(1) +typedef struct { + IP6_ICMP_HEAD Head; + UINT16 MaxRespDelay; + UINT16 Reserved; + EFI_IPv6_ADDRESS Group; +} IP6_MLD_HEAD; +#pragma pack() + +// +// The status of multicast group. It isn't necessary to maintain +// explicit state of host state diagram. A group with finity +// DelayTime (less than 0xffffffff) is in "delaying listener" state. otherwise, it is in +// "idle listener" state. +// +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + EFI_IPv6_ADDRESS Address; + UINT32 DelayTimer; + BOOLEAN SendByUs; + EFI_MAC_ADDRESS Mac; +} IP6_MLD_GROUP; + +// +// The MLD status. Each IP6 service instance has a MLD_SERVICE_DATA +// attached. The Mldv1QuerySeen remember whether the server on this +// connected network is v1 or v2. +// +typedef struct { + INTN Mldv1QuerySeen; + LIST_ENTRY Groups; +} IP6_MLD_SERVICE_DATA; + +/** + Search a IP6_MLD_GROUP list entry node from a list array. + + @param[in] IpSb Points to an IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be searched. + + @return The found IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6FindMldEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ); + +/** + Init the MLD data of the IP6 service instance, configure + MNP to receive ALL SYSTEM multicasts. + + @param[in] IpSb The IP6 service whose MLD is to be initialized. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD module successfully initialized. + +**/ +EFI_STATUS +Ip6InitMld ( + IN IP6_SERVICE *IpSb + ); + +/** + Join the multicast group on behalf of this IP6 service binding instance. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Interface Points to an IP6_INTERFACE structure. + @param[in] Address The group address to join. + + @retval EFI_SUCCESS Successfully joined the multicast group. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip6JoinGroup ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Address + ); + +/** + Leave the IP6 multicast group. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP6 service instance isn't in the group. + @retval EFI_SUCCESS Successfully left the multicast group. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip6LeaveGroup ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ); + +/** + Worker function for EfiIp6Groups(). The caller + should verify that the parameters are valid. + + @param[in] IpInstance The IP6 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it. + @param[in] GroupAddress The target group address. If NULL, leave all + the group addresses. + + @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_DEVICE_ERROR Failed to set the group configuraton. + @retval EFI_SUCCESS Successfully updated the group setting. + @retval EFI_NOT_FOUND Tried to leave a group of whom it isn't a member. + +**/ +EFI_STATUS +Ip6Groups ( + IN IP6_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ); + +/** + Process the Multicast Listener Query message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD query packet. + @param[in] Packet The content of the MLD query packet with IP head + removed. + + @retval EFI_SUCCESS The MLD query packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessMldQuery ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Multicast Listener Report message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD report packet. + @param[in] Packet The content of the MLD report packet with IP head + removed. + + @retval EFI_SUCCESS The MLD report packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + +**/ +EFI_STATUS +Ip6ProcessMldReport ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + + +/** + The heartbeat timer of the MLD module. It sends out solicited MLD report when + DelayTimer expires. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6MldTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +#endif + diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.c b/NetworkPkg/Ip6Dxe/Ip6Nd.c new file mode 100644 index 0000000000..f2a47a8073 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Nd.c @@ -0,0 +1,3141 @@ +/** @file + Implementation of Neighbor Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +EFI_MAC_ADDRESS mZeroMacAddress; + +/** + Update the ReachableTime in IP6 service binding instance data, in milliseconds. + + @param[in, out] IpSb Points to the IP6_SERVICE. + +**/ +VOID +Ip6UpdateReachableTime ( + IN OUT IP6_SERVICE *IpSb + ) +{ + UINT32 Random; + + Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE; + Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED; + IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE; +} + +/** + Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number + of EFI_IP6_NEIGHBOR_CACHE is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] NeighborCount The number of returned neighbor cache entries. + @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE. + + @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiNeighborCache ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *NeighborCount, + OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + LIST_ENTRY *Entry; + IP6_SERVICE *IpSb; + UINT32 Count; + EFI_IP6_NEIGHBOR_CACHE *EfiNeighborCache; + EFI_IP6_NEIGHBOR_CACHE *NeighborCacheTmp; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (NeighborCount != NULL && NeighborCache != NULL); + + IpSb = IpInstance->Service; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { + Count++; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE)); + if (NeighborCacheTmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *NeighborCount = Count; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { + Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + + EfiNeighborCache = NeighborCacheTmp + Count; + + EfiNeighborCache->State = Neighbor->State; + IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor); + IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress); + + Count++; + } + + ASSERT (*NeighborCount == Count); + *NeighborCache = NeighborCacheTmp; + + return EFI_SUCCESS; +} + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of prefix entries is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] PrefixCount The number of returned prefix entries. + @param[out] PrefixTable The pointer to the array of PrefixTable. + + @retval EFI_SUCCESS The prefix table successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table. + +**/ +EFI_STATUS +Ip6BuildPrefixTable ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *PrefixCount, + OUT EFI_IP6_ADDRESS_INFO **PrefixTable + ) +{ + LIST_ENTRY *Entry; + IP6_SERVICE *IpSb; + UINT32 Count; + IP6_PREFIX_LIST_ENTRY *PrefixList; + EFI_IP6_ADDRESS_INFO *EfiPrefix; + EFI_IP6_ADDRESS_INFO *PrefixTableTmp; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (PrefixCount != NULL && PrefixTable != NULL); + + IpSb = IpInstance->Service; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + Count++; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO)); + if (PrefixTableTmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *PrefixCount = Count; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + EfiPrefix = PrefixTableTmp + Count; + IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix); + EfiPrefix->PrefixLength = PrefixList->PrefixLength; + + Count++; + } + + ASSERT (*PrefixCount == Count); + *PrefixTable = PrefixTableTmp; + + return EFI_SUCCESS; +} + +/** + Allocate and initialize a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list. + Otherwise, it is created for the autoconfiguration prefix list. + @param[in] ValidLifetime The length of time in seconds that the prefix + is valid for the purpose of on-link determination. + @param[in] PreferredLifetime The length of time in seconds that addresses + generated from the prefix via stateless address + autoconfiguration remain preferred. + @param[in] PrefixLength The prefix length of the Prefix. + @param[in] Prefix The prefix address. + + @return NULL if it failed to allocate memory for the prefix node. Otherwise, point + to the created or existing prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6CreatePrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + IP6_ROUTE_ENTRY *RtEntry; + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + IP6_PREFIX_LIST_ENTRY *TmpPrefixEntry; + + if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength >= IP6_PREFIX_NUM) { + return NULL; + } + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + OnLinkOrAuto, + PrefixLength, + Prefix + ); + if (PrefixEntry != NULL) { + PrefixEntry->RefCnt ++; + return PrefixEntry; + } + + PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY)); + if (PrefixEntry == NULL) { + return NULL; + } + + PrefixEntry->RefCnt = 1; + PrefixEntry->ValidLifetime = ValidLifetime; + PrefixEntry->PreferredLifetime = PreferredLifetime; + PrefixEntry->PrefixLength = PrefixLength; + IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix); + + ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix; + + // + // Create a direct route entry for on-link prefix and insert to route area. + // + if (OnLinkOrAuto) { + RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL); + if (RtEntry == NULL) { + FreePool (PrefixEntry); + return NULL; + } + + RtEntry->Flag = IP6_DIRECT_ROUTE; + InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link); + IpSb->RouteTable->TotalNum++; + } + + // + // Insert the prefix entry in the order that a prefix with longer prefix length + // is put ahead in the list. + // + NET_LIST_FOR_EACH (Entry, ListHead) { + TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) { + break; + } + } + + NetListInsertBefore (Entry, &PrefixEntry->Link); + + return PrefixEntry; +} + +/** + Destory a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] PrefixEntry The to be destroyed prefix list entry. + @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list. + Otherwise remove from autoconfiguration prefix list. + @param[in] ImmediateDelete If TRUE, remove the entry directly. + Otherwise, check the reference count to see whether + it should be removed. + +**/ +VOID +Ip6DestroyPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_PREFIX_LIST_ENTRY *PrefixEntry, + IN BOOLEAN OnLinkOrAuto, + IN BOOLEAN ImmediateDelete + ) +{ + LIST_ENTRY *Entry; + IP6_INTERFACE *IpIf; + EFI_STATUS Status; + + if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) { + return ; + } + + if (OnLinkOrAuto) { + // + // Remove the direct route for onlink prefix from route table. + // + do { + Status = Ip6DelRoute ( + IpSb->RouteTable, + &PrefixEntry->Prefix, + PrefixEntry->PrefixLength, + NULL + ); + } while (Status != EFI_NOT_FOUND); + } else { + // + // Remove the corresponding addresses generated from this autonomous prefix. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength); + } + } + + RemoveEntryList (&PrefixEntry->Link); + FreePool (PrefixEntry); +} + +/** + Search the list array to find an IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the search the link prefix list, + Otherwise search the autoconfiguration prefix list. + @param[in] PrefixLength The prefix length of the Prefix + @param[in] Prefix The prefix address. + + @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the + pointer to the IP6 prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6FindPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixList; + LIST_ENTRY *Entry; + LIST_ENTRY *ListHead; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Prefix != NULL); + + if (OnLinkOrAuto) { + ListHead = &IpSb->OnlinkPrefix; + } else { + ListHead = &IpSb->AutonomousPrefix; + } + + NET_LIST_FOR_EACH (Entry, ListHead) { + PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixLength != 255) { + // + // Perform exactly prefix match. + // + if (PrefixList->PrefixLength == PrefixLength && + NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) { + return PrefixList; + } + } else { + // + // Perform the longest prefix match. The list is already sorted with + // the longest length prefix put at the head of the list. + // + if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) { + return PrefixList; + } + } + } + + return NULL; +} + +/** + Release the resource in the prefix list table, and destroy the list entry and + corresponding addresses or route entries. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] ListHead The list entry head of the prefix list table. + +**/ +VOID +Ip6CleanPrefixListTable ( + IN IP6_SERVICE *IpSb, + IN LIST_ENTRY *ListHead + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixList; + BOOLEAN OnLink; + + OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix); + + while (!IsListEmpty (ListHead)) { + PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link); + Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE); + } +} + +/** + Callback function when address resolution is finished. It will cancel + all the queued frames if the address resolution failed, or transmit them + if the request succeeded. + + @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY. + +**/ +VOID +Ip6OnArpResolved ( + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_NEIGHBOR_ENTRY *ArpQue; + IP6_SERVICE *IpSb; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + BOOLEAN Sent; + + ArpQue = (IP6_NEIGHBOR_ENTRY *) Context; + if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) { + return ; + } + + IpSb = ArpQue->Interface->Service; + if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) { + return ; + } + + // + // ARP resolve failed for some reason. Release all the frame + // and ARP queue itself. Ip6FreeArpQue will call the frame's + // owner back. + // + if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) { + Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL); + return ; + } + + // + // ARP resolve succeeded, Transmit all the frame. + // + Sent = FALSE; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + RemoveEntryList (Entry); + + Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress); + + // + // Insert the tx token before transmitting it via MNP as the FrameSentDpc + // may be called before Mnp->Transmit returns which will remove this tx + // token from the SentFrames list. Remove it from the list if the returned + // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the + // FrameSentDpc won't be queued. + // + InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link); + + Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + Token->CallBack (Token->Packet, Status, 0, Token->Context); + + Ip6FreeLinkTxToken (Token); + continue; + } else { + Sent = TRUE; + } + } + + // + // Free the ArpQue only but not the whole neighbor entry. + // + Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL); + + if (Sent && (ArpQue->State == EfiNeighborStale)) { + ArpQue->State = EfiNeighborDelay; + ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME); + } +} + +/** + Allocate and initialize an IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] CallBack The callback function to be called when + address resolution is finished. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + @param[in] LinkAddress Points to the MAC address of the neighbor. + Ignored if NULL. + + @return NULL if failed to allocate memory for the neighbor cache entry. + Otherwise, point to the created neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6CreateNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_ARP_CALLBACK CallBack, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL + ) +{ + IP6_NEIGHBOR_ENTRY *Entry; + IP6_DEFAULT_ROUTER *DefaultRouter; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address!= NULL); + + Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY)); + if (Entry == NULL) { + return NULL; + } + + Entry->RefCnt = 1; + Entry->IsRouter = FALSE; + Entry->ArpFree = FALSE; + Entry->Dynamic = FALSE; + Entry->State = EfiNeighborInComplete; + Entry->Transmit = IP6_MAX_MULTICAST_SOLICIT + 1; + Entry->CallBack = CallBack; + Entry->Interface = NULL; + + InitializeListHead (&Entry->Frames); + + IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address); + + if (LinkAddress != NULL) { + IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress); + } else { + IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress); + } + + InsertHeadList (&IpSb->NeighborTable, &Entry->Link); + + // + // If corresponding default router entry exists, establish the relationship. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address); + if (DefaultRouter != NULL) { + DefaultRouter->NeighborCache = Entry; + } + + return Entry; +} + +/** + Search a IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + + @return NULL if it failed to find the matching neighbor cache entry. + Otherwise, point to the found neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6FindNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_NEIGHBOR_ENTRY *Neighbor; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) { + Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) { + RemoveEntryList (Entry); + InsertHeadList (&IpSb->NeighborTable, Entry); + + return Neighbor; + } + } + + return NULL; +} + +/** + Free a IP6 neighbor cache entry and remove all the frames on the address + resolution queue that pass the FrameToCancel. That is, either FrameToCancel + is NULL, or it returns true for the frame. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] NeighborCache The to be free neighbor cache entry. + @param[in] SendIcmpError If TRUE, send out ICMP error. + @param[in] FullFree If TRUE, remove the neighbor cache entry. + Otherwise remove the pending frames. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + This is an optional parameter that may be NULL. + @param[in] Context Opaque parameter to the FrameToCancel. + Ignored if FrameToCancel is NULL. + + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + @retval EFI_SUCCESS The operation finished successfully. + +**/ +EFI_STATUS +Ip6FreeNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_NEIGHBOR_ENTRY *NeighborCache, + IN BOOLEAN SendIcmpError, + IN BOOLEAN FullFree, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_LINK_TX_TOKEN *TxToken; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_DEFAULT_ROUTER *DefaultRouter; + + // + // If FrameToCancel fails, the token will not be released. + // To avoid the memory leak, stop this usage model. + // + if (FullFree && FrameToCancel != NULL) { + return EFI_INVALID_PARAMETER; + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) { + TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + + if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + TxToken->Packet, + NULL, + &TxToken->Packet->Ip.Ip6->SourceAddress, + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ADDR_UNREACHABLE, + NULL + ); + } + + if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) { + RemoveEntryList (Entry); + TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context); + Ip6FreeLinkTxToken (TxToken); + } + } + + if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) { + RemoveEntryList (&NeighborCache->ArpList); + NeighborCache->ArpFree = FALSE; + } + + if (FullFree) { + if (NeighborCache->IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + RemoveEntryList (&NeighborCache->Link); + FreePool (NeighborCache); + } + + return EFI_SUCCESS; +} + +/** + Allocate and initialize an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the default router. + @param[in] RouterLifetime The lifetime associated with the default + router, in units of seconds. + + @return NULL if it failed to allocate memory for the default router node. + Otherwise, point to the created default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6CreateDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN UINT16 RouterLifetime + ) +{ + IP6_DEFAULT_ROUTER *Entry; + IP6_ROUTE_ENTRY *RtEntry; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER)); + if (Entry == NULL) { + return NULL; + } + + Entry->RefCnt = 1; + Entry->Lifetime = RouterLifetime; + Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address); + IP6_COPY_ADDRESS (&Entry->Router, Ip6Address); + + // + // Add a default route into route table with both Destination and PrefixLength set to zero. + // + RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address); + if (RtEntry == NULL) { + FreePool (Entry); + return NULL; + } + + InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link); + IpSb->RouteTable->TotalNum++; + + InsertTailList (&IpSb->DefaultRouterList, &Entry->Link); + + return Entry; +} + +/** + Destroy an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6DestroyDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN IP6_DEFAULT_ROUTER *DefaultRouter + ) +{ + EFI_STATUS Status; + + RemoveEntryList (&DefaultRouter->Link); + + // + // Update the Destination Cache - all entries using the time-out router as next-hop + // should perform next-hop determination again. + // + do { + Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router); + } while (Status != EFI_NOT_FOUND); + + FreePool (DefaultRouter); +} + +/** + Clean an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6CleanDefaultRouterList ( + IN IP6_SERVICE *IpSb + ) +{ + IP6_DEFAULT_ROUTER *DefaultRouter; + + while (!IsListEmpty (&IpSb->DefaultRouterList)) { + DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link); + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } +} + +/** + Search a default router node from an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the to be searched default router node. + + @return NULL if it failed to find the matching default router node. + Otherwise, point to the found default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6FindDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ) +{ + LIST_ENTRY *Entry; + IP6_DEFAULT_ROUTER *DefaultRouter; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) { + DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link); + if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) { + return DefaultRouter; + } + } + + return NULL; +} + +/** + The function to be called after DAD (Duplicate Address Detection) is performed. + + @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed. + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] DadEntry The DAD entry which already performed DAD. + +**/ +VOID +Ip6OnDADFinished ( + IN BOOLEAN IsDadPassed, + IN IP6_INTERFACE *IpIf, + IN IP6_DAD_ENTRY *DadEntry + ) +{ + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddrInfo; + EFI_DHCP6_PROTOCOL *Dhcp6; + UINT16 OptBuf[4]; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + + IpSb = IpIf->Service; + AddrInfo = DadEntry->AddressInfo; + + if (IsDadPassed) { + // + // DAD succeed. + // + if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + ASSERT (!IpSb->LinkLocalOk); + + IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address); + IpSb->LinkLocalOk = TRUE; + IpIf->Configured = TRUE; + + // + // Check whether DHCP6 need to be started. + // + Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6; + + if (IpSb->Dhcp6NeedStart) { + Dhcp6->Start (Dhcp6); + IpSb->Dhcp6NeedStart = FALSE; + } + + if (IpSb->Dhcp6NeedInfoRequest) { + // + // Set the exta options to send. Here we only want the option request option + // with DNS SERVERS. + // + Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf; + Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO); + Oro->OpLen = HTONS (2); + *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS); + + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 64; + InfoReqReXmit.Mrt = 60; + InfoReqReXmit.Mrd = 0; + + Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + IpSb->Ip6ConfigInstance.Dhcp6Event, + Ip6ConfigOnDhcp6Reply, + &IpSb->Ip6ConfigInstance + ); + } + + // + // Add an on-link prefix for link-local address. + // + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + IP6_LINK_LOCAL_PREFIX_LENGTH, + &IpSb->LinkLocalAddr + ); + + } else { + // + // Global scope unicast address. + // + Ip6AddAddr (IpIf, AddrInfo); + + // + // Add an on-link prefix for this address. + // + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + AddrInfo->ValidLifetime, + AddrInfo->PreferredLifetime, + AddrInfo->PrefixLength, + &AddrInfo->Address + ); + + IpIf->Configured = TRUE; + } + } else { + // + // Leave the group we joined before. + // + Ip6LeaveGroup (IpSb, &DadEntry->Destination); + } + + if (DadEntry->Callback != NULL) { + DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context); + } + + if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + FreePool (AddrInfo); + RemoveEntryList (&DadEntry->Link); + FreePool (DadEntry); + // + // Disable IP operation since link-local address is a duplicate address. + // + IpSb->LinkLocalDadFail = TRUE; + IpSb->Mnp->Configure (IpSb->Mnp, NULL); + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0); + return ; + } + + if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + // + // Free the AddressInfo we hold if DAD fails or it is a link-local address. + // + FreePool (AddrInfo); + } + + RemoveEntryList (&DadEntry->Link); + FreePool (DadEntry); +} + +/** + Create a DAD (Duplicate Address Detection) entry and queue it to be performed. + + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] AddressInfo The address information which needs DAD performed. + @param[in] Callback The callback routine that will be called after DAD + is performed. This is an optional parameter that + may be NULL. + @param[in] Context The opaque parameter for a DAD callback routine. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The DAD entry was created and queued. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the + operation. + + +**/ +EFI_STATUS +Ip6InitDADProcess ( + IN IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddressInfo, + IN IP6_DAD_CALLBACK Callback OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_DAD_ENTRY *Entry; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits; + IP6_SERVICE *IpSb; + EFI_STATUS Status; + UINT32 MaxDelayTick; + + NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE); + ASSERT (AddressInfo != NULL); + + Status = EFI_SUCCESS; + IpSb = IpIf->Service; + DadXmits = &IpSb->Ip6ConfigInstance.DadXmits; + + // + // Allocate the resources and insert info + // + Entry = AllocatePool (sizeof (IP6_DAD_ENTRY)); + if (Entry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Map the incoming unicast address to solicited-node multicast address + // + Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination); + + // + // Join in the solicited-node multicast address. + // + Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination); + if (EFI_ERROR (Status)) { + FreePool (Entry); + return Status; + } + + Entry->Signature = IP6_DAD_ENTRY_SIGNATURE; + Entry->MaxTransmit = DadXmits->DupAddrDetectTransmits; + Entry->Transmit = 0; + Entry->Receive = 0; + MaxDelayTick = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS; + Entry->RetransTick = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5; + Entry->AddressInfo = AddressInfo; + Entry->Callback = Callback; + Entry->Context = Context; + InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link); + + if (Entry->MaxTransmit == 0) { + // + // DAD is disabled on this interface, immediately mark this DAD successful. + // + Ip6OnDADFinished (TRUE, IpIf, Entry); + } + + return EFI_SUCCESS; +} + +/** + Search IP6_DAD_ENTRY from the Duplicate Address Detection List. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Target The address information which needs DAD performed . + @param[out] Interface If not NULL, output the IP6 interface that configures + the tentative address. + + @return NULL if failed to find the matching DAD entry. + Otherwise, point to the found DAD entry. + +**/ +IP6_DAD_ENTRY * +Ip6FindDADEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Target, + OUT IP6_INTERFACE **Interface OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_ADDRESS_INFO *AddrInfo; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) { + DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); + AddrInfo = DupAddrDetect->AddressInfo; + if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) { + if (Interface != NULL) { + *Interface = IpIf; + } + return DupAddrDetect; + } + } + } + + return NULL; +} + +/** + Generate router solicit message and send it out to Destination Address or + All Router Link Local scope multicast address. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface If not NULL, points to the IP6 interface to send + the packet. + @param[in] SourceAddress If not NULL, the source address of the message. + @param[in] DestinationAddress If not NULL, the destination address of the message. + @param[in] SourceLinkAddress If not NULL, the MAC address of the source. + A source link-layer address option will be appended + to the message. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The router solicit message was successfully sent. + +**/ +EFI_STATUS +Ip6SendRouterSolicit ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + UINT16 PayloadLen; + IP6_INTERFACE *IpIf; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + IpIf = Interface; + if (IpIf == NULL && IpSb->DefaultInterface != NULL) { + IpIf = IpSb->DefaultInterface; + } + + // + // Generate the packet to be sent + // + + PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD); + if (SourceLinkAddress != NULL) { + PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION); + } + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + + if (DestinationAddress != NULL) { + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + } else { + Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress); + } + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, and Source link-layer address if contained. + // + + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT; + IcmpHead->Head.Code = 0; + + LinkLayerOption = NULL; + if (SourceLinkAddress != NULL) { + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherSource; + LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION); + CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6); + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate a Neighbor Advertisement message and send it out to Destination Address. + + @param[in] IpSb The IP service to send the packet. + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The target address field in the Neighbor Solicitation + message that prompted this advertisement. + @param[in] TargetLinkAddress The MAC address for the target, i.e. the sender + of the advertisement. + @param[in] IsRouter If TRUE, indicates the sender is a router. + @param[in] Override If TRUE, indicates the advertisement should override + an existing cache entry and update the MAC address. + @param[in] Solicited If TRUE, indicates the advertisement was sent + in response to a Neighbor Solicitation from + the Destination address. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress, + IN BOOLEAN IsRouter, + IN BOOLEAN Override, + IN BOOLEAN Solicited + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_IPv6_ADDRESS *Target; + UINT16 PayloadLen; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // The Neighbor Advertisement message must include a Target link-layer address option + // when responding to multicast solicitation and should include such option when + // responding to unicast solicitation. It also must include such option as unsolicited + // advertisement. + // + ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL); + + PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION)); + + // + // Generate the packet to be sent + // + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, Target address, and Target link-layer address. + // Set the Router flag, Solicited flag and Override flag. + // + + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE; + IcmpHead->Head.Code = 0; + + if (IsRouter) { + IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG; + } + + if (Solicited) { + IcmpHead->Fourth |= IP6_SOLICITED_FLAG; + } + + if (Override) { + IcmpHead->Fourth |= IP6_OVERRIDE_FLAG; + } + + Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE); + ASSERT (Target != NULL); + IP6_COPY_ADDRESS (Target, TargetIp6Address); + + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherTarget; + LinkLayerOption->Length = 1; + CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6); + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate the Neighbor Solicitation message and send it to the Destination Address. + + @param[in] IpSb The IP service to send the packet + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The IP address of the target of the solicitation. + It must not be a multicast address. + @param[in] SourceLinkAddress The MAC address for the sender. If not NULL, + a source link-layer address option will be appended + to the message. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_IPv6_ADDRESS *Target; + BOOLEAN IsDAD; + UINT16 PayloadLen; + IP6_NEIGHBOR_ENTRY *Neighbor; + + // + // Check input parameters + // + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + if (DestinationAddress == NULL || TargetIp6Address == NULL) { + return EFI_INVALID_PARAMETER; + } + + IsDAD = FALSE; + + if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) { + IsDAD = TRUE; + } + + // + // The Neighbor Solicitation message should include a source link-layer address option + // if the solicitation is not sent by performing DAD - Duplicate Address Detection. + // Otherwise must not include it. + // + PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS)); + + if (!IsDAD) { + if (SourceLinkAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION)); + } + + // + // Generate the packet to be sent + // + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, Target address, and Source link-layer address. + // + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT; + IcmpHead->Head.Code = 0; + + Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE); + ASSERT (Target != NULL); + IP6_COPY_ADDRESS (Target, TargetIp6Address); + + LinkLayerOption = NULL; + if (!IsDAD) { + + // + // Fill in the source link-layer address option + // + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherSource; + LinkLayerOption->Length = 1; + CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6); + } + + // + // Create a Neighbor Cache entry in the INCOMPLETE state when performing + // address resolution. + // + if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) { + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor == NULL) { + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL); + ASSERT (Neighbor != NULL); + } + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Process the Neighbor Solicitation message. The message may be sent for Duplicate + Address Detection or Address Resolution. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_IPv6_ADDRESS Target; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + BOOLEAN IsDAD; + BOOLEAN IsUnicast; + BOOLEAN IsMaintained; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_INTERFACE *IpIf; + IP6_NEIGHBOR_ENTRY *Neighbor; + BOOLEAN Solicited; + BOOLEAN UpdateCache; + EFI_IPv6_ADDRESS Dest; + UINT16 OptionLen; + UINT8 *Option; + BOOLEAN Provided; + EFI_STATUS Status; + VOID *MacAddress; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr); + + // + // Perform Message Validation: + // The IP Hop Limit field has a value of 255, i.e., the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // Target Address is not a multicast address. + // + Status = EFI_INVALID_PARAMETER; + + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) { + goto Exit; + } + + // + // ICMP length is 24 or more octets. + // + OptionLen = 0; + if (Head->PayloadLength < IP6_ND_LENGTH) { + goto Exit; + } else { + OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH); + Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL); + + // + // All included options should have a length that is greater than zero. + // + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + + IsDAD = NetIp6IsUnspecifiedAddr (&Head->SourceAddress); + IsUnicast = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress); + IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL); + + Provided = FALSE; + if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) { + NetbufCopy ( + Packet, + IP6_ND_LENGTH, + sizeof (IP6_ETHER_ADDR_OPTION), + (UINT8 *) &LinkLayerOption + ); + // + // The solicitation for neighbor discovery should include a source link-layer + // address option. If the option is not recognized, silently ignore it. + // + if (LinkLayerOption.Type == Ip6OptionEtherSource) { + if (IsDAD) { + // + // If the IP source address is the unspecified address, the source + // link-layer address option must not be included in the message. + // + goto Exit; + } + + Provided = TRUE; + } + } + + // + // If the IP source address is the unspecified address, the IP + // destination address is a solicited-node multicast address. + // + if (IsDAD && IsUnicast) { + goto Exit; + } + + // + // If the target address is tentative, and the source address is a unicast address, + // the solicitation's sender is performing address resolution on the target; + // the solicitation should be silently ignored. + // + if (!IsDAD && !IsMaintained) { + goto Exit; + } + + // + // If received unicast neighbor solicitation but destination is not this node, + // drop the packet. + // + if (IsUnicast && !IsMaintained) { + goto Exit; + } + + // + // In DAD, when target address is a tentative address, + // process the received neighbor solicitation message but not send out response. + // + if (IsDAD && !IsMaintained) { + DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf); + if (DupAddrDetect != NULL) { + if (DupAddrDetect->Transmit == 0) { + // + // The NS is from another node to performing DAD on the same address since + // we haven't send out any NS yet. Fail DAD for the tentative address. + // + Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect); + Status = EFI_ICMP_ERROR; + goto Exit; + } + + // + // Check the MAC address of the incoming packet. + // + if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) { + goto Exit; + } + + MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress; + if (MacAddress != NULL) { + if (CompareMem ( + MacAddress, + &IpSb->SnpMode.CurrentAddress, + IpSb->SnpMode.HwAddressSize + ) != 0) { + // + // The NS is from another node to performing DAD on the same address. + // Fail DAD for the tentative address. + // + Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect); + Status = EFI_ICMP_ERROR; + } else { + // + // The below layer loopback the NS we sent. Record it and wait for more. + // + DupAddrDetect->Receive++; + Status = EFI_SUCCESS; + } + } + } + goto Exit; + } + + // + // If the solicitation does not contain a link-layer address, DO NOT create or + // update the neighbor cache entries. + // + if (Provided) { + Neighbor = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + UpdateCache = FALSE; + + if (Neighbor == NULL) { + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL); + if (Neighbor == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + UpdateCache = TRUE; + } else { + if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) { + UpdateCache = TRUE; + } + } + + if (UpdateCache) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + // + // Send queued packets if exist. + // + Neighbor->CallBack ((VOID *) Neighbor); + } + } + + // + // Sends a Neighbor Advertisement as response. + // Set the Router flag to zero since the node is a host. + // If the source address of the solicitation is unspeicifed, and target address + // is one of the maintained address, reply a unsolicited multicast advertisement. + // + if (IsDAD && IsMaintained) { + Solicited = FALSE; + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest); + } else { + Solicited = TRUE; + IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress); + } + + Status = Ip6SendNeighborAdvertise ( + IpSb, + &Target, + &Dest, + &Target, + &IpSb->SnpMode.CurrentAddress, + FALSE, + TRUE, + Solicited + ); +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Neighbor Advertisement message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_IPv6_ADDRESS Target; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + BOOLEAN Provided; + INTN Compare; + IP6_NEIGHBOR_ENTRY *Neighbor; + IP6_DEFAULT_ROUTER *DefaultRouter; + BOOLEAN Solicited; + BOOLEAN IsRouter; + BOOLEAN Override; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_INTERFACE *IpIf; + UINT16 OptionLen; + UINT8 *Option; + EFI_STATUS Status; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr); + + // + // Validate the incoming Neighbor Advertisement + // + Status = EFI_INVALID_PARAMETER; + // + // The IP Hop Limit field has a value of 255, i.e., the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // Target Address is not a multicast address. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) { + goto Exit; + } + + // + // ICMP length is 24 or more octets. + // + Provided = FALSE; + OptionLen = 0; + if (Head->PayloadLength < IP6_ND_LENGTH) { + goto Exit; + } else { + OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH); + Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL); + + // + // All included options should have a length that is greater than zero. + // + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + + // + // If the IP destination address is a multicast address, Solicited Flag is ZERO. + // + Solicited = FALSE; + if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) { + Solicited = TRUE; + } + if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) { + goto Exit; + } + + // + // DAD - Check whether the Target is one of our tentative address. + // + DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf); + if (DupAddrDetect != NULL) { + // + // DAD fails, some other node is using this address. + // + NetbufFree (Packet); + Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect); + return EFI_ICMP_ERROR; + } + + // + // Search the Neighbor Cache for the target's entry. If no entry exists, + // the advertisement should be silently discarded. + // + Neighbor = Ip6FindNeighborEntry (IpSb, &Target); + if (Neighbor == NULL) { + goto Exit; + } + + // + // Get IsRouter Flag and Override Flag + // + IsRouter = FALSE; + Override = FALSE; + if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) { + IsRouter = TRUE; + } + if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) { + Override = TRUE; + } + + // + // Check whether link layer option is included. + // + if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) { + NetbufCopy ( + Packet, + IP6_ND_LENGTH, + sizeof (IP6_ETHER_ADDR_OPTION), + (UINT8 *) &LinkLayerOption + ); + + if (LinkLayerOption.Type == Ip6OptionEtherTarget) { + Provided = TRUE; + } + } + + Compare = 0; + if (Provided) { + Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + } + + if (!Neighbor->IsRouter && IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target); + if (DefaultRouter != NULL) { + DefaultRouter->NeighborCache = Neighbor; + } + } + + if (Neighbor->State == EfiNeighborInComplete) { + // + // If the target's Neighbor Cache entry is in INCOMPLETE state and no + // Target Link-Layer address option is included while link layer has + // address, the message should be silently discarded. + // + if (!Provided) { + goto Exit; + } + // + // Update the Neighbor Cache + // + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + if (Solicited) { + Neighbor->State = EfiNeighborReachable; + Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime); + } else { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + // + // Send any packets queued for the neighbor awaiting address resolution. + // + Neighbor->CallBack ((VOID *) Neighbor); + } + + Neighbor->IsRouter = IsRouter; + + } else { + if (!Override && Compare != 0) { + // + // When the Override Flag is clear and supplied link-layer address differs from + // that in the cache, if the state of the entry is not REACHABLE, ignore the + // message. Otherwise set it to STALE but do not update the entry in any + // other way. + // + if (Neighbor->State == EfiNeighborReachable) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } else { + if (Compare != 0) { + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + } + // + // Update the entry's state + // + if (Solicited) { + Neighbor->State = EfiNeighborReachable; + Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime); + } else { + if (Compare != 0) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } + + // + // When IsRouter is changed from TRUE to FALSE, remove the router from the + // Default Router List and remove the Destination Cache entries for all destinations + // using the neighbor as a router. + // + if (Neighbor->IsRouter && !IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + Neighbor->IsRouter = IsRouter; + } + } + + if (Neighbor->State == EfiNeighborReachable) { + Neighbor->CallBack ((VOID *) Neighbor); + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Router Advertisement message according to RFC4861. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with the IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessRouterAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + UINT32 ReachableTime; + UINT32 RetransTimer; + UINT16 RouterLifetime; + UINT16 Offset; + UINT8 Type; + UINT8 Length; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + UINT32 Fourth; + UINT8 CurHopLimit; + BOOLEAN Mflag; + BOOLEAN Oflag; + IP6_DEFAULT_ROUTER *DefaultRouter; + IP6_NEIGHBOR_ENTRY *NeighborCache; + EFI_MAC_ADDRESS LinkLayerAddress; + IP6_MTU_OPTION MTUOption; + IP6_PREFIX_INFO_OPTION PrefixOption; + IP6_PREFIX_LIST_ENTRY *PrefixList; + BOOLEAN OnLink; + BOOLEAN Autonomous; + EFI_IPv6_ADDRESS StatelessAddress; + EFI_STATUS Status; + UINT16 OptionLen; + UINT8 *Option; + INTN Result; + + Status = EFI_INVALID_PARAMETER; + + if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) { + // + // Skip the process below as it's not required under the current policy. + // + goto Exit; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + // + // Validate the incoming Router Advertisement + // + + // + // The IP source address must be a link-local address + // + if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + // + // The IP Hop Limit field has a value of 255, i.e. the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // ICMP length (derived from the IP length) is 16 or more octets. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || + Head->PayloadLength < IP6_RA_LENGTH) { + goto Exit; + } + + // + // All included options have a length that is greater than zero. + // + OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH); + Option = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL); + + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + + // + // Process Fourth field. + // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag, + // and Router Lifetime (16 bit). + // + + Fourth = NTOHL (Icmp.Fourth); + CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16)); + + // + // If the source address already in the default router list, update it. + // Otherwise create a new entry. + // A Lifetime of zero indicates that the router is not a default router. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress); + if (DefaultRouter == NULL) { + if (RouterLifetime != 0) { + DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime); + if (DefaultRouter == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + } else { + if (RouterLifetime != 0) { + DefaultRouter->Lifetime = RouterLifetime; + // + // Check the corresponding neighbor cache entry here. + // + if (DefaultRouter->NeighborCache == NULL) { + DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + } + } else { + // + // If the address is in the host's default router list and the router lifetime is zero, + // immediately time-out the entry. + // + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + CurHopLimit = *((UINT8 *) &Fourth + 3); + if (CurHopLimit != 0) { + IpSb->CurHopLimit = CurHopLimit; + } + + Mflag = FALSE; + Oflag = FALSE; + if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) { + Mflag = TRUE; + } else { + if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) { + Oflag = TRUE; + } + } + + if (Mflag || Oflag) { + // + // Use Ip6Config to get available addresses or other configuration from DHCP. + // + Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag); + } + + // + // Process Reachable Time and Retrans Timer fields. + // + NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime); + NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer); + ReachableTime = NTOHL (ReachableTime); + RetransTimer = NTOHL (RetransTimer); + + if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) { + // + // If new value is not unspecified and differs from the previous one, record it + // in BaseReachableTime and recompute a ReachableTime. + // + IpSb->BaseReachableTime = ReachableTime; + Ip6UpdateReachableTime (IpSb); + } + + if (RetransTimer != 0) { + IpSb->RetransTimer = RetransTimer; + } + + // + // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists. + // + NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + if (NeighborCache != NULL) { + NeighborCache->IsRouter = TRUE; + } + + // + // If an valid router advertisment is received, stops router solicitation. + // + IpSb->RouterAdvertiseReceived = TRUE; + + // + // The only defined options that may appear are the Source + // Link-Layer Address, Prefix information and MTU options. + // All included options have a length that is greater than zero. + // + Offset = 16; + while (Offset < Head->PayloadLength) { + NetbufCopy (Packet, Offset, sizeof (UINT8), &Type); + switch (Type) { + case Ip6OptionEtherSource: + // + // Update the neighbor cache + // + NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption); + if (LinkLayerOption.Length <= 0) { + goto Exit; + } + + ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6); + + if (NeighborCache == NULL) { + NeighborCache = Ip6CreateNeighborEntry ( + IpSb, + Ip6OnArpResolved, + &Head->SourceAddress, + &LinkLayerAddress + ); + if (NeighborCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + NeighborCache->IsRouter = TRUE; + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } else { + Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6); + + // + // If the link-local address is the same as that already in the cache, + // the cache entry's state remains unchanged. Otherwise update the + // reachability state to STALE. + // + if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) { + CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6); + + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + + if (NeighborCache->State == EfiNeighborInComplete) { + // + // Send queued packets if exist. + // + NeighborCache->State = EfiNeighborStale; + NeighborCache->CallBack ((VOID *) NeighborCache); + } else { + NeighborCache->State = EfiNeighborStale; + } + } + } + + Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8); + break; + case Ip6OptionPrefixInfo: + NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption); + if (PrefixOption.Length != 4) { + goto Exit; + } + PrefixOption.ValidLifetime = NTOHL (PrefixOption.ValidLifetime); + PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime); + + // + // Get L and A flag, recorded in the lower 2 bits of Reserved1 + // + OnLink = FALSE; + if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) { + OnLink = TRUE; + } + Autonomous = FALSE; + if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) { + Autonomous = TRUE; + } + + // + // If the prefix is the link-local prefix, silently ignore the prefix option. + // + if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH && + NetIp6IsLinkLocalAddr (&PrefixOption.Prefix) + ) { + Offset += sizeof (IP6_PREFIX_INFO_OPTION); + break; + } + // + // Do following if on-link flag is set according to RFC4861. + // + if (OnLink) { + PrefixList = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + // + // Create a new entry for the prefix, if the ValidLifetime is zero, + // silently ignore the prefix option. + // + if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) { + PrefixList = Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + if (PrefixList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } else if (PrefixList != NULL) { + if (PrefixOption.ValidLifetime != 0) { + PrefixList->ValidLifetime = PrefixOption.ValidLifetime; + } else { + // + // If the prefix exists and incoming ValidLifetime is zero, immediately + // remove the prefix. + Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE); + } + } + } + + // + // Do following if Autonomous flag is set according to RFC4862. + // + if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) { + PrefixList = Ip6FindPrefixListEntry ( + IpSb, + FALSE, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + // + // Create a new entry for the prefix, and form an address by prefix + interface id + // If the sum of the prefix length and interface identifier length + // does not equal 128 bits, the Prefix Information option MUST be ignored. + // + if (PrefixList == NULL && + PrefixOption.ValidLifetime != 0 && + PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128 + ) { + // + // Form the address in network order. + // + CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64)); + CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64)); + + // + // If the address is not yet in the assigned address list, adds it into. + // + if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) { + // + // And also not in the DAD process, check its uniqeness firstly. + // + if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) { + Status = Ip6SetAddress ( + IpSb->DefaultInterface, + &StatelessAddress, + FALSE, + PrefixOption.PrefixLength, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + } + + // + // Adds the prefix option to stateless prefix option list. + // + PrefixList = Ip6CreatePrefixListEntry ( + IpSb, + FALSE, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + if (PrefixList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } else if (PrefixList != NULL) { + + // + // Reset the preferred lifetime of the address if the advertised prefix exists. + // Perform specific action to valid lifetime together. + // + PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime; + if ((PrefixOption.ValidLifetime > 7200) || + (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) { + // + // If the received Valid Lifetime is greater than 2 hours or + // greater than RemainingLifetime, set the valid lifetime of the + // corresponding address to the advertised Valid Lifetime. + // + PrefixList->ValidLifetime = PrefixOption.ValidLifetime; + + } else if (PrefixList->ValidLifetime <= 7200) { + // + // If RemainingLifetime is less than or equls to 2 hours, ignore the + // Prefix Information option with regards to the valid lifetime. + // TODO: If this option has been authenticated, set the valid lifetime. + // + } else { + // + // Otherwise, reset the valid lifetime of the corresponding + // address to 2 hours. + // + PrefixList->ValidLifetime = 7200; + } + } + } + + Offset += sizeof (IP6_PREFIX_INFO_OPTION); + break; + case Ip6OptionMtu: + NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption); + if (MTUOption.Length != 1) { + goto Exit; + } + + // + // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order + // to omit implementation of Path MTU Discovery. Thus ignore the MTU option + // in Router Advertisement. + // + + Offset += sizeof (IP6_MTU_OPTION); + break; + default: + // + // Silently ignore unrecognized options + // + NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length); + if (Length <= 0) { + goto Exit; + } + + Offset = (UINT16) (Offset + (UINT16) Length * 8); + break; + } + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the ICMPv6 redirect message. Find the instance, then update + its route cache. + + @param[in] IpSb The IP6 service binding instance that received + the packet. + @param[in] Head The IP head of the received ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 redirect packet with + the IP head removed. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the + operation. + @retval EFI_SUCCESS Successfully updated the route caches. + +**/ +EFI_STATUS +Ip6ProcessRedirect ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD *Icmp; + EFI_IPv6_ADDRESS *Target; + EFI_IPv6_ADDRESS *IcmpDest; + UINT8 *Option; + UINT16 OptionLen; + IP6_ROUTE_ENTRY *RouteEntry; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + IP6_NEIGHBOR_ENTRY *NeighborCache; + INT32 Length; + UINT8 OptLen; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_MAC_ADDRESS Mac; + UINT32 Index; + BOOLEAN IsRouter; + EFI_STATUS Status; + INTN Result; + + Status = EFI_INVALID_PARAMETER; + + Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL); + if (Icmp == NULL) { + goto Exit; + } + + // + // Validate the incoming Redirect message + // + + // + // The IP Hop Limit field has a value of 255, i.e. the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // ICMP length (derived from the IP length) is 40 or more octets. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 || + Head->PayloadLength < IP6_REDITECT_LENGTH) { + goto Exit; + } + + // + // The IP source address must be a link-local address + // + if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + // + // The dest of this ICMP redirect message is not us. + // + if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { + goto Exit; + } + + // + // All included options have a length that is greater than zero. + // + OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH); + Option = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL); + + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + + Target = (EFI_IPv6_ADDRESS *) (Icmp + 1); + IcmpDest = Target + 1; + + // + // The ICMP Destination Address field in the redirect message does not contain + // a multicast address. + // + if (IP6_IS_MULTICAST (IcmpDest)) { + goto Exit; + } + + // + // The ICMP Target Address is either a link-local address (when redirected to + // a router) or the same as the ICMP Destination Address (when redirected to + // the on-link destination). + // + IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest); + if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) { + goto Exit; + } + + // + // Check the options. The only interested option here is the target-link layer + // address option. + // + Length = Packet->TotalSize - 40; + Option = (UINT8 *) (IcmpDest + 1); + LinkLayerOption = NULL; + while (Length > 0) { + switch (*Option) { + case Ip6OptionEtherTarget: + + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option; + OptLen = LinkLayerOption->Length; + if (OptLen != 1) { + // + // For ethernet, the length must be 1. + // + goto Exit; + } + break; + + default: + + OptLen = *(Option + 1); + if (OptLen == 0) { + // + // A length of 0 is invalid. + // + goto Exit; + } + break; + } + + Length -= 8 * OptLen; + Option += 8 * OptLen; + } + + if (Length != 0) { + goto Exit; + } + + // + // The IP source address of the Redirect is the same as the current + // first-hop router for the specified ICMP Destination Address. + // + RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress); + if (RouteCache != NULL) { + if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) { + // + // The source of this Redirect message must match the NextHop of the + // corresponding route cache entry. + // + goto Exit; + } + + // + // Update the NextHop. + // + IP6_COPY_ADDRESS (&RouteCache->NextHop, Target); + + if (!IsRouter) { + RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag; + RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE; + } + + } else { + // + // Get the Route Entry. + // + RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL); + if (RouteEntry == NULL) { + RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL); + if (RouteEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + + if (!IsRouter) { + RouteEntry->Flag = IP6_DIRECT_ROUTE; + } + + // + // Create a route cache for this. + // + RouteCache = Ip6CreateRouteCacheEntry ( + IcmpDest, + &Head->DestinationAddress, + Target, + (UINTN) RouteEntry + ); + if (RouteCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Insert the newly created route cache entry. + // + Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress); + InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link); + } + + // + // Try to locate the neighbor cache for the Target. + // + NeighborCache = Ip6FindNeighborEntry (IpSb, Target); + + if (LinkLayerOption != NULL) { + if (NeighborCache == NULL) { + // + // Create a neighbor cache for the Target. + // + ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&Mac, LinkLayerOption->EtherAddr, 6); + NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac); + if (NeighborCache == NULL) { + // + // Just report a success here. The neighbor cache can be created in + // some other place. + // + Status = EFI_SUCCESS; + goto Exit; + } + + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } else { + Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6); + + // + // If the link-local address is the same as that already in the cache, + // the cache entry's state remains unchanged. Otherwise update the + // reachability state to STALE. + // + if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) { + CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6); + + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + + if (NeighborCache->State == EfiNeighborInComplete) { + // + // Send queued packets if exist. + // + NeighborCache->State = EfiNeighborStale; + NeighborCache->CallBack ((VOID *) NeighborCache); + } else { + NeighborCache->State = EfiNeighborStale; + } + } + } + } + + if (NeighborCache != NULL && IsRouter) { + // + // The Target is a router, set IsRouter to TRUE. + // + NeighborCache->IsRouter = TRUE; + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Add Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been added. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache + due to insufficient resources. + @retval EFI_NOT_FOUND TargetLinkAddress is NULL. + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when DeleteFlag + is FALSE). + +**/ +EFI_STATUS +Ip6AddNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor != NULL) { + if (!Override) { + return EFI_ACCESS_DENIED; + } else { + if (TargetLinkAddress != NULL) { + IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress); + } + } + } else { + if (TargetLinkAddress == NULL) { + return EFI_NOT_FOUND; + } + + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress); + if (Neighbor == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + Neighbor->State = EfiNeighborReachable; + + if (Timeout != 0) { + Neighbor->Ticks = IP6_GET_TICKS (Timeout / TICKS_PER_MS); + Neighbor->Dynamic = TRUE; + } else { + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + + return EFI_SUCCESS; +} + +/** + Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache. + +**/ +EFI_STATUS +Ip6DelNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor == NULL) { + return EFI_NOT_FOUND; + } + + RemoveEntryList (&Neighbor->Link); + FreePool (Neighbor); + + return EFI_SUCCESS; +} + +/** + The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds. + This time routine handles DAD module and neighbor state transition. + It is also responsible for sending out router solicitations. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6NdFasterTimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_DELAY_JOIN_LIST *DelayNode; + EFI_IPv6_ADDRESS Source; + IP6_DAD_ENTRY *DupAddrDetect; + EFI_STATUS Status; + IP6_NEIGHBOR_ENTRY *NeighborCache; + EFI_IPv6_ADDRESS Destination; + IP6_SERVICE *IpSb; + BOOLEAN Flag; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS)); + + // + // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router + // Solicitation messages, each separated by at least + // RTR_SOLICITATION_INTERVAL (4) seconds. + // + if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) && + !IpSb->RouterAdvertiseReceived && + IpSb->SolicitTimer > 0 + ) { + if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) { + Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL); + if (!EFI_ERROR (Status)) { + IpSb->SolicitTimer--; + IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL); + } + } + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + // + // Process the delay list to join the solicited-node multicast address. + // + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { + DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); + if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) { + // + // The timer expires, init the duplicate address detection. + // + Ip6InitDADProcess ( + DelayNode->Interface, + DelayNode->AddressInfo, + DelayNode->DadCallback, + DelayNode->Context + ); + + // + // Remove the delay node + // + RemoveEntryList (&DelayNode->Link); + FreePool (DelayNode); + } + } + + // + // Process the duplicate address detection list. + // + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { + DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link); + + if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) { + // + // The timer expires, check the remaining transmit counts. + // + if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) { + // + // Send the Neighbor Solicitation message with + // Source - unspecified address, destination - solicited-node multicast address + // Target - the address to be validated + // + Status = Ip6SendNeighborSolicit ( + IpSb, + NULL, + &DupAddrDetect->Destination, + &DupAddrDetect->AddressInfo->Address, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + + DupAddrDetect->Transmit++; + DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer); + } else { + // + // All required solicitation has been sent out, and the RetransTime after the last + // Neighbor Solicit is elapsed, finish the DAD process. + // + Flag = FALSE; + if ((DupAddrDetect->Receive == 0) || + (DupAddrDetect->Transmit == DupAddrDetect->Receive)) { + Flag = TRUE; + } + + Ip6OnDADFinished (Flag, IpIf, DupAddrDetect); + } + } + } + } + + // + // Polling the state of Neighbor cache + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) { + NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + + switch (NeighborCache->State) { + case EfiNeighborInComplete: + if (NeighborCache->Ticks > 0) { + --NeighborCache->Ticks; + } + + // + // Retransmit Neighbor Solicitation messages approximately every + // RetransTimer milliseconds while awaiting a response. + // + if (NeighborCache->Ticks == 0) { + if (NeighborCache->Transmit > 1) { + // + // Send out multicast neighbor solicitation for address resolution. + // After last neighbor solicitation message has been sent out, wait + // for RetransTimer and then remove entry if no response is received. + // + Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination); + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &Destination, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + } + + // + // Update the retransmit times. + // + if (NeighborCache->Transmit > 0) { + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + } + } + + if (NeighborCache->Transmit == 0) { + // + // Timeout, send ICMP destination unreachable packet and then remove entry + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + TRUE, + TRUE, + EFI_ICMP_ERROR, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } + + break; + + case EfiNeighborReachable: + // + // This entry is inserted by EfiIp6Neighbors() as static entry + // and will not timeout. + // + if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) { + break; + } + + if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) { + if (NeighborCache->Dynamic) { + // + // This entry is inserted by EfiIp6Neighbors() as dynamic entry + // and will be deleted after timeout. + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + FALSE, + TRUE, + EFI_TIMEOUT, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } else { + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } + + break; + + case EfiNeighborDelay: + if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) { + + NeighborCache->State = EfiNeighborProbe; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1; + // + // Send out unicast neighbor solicitation for Neighbor Unreachability Detection + // + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &NeighborCache->Neighbor, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + + NeighborCache->Transmit--; + } + + break; + + case EfiNeighborProbe: + if (NeighborCache->Ticks > 0) { + --NeighborCache->Ticks; + } + + // + // Retransmit Neighbor Solicitation messages approximately every + // RetransTimer milliseconds while awaiting a response. + // + if (NeighborCache->Ticks == 0) { + if (NeighborCache->Transmit > 1) { + // + // Send out unicast neighbor solicitation for Neighbor Unreachability + // Detection. After last neighbor solicitation message has been sent out, + // wait for RetransTimer and then remove entry if no response is received. + // + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &NeighborCache->Neighbor, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + } + + // + // Update the retransmit times. + // + if (NeighborCache->Transmit > 0) { + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + } + } + + if (NeighborCache->Transmit == 0) { + // + // Delete the neighbor entry. + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + FALSE, + TRUE, + EFI_TIMEOUT, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } + + break; + + default: + break; + } + } +} + +/** + The heartbeat timer of ND module in 1 second. This time routine handles following + things: 1) maitain default router list; 2) maintain prefix options; + 3) maintain route caches. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6NdTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_DEFAULT_ROUTER *DefaultRouter; + IP6_PREFIX_LIST_ENTRY *PrefixOption; + UINT8 Index; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + + // + // Decrease the lifetime of default router, if expires remove it from default router list. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) { + DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link); + if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) { + if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + } + + // + // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) { + PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { + if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) { + if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) && + (PrefixOption->PreferredLifetime > 0) + ) { + --PrefixOption->PreferredLifetime; + } + } else { + Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE); + } + } + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) { + PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { + if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) { + Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE); + } + } + } + + // + // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries. + // Remove the entries at the tail of the bucket. These entries + // are likely to be used least. + // Reclaim frequency is set to 1 second. + // + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) { + Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]); + if (Entry == NULL) { + break; + } + + RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + Ip6FreeRouteCacheEntry (RouteCache); + ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0); + IpSb->RouteTable->Cache.CacheNum[Index]--; + } + } +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.h b/NetworkPkg/Ip6Dxe/Ip6Nd.h new file mode 100644 index 0000000000..57fb8feca3 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Nd.h @@ -0,0 +1,750 @@ +/** @file + Definition of Neighbor Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_ND_H__ +#define __EFI_IP6_ND_H__ + +#define IP6_GET_TICKS(Ms) (((Ms) + IP6_TIMER_INTERVAL_IN_MS - 1) / IP6_TIMER_INTERVAL_IN_MS) + +enum { + IP6_INF_ROUTER_LIFETIME = 0xFFFF, + + IP6_MAX_RTR_SOLICITATION_DELAY = 1000, ///< 1000 milliseconds + IP6_MAX_RTR_SOLICITATIONS = 3, + IP6_RTR_SOLICITATION_INTERVAL = 4000, + + IP6_MIN_RANDOM_FACTOR_SCALED = 1, + IP6_MAX_RANDOM_FACTOR_SCALED = 3, + IP6_RANDOM_FACTOR_SCALE = 2, + + IP6_MAX_MULTICAST_SOLICIT = 3, + IP6_MAX_UNICAST_SOLICIT = 3, + IP6_MAX_ANYCAST_DELAY_TIME = 1, + IP6_MAX_NEIGHBOR_ADV = 3, + IP6_REACHABLE_TIME = 30000, + IP6_RETRANS_TIMER = 1000, + IP6_DELAY_FIRST_PROBE_TIME = 5000, + + IP6_MIN_LINK_MTU = 1280, + IP6_MAX_LINK_MTU = 1500, + + IP6_IS_ROUTER_FLAG = 0x80, + IP6_SOLICITED_FLAG = 0x40, + IP6_OVERRIDE_FLAG = 0x20, + + IP6_M_ADDR_CONFIG_FLAG = 0x80, + IP6_O_CONFIG_FLAG = 0x40, + + IP6_ON_LINK_FLAG = 0x80, + IP6_AUTO_CONFIG_FLAG = 0x40, + + IP6_ND_LENGTH = 24, + IP6_RA_LENGTH = 16, + IP6_REDITECT_LENGTH = 40, + IP6_DAD_ENTRY_SIGNATURE = SIGNATURE_32 ('I', 'P', 'D', 'E') +}; + +typedef +VOID +(*IP6_ARP_CALLBACK) ( + VOID *Context + ); + +typedef struct _IP6_ETHE_ADDR_OPTION { + UINT8 Type; + UINT8 Length; + UINT8 EtherAddr[6]; +} IP6_ETHER_ADDR_OPTION; + +typedef struct _IP6_MTU_OPTION { + UINT8 Type; + UINT8 Length; + UINT16 Reserved; + UINT32 Mtu; +} IP6_MTU_OPTION; + +typedef struct _IP6_PREFIX_INFO_OPTION { + UINT8 Type; + UINT8 Length; + UINT8 PrefixLength; + UINT8 Reserved1; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; + UINT32 Reserved2; + EFI_IPv6_ADDRESS Prefix; +} IP6_PREFIX_INFO_OPTION; + +typedef +VOID +(*IP6_DAD_CALLBACK) ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ); + +typedef struct _IP6_DAD_ENTRY { + UINT32 Signature; + LIST_ENTRY Link; + UINT32 MaxTransmit; + UINT32 Transmit; + UINT32 Receive; + UINT32 RetransTick; + IP6_ADDRESS_INFO *AddressInfo; + EFI_IPv6_ADDRESS Destination; + IP6_DAD_CALLBACK Callback; + VOID *Context; +} IP6_DAD_ENTRY; + +typedef struct _IP6_DELAY_JOIN_LIST { + LIST_ENTRY Link; + UINT32 DelayTime; ///< in tick per 50 milliseconds + IP6_INTERFACE *Interface; + IP6_ADDRESS_INFO *AddressInfo; + IP6_DAD_CALLBACK DadCallback; + VOID *Context; +} IP6_DELAY_JOIN_LIST; + +typedef struct _IP6_NEIGHBOR_ENTRY { + LIST_ENTRY Link; + LIST_ENTRY ArpList; + INTN RefCnt; + BOOLEAN IsRouter; + BOOLEAN ArpFree; + BOOLEAN Dynamic; + EFI_IPv6_ADDRESS Neighbor; + EFI_MAC_ADDRESS LinkAddress; + EFI_IP6_NEIGHBOR_STATE State; + UINT32 Transmit; + UINT32 Ticks; + + LIST_ENTRY Frames; + IP6_INTERFACE *Interface; + IP6_ARP_CALLBACK CallBack; +} IP6_NEIGHBOR_ENTRY; + +typedef struct _IP6_DEFAULT_ROUTER { + LIST_ENTRY Link; + INTN RefCnt; + UINT16 Lifetime; + EFI_IPv6_ADDRESS Router; + IP6_NEIGHBOR_ENTRY *NeighborCache; +} IP6_DEFAULT_ROUTER; + +typedef struct _IP6_PREFIX_LIST_ENTRY { + LIST_ENTRY Link; + INTN RefCnt; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS Prefix; +} IP6_PREFIX_LIST_ENTRY; + +/** + Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number + of EFI_IP6_NEIGHBOR_CACHE is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] NeighborCount The number of returned neighbor cache entries. + @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE. + + @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiNeighborCache ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *NeighborCount, + OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache + ); + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of prefix entries is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] PrefixCount The number of returned prefix entries. + @param[out] PrefixTable The pointer to the array of PrefixTable. + + @retval EFI_SUCCESS The prefix table successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table. + +**/ +EFI_STATUS +Ip6BuildPrefixTable ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *PrefixCount, + OUT EFI_IP6_ADDRESS_INFO **PrefixTable + ); + +/** + Allocate and initialize an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the default router. + @param[in] RouterLifetime The lifetime associated with the default + router, in units of seconds. + + @return NULL if it failed to allocate memory for the default router node. + Otherwise, point to the created default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6CreateDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN UINT16 RouterLifetime + ); + +/** + Destroy an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6DestroyDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN IP6_DEFAULT_ROUTER *DefaultRouter + ); + +/** + Clean an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6CleanDefaultRouterList ( + IN IP6_SERVICE *IpSb + ); + +/** + Search a default router node from an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the to be searched default router node. + + @return NULL if it failed to find the matching default router node. + Otherwise, point to the found default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6FindDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ); + +/** + The function to be called after DAD (Duplicate Address Detection) is performed. + + @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed. + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] DadEntry The DAD entry which already performed DAD. + +**/ +VOID +Ip6OnDADFinished ( + IN BOOLEAN IsDadPassed, + IN IP6_INTERFACE *IpIf, + IN IP6_DAD_ENTRY *DadEntry + ); + +/** + Create a DAD (Duplicate Address Detection) entry and queue it to be performed. + + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] AddressInfo The address information which needs DAD performed. + @param[in] Callback The callback routine that will be called after DAD + is performed. This is an optional parameter that + may be NULL. + @param[in] Context The opaque parameter for a DAD callback routine. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The DAD entry was created and queued. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the + operation. + + +**/ +EFI_STATUS +Ip6InitDADProcess ( + IN IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddressInfo, + IN IP6_DAD_CALLBACK Callback OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Search IP6_DAD_ENTRY from the Duplicate Address Detection List. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Target The address information which needs DAD performed . + @param[out] Interface If not NULL, output the IP6 interface that configures + the tentative address. + + @return NULL if failed to find the matching DAD entry. + Otherwise, point to the found DAD entry. + +**/ +IP6_DAD_ENTRY * +Ip6FindDADEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Target, + OUT IP6_INTERFACE **Interface OPTIONAL + ); + +/** + Allocate and initialize a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list. + Otherwise, it is created for the autoconfiguration prefix list. + @param[in] ValidLifetime The length of time in seconds that the prefix + is valid for the purpose of on-link determination. + @param[in] PreferredLifetime The length of time in seconds that addresses + generated from the prefix via stateless address + autoconfiguration remain preferred. + @param[in] PrefixLength The prefix length of the Prefix. + @param[in] Prefix The prefix address. + + @return NULL if it failed to allocate memory for the prefix node. Otherwise, point + to the created or existing prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6CreatePrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ); + +/** + Destory a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] PrefixEntry The to be destroyed prefix list entry. + @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list. + Otherwise remove from autoconfiguration prefix list. + @param[in] ImmediateDelete If TRUE, remove the entry directly. + Otherwise, check the reference count to see whether + it should be removed. + +**/ +VOID +Ip6DestroyPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_PREFIX_LIST_ENTRY *PrefixEntry, + IN BOOLEAN OnLinkOrAuto, + IN BOOLEAN ImmediateDelete + ); + +/** + Search the list array to find an IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the search the link prefix list, + Otherwise search the autoconfiguration prefix list. + @param[in] PrefixLength The prefix length of the Prefix + @param[in] Prefix The prefix address. + + @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the + pointer to the IP6 prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6FindPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ); + +/** + Release the resource in prefix list table, and destroy the list entry and + corresponding addresses or route entries. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] ListHead The list entry head of the prefix list table. + +**/ +VOID +Ip6CleanPrefixListTable ( + IN IP6_SERVICE *IpSb, + IN LIST_ENTRY *ListHead + ); + +/** + Allocate and initialize an IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] CallBack The callback function to be called when + address resolution is finished. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + @param[in] LinkAddress Points to the MAC address of the neighbor. + Ignored if NULL. + + @return NULL if failed to allocate memory for the neighbor cache entry. + Otherwise, point to the created neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6CreateNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_ARP_CALLBACK CallBack, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL + ); + +/** + Search a IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + + @return NULL if it failed to find the matching neighbor cache entry. + Otherwise, point to the found neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6FindNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ); + +/** + Free a IP6 neighbor cache entry and remove all the frames on the address + resolution queue that pass the FrameToCancel. That is, either FrameToCancel + is NULL, or it returns true for the frame. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] NeighborCache The to be free neighbor cache entry. + @param[in] SendIcmpError If TRUE, send out ICMP error. + @param[in] FullFree If TRUE, remove the neighbor cache entry. + Otherwise remove the pending frames. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + This is an optional parameter that may be NULL. + @param[in] Context Opaque parameter to the FrameToCancel. + Ignored if FrameToCancel is NULL. + + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + @retval EFI_SUCCESS The operation finished successfully. + +**/ +EFI_STATUS +Ip6FreeNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_NEIGHBOR_ENTRY *NeighborCache, + IN BOOLEAN SendIcmpError, + IN BOOLEAN FullFree, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Add Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been added. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache + due to insufficient resources. + @retval EFI_NOT_FOUND TargetLinkAddress is NULL. + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when DeleteFlag + is FALSE). + +**/ +EFI_STATUS +Ip6AddNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache. + +**/ +EFI_STATUS +Ip6DelNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Process the Neighbor Solicitation message. The message may be sent for Duplicate + Address Detection or Address Resolution. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Neighbor Advertisement message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Router Advertisement message according to RFC4861. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with the IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessRouterAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the ICMPv6 redirect message. Find the instance, then update + its route cache. + + @param[in] IpSb The IP6 service binding instance that received + the packet. + @param[in] Head The IP head of the received ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 redirect packet with + the IP head removed. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the + operation. + @retval EFI_SUCCESS Successfully updated the route caches. + +**/ +EFI_STATUS +Ip6ProcessRedirect ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Generate router solicit message and send it out to Destination Address or + All Router Link Local scope multicast address. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface If not NULL, points to the IP6 interface to send + the packet. + @param[in] SourceAddress If not NULL, the source address of the message. + @param[in] DestinationAddress If not NULL, the destination address of the message. + @param[in] SourceLinkAddress If not NULL, the MAC address of the source. + A source link-layer address option will be appended + to the message. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation. + @retval EFI_SUCCESS The router solicit message was successfully sent. + +**/ +EFI_STATUS +Ip6SendRouterSolicit ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ); + +/** + Generate the Neighbor Solicitation message and send it to the Destination Address. + + @param[in] IpSb The IP service to send the packet + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The IP address of the target of the solicitation. + It must not be a multicast address. + @param[in] SourceLinkAddress The MAC address for the sender. If not NULL, + a source link-layer address option will be appended + to the message. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ); + +/** + Set the interface's address. This will trigger the DAD process for the + address to set. To set an already set address, the lifetimes wil be + updated to the new value passed in. + + @param[in] Interface The interface to set the address. + @param[in] Ip6Addr The interface's to be assigned IPv6 address. + @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast. + Otherwise, it is not anycast. + @param[in] PrefixLength The prefix length of the Ip6Addr. + @param[in] ValidLifetime The valid lifetime for this address. + @param[in] PreferredLifetime The preferred lifetime for this address. + @param[in] DadCallback The caller's callback to trigger when DAD finishes. + This is an optional parameter that may be NULL. + @param[in] Context The context that will be passed to DadCallback. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The interface is scheduled to be configured with + the specified address. + @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to + lack of resources. + +**/ +EFI_STATUS +Ip6SetAddress ( + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Ip6Addr, + IN BOOLEAN IsAnycast, + IN UINT8 PrefixLength, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN IP6_DAD_CALLBACK DadCallback OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds. + This time routine handles DAD module and neighbor state transition. + It is also responsible for sending out router solicitations. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6NdFasterTimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + The heartbeat timer of ND module in 1 second. This time routine handles following + things: 1) maitain default router list; 2) maintain prefix options; + 3) maintain route caches. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6NdTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +/** + Callback function when address resolution is finished. It will cancel + all the queued frames if the address resolution failed, or transmit them + if the request succeeded. + + @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY. + +**/ +VOID +Ip6OnArpResolved ( + IN VOID *Context + ); + +/** + Update the ReachableTime in IP6 service binding instance data, in milliseconds. + + @param[in, out] IpSb Points to the IP6_SERVICE. + +**/ +VOID +Ip6UpdateReachableTime ( + IN OUT IP6_SERVICE *IpSb + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6NvData.h b/NetworkPkg/Ip6Dxe/Ip6NvData.h new file mode 100644 index 0000000000..715473fde3 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6NvData.h @@ -0,0 +1,70 @@ +/** @file + NVData structure used by the IP6 configuration component. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _IP6_NV_DATA_H_ +#define _IP6_NV_DATA_H_ + +#define IP6_CONFIG_NVDATA_GUID \ + { \ + 0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99 } \ + } + +#define FORMID_MAIN_FORM 1 +#define FORMID_MANUAL_CONFIG_FORM 2 + +#define IP6_POLICY_AUTO 0 +#define IP6_POLICY_MANUAL 1 +#define DAD_MAX_TRANSMIT_COUNT 10 + +#define KEY_INTERFACE_ID 0x101 +#define KEY_MANUAL_ADDRESS 0x102 +#define KEY_GATEWAY_ADDRESS 0x103 +#define KEY_DNS_ADDRESS 0x104 +#define KEY_SAVE_CHANGES 0x105 +#define KEY_SAVE_CONFIG_CHANGES 0x106 +#define KEY_IGNORE_CONFIG_CHANGES 0x107 + +#define HOST_ADDRESS_LABEL 0x9000 +#define ROUTE_TABLE_LABEL 0xa000 +#define GATEWAY_ADDRESS_LABEL 0xb000 +#define DNS_ADDRESS_LABEL 0xc000 +#define LABEL_END 0xffff + +#define INTERFACE_ID_STR_MIN_SIZE 1 +#define INTERFACE_ID_STR_MAX_SIZE 23 +#define INTERFACE_ID_STR_STORAGE 24 +#define IP6_STR_MAX_SIZE 40 +#define ADDRESS_STR_MIN_SIZE 2 +#define ADDRESS_STR_MAX_SIZE 255 + +/// +/// IP6_CONFIG_IFR_NVDATA contains the IP6 configure +/// parameters for that NIC. +/// +#pragma pack(1) +typedef struct { + UINT8 IfType; ///< interface type + UINT8 Padding[3]; + UINT32 Policy; ///< manual or automatic + UINT32 DadTransmitCount; ///< dad transmits count + CHAR16 InterfaceId[INTERFACE_ID_STR_STORAGE]; ///< alternative interface id + CHAR16 ManualAddress[ADDRESS_STR_MAX_SIZE]; ///< IP addresses + CHAR16 GatewayAddress[ADDRESS_STR_MAX_SIZE]; ///< Gateway address + CHAR16 DnsAddress[ADDRESS_STR_MAX_SIZE]; ///< DNS server address +} IP6_CONFIG_IFR_NVDATA; +#pragma pack() + +#endif + diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.c b/NetworkPkg/Ip6Dxe/Ip6Option.c new file mode 100644 index 0000000000..9a91fd7cd1 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Option.c @@ -0,0 +1,758 @@ +/** @file + IP6 option support functions and routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is malformated. + + @param[in] IpSb The IP6 service data. + @param[in] Packet The to be validated packet. + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + @param[in] Pointer Identifies the octet offset within + the invoking packet where the error was detected. + + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsOptionValid ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT8 OptionLen, + IN UINT32 Pointer + ) +{ + UINT8 Offset; + UINT8 OptionType; + + Offset = 0; + + while (Offset < OptionLen) { + OptionType = *(Option + Offset); + + switch (OptionType) { + case Ip6OptionPad1: + // + // It is a Pad1 option + // + Offset++; + break; + case Ip6OptionPadN: + // + // It is a PadN option + // + Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2); + break; + case Ip6OptionRouterAlert: + // + // It is a Router Alert Option + // + Offset += 4; + break; + default: + // + // The highest-order two bits specify the action must be taken if + // the processing IPv6 node does not recognize the option type. + // + switch (OptionType & Ip6OptionMask) { + case Ip6OptionSkip: + Offset = (UINT8) (Offset + *(Option + Offset + 1)); + break; + case Ip6OptionDiscard: + return FALSE; + case Ip6OptionParameterProblem: + Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 2, + &Pointer + ); + return FALSE; + case Ip6OptionMask: + if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 2, + &Pointer + ); + } + + return FALSE; + break; + } + + break; + } + + } + + return TRUE; +} + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It supports the defined options in Neighbor + Discovery messages. + + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsNDOptionValid ( + IN UINT8 *Option, + IN UINT16 OptionLen + ) +{ + UINT16 Offset; + UINT8 OptionType; + UINT16 Length; + + Offset = 0; + + while (Offset < OptionLen) { + OptionType = *(Option + Offset); + Length = (UINT16) (*(Option + Offset + 1) * 8); + + switch (OptionType) { + case Ip6OptionPrefixInfo: + if (Length != 32) { + return FALSE; + } + + break; + + case Ip6OptionMtu: + if (Length != 8) { + return FALSE; + } + + break; + + default: + // + // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and + // Ip6OptionRedirected here. For unrecognized options, silently ignore + // and continue processsing the message. + // + if (Length == 0) { + return FALSE; + } + + break; + } + + Offset = (UINT16) (Offset + Length); + } + + return TRUE; +} + + +/** + Validate whether the NextHeader is a known valid protocol or one of the user configured + protocols from the upper layer. + + @param[in] IpSb The IP6 service instance. + @param[in] NextHeader The next header field. + + @retval TRUE The NextHeader is a known valid protocol or user configured. + @retval FALSE The NextHeader is not a known valid protocol. + +**/ +BOOLEAN +Ip6IsValidProtocol ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader + ) +{ + LIST_ENTRY *Entry; + IP6_PROTOCOL *IpInstance; + + if (NextHeader == EFI_IP_PROTO_TCP || + NextHeader == EFI_IP_PROTO_UDP || + NextHeader == IP6_ICMP || + NextHeader == IP6_ESP + ) { + return TRUE; + } + + if (IpSb == NULL) { + return FALSE; + } + + if (IpSb->Signature != IP6_SERVICE_SIGNATURE) { + return FALSE; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + if (IpInstance->State == IP6_STATE_CONFIGED) { + if (IpInstance->ConfigData.DefaultProtocol == NextHeader) { + return TRUE; + } + } + } + + return FALSE; +} + +/** + Validate the IP6 extension header format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is mal-formated. + + @param[in] IpSb The IP6 service instance. This is an optional parameter. + @param[in] Packet The data of the packet. Ignored if NULL. + @param[in] NextHeader The next header field in IPv6 basic header. + @param[in] ExtHdrs The first byte of the option. + @param[in] ExtHdrsLen The length of the whole option. + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise, the option we want to transmit. + @param[out] FormerHeader The offset of NextHeader which points to Fragment + Header when we received, of the ExtHdrs. + Ignored if we transmit. + @param[out] LastHeader The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] RealExtsLen The length of extension headers processed by IP6 layer. + This is an optional parameter that may be NULL. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + This is an optional parameter that may be NULL. + @param[out] Fragmented Indicate whether the packet is fragmented. + This is an optional parameter that may be NULL. + + @retval TRUE The option is properly formated. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsExtsValid ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN NET_BUF *Packet OPTIONAL, + IN UINT8 *NextHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN BOOLEAN Rcvd, + OUT UINT32 *FormerHeader OPTIONAL, + OUT UINT8 **LastHeader, + OUT UINT32 *RealExtsLen OPTIONAL, + OUT UINT32 *UnFragmentLen OPTIONAL, + OUT BOOLEAN *Fragmented OPTIONAL + ) +{ + UINT32 Pointer; + UINT32 Offset; + UINT8 *Option; + UINT8 OptionLen; + BOOLEAN Flag; + UINT8 CountD; + UINT8 CountA; + IP6_FRAGMENT_HEADER *FragmentHead; + UINT16 FragmentOffset; + IP6_ROUTING_HEADER *RoutingHead; + + if (RealExtsLen != NULL) { + *RealExtsLen = 0; + } + + if (UnFragmentLen != NULL) { + *UnFragmentLen = 0; + } + + if (Fragmented != NULL) { + *Fragmented = FALSE; + } + + *LastHeader = NextHeader; + + if (ExtHdrs == NULL && ExtHdrsLen == 0) { + return TRUE; + } + + if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) { + return FALSE; + } + + Pointer = 0; + Offset = 0; + Flag = FALSE; + CountD = 0; + CountA = 0; + + while (Offset <= ExtHdrsLen) { + + switch (*NextHeader) { + case IP6_HOP_BY_HOP: + if (Offset != 0) { + if (!Rcvd) { + return FALSE; + } + // + // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only. + // If not, generate a ICMP parameter problem message with code value of 1. + // + if (Pointer == 0) { + Pointer = sizeof (EFI_IP6_HEADER); + } else { + Pointer = Offset + sizeof (EFI_IP6_HEADER); + } + + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 1, + &Pointer + ); + } + return FALSE; + } + + Flag = TRUE; + + // + // Fall through + // + case IP6_DESTINATION: + if (*NextHeader == IP6_DESTINATION) { + CountD++; + } + + if (CountD > 2) { + return FALSE; + } + + NextHeader = ExtHdrs + Offset; + Pointer = Offset; + + Offset++; + Option = ExtHdrs + Offset; + OptionLen = (UINT8) ((*Option + 1) * 8 - 2); + Option++; + Offset++; + + if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) { + return FALSE; + } + + Offset = Offset + OptionLen; + + if (Flag) { + if (UnFragmentLen != NULL) { + *UnFragmentLen = Offset; + } + + Flag = FALSE; + } + + break; + + case IP6_ROUTING: + NextHeader = ExtHdrs + Offset; + RoutingHead = (IP6_ROUTING_HEADER *) NextHeader; + + // + // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095. + // Thus all routing types are processed as unrecognized. + // + if (RoutingHead->SegmentsLeft == 0) { + // + // Ignore the routing header and proceed to process the next header. + // + Offset = Offset + (RoutingHead->HeaderLen + 1) * 8; + + if (UnFragmentLen != NULL) { + *UnFragmentLen = Offset; + } + + } else { + // + // Discard the packet and send an ICMP Parameter Problem, Code 0, message + // to the packet's source address, pointing to the unrecognized routing + // type. + // + Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER); + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 0, + &Pointer + ); + } + + return FALSE; + } + + break; + + case IP6_FRAGMENT: + + // + // RFC2402, AH header should after fragment header. + // + if (CountA > 1) { + return FALSE; + } + + // + // RFC2460, ICMP Parameter Problem message with code 0 should be sent + // if the length of a fragment is not a multiple of 8 octects and the M + // flag of that fragment is 1, pointing to the Payload length field of the + // fragment packet. + // + if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) { + // + // Check whether it is the last fragment. + // + FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset); + if (FragmentHead == NULL) { + return FALSE; + } + + FragmentOffset = NTOHS (FragmentHead->FragmentOffset); + + if (((FragmentOffset & 0x1) == 0x1) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Pointer = sizeof (UINT32); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 0, + &Pointer + ); + return FALSE; + } + } + + if (Fragmented != NULL) { + *Fragmented = TRUE; + } + + if (Rcvd && FormerHeader != NULL) { + *FormerHeader = (UINT32) (NextHeader - ExtHdrs); + } + + NextHeader = ExtHdrs + Offset; + Offset = Offset + 8; + break; + + case IP6_AH: + if (++CountA > 1) { + return FALSE; + } + + Option = ExtHdrs + Offset; + NextHeader = Option; + Option++; + // + // RFC2402, Payload length is specified in 32-bit words, minus "2". + // + OptionLen = (UINT8) ((*Option + 2) * 4); + Offset = Offset + OptionLen; + break; + + case IP6_NO_NEXT_HEADER: + *LastHeader = NextHeader; + return FALSE; + break; + + default: + if (Ip6IsValidProtocol (IpSb, *NextHeader)) { + + *LastHeader = NextHeader; + + if (RealExtsLen != NULL) { + *RealExtsLen = Offset; + } + + return TRUE; + } + + // + // The Next Header value is unrecognized by the node, discard the packet and + // send an ICMP parameter problem message with code value of 1. + // + if (Offset == 0) { + // + // The Next Header directly follows IPv6 basic header. + // + Pointer = 6; + } else { + if (Pointer == 0) { + Pointer = sizeof (EFI_IP6_HEADER); + } else { + Pointer = Offset + sizeof (EFI_IP6_HEADER); + } + } + + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 1, + &Pointer + ); + } + return FALSE; + } + } + + *LastHeader = NextHeader; + + if (RealExtsLen != NULL) { + *RealExtsLen = Offset; + } + + return TRUE; +} + +/** + Generate an IPv6 router alert option in network order and output it through Buffer. + + @param[out] Buffer Points to a buffer to record the generated option. + @param[in, out] BufferLen The length of Buffer, in bytes. + @param[in] NextHeader The 8-bit selector indicates the type of header + immediately following the Hop-by-Hop Options header. + + @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated + option. BufferLen is updated for the required size. + + @retval EFI_SUCCESS The option is generated and filled in to Buffer. + +**/ +EFI_STATUS +Ip6FillHopByHop ( + OUT UINT8 *Buffer, + IN OUT UINTN *BufferLen, + IN UINT8 NextHeader + ) +{ + UINT8 BufferArray[8]; + + if (*BufferLen < 8) { + *BufferLen = 8; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Form the Hop-By-Hop option in network order. + // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN + // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets. + // + ZeroMem (BufferArray, sizeof (BufferArray)); + BufferArray[0] = NextHeader; + BufferArray[2] = 0x5; + BufferArray[3] = 0x2; + BufferArray[6] = 1; + + CopyMem (Buffer, BufferArray, sizeof (BufferArray)); + return EFI_SUCCESS; +} + +/** + Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] NextHeader The extension header type of first extension header. + @param[in] LastHeader The extension header type of last extension header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted. + It's caller's responsiblity to free this buffer. + + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of + resource. + @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not + supported currently. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6FillFragmentHeader ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader, + IN UINT8 LastHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT16 FragmentOffset, + OUT UINT8 **UpdatedExtHdrs + ) +{ + UINT32 Length; + UINT8 *Buffer; + UINT32 FormerHeader; + UINT32 Offset; + UINT32 Part1Len; + UINT32 HeaderLen; + UINT8 Current; + IP6_FRAGMENT_HEADER FragmentHead; + + if (UpdatedExtHdrs == NULL) { + return EFI_INVALID_PARAMETER; + } + + Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + Buffer = AllocatePool (Length); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Offset = 0; + Part1Len = 0; + FormerHeader = 0; + Current = NextHeader; + + while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) { + switch (NextHeader) { + case IP6_ROUTING: + case IP6_HOP_BY_HOP: + case IP6_DESTINATION: + Current = NextHeader; + NextHeader = *(ExtHdrs + Offset); + + if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) { + // + // Destination Options header should occur at most twice, once before + // a Routing header and once before the upper-layer header. Here we + // find the one before the upper-layer header. Insert the Fragment + // Header before it. + // + CopyMem (Buffer, ExtHdrs, Part1Len); + *(Buffer + FormerHeader) = IP6_FRAGMENT; + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + } + + + FormerHeader = Offset; + HeaderLen = (*(ExtHdrs + Offset + 1) + 1) * 8; + Part1Len = Part1Len + HeaderLen; + Offset = Offset + HeaderLen; + break; + + case IP6_FRAGMENT: + Current = NextHeader; + + if (Part1Len != 0) { + CopyMem (Buffer, ExtHdrs, Part1Len); + } + + *(Buffer + FormerHeader) = IP6_FRAGMENT; + + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + + case IP6_AH: + Current = NextHeader; + NextHeader = *(ExtHdrs + Offset); + // + // RFC2402, Payload length is specified in 32-bit words, minus "2". + // + HeaderLen = (*(ExtHdrs + Offset + 1) + 2) * 4; + Part1Len = Part1Len + HeaderLen; + Offset = Offset + HeaderLen; + break; + + default: + if (Ip6IsValidProtocol (IpSb, NextHeader)) { + Current = NextHeader; + CopyMem (Buffer, ExtHdrs, Part1Len); + *(Buffer + FormerHeader) = IP6_FRAGMENT; + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + } + + FreePool (Buffer); + return EFI_UNSUPPORTED; + } + } + + // + // Append the Fragment header. If the fragment offset indicates the fragment + // is the first fragment. + // + if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) { + FragmentHead.NextHeader = Current; + } else { + FragmentHead.NextHeader = LastHeader; + } + + FragmentHead.Reserved = 0; + FragmentHead.FragmentOffset = HTONS (FragmentOffset); + FragmentHead.Identification = mIp6Id; + + CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER)); + + if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) { + // + // Append the part2 (fragmentable part) of Extension headers + // + CopyMem ( + Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER), + ExtHdrs + Part1Len, + ExtHdrsLen - Part1Len + ); + } + + *UpdatedExtHdrs = Buffer; + + return EFI_SUCCESS; +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.h b/NetworkPkg/Ip6Dxe/Ip6Option.h new file mode 100644 index 0000000000..b62a04216e --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Option.h @@ -0,0 +1,191 @@ +/** @file + Definition of IP6 option process routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_OPTION_H__ +#define __EFI_IP6_OPTION_H__ + +#define IP6_FRAGMENT_OFFSET_MASK (~0x3) + +typedef struct _IP6_FRAGMENT_HEADER { + UINT8 NextHeader; + UINT8 Reserved; + UINT16 FragmentOffset; + UINT32 Identification; +} IP6_FRAGMENT_HEADER; + +typedef struct _IP6_ROUTING_HEADER { + UINT8 NextHeader; + UINT8 HeaderLen; + UINT8 RoutingType; + UINT8 SegmentsLeft; +} IP6_ROUTING_HEADER; + +typedef enum { + Ip6OptionPad1 = 0, + Ip6OptionPadN = 1, + Ip6OptionRouterAlert = 5, + Ip6OptionSkip = 0, + Ip6OptionDiscard = 0x40, + Ip6OptionParameterProblem = 0x80, + Ip6OptionMask = 0xc0, + + Ip6OptionEtherSource = 1, + Ip6OptionEtherTarget = 2, + Ip6OptionPrefixInfo = 3, + Ip6OptionRedirected = 4, + Ip6OptionMtu = 5 +} IP6_OPTION_TYPE; + +/** + Validate the IP6 extension header format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is mal-formated. + + @param[in] IpSb The IP6 service instance. This is an optional parameter. + @param[in] Packet The data of the packet. Ignored if NULL. + @param[in] NextHeader The next header field in IPv6 basic header. + @param[in] ExtHdrs The first byte of the option. + @param[in] ExtHdrsLen The length of the whole option. + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise, the option we want to transmit. + @param[out] FormerHeader The offset of NextHeader which points to Fragment + Header when we received, of the ExtHdrs. + Ignored if we transmit. + @param[out] LastHeader The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] RealExtsLen The length of extension headers processed by IP6 layer. + This is an optional parameter that may be NULL. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + This is an optional parameter that may be NULL. + @param[out] Fragmented Indicate whether the packet is fragmented. + This is an optional parameter that may be NULL. + + @retval TRUE The option is properly formated. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsExtsValid ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN NET_BUF *Packet OPTIONAL, + IN UINT8 *NextHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN BOOLEAN Rcvd, + OUT UINT32 *FormerHeader OPTIONAL, + OUT UINT8 **LastHeader, + OUT UINT32 *RealExtsLen OPTIONAL, + OUT UINT32 *UnFragmentLen OPTIONAL, + OUT BOOLEAN *Fragmented OPTIONAL + ); + +/** + Generate an IPv6 router alert option in network order and output it through Buffer. + + @param[out] Buffer Points to a buffer to record the generated option. + @param[in, out] BufferLen The length of Buffer, in bytes. + @param[in] NextHeader The 8-bit selector indicates the type of header + immediately following the Hop-by-Hop Options header. + + @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated + option. BufferLen is updated for the required size. + + @retval EFI_SUCCESS The option is generated and filled in to Buffer. + +**/ +EFI_STATUS +Ip6FillHopByHop ( + OUT UINT8 *Buffer, + IN OUT UINTN *BufferLen, + IN UINT8 NextHeader + ); + +/** + Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] NextHeader The extension header type of first extension header. + @param[in] LastHeader The extension header type of last extension header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted. + It's caller's responsiblity to free this buffer. + + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of + resource. + @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not + supported currently. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6FillFragmentHeader ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader, + IN UINT8 LastHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT16 FragmentOffset, + OUT UINT8 **UpdatedExtHdrs + ); + +/** + Copy the extension headers from the original to buffer. A Fragment header is + appended to the end. + + @param[in] NextHeader The 8-bit selector indicates the type of + the fragment header's next header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] LastHeader The pointer of next header of last extension header. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[in] UnFragmentHdrLen The length of unfragmented length of extension headers. + @param[in, out] Buf The buffer to copy options to. + @param[in, out] BufLen The length of the buffer. + + @retval EFI_SUCCESS The options are copied over. + @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small. + +**/ +EFI_STATUS +Ip6CopyExts ( + IN UINT8 NextHeader, + IN UINT8 *ExtHdrs, + IN UINT8 *LastHeader, + IN UINT16 FragmentOffset, + IN UINT32 UnFragmentHdrLen, + IN OUT UINT8 *Buf, + IN OUT UINT32 *BufLen + ); + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It supports the defined options in Neighbor + Discovery messages. + + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsNDOptionValid ( + IN UINT8 *Option, + IN UINT16 OptionLen + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.c b/NetworkPkg/Ip6Dxe/Ip6Output.c new file mode 100644 index 0000000000..baa4904fc9 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Output.c @@ -0,0 +1,1077 @@ +/** @file + The internal functions and routines to transmit the IP6 packet. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +UINT32 mIp6Id; + +/** + Output all the available source addresses to a list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to an IP6 service binding instance. + @param[out] SourceList The list entry head of all source addresses. + It is the caller's responsiblity to free the + resources. + @param[out] SourceCount The number of source addresses. + + @retval EFI_SUCCESS The source addresses were copied to a list entry head + SourceList. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + +**/ +EFI_STATUS +Ip6CandidateSource ( + IN IP6_SERVICE *IpSb, + OUT LIST_ENTRY *SourceList, + OUT UINT32 *SourceCount + ) +{ + IP6_INTERFACE *IpIf; + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_ADDRESS_INFO *AddrInfo; + IP6_ADDRESS_INFO *Copy; + + *SourceCount = 0; + + if (IpSb->LinkLocalOk) { + Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO)); + if (Copy == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Copy->Signature = IP6_ADDR_INFO_SIGNATURE; + IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr); + Copy->IsAnycast = FALSE; + Copy->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + Copy->ValidLifetime = (UINT32) IP6_INFINIT_LIFETIME; + Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME; + + InsertTailList (SourceList, &Copy->Link); + (*SourceCount)++; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (AddrInfo->IsAnycast) { + // + // Never use an anycast address. + // + continue; + } + + Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo); + if (Copy == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InsertTailList (SourceList, &Copy->Link); + (*SourceCount)++; + } + } + + return EFI_SUCCESS; +} + +/** + Caculate how many bits are the same between two IPv6 addresses. + + @param[in] AddressA Points to an IPv6 address. + @param[in] AddressB Points to another IPv6 address. + + @return The common bits of the AddressA and AddressB. + +**/ +UINT8 +Ip6CommonPrefixLen ( + IN EFI_IPv6_ADDRESS *AddressA, + IN EFI_IPv6_ADDRESS *AddressB + ) +{ + UINT8 Count; + UINT8 Index; + UINT8 ByteA; + UINT8 ByteB; + UINT8 NumBits; + + Count = 0; + Index = 0; + + while (Index < 16) { + ByteA = AddressA->Addr[Index]; + ByteB = AddressB->Addr[Index]; + + if (ByteA == ByteB) { + Count += 8; + Index++; + continue; + } + + // + // Check how many bits are common between the two bytes. + // + NumBits = 8; + ByteA = (UINT8) (ByteA ^ ByteB); + + while (ByteA != 0) { + NumBits--; + ByteA = (UINT8) (ByteA >> 1); + } + + return (UINT8) (Count + NumBits); + } + + return Count; +} + +/** + Output all the available source addresses to a list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Destination The IPv6 destination address. + @param[out] Source The selected IPv6 source address according to + the Destination. + + @retval EFI_SUCCESS The source addresses were copied to a list entry + head SourceList. + @retval EFI_NO_MAPPING The IPv6 stack is not auto configured. + +**/ +EFI_STATUS +Ip6SelectSourceAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + OUT EFI_IPv6_ADDRESS *Source + ) +{ + EFI_STATUS Status; + LIST_ENTRY SourceList; + UINT32 SourceCount; + UINT8 ScopeD; + LIST_ENTRY *Entry; + IP6_ADDRESS_INFO *AddrInfo; + IP6_PREFIX_LIST_ENTRY *Prefix; + UINT8 LastCommonLength; + UINT8 CurrentCommonLength; + EFI_IPv6_ADDRESS *TmpAddress; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Status = EFI_SUCCESS; + InitializeListHead (&SourceList); + + if (!IpSb->LinkLocalOk) { + return EFI_NO_MAPPING; + } + + // + // Rule 1: Prefer same address. + // + if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) { + IP6_COPY_ADDRESS (Source, Destination); + goto Exit; + } + + // + // Rule 2: Prefer appropriate scope. + // + if (IP6_IS_MULTICAST (Destination)) { + ScopeD = (UINT8) (Destination->Addr[1] >> 4); + } else if (NetIp6IsLinkLocalAddr (Destination)) { + ScopeD = 0x2; + } else { + ScopeD = 0xE; + } + + if (ScopeD <= 0x2) { + // + // Return the link-local address if it exists + // One IP6_SERVICE only has one link-local address. + // + IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); + goto Exit; + } + + // + // All candidate source addresses are global unicast address. + // + Ip6CandidateSource (IpSb, &SourceList, &SourceCount); + + if (SourceCount == 0) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); + + if (SourceCount == 1) { + goto Exit; + } + + // + // Rule 3: Avoid deprecated addresses. + // TODO: check the "deprecated" state of the stateful configured address + // + NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) { + Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (Prefix->PreferredLifetime == 0) { + Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength); + + if (SourceCount == 1) { + goto Exit; + } + } + } + + // + // TODO: Rule 4: Prefer home addresses. + // TODO: Rule 5: Prefer outgoing interface. + // TODO: Rule 6: Prefer matching label. + // TODO: Rule 7: Prefer public addresses. + // + + // + // Rule 8: Use longest matching prefix. + // + LastCommonLength = Ip6CommonPrefixLen (Source, Destination); + TmpAddress = NULL; + + for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination); + if (CurrentCommonLength > LastCommonLength) { + LastCommonLength = CurrentCommonLength; + TmpAddress = &AddrInfo->Address; + } + } + + if (TmpAddress != NULL) { + IP6_COPY_ADDRESS (Source, TmpAddress); + } + +Exit: + + Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0); + + return Status; +} + +/** + Select an interface to send the packet generated in the IP6 driver + itself: that is, not by the requests of the IP6 child's consumer. Such + packets include the ICMPv6 echo replies and other ICMPv6 error packets. + + @param[in] IpSb The IP4 service that wants to send the packets. + @param[in] Destination The destination of the packet. + @param[in, out] Source The source of the packet. + + @return NULL if no proper interface is found, otherwise, the interface that + can be used to send the system packet from. + +**/ +IP6_INTERFACE * +Ip6SelectInterface ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + IN OUT EFI_IPv6_ADDRESS *Source + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS SelectedSource; + IP6_INTERFACE *IpIf; + BOOLEAN Exist; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Destination != NULL && Source != NULL); + + if (NetIp6IsUnspecifiedAddr (Destination)) { + return NULL; + } + + if (!NetIp6IsUnspecifiedAddr (Source)) { + Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL); + ASSERT (Exist); + + return IpIf; + } + + // + // If source is unspecified, select a source according to the destination. + // + Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource); + if (EFI_ERROR (Status)) { + return IpSb->DefaultInterface; + } + + Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL); + IP6_COPY_ADDRESS (Source, &SelectedSource); + + return IpIf; +} + +/** + The default callback function for the system generated packet. + It will free the packet. + + @param[in] Packet The packet that transmitted. + @param[in] IoStatus The result of the transmission, succeeded or failed. + @param[in] LinkFlag Not used when transmitted. Check IP6_FRAME_CALLBACK + for reference. + @param[in] Context The context provided by us. + +**/ +VOID +Ip6SysPacketSent ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ) +{ + NetbufFree (Packet); + Packet = NULL; +} + +/** + Prefix an IP6 basic head and unfragmentable extension headers and a fragment header + to the Packet. Used for IP6 fragmentation. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Packet The packet to prefix the IP6 header to. + @param[in] Head The caller supplied header. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] LastHeader The pointer of next header of last extension header. + @param[in] HeadLen The length of the unfragmented part of the IP6 header. + + @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of + Packet. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6PrependHead ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT16 FragmentOffset, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT8 LastHeader, + IN UINT32 HeadLen + ) +{ + UINT32 Len; + UINT32 UnFragExtHdrsLen; + EFI_IP6_HEADER *PacketHead; + UINT8 *UpdatedExtHdrs; + EFI_STATUS Status; + UINT8 NextHeader; + + // + // HeadLen is the length of the fixed part of the sequences of fragments, i.e. + // the unfragment part. + // + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + if (PacketHead == NULL) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Set the head up, convert the host byte order to network byte order + // + CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); + PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER))); + Packet->Ip.Ip6 = PacketHead; + + Len = HeadLen - sizeof (EFI_IP6_HEADER); + UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER); + + if (UnFragExtHdrsLen == 0) { + PacketHead->NextHeader = IP6_FRAGMENT; + } + + // + // Append the extension headers: firstly copy the unfragmentable headers, then append + // fragmentation header. + // + if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) { + NextHeader = Head->NextHeader; + } else { + NextHeader = PacketHead->NextHeader; + } + + Status = Ip6FillFragmentHeader ( + IpSb, + NextHeader, + LastHeader, + ExtHdrs, + ExtHdrsLen, + FragmentOffset, + &UpdatedExtHdrs + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem ( + (UINT8 *) (PacketHead + 1), + UpdatedExtHdrs, + UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER) + ); + + FreePool (UpdatedExtHdrs); + return EFI_SUCCESS; +} + +/** + Transmit an IP6 packet. The packet comes either from the IP6 + child's consumer (IpInstance != NULL) or the IP6 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through an interface. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Interface The IP6 interface to transmit the packet. Ignored + if NULL. + @param[in] IpInstance The IP6 child that issues the transmission. It is + NULL if the packet is from the system. + @param[in] Packet The user data to send, excluding the IP header. + @param[in] Head The caller supplied header. The caller should set + the following header fields: NextHeader, HopLimit, + Src, Dest, FlowLabel, PayloadLength. This function + will fill in the Ver, TrafficClass. + @param[in] ExtHdrs The extension headers to append to the IPv6 basic + header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback. + + @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid. + @retval EFI_NO_MAPPING There is no interface to the destination. + @retval EFI_NOT_FOUND There is no route to the destination. + @retval EFI_SUCCESS The packet successfully transmitted. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resources. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip6Output ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN IP6_FRAME_CALLBACK Callback, + IN VOID *Context + ) +{ + IP6_INTERFACE *IpIf; + EFI_IPv6_ADDRESS NextHop; + IP6_NEIGHBOR_ENTRY *NeighborCache; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + EFI_STATUS Status; + UINT32 Mtu; + UINT32 HeadLen; + UINT16 FragmentOffset; + UINT8 *LastHeader; + UINT32 UnFragmentLen; + UINT32 UnFragmentHdrsLen; + UINT32 FragmentHdrsLen; + UINT16 *Checksum; + UINT16 PacketChecksum; + UINT16 PseudoChecksum; + UINT32 Index; + UINT32 PacketLen; + UINT32 RealExtLen; + UINT32 Offset; + NET_BUF *TmpPacket; + NET_BUF *Fragment; + UINT32 Num; + UINT8 *Buf; + EFI_IP6_HEADER *PacketHead; + IP6_ICMP_HEAD *IcmpHead; + IP6_TXTOKEN_WRAP *Wrap; + IP6_ROUTE_ENTRY *RouteEntry; + UINT8 *UpdatedExtHdrs; + UINT8 NextHeader; + UINT8 LastHeaderBackup; + BOOLEAN FragmentHeadInserted; + UINT8 *ExtHdrsBackup; + UINT8 NextHeaderBackup; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS Destination; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // RFC2460: Each extension header is an integer multiple of 8 octets long, + // in order to retain 8-octet alignment for subsequent headers. + // + if ((ExtHdrsLen & 0x7) != 0) { + return EFI_INVALID_PARAMETER; + } + + LastHeader = NULL; + + Ip6IsExtsValid ( + NULL, + NULL, + &Head->NextHeader, + ExtHdrs, + ExtHdrsLen, + FALSE, + NULL, + &LastHeader, + NULL, + NULL, + NULL + ); + + // + // Select an interface/source for system packet, application + // should select them itself. + // + IpIf = Interface; + if (IpIf == NULL) { + // + // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress + // and destinationaddress is unspecified. + // + if (IpInstance == NULL || IpInstance->Interface == NULL) { + IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress); + if (IpInstance != NULL) { + IpInstance->Interface = IpIf; + } + } else { + IpIf = IpInstance->Interface; + } + } + + if (IpIf == NULL) { + return EFI_NO_MAPPING; + } + + // + // Update the common field in Head here. + // + Head->Version = 6; + Head->TrafficClassL = 0; + Head->TrafficClassH = 0; + + Checksum = NULL; + NextHeader = *LastHeader; + + switch (NextHeader) { + case EFI_IP_PROTO_UDP: + Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Packet->Udp != NULL); + if (Packet->Udp->Checksum == 0) { + Checksum = &Packet->Udp->Checksum; + } + break; + + case EFI_IP_PROTO_TCP: + Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Packet->Tcp != NULL); + if (Packet->Tcp->Checksum == 0) { + Checksum = &Packet->Tcp->Checksum; + } + break; + + case IP6_ICMP: + // + // Don't send ICMP packet to an IPv6 anycast address. + // + if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) { + return EFI_INVALID_PARAMETER; + } + + IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (IcmpHead != NULL); + if (IcmpHead->Checksum == 0) { + Checksum = &IcmpHead->Checksum; + } + break; + + default: + break; + } + + if (Checksum != NULL) { + // + // Calculate the checksum for upper layer protocol if it is not calculated due to lack of + // IPv6 source address. + // + PacketChecksum = NetbufChecksum (Packet); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head->SourceAddress, + &Head->DestinationAddress, + NextHeader, + Packet->TotalSize + ); + *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum); + } + + Status = Ip6IpSecProcessPacket ( + IpSb, + Head, + LastHeader, // no need get the lasthead value for output + &Packet, + ExtHdrs, + ExtHdrsLen, + EfiIPsecOutBound, + Context + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + LastHeader = NULL; + // + // Check incoming parameters. + // + if (!Ip6IsExtsValid ( + IpSb, + Packet, + &Head->NextHeader, + ExtHdrs, + ExtHdrsLen, + FALSE, + NULL, + &LastHeader, + &RealExtLen, + &UnFragmentHdrsLen, + NULL + )) { + return EFI_INVALID_PARAMETER; + } + + if ((RealExtLen & 0x7) != 0) { + return EFI_INVALID_PARAMETER; + } + + LastHeaderBackup = *LastHeader; + + // + // Perform next hop determination: + // For multicast packets, the next-hop is always the destination address and + // is considered to be on-link. + // + if (IP6_IS_MULTICAST (&Head->DestinationAddress)) { + IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress); + } else { + // + // For unicast packets, use a combination of the Destination Cache, the Prefix List + // and the Default Router List to determine the IP address of the appropriate next hop. + // + RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress); + if (RouteCache == NULL) { + return EFI_NOT_FOUND; + } + + IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop); + Ip6FreeRouteCacheEntry (RouteCache); + } + + // + // Examines the Neighbor Cache for link-layer information about that neighbor. + // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error. + // + if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) { + NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop); + if (NeighborCache == NULL) { + NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL); + + if (NeighborCache == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Send out multicast neighbor solicitation for address resolution immediatly. + // + Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination); + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &Destination, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1; + } + + NeighborCache->Interface = IpIf; + } + + UpdatedExtHdrs = NULL; + ExtHdrsBackup = NULL; + NextHeaderBackup = 0; + FragmentHeadInserted = FALSE; + + // + // Check whether we received Packet Too Big message for the packet sent to the + // Destination. If yes include a Fragment Header in the subsequent packets. + // + RouteEntry = Ip6FindRouteEntry ( + IpSb->RouteTable, + &Head->DestinationAddress, + NULL + ); + if (RouteEntry != NULL) { + if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) { + + // + // FragmentHead is inserted after Hop-by-Hop Options header, Destination + // Options header (first occur), Routing header, and before Fragment header, + // Authentication header, Encapsulating Security Payload header, and + // Destination Options header (last occur), and upper-layer header. + // + Status = Ip6FillFragmentHeader ( + IpSb, + Head->NextHeader, + LastHeaderBackup, + ExtHdrs, + ExtHdrsLen, + 0, + &UpdatedExtHdrs + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { + NextHeaderBackup = Head->NextHeader; + Head->NextHeader = IP6_FRAGMENT; + } + + ExtHdrsBackup = ExtHdrs; + ExtHdrs = UpdatedExtHdrs; + ExtHdrsLen = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + RealExtLen = RealExtLen + sizeof (IP6_FRAGMENT_HEADER); + + mIp6Id++; + + FragmentHeadInserted = TRUE; + } + + Ip6FreeRouteEntry (RouteEntry); + } + + // + // OK, selected the source and route, fragment the packet then send + // them. Tag each fragment other than the first one as spawn from it. + // Each extension header is an integar multiple of 8 octets long, in + // order to retain 8-octet alignment for subsequent headers. + // + Mtu = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER); + HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen; + + if (Packet->TotalSize + HeadLen > Mtu) { + // + // Remove the inserted Fragment Header since we need fragment the packet. + // + if (FragmentHeadInserted) { + ExtHdrs = ExtHdrsBackup; + ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER); + + if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { + Head->NextHeader = NextHeaderBackup; + } + } + + FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen; + + // + // The packet is beyond the maximum which can be described through the + // fragment offset field in Fragment header. + // + if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + + if (FragmentHdrsLen != 0) { + // + // Append the fragmentable extension hdrs before the upper layer payload + // to form a new NET_BUF. This NET_BUF contains all the buffer which will + // be fragmented below. + // + TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen); + ASSERT (TmpPacket != NULL); + + // + // Allocate the space to contain the fragmentable hdrs and copy the data. + // + Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE); + ASSERT (Buf != NULL); + CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen); + + // + // Free the old Packet. + // + NetbufFree (Packet); + Packet = TmpPacket; + } + + // + // The unfragment part which appears in every fragmented IPv6 packet includes + // the IPv6 header, the unfragmentable extension hdrs and the fragment header. + // + UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + + // + // Mtu now is the length of the fragment part in a full-length fragment. + // + Mtu = (Mtu - UnFragmentLen) & (~0x07); + Num = (Packet->TotalSize + Mtu - 1) / Mtu; + + for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) { + // + // Get fragment from the Packet, append UnFragnmentLen spare buffer + // before the fragmented data, the corresponding data is filled in later. + // + Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen); + if (Fragment == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + FragmentOffset = (UINT16) ((UINT16) Offset | 0x1); + if (Index == Num - 1){ + // + // The last fragment, clear the M flag. + // + FragmentOffset &= (~0x1); + } + + Status = Ip6PrependHead ( + IpSb, + Fragment, + Head, + FragmentOffset, + ExtHdrs, + ExtHdrsLen, + LastHeaderBackup, + UnFragmentLen + ); + ASSERT (Status == EFI_SUCCESS); + + Status = Ip6SendFrame ( + IpIf, + IpInstance, + Fragment, + &NextHop, + Ip6SysPacketSent, + Packet + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // The last fragment of upper layer packet, update the IP6 token status. + // + if ((Index == Num -1) && (Context != NULL)) { + Wrap = (IP6_TXTOKEN_WRAP *) Context; + Wrap->Token->Status = Status; + } + + Offset += PacketLen; + PacketLen = Packet->TotalSize - Offset; + if (PacketLen > Mtu) { + PacketLen = Mtu; + } + } + + NetbufFree (Packet); + mIp6Id++; + + if (UpdatedExtHdrs != NULL) { + FreePool (UpdatedExtHdrs); + } + + return EFI_SUCCESS; + } + + // + // Need not fragment the packet, send it in one frame. + // + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + if (PacketHead == NULL) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + + CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); + Packet->Ip.Ip6 = PacketHead; + + if (ExtHdrs != NULL) { + Buf = (UINT8 *) (PacketHead + 1); + CopyMem (Buf, ExtHdrs, ExtHdrsLen); + } + + if (UpdatedExtHdrs != NULL) { + // + // A Fragment Header is inserted to the packet, update the payload length. + // + PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) + + sizeof (IP6_FRAGMENT_HEADER)); + PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength); + FreePool (UpdatedExtHdrs); + } + + return Ip6SendFrame ( + IpIf, + IpInstance, + Packet, + &NextHop, + Callback, + Context + ); + +Error: + if (UpdatedExtHdrs != NULL) { + FreePool (UpdatedExtHdrs); + } + Ip6CancelPacket (IpIf, Packet, Status); + return Status; +} + +/** + The filter function to find a packet and all its fragments. + The packet's fragments have their Context set to the packet. + + @param[in] Frame The frames hold by the low level interface. + @param[in] Context Context to the function, which is the packet. + + @retval TRUE This is the packet to cancel or its fragments. + @retval FALSE This is an unrelated packet. + +**/ +BOOLEAN +Ip6CancelPacketFragments ( + IN IP6_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) { + return TRUE; + } + + return FALSE; +} + +/** + Remove all the frames on the interface that pass the FrameToCancel, + either queued on ARP queues or that have already been delivered to + MNP and not yet recycled. + + @param[in] Interface Interface to remove the frames from. + @param[in] IoStatus The transmit status returned to the frames' callback. + @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all. + @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if + FrameToCancel is NULL. + +**/ +VOID +Ip6CancelFrames ( + IN IP6_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_LINK_TX_TOKEN *Token; + IP6_SERVICE *IpSb; + IP6_NEIGHBOR_ENTRY *ArpQue; + EFI_STATUS Status; + + IpSb = Interface->Service; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // Cancel all the pending frames on ARP requests + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList); + + Status = Ip6FreeNeighborEntry ( + IpSb, + ArpQue, + FALSE, + FALSE, + IoStatus, + FrameToCancel, + Context + ); + ASSERT_EFI_ERROR (Status); + } + + // + // Cancel all the frames that have been delivered to MNP + // but not yet recycled. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) { + Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + + if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { + IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken); + } + } +} + +/** + Cancel the Packet and all its fragments. + + @param[in] IpIf The interface from which the Packet is sent. + @param[in] Packet The Packet to cancel. + @param[in] IoStatus The status returns to the sender. + +**/ +VOID +Ip6CancelPacket ( + IN IP6_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ) +{ + Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet); +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.h b/NetworkPkg/Ip6Dxe/Ip6Output.h new file mode 100644 index 0000000000..80abe858e6 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Output.h @@ -0,0 +1,141 @@ +/** @file + The internal functions and routines to transmit the IP6 packet. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_OUTPUT_H__ +#define __EFI_IP6_OUTPUT_H__ + +extern UINT32 mIp6Id; + +/** + Output all the available source addresses to the list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Destination The IPv6 destination address. + @param[out] Source The selected IPv6 source address according to + the Destination. + + @retval EFI_SUCCESS The source addresses were copied to the list entry + head SourceList. + @retval EFI_NO_MAPPING The IPv6 stack is not auto configured. + +**/ +EFI_STATUS +Ip6SelectSourceAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + OUT EFI_IPv6_ADDRESS *Source + ); + +/** + The default callback function for system generated packet. + It will free the packet. + + @param[in] Packet The packet that transmitted. + @param[in] IoStatus The result of the transmission: succeeded or failed. + @param[in] LinkFlag Not used when transmission. Check IP6_FRAME_CALLBACK + for reference. + @param[in] Context The context provided by us. + +**/ +VOID +Ip6SysPacketSent ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +/** + Transmit an IP6 packet. The packet comes either from the IP6 + child's consumer (IpInstance != NULL) or the IP6 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through an interface. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Interface The IP6 interface to transmit the packet. Ignored + if NULL. + @param[in] IpInstance The IP6 child that issues the transmission. It is + NULL if the packet is from the system. + @param[in] Packet The user data to send, excluding the IP header. + @param[in] Head The caller supplied header. The caller should set + the following header fields: NextHeader, HopLimit, + Src, Dest, FlowLabel, PayloadLength. This function + will fill in the Ver, TrafficClass. + @param[in] ExtHdrs The extension headers to append to the IPv6 basic + header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback. + + @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid. + @retval EFI_NO_MAPPING There is no interface to the destination. + @retval EFI_NOT_FOUND There is no route to the destination. + @retval EFI_SUCCESS The packet successfully transmitted. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resources. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip6Output ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN IP6_FRAME_CALLBACK Callback, + IN VOID *Context + ); + +/** + Remove all the frames on the interface that pass the FrameToCancel, + either queued on ARP queues, or that have already been delivered to + MNP and not yet recycled. + + @param[in] Interface Interface to remove the frames from. + @param[in] IoStatus The transmit status returned to the frames' callback. + @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all. + @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if + FrameToCancel is NULL. + +**/ +VOID +Ip6CancelFrames ( + IN IP6_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Cancel the Packet and all its fragments. + + @param[in] IpIf The interface from which the Packet is sent. + @param[in] Packet The Packet to cancel. + @param[in] IoStatus The status returns to the sender. + +**/ +VOID +Ip6CancelPacket ( + IN IP6_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ); + +#endif diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.c b/NetworkPkg/Ip6Dxe/Ip6Route.c new file mode 100644 index 0000000000..bba365c152 --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Route.c @@ -0,0 +1,635 @@ +/** @file + The functions and routines to handle the route caches and route table. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Ip6Impl.h" + +/** + This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value + as the index of the route cache bucket according to the prefix of two IPv6 addresses. + + @param[in] Ip1 The IPv6 address. + @param[in] Ip2 The IPv6 address. + + @return The hash value of the prefix of two IPv6 addresses. + +**/ +UINT32 +Ip6RouteCacheHash ( + IN EFI_IPv6_ADDRESS *Ip1, + IN EFI_IPv6_ADDRESS *Ip2 + ) +{ + UINT32 Prefix1; + UINT32 Prefix2; + + Prefix1 = *((UINT32 *) ((UINTN *) (Ip1))); + Prefix2 = *((UINT32 *) ((UINTN *) (Ip2))); + + return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE); +} + +/** + Allocate a route entry then initialize it with the Destination/PrefixLength + and Gateway. + + @param[in] Destination The IPv6 destination address. This is an optional + parameter that may be NULL. + @param[in] PrefixLength The destination network's prefix length. + @param[in] GatewayAddress The next hop address. This is an optional parameter + that may be NULL. + + @return NULL if failed to allocate memeory; otherwise, the newly created route entry. + +**/ +IP6_ROUTE_ENTRY * +Ip6CreateRouteEntry ( + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ) +{ + IP6_ROUTE_ENTRY *RtEntry; + + RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY)); + + if (RtEntry == NULL) { + return NULL; + } + + RtEntry->RefCnt = 1; + RtEntry->Flag = 0; + RtEntry->PrefixLength = PrefixLength; + + if (Destination != NULL) { + IP6_COPY_ADDRESS (&RtEntry->Destination, Destination); + } + + if (GatewayAddress != NULL) { + IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress); + } + + return RtEntry; +} + +/** + Free the route table entry. It is reference counted. + + @param[in, out] RtEntry The route entry to free. + +**/ +VOID +Ip6FreeRouteEntry ( + IN OUT IP6_ROUTE_ENTRY *RtEntry + ) +{ + ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0)); + + if (--RtEntry->RefCnt == 0) { + FreePool (RtEntry); + } +} + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (prefix length == 128) to the shortest route area + (default routes). In each route area, it will first search the instance's + route table, then the default route table. This is required per the following + requirements: + 1. IP search the route table for a most specific match. + 2. The local route entries have precedence over the default route entry. + + @param[in] RtTable The route table to search from. + @param[in] Destination The destionation address to search. If NULL, search + the route table by NextHop. + @param[in] NextHop The next hop address. If NULL, search the route table + by Destination. + + @return NULL if no route matches the Dst. Otherwise, the point to the + @return most specific route to the Dst. + +**/ +IP6_ROUTE_ENTRY * +Ip6FindRouteEntry ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN EFI_IPv6_ADDRESS *NextHop OPTIONAL + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *RtEntry; + INTN Index; + + ASSERT (Destination != NULL || NextHop != NULL); + + RtEntry = NULL; + + for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) { + NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (Destination != NULL) { + if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } else if (NextHop != NULL) { + if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } + + } + } + + return NULL; +} + +/** + Allocate and initialize a IP6 route cache entry. + + @param[in] Dst The destination address. + @param[in] Src The source address. + @param[in] GateWay The next hop address. + @param[in] Tag The tag from the caller. This marks all the cache entries + spawned from one route table entry. + + @return NULL if failed to allocate memory for the cache. Otherwise, point + to the created route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6CreateRouteCacheEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN EFI_IPv6_ADDRESS *GateWay, + IN UINTN Tag + ) +{ + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + + RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY)); + + if (RtCacheEntry == NULL) { + return NULL; + } + + RtCacheEntry->RefCnt = 1; + RtCacheEntry->Tag = Tag; + + IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst); + IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src); + IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay); + + return RtCacheEntry; +} + +/** + Free the route cache entry. It is reference counted. + + @param[in, out] RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip6FreeRouteCacheEntry ( + IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry + ) +{ + ASSERT (RtCacheEntry->RefCnt > 0); + + if (--RtCacheEntry->RefCnt == 0) { + FreePool (RtCacheEntry); + } +} + +/** + Find a route cache with the destination and source address. This is + used by the ICMPv6 redirect messasge process. + + @param[in] RtTable The route table to search the cache for. + @param[in] Dest The destination address. + @param[in] Src The source address. + + @return NULL if no route entry to the (Dest, Src). Otherwise, the pointer + to the correct route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6FindRouteCache ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + Index = IP6_ROUTE_CACHE_HASH (Dest, Src); + + NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + + if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) { + NET_GET_REF (RtCacheEntry); + return RtCacheEntry; + } + } + + return NULL; +} + +/** + Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number + of EFI_IP6_ROUTE_TABLE is also returned. + + @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used. + @param[out] EfiRouteCount The number of returned route entries. + @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE. + If NULL, only the route entry count is returned. + + @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiRouteTable ( + IN IP6_ROUTE_TABLE *RouteTable, + OUT UINT32 *EfiRouteCount, + OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *RtEntry; + EFI_IP6_ROUTE_TABLE *EfiTable; + UINT32 Count; + INT32 Index; + + ASSERT (EfiRouteCount != NULL); + + Count = RouteTable->TotalNum; + *EfiRouteCount = Count; + + if ((EfiRouteTable == NULL) || (Count == 0)) { + return EFI_SUCCESS; + } + + if (*EfiRouteTable == NULL) { + *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count); + if (*EfiRouteTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + EfiTable = *EfiRouteTable; + + // + // Copy the route entry to EFI route table. + // + Count = 0; + + for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) { + + NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + Ip6CopyAddressByPrefix ( + &EfiTable[Count].Destination, + &RtEntry->Destination, + RtEntry->PrefixLength + ); + + IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop); + EfiTable[Count].PrefixLength = RtEntry->PrefixLength; + + Count++; + } + } + + ASSERT (Count == RouteTable->TotalNum); + + return EFI_SUCCESS; +} + +/** + Create an empty route table. This includes its internal route cache. + + @return NULL if failed to allocate memory for the route table. Otherwise, + the point to newly created route table. + +**/ +IP6_ROUTE_TABLE * +Ip6CreateRouteTable ( + VOID + ) +{ + IP6_ROUTE_TABLE *RtTable; + UINT32 Index; + + RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE)); + if (RtTable == NULL) { + return NULL; + } + + RtTable->RefCnt = 1; + RtTable->TotalNum = 0; + + for (Index = 0; Index < IP6_PREFIX_NUM; Index++) { + InitializeListHead (&RtTable->RouteArea[Index]); + } + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + InitializeListHead (&RtTable->Cache.CacheBucket[Index]); + RtTable->Cache.CacheNum[Index] = 0; + } + + return RtTable; +} + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in, out] RtTable The route table to free. + +**/ +VOID +Ip6CleanRouteTable ( + IN OUT IP6_ROUTE_TABLE *RtTable + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_ENTRY *RtEntry; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + ASSERT (RtTable->RefCnt > 0); + + if (--RtTable->RefCnt > 0) { + return ; + } + + // + // Free all the route table entry and its route cache. + // + for (Index = 0; Index < IP6_PREFIX_NUM; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + RemoveEntryList (Entry); + Ip6FreeRouteEntry (RtEntry); + } + } + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + RemoveEntryList (Entry); + Ip6FreeRouteCacheEntry (RtCacheEntry); + } + } + + FreePool (RtTable); +} + +/** + Remove all the cache entries bearing the Tag. When a route cache + entry is created, it is tagged with the address of route entry + from which it is spawned. When a route entry is deleted, the cache + entries spawned from it are also deleted. + + @param[in] RtCache Route cache to remove the entries from. + @param[in] Tag The Tag of the entries to remove. + +**/ +VOID +Ip6PurgeRouteCache ( + IN IP6_ROUTE_CACHE *RtCache, + IN UINTN Tag + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) { + + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + + if (RtCacheEntry->Tag == Tag) { + RemoveEntryList (Entry); + Ip6FreeRouteCacheEntry (RtCacheEntry); + } + } + } +} + +/** + Add a route entry to the route table. It is the help function for EfiIp6Routes. + + @param[in, out] RtTable Route table to add route to. + @param[in] Destination The destination of the network. + @param[in] PrefixLength The PrefixLength of the destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_ACCESS_DENIED The same route already exists. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry. + @retval EFI_SUCCESS The route was added successfully. + +**/ +EFI_STATUS +Ip6AddRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ) +{ + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *Route; + + ListHead = &RtTable->RouteArea[PrefixLength]; + + // + // First check whether the route exists + // + NET_LIST_FOR_EACH (Entry, ListHead) { + Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) && + EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) { + return EFI_ACCESS_DENIED; + } + } + + // + // Create a route entry and insert it to the route area. + // + Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress); + + if (Route == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (NetIp6IsUnspecifiedAddr (GatewayAddress)) { + Route->Flag = IP6_DIRECT_ROUTE; + } + + InsertHeadList (ListHead, &Route->Link); + RtTable->TotalNum++; + + return EFI_SUCCESS; +} + +/** + Remove a route entry and all the route caches spawn from it. + It is the help function for EfiIp6Routes. + + @param[in, out] RtTable The route table to remove the route from. + @param[in] Destination The destination network. + @param[in] PrefixLength The PrefixLength of the Destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_SUCCESS The route entry was successfully removed. + @retval EFI_NOT_FOUND There is no route entry in the table with that + property. + +**/ +EFI_STATUS +Ip6DelRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ) +{ + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_ENTRY *Route; + UINT32 TotalNum; + + ListHead = &RtTable->RouteArea[PrefixLength]; + TotalNum = RtTable->TotalNum; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) { + Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) { + continue; + } + if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) { + continue; + } + + Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route); + RemoveEntryList (Entry); + Ip6FreeRouteEntry (Route); + + ASSERT (RtTable->TotalNum > 0); + RtTable->TotalNum--; + } + + return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS; +} + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] IpSb The IP6 service data. + @param[in] Dest The destination address to search for. + @param[in] Src The source address to search for. + + @return NULL if it failed to route the packet. Otherwise, a route cache + entry that can be used to route packets. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6Route ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ) +{ + IP6_ROUTE_TABLE *RtTable; + LIST_ENTRY *ListHead; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + IP6_ROUTE_ENTRY *RtEntry; + EFI_IPv6_ADDRESS NextHop; + UINT32 Index; + + RtTable = IpSb->RouteTable; + + ASSERT (RtTable != NULL); + + // + // Search the destination cache in IP6_ROUTE_TABLE. + // + Index = IP6_ROUTE_CACHE_HASH (Dest, Src); + ListHead = &RtTable->Cache.CacheBucket[Index]; + + RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src); + + // + // If found, promote the cache entry to the head of the hash bucket. + // + if (RtCacheEntry != NULL) { + RemoveEntryList (&RtCacheEntry->Link); + InsertHeadList (ListHead, &RtCacheEntry->Link); + return RtCacheEntry; + } + + // + // Search the route table for the most specific route + // + RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL); + if (RtEntry == NULL) { + return NULL; + } + + // + // Found a route to the Dest, if it is a direct route, the packet + // will be send directly to the destination, such as for connected + // network. Otherwise, it is an indirect route, the packet will be + // send the next hop router. + // + if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) { + IP6_COPY_ADDRESS (&NextHop, Dest); + } else { + IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop); + } + + Ip6FreeRouteEntry (RtEntry); + + // + // Create a route cache entry, and tag it as spawned from this route entry + // + RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry); + + if (RtCacheEntry == NULL) { + return NULL; + } + + InsertHeadList (ListHead, &RtCacheEntry->Link); + NET_GET_REF (RtCacheEntry); + RtTable->Cache.CacheNum[Index]++; + + return RtCacheEntry; +} + diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.h b/NetworkPkg/Ip6Dxe/Ip6Route.h new file mode 100644 index 0000000000..d81e07b19c --- /dev/null +++ b/NetworkPkg/Ip6Dxe/Ip6Route.h @@ -0,0 +1,299 @@ +/** @file + EFI IP6 route table and route cache table defintions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_IP6_ROUTE_H__ +#define __EFI_IP6_ROUTE_H__ + +#define IP6_DIRECT_ROUTE 0x00000001 +#define IP6_PACKET_TOO_BIG 0x00000010 + +#define IP6_ROUTE_CACHE_HASH_SIZE 31 +/// +/// Max NO. of cache entry per hash bucket +/// +#define IP6_ROUTE_CACHE_MAX 32 + +#define IP6_ROUTE_CACHE_HASH(Ip1, Ip2) Ip6RouteCacheHash ((Ip1), (Ip2)) + +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + UINT32 Flag; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS Destination; + EFI_IPv6_ADDRESS NextHop; +} IP6_ROUTE_ENTRY; + +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + UINTN Tag; + EFI_IPv6_ADDRESS Destination; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS NextHop; +} IP6_ROUTE_CACHE_ENTRY; + +typedef struct { + LIST_ENTRY CacheBucket[IP6_ROUTE_CACHE_HASH_SIZE]; + UINT8 CacheNum[IP6_ROUTE_CACHE_HASH_SIZE]; +} IP6_ROUTE_CACHE; + +// +// Each IP6 instance has its own route table. Each ServiceBinding +// instance has a default route table and default address. +// +// All the route table entries with the same prefix length are linked +// together in one route area. For example, RouteArea[0] contains +// the default routes. A route table also contains a route cache. +// + +typedef struct _IP6_ROUTE_TABLE { + INTN RefCnt; + UINT32 TotalNum; + LIST_ENTRY RouteArea[IP6_PREFIX_NUM]; + IP6_ROUTE_CACHE Cache; +} IP6_ROUTE_TABLE; + +/** + This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value + as the index of the route cache bucket according to the prefix of two IPv6 addresses. + + @param[in] Ip1 The IPv6 address. + @param[in] Ip2 The IPv6 address. + + @return The hash value of the prefix of two IPv6 addresses. + +**/ +UINT32 +Ip6RouteCacheHash ( + IN EFI_IPv6_ADDRESS *Ip1, + IN EFI_IPv6_ADDRESS *Ip2 + ); + +/** + Allocate and initialize an IP6 route cache entry. + + @param[in] Dst The destination address. + @param[in] Src The source address. + @param[in] GateWay The next hop address. + @param[in] Tag The tag from the caller. This marks all the cache entries + spawned from one route table entry. + + @return NULL if it failed to allocate memory for the cache. Otherwise, point + to the created route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6CreateRouteCacheEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN EFI_IPv6_ADDRESS *GateWay, + IN UINTN Tag + ); + +/** + Free the route cache entry. It is reference counted. + + @param[in, out] RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip6FreeRouteCacheEntry ( + IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry + ); + +/** + Find a route cache with the destination and source address. This is + used by the ICMPv6 redirect messasge process. + + @param[in] RtTable The route table to search the cache for. + @param[in] Dest The destination address. + @param[in] Src The source address. + + @return NULL if no route entry to the (Dest, Src). Otherwise, point + to the correct route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6FindRouteCache ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ); + +/** + Build a array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number + of EFI_IP6_ROUTE_TABLE is also returned. + + @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used. + @param[out] EfiRouteCount The number of returned route entries. + @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE. + If NULL, only the route entry count is returned. + + @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiRouteTable ( + IN IP6_ROUTE_TABLE *RouteTable, + OUT UINT32 *EfiRouteCount, + OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL + ); + +/** + Create an empty route table, includes its internal route cache. + + @return NULL if failed to allocate memory for the route table. Otherwise, + the point to newly created route table. + +**/ +IP6_ROUTE_TABLE * +Ip6CreateRouteTable ( + VOID + ); + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in, out] RtTable The route table to free. + +**/ +VOID +Ip6CleanRouteTable ( + IN OUT IP6_ROUTE_TABLE *RtTable + ); + +/** + Allocate a route entry then initialize it with the Destination/PrefixLength + and Gateway. + + @param[in] Destination The IPv6 destination address. This is an optional + parameter that may be NULL. + @param[in] PrefixLength The destination network's prefix length. + @param[in] GatewayAddress The next hop address. This is optional parameter + that may be NULL. + + @return NULL if it failed to allocate memeory. Otherwise, the newly created route entry. + +**/ +IP6_ROUTE_ENTRY * +Ip6CreateRouteEntry ( + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ); + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (prefix length == 128) to the shortest route area + (default routes). In each route area, it will first search the instance's + route table, then the default route table. This is required per the following + requirements: + 1. IP search the route table for a most specific match. + 2. The local route entries have precedence over the default route entry. + + @param[in] RtTable The route table to search from. + @param[in] Destination The destionation address to search. If NULL, search + the route table by NextHop. + @param[in] NextHop The next hop address. If NULL, search the route table + by Destination. + + @return NULL if no route matches the Dst. Otherwise the point to the + most specific route to the Dst. + +**/ +IP6_ROUTE_ENTRY * +Ip6FindRouteEntry ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN EFI_IPv6_ADDRESS *NextHop OPTIONAL + ); + +/** + Free the route table entry. It is reference counted. + + @param[in, out] RtEntry The route entry to free. + +**/ +VOID +Ip6FreeRouteEntry ( + IN OUT IP6_ROUTE_ENTRY *RtEntry + ); + +/** + Add a route entry to the route table. It is the help function for EfiIp6Routes. + + @param[in, out] RtTable Route table to add route to. + @param[in] Destination The destination of the network. + @param[in] PrefixLength The PrefixLength of the destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_ACCESS_DENIED The same route already exists. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry. + @retval EFI_SUCCESS The route was added successfully. + +**/ +EFI_STATUS +Ip6AddRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ); + +/** + Remove a route entry and all the route caches spawn from it. + It is the help function for EfiIp6Routes. + + @param[in, out] RtTable The route table to remove the route from. + @param[in] Destination The destination network. + @param[in] PrefixLength The PrefixLength of the Destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_SUCCESS Successfully removed the route entry. + @retval EFI_NOT_FOUND There is no route entry in the table with that + properity. + +**/ +EFI_STATUS +Ip6DelRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ); + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] IpSb The IP6 service data. + @param[in] Dest The destination address to search for. + @param[in] Src The source address to search for. + + @return NULL if failed to route packet. Otherwise, a route cache + entry that can be used to route packet. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6Route ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ); + +#endif diff --git a/NetworkPkg/IpSecDxe/ComponentName.c b/NetworkPkg/IpSecDxe/ComponentName.c new file mode 100644 index 0000000000..22b3861081 --- /dev/null +++ b/NetworkPkg/IpSecDxe/ComponentName.c @@ -0,0 +1,310 @@ +/** @file + UEFI Component Name(2) protocol implementation for IPsec driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecImpl.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, OPTIONAL + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName = { + IpSecComponentNameGetDriverName, + IpSecComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IpSecComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IpSecComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIpSecDriverNameTable[] = { + { + "eng;en", + L"IpSec Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIpSecDriverNameTable, + DriverName, + (BOOLEAN) (This == &gIpSecComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, OPTIONAL + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.c b/NetworkPkg/IpSecDxe/IpSecConfigImpl.c new file mode 100644 index 0000000000..e671e42e27 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecConfigImpl.c @@ -0,0 +1,2928 @@ +/** @file + The implementation of IPSEC_CONFIG_PROTOCOL. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfigImpl.h" +#include "IpSecDebug.h" + +LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum]; +BOOLEAN mSetBySelf = FALSE; + +// +// Common CompareSelector routine entry for spd/sad/pad. +// +IPSEC_COMPARE_SELECTOR mCompareSelector[] = { + (IPSEC_COMPARE_SELECTOR) CompareSpdSelector, + (IPSEC_COMPARE_SELECTOR) CompareSaId, + (IPSEC_COMPARE_SELECTOR) ComparePadId +}; + +// +// Common IsZeroSelector routine entry for spd/sad/pad. +// +IPSEC_IS_ZERO_SELECTOR mIsZeroSelector[] = { + (IPSEC_IS_ZERO_SELECTOR) IsZeroSpdSelector, + (IPSEC_IS_ZERO_SELECTOR) IsZeroSaId, + (IPSEC_IS_ZERO_SELECTOR) IsZeroPadId +}; + +// +// Common DuplicateSelector routine entry for spd/sad/pad. +// +IPSEC_DUPLICATE_SELECTOR mDuplicateSelector[] = { + (IPSEC_DUPLICATE_SELECTOR) DuplicateSpdSelector, + (IPSEC_DUPLICATE_SELECTOR) DuplicateSaId, + (IPSEC_DUPLICATE_SELECTOR) DuplicatePadId +}; + +// +// Common FixPolicyEntry routine entry for spd/sad/pad. +// +IPSEC_FIX_POLICY_ENTRY mFixPolicyEntry[] = { + (IPSEC_FIX_POLICY_ENTRY) FixSpdEntry, + (IPSEC_FIX_POLICY_ENTRY) FixSadEntry, + (IPSEC_FIX_POLICY_ENTRY) FixPadEntry +}; + +// +// Common UnfixPolicyEntry routine entry for spd/sad/pad. +// +IPSEC_FIX_POLICY_ENTRY mUnfixPolicyEntry[] = { + (IPSEC_FIX_POLICY_ENTRY) UnfixSpdEntry, + (IPSEC_FIX_POLICY_ENTRY) UnfixSadEntry, + (IPSEC_FIX_POLICY_ENTRY) UnfixPadEntry +}; + +// +// Common SetPolicyEntry routine entry for spd/sad/pad. +// +IPSEC_SET_POLICY_ENTRY mSetPolicyEntry[] = { + (IPSEC_SET_POLICY_ENTRY) SetSpdEntry, + (IPSEC_SET_POLICY_ENTRY) SetSadEntry, + (IPSEC_SET_POLICY_ENTRY) SetPadEntry +}; + +// +// Common GetPolicyEntry routine entry for spd/sad/pad. +// +IPSEC_GET_POLICY_ENTRY mGetPolicyEntry[] = { + (IPSEC_GET_POLICY_ENTRY) GetSpdEntry, + (IPSEC_GET_POLICY_ENTRY) GetSadEntry, + (IPSEC_GET_POLICY_ENTRY) GetPadEntry +}; + +// +// Routine entry for IpSecConfig protocol. +// +EFI_IPSEC_CONFIG_PROTOCOL mIpSecConfigInstance = { + EfiIpSecConfigSetData, + EfiIpSecConfigGetData, + EfiIpSecConfigGetNextSelector, + EfiIpSecConfigRegisterNotify, + EfiIpSecConfigUnregisterNotify +}; + +/** + Get the all IPSec configuration variables and store those variables + to the internal data structure. + + This founction is called by IpSecConfigInitialize() that is to intialize the + IPsecConfiguration Protocol. + + @param[in] Private Point to IPSEC_PRIVATE_DATA. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS Restore the IPsec Configuration successfully. + @retval others Other errors is found during the variable getting. + +**/ +EFI_STATUS +IpSecConfigRestore ( + IN IPSEC_PRIVATE_DATA *Private + ); + +/** + Check if the specified EFI_IP_ADDRESS_INFO is in EFI_IP_ADDRESS_INFO list. + + @param[in] AddressInfo Pointer of IP_ADDRESS_INFO to be search in AddressInfo list. + @param[in] AddressInfoList A list that contains IP_ADDRESS_INFOs. + @param[in] AddressCount Point out how many IP_ADDRESS_INFO in the list. + + @retval TRUE The specified AddressInfo is in the AddressInfoList. + @retval FALSE The specified AddressInfo is not in the AddressInfoList. + +**/ +BOOLEAN +IsInAddressInfoList( + IN EFI_IP_ADDRESS_INFO *AddressInfo, + IN EFI_IP_ADDRESS_INFO *AddressInfoList, + IN UINT32 AddressCount + ) +{ + UINT8 Index; + + for (Index = 0; Index < AddressCount ; Index++) { + if (CompareMem ( + AddressInfo, + &AddressInfoList[Index].Address, + sizeof (EFI_IP_ADDRESS) + ) == 0 && + AddressInfo->PrefixLength == AddressInfoList[Index].PrefixLength + ) { + return TRUE; + } + } + return FALSE; +} + +/** + Compare two SPD Selectors. + + Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/ + NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the + Local Addresses and remote Addresses. + + @param[in] Selector1 Pointer of first SPD Selector. + @param[in] Selector2 Pointer of second SPD Selector. + + @retval TRUE This two Selector have the same value in above fields. + @retval FALSE Not all above fields have the same value in these two Selectors. + +**/ +BOOLEAN +CompareSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel1; + EFI_IPSEC_SPD_SELECTOR *SpdSel2; + BOOLEAN IsMatch; + UINTN Index; + + SpdSel1 = &Selector1->SpdSelector; + SpdSel2 = &Selector2->SpdSelector; + IsMatch = TRUE; + + // + // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/ + // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the + // two Spdselectors. Since the SPD supports two directions, it needs to + // compare two directions. + // + if ((SpdSel1->LocalAddressCount != SpdSel2->LocalAddressCount && + SpdSel1->LocalAddressCount != SpdSel2->RemoteAddressCount) || + (SpdSel1->RemoteAddressCount != SpdSel2->RemoteAddressCount && + SpdSel1->RemoteAddressCount != SpdSel2->LocalAddressCount) || + SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol || + SpdSel1->LocalPort != SpdSel2->LocalPort || + SpdSel1->LocalPortRange != SpdSel2->LocalPortRange || + SpdSel1->RemotePort != SpdSel2->RemotePort || + SpdSel1->RemotePortRange != SpdSel2->RemotePortRange + ) { + IsMatch = FALSE; + return IsMatch; + } + + // + // Compare the all LocalAddress fields in the two Spdselectors. + // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare + // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return + // TRUE. + // + for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->LocalAddress[Index], + SpdSel2->LocalAddress, + SpdSel2->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->LocalAddress[Index], + SpdSel1->LocalAddress, + SpdSel1->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->RemoteAddress[Index], + SpdSel2->RemoteAddress, + SpdSel2->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->RemoteAddress[Index], + SpdSel1->RemoteAddress, + SpdSel1->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + // + // Finish the one direction compare. If it is matched, return; otherwise, + // compare the other direction. + // + if (IsMatch) { + return IsMatch; + } + // + // Secondly, the SpdSel1->LocalAddress doesn't equal to SpdSel2->LocalAddress and + // SpdSel1->RemoteAddress doesn't equal to SpdSel2->RemoteAddress. Try to compare + // the RemoteAddress to LocalAddress. + // + IsMatch = TRUE; + for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->RemoteAddress[Index], + SpdSel2->LocalAddress, + SpdSel2->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->RemoteAddress[Index], + SpdSel1->LocalAddress, + SpdSel1->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->LocalAddress[Index], + SpdSel2->RemoteAddress, + SpdSel2->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->LocalAddress[Index], + SpdSel1->RemoteAddress, + SpdSel1->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + return IsMatch; +} + +/** + Compare two SA IDs. + + @param[in] Selector1 Pointer of first SA ID. + @param[in] Selector2 Pointer of second SA ID. + + @retval TRUE This two Selectors have the same SA ID. + @retval FALSE This two Selecotrs don't have the same SA ID. + +**/ +BOOLEAN +CompareSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ) +{ + EFI_IPSEC_SA_ID *SaId1; + EFI_IPSEC_SA_ID *SaId2; + BOOLEAN IsMatch; + + SaId1 = &Selector1->SaId; + SaId2 = &Selector2->SaId; + IsMatch = TRUE; + + if (CompareMem (SaId1, SaId2, sizeof (EFI_IPSEC_SA_ID)) != 0) { + IsMatch = FALSE; + } + + return IsMatch; +} + +/** + Compare two PAD IDs. + + @param[in] Selector1 Pointer of first PAD ID. + @param[in] Selector2 Pointer of second PAD ID. + + @retval TRUE This two Selectors have the same PAD ID. + @retval FALSE This two Selecotrs don't have the same PAD ID. + +**/ +BOOLEAN +ComparePadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ) +{ + EFI_IPSEC_PAD_ID *PadId1; + EFI_IPSEC_PAD_ID *PadId2; + BOOLEAN IsMatch; + + PadId1 = &Selector1->PadId; + PadId2 = &Selector2->PadId; + IsMatch = TRUE; + + // + // Compare the PeerIdValid fields in PadId. + // + if (PadId1->PeerIdValid != PadId2->PeerIdValid) { + IsMatch = FALSE; + } + // + // Compare the PeerId fields in PadId if PeerIdValid is true. + // + if (IsMatch && + PadId1->PeerIdValid && + AsciiStriCmp ((CONST CHAR8 *) PadId1->Id.PeerId, (CONST CHAR8 *) PadId2->Id.PeerId) != 0 + ) { + IsMatch = FALSE; + } + // + // Compare the IpAddress fields in PadId if PeerIdValid is false. + // + if (IsMatch && + !PadId1->PeerIdValid && + (PadId1->Id.IpAddress.PrefixLength != PadId2->Id.IpAddress.PrefixLength || + CompareMem (&PadId1->Id.IpAddress.Address, &PadId2->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) + ) { + IsMatch = FALSE; + } + + return IsMatch; +} + +/** + Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount + fields. + + @param[in] Selector Pointer of the SPD Selector. + + @retval TRUE If the SPD Selector is Zero. + @retval FALSE If the SPD Selector is not Zero. + +**/ +BOOLEAN +IsZeroSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel; + BOOLEAN IsZero; + + SpdSel = &Selector->SpdSelector; + IsZero = FALSE; + + if (SpdSel->LocalAddressCount == 0 && SpdSel->RemoteAddressCount == 0) { + IsZero = TRUE; + } + + return IsZero; +} + +/** + Check if the SA ID is Zero by its DestAddress. + + @param[in] Selector Pointer of the SA ID. + + @retval TRUE If the SA ID is Zero. + @retval FALSE If the SA ID is not Zero. + +**/ +BOOLEAN +IsZeroSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + EFI_IP_ADDRESS *DestAddr; + EFI_IP_ADDRESS ZeroAddr; + BOOLEAN IsZero; + + DestAddr = &Selector->SaId.DestAddress; + IsZero = FALSE; + + ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS)); + + if (CompareMem (DestAddr, &ZeroAddr, sizeof (EFI_IP_ADDRESS)) == 0) { + IsZero = TRUE; + } + + return IsZero; +} + +/** + Check if the PAD ID is Zero. + + @param[in] Selector Pointer of the PAD ID. + + @retval TRUE If the PAD ID is Zero. + @retval FALSE If the PAD ID is not Zero. + +**/ +BOOLEAN +IsZeroPadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + EFI_IPSEC_PAD_ID *PadId; + EFI_IPSEC_PAD_ID ZeroId; + BOOLEAN IsZero; + + PadId = &Selector->PadId; + IsZero = FALSE; + + ZeroMem (&ZeroId, sizeof (EFI_IPSEC_PAD_ID)); + + if (CompareMem (PadId, &ZeroId, sizeof (EFI_IPSEC_PAD_ID)) == 0) { + IsZero = TRUE; + } + + return IsZero; +} + +/** + Copy Source SPD Selector to the Destination SPD Selector. + + @param[in, out] DstSel Pointer of Destination SPD Selector. + @param[in] SrcSel Pointer of Source SPD Selector. + @param[in, out] Size The size of the Destination SPD Selector. If it + not NULL and its value less than the size of + Source SPD Selector, the value of Source SPD + Selector's size will be passed to caller by this + parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL + @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of the Source SPD Selector. + @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD + Selector successfully. + +**/ +EFI_STATUS +DuplicateSpdSelector ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ) +{ + EFI_IPSEC_SPD_SELECTOR *Dst; + EFI_IPSEC_SPD_SELECTOR *Src; + + Dst = &DstSel->SpdSelector; + Src = &SrcSel->SpdSelector; + + if (Dst == NULL || Src == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Size != NULL && (*Size) < SIZE_OF_SPD_SELECTOR (Src)) { + *Size = SIZE_OF_SPD_SELECTOR (Src); + return EFI_BUFFER_TOO_SMALL; + } + // + // Copy the base structure of spd selector. + // + CopyMem (Dst, Src, sizeof (EFI_IPSEC_SPD_SELECTOR)); + + // + // Copy the local address array of spd selector. + // + Dst->LocalAddress = (EFI_IP_ADDRESS_INFO *) (Dst + 1); + CopyMem ( + Dst->LocalAddress, + Src->LocalAddress, + sizeof (EFI_IP_ADDRESS_INFO) * Dst->LocalAddressCount + ); + + // + // Copy the remote address array of spd selector. + // + Dst->RemoteAddress = Dst->LocalAddress + Dst->LocalAddressCount; + CopyMem ( + Dst->RemoteAddress, + Src->RemoteAddress, + sizeof (EFI_IP_ADDRESS_INFO) * Dst->RemoteAddressCount + ); + + return EFI_SUCCESS; +} + +/** + Copy Source SA ID to the Destination SA ID. + + @param[in, out] DstSel Pointer of Destination SA ID. + @param[in] SrcSel Pointer of Source SA ID. + @param[in, out] Size The size of the Destination SA ID. If it + not NULL and its value less than the size of + Source SA ID, the value of Source SA ID's size + will be passed to caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID. + @retval EFI_SUCCESS Copy Source SA ID to the Destination SA ID successfully. + +**/ +EFI_STATUS +DuplicateSaId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ) +{ + EFI_IPSEC_SA_ID *Dst; + EFI_IPSEC_SA_ID *Src; + + Dst = &DstSel->SaId; + Src = &SrcSel->SaId; + + if (Dst == NULL || Src == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Size != NULL && *Size < sizeof (EFI_IPSEC_SA_ID)) { + *Size = sizeof (EFI_IPSEC_SA_ID); + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (Dst, Src, sizeof (EFI_IPSEC_SA_ID)); + + return EFI_SUCCESS; +} + +/** + Copy Source PAD ID to the Destination PAD ID. + + @param[in, out] DstSel Pointer of Destination PAD ID. + @param[in] SrcSel Pointer of Source PAD ID. + @param[in, out] Size The size of the Destination PAD ID. If it + not NULL and its value less than the size of + Source PAD ID, the value of Source PAD ID's size + will be passed to caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID . + @retval EFI_SUCCESS Copy Source PAD ID to the Destination PAD ID successfully. + +**/ +EFI_STATUS +DuplicatePadId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ) +{ + EFI_IPSEC_PAD_ID *Dst; + EFI_IPSEC_PAD_ID *Src; + + Dst = &DstSel->PadId; + Src = &SrcSel->PadId; + + if (Dst == NULL || Src == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Size != NULL && *Size < sizeof (EFI_IPSEC_PAD_ID)) { + *Size = sizeof (EFI_IPSEC_PAD_ID); + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (Dst, Src, sizeof (EFI_IPSEC_PAD_ID)); + + return EFI_SUCCESS; +} + +/** + Fix the value of some members of SPD Selector. + + This function is called by IpSecCopyPolicyEntry()which copy the Policy + Entry into the Variable. Since some members in SPD Selector are pointers, + a physical address to relative address convertion is required before copying + this SPD entry into the variable. + + @param[in] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +FixSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in spd selector and data are + // stored in the continous memory and close to the base structure. + // + FIX_REF_BUF_ADDR (Selector->LocalAddress, Selector); + FIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector); + + if (Data->ProcessingPolicy != NULL) { + if (Data->ProcessingPolicy->TunnelOption != NULL) { + FIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data); + } + + FIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data); + } + +} + +/** + Fix the value of some members of SA ID. + + This function is called by IpSecCopyPolicyEntry()which copy the Policy + Entry into the Variable. Since some members in SA ID are pointers, + a physical address to relative address conversion is required before copying + this SAD into the variable. + + @param[in] SaId Pointer of SA ID + @param[in, out] Data Pointer of SA Data. + +**/ +VOID +FixSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA *Data + ) +{ + // + // It assumes that all ref buffers in sad selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data); + } + + if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data); + } + + if (Data->SpdSelector != NULL) { + if (Data->SpdSelector->LocalAddress != NULL) { + FIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data); + } + + FIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data); + FIX_REF_BUF_ADDR (Data->SpdSelector, Data); + } + +} + +/** + Fix the value of some members of PAD ID. + + This function is called by IpSecCopyPolicyEntry()which copy the Policy + Entry into the Variable. Since some members in PAD ID are pointers, + a physical address to relative address conversion is required before copying + this PAD into the variable. + + @param[in] PadId Pointer of PAD ID. + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +FixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in pad selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AuthData != NULL) { + FIX_REF_BUF_ADDR (Data->AuthData, Data); + } + + if (Data->RevocationData != NULL) { + FIX_REF_BUF_ADDR (Data->RevocationData, Data); + } + +} + +/** + Recover the value of some members of SPD Selector. + + This function is corresponding to FixSpdEntry(). It recovers the value of members + of SPD Selector that are fixed by FixSpdEntry(). + + @param[in, out] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +UnfixSpdEntry ( + IN OUT EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in spd selector and data are + // stored in the continous memory and close to the base structure. + // + UNFIX_REF_BUF_ADDR (Selector->LocalAddress, Selector); + UNFIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector); + + if (Data->ProcessingPolicy != NULL) { + UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data); + if (Data->ProcessingPolicy->TunnelOption != NULL) { + UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data); + } + } + +} + +/** + Recover the value of some members of SA ID. + + This function is corresponding to FixSadEntry(). It recovers the value of members + of SAD ID that are fixed by FixSadEntry(). + + @param[in, out] SaId Pointer of SAD ID. + @param[in, out] Data Pointer of SAD Data. + +**/ +VOID +UnfixSadEntry ( + IN OUT EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA *Data + ) +{ + // + // It assumes that all ref buffers in sad selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data); + } + + if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data); + } + + if (Data->SpdSelector != NULL) { + UNFIX_REF_BUF_ADDR (Data->SpdSelector, Data); + if (Data->SpdSelector->LocalAddress != NULL) { + UNFIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data); + } + + UNFIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data); + } + +} + +/** + Recover the value of some members of PAD ID. + + This function is corresponding to FixPadEntry(). It recovers the value of members + of PAD ID that are fixed by FixPadEntry(). + + @param[in] PadId Pointer of PAD ID. + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +UnfixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in pad selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AuthData != NULL) { + UNFIX_REF_BUF_ADDR (Data->AuthData, Data); + } + + if (Data->RevocationData != NULL) { + UNFIX_REF_BUF_ADDR (Data->RevocationData, Data); + } + +} + +/** + Set the security policy information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SPD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL, the new entry will + be appended the end of database. + + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - Selector is not NULL and its LocalAddress + is NULL or its RemoteAddress is NULL. + - Data is not NULL and its Action is Protected + and its plolicy is NULL. + - Data is not NULL, its Action is not protected, + and its policy is not NULL. + - The Action of Data is Protected, its policy + mode is Tunnel, and its tunnel option is NULL. + - The Action of Data is protected and its policy + mode is not Tunnel and it tunnel option is not NULL. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel; + EFI_IPSEC_SPD_DATA *SpdData; + EFI_IPSEC_SPD_SELECTOR *InsertBefore; + LIST_ENTRY *SpdList; + LIST_ENTRY *SadList; + LIST_ENTRY *SpdSas; + LIST_ENTRY *EntryInsertBefore; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + LIST_ENTRY *Entry2; + IPSEC_SPD_ENTRY *SpdEntry; + IPSEC_SAD_ENTRY *SadEntry; + UINTN SpdEntrySize; + UINTN Index; + + SpdSel = (Selector == NULL) ? NULL : &Selector->SpdSelector; + SpdData = (Data == NULL) ? NULL : (EFI_IPSEC_SPD_DATA *) Data; + InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SpdSelector; + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + if (SpdSel != NULL) { + if (SpdSel->LocalAddress == NULL || SpdSel->RemoteAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + } + + if (SpdData != NULL) { + if ((SpdData->Action == EfiIPsecActionProtect && SpdData->ProcessingPolicy == NULL) || + (SpdData->Action != EfiIPsecActionProtect && SpdData->ProcessingPolicy != NULL) + ) { + return EFI_INVALID_PARAMETER; + } + + if (SpdData->Action == EfiIPsecActionProtect) { + if ((SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption == NULL) || + (SpdData->ProcessingPolicy->Mode != EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption != NULL) + ) { + return EFI_INVALID_PARAMETER; + } + } + } + // + // The default behavior is to insert the node ahead of the header. + // + EntryInsertBefore = SpdList; + + // + // Remove the existed spd entry. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SpdList) { + + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + if (SpdSel == NULL || + CompareSpdSelector ((EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel) + ) { + // + // Record the existed entry position to keep the original order. + // + EntryInsertBefore = SpdEntry->List.ForwardLink; + RemoveEntryList (&SpdEntry->List); + + // + // Update the reverse ref of sad entry in the spd.sas list. + // + SpdSas = &SpdEntry->Data->Sas; + NET_LIST_FOR_EACH (Entry2, SpdSas) { + SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry2); + SadEntry->Data->SpdEntry = NULL; + } + // + // Free the existed spd entry + // + FreePool (SpdEntry); + } + } + // + // Return success here if only want to remove the spd entry. + // + if (SpdData == NULL || SpdSel == NULL) { + return EFI_SUCCESS; + } + // + // Search the appointed entry position if InsertBefore is not NULL. + // + if (InsertBefore != NULL) { + + NET_LIST_FOR_EACH (Entry, SpdList) { + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + if (CompareSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, + (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore + )) { + EntryInsertBefore = Entry; + break; + } + } + } + + // + // Do Padding for the different Arch. + // + SpdEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SPD_ENTRY)); + SpdEntrySize = ALIGN_VARIABLE (SpdEntrySize + (UINTN)SIZE_OF_SPD_SELECTOR (SpdSel)); + SpdEntrySize += IpSecGetSizeOfEfiSpdData (SpdData); + + SpdEntry = AllocateZeroPool (SpdEntrySize); + + if (SpdEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fix the address of Selector and Data buffer and copy them, which is + // continous memory and close to the base structure of spd entry. + // + SpdEntry->Selector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ((SpdEntry + 1), sizeof (UINTN)); + SpdEntry->Data = (IPSEC_SPD_DATA *) ALIGN_POINTER ( + ((UINT8 *) SpdEntry->Selector + SIZE_OF_SPD_SELECTOR (SpdSel)), + sizeof (UINTN) + ); + + DuplicateSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, + (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel, + NULL + ); + + CopyMem ( + SpdEntry->Data->Name, + SpdData->Name, + sizeof (SpdData->Name) + ); + SpdEntry->Data->PackageFlag = SpdData->PackageFlag; + SpdEntry->Data->Action = SpdData->Action; + + // + // Fix the address of ProcessingPolicy and copy it if need, which is continous + // memory and close to the base structure of sad data. + // + if (SpdData->Action != EfiIPsecActionProtect) { + SpdEntry->Data->ProcessingPolicy = NULL; + } else { + SpdEntry->Data->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER ( + SpdEntry->Data + 1, + sizeof (UINTN) + ); + IpSecDuplicateProcessPolicy (SpdEntry->Data->ProcessingPolicy, SpdData->ProcessingPolicy); + } + // + // Update the sas list of the new spd entry. + // + InitializeListHead (&SpdEntry->Data->Sas); + + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + NET_LIST_FOR_EACH (Entry, SadList) { + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + for (Index = 0; Index < SpdData->SaIdCount; Index++) { + + if (CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) &SpdData->SaId[Index], + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id + )) { + InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd); + SadEntry->Data->SpdEntry = SpdEntry; + } + } + } + // + // Insert the new spd entry. + // + InsertTailList (EntryInsertBefore, &SpdEntry->List); + + return EFI_SUCCESS; +} + +/** + Set the security association information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SA_DATA. + @param[in] Context Pointer to one entry selector which describes + the expected position the new data entry will + be added. If Context is NULL,the new entry will + be appended the end of database. + + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ) +{ + IPSEC_SAD_ENTRY *SadEntry; + IPSEC_SPD_ENTRY *SpdEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + LIST_ENTRY *SadList; + LIST_ENTRY *SpdList; + EFI_IPSEC_SA_ID *SaId; + EFI_IPSEC_SA_DATA *SaData; + EFI_IPSEC_SA_ID *InsertBefore; + LIST_ENTRY *EntryInsertBefore; + UINTN SadEntrySize; + + SaId = (Selector == NULL) ? NULL : &Selector->SaId; + SaData = (Data == NULL) ? NULL : (EFI_IPSEC_SA_DATA *) Data; + InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SaId; + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + // + // The default behavior is to insert the node ahead of the header. + // + EntryInsertBefore = SadList; + + // + // Remove the existed sad entry. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SadList) { + + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + if (SaId == NULL || + CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id, + (EFI_IPSEC_CONFIG_SELECTOR *) SaId + )) { + // + // Record the existed entry position to keep the original order. + // + EntryInsertBefore = SadEntry->List.ForwardLink; + + // + // Update the related sad.byspd field. + // + if (SadEntry->Data->SpdEntry != NULL) { + RemoveEntryList (&SadEntry->BySpd); + } + + RemoveEntryList (&SadEntry->List); + FreePool (SadEntry); + } + } + // + // Return success here if only want to remove the sad entry + // + if (SaData == NULL || SaId == NULL) { + return EFI_SUCCESS; + } + // + // Search the appointed entry position if InsertBefore is not NULL. + // + if (InsertBefore != NULL) { + + NET_LIST_FOR_EACH (Entry, SadList) { + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + if (CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id, + (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore + )) { + EntryInsertBefore = Entry; + break; + } + } + } + + // + // Do Padding for different Arch. + // + SadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SAD_ENTRY)); + SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (EFI_IPSEC_SA_DATA)); + SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (IPSEC_SAD_DATA)); + + if (SaId->Proto == EfiIPsecAH) { + SadEntrySize += SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength; + } else { + SadEntrySize = ALIGN_VARIABLE (SadEntrySize + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength); + SadEntrySize += SaData->AlgoInfo.EspAlgoInfo.EncKeyLength; + } + + SadEntry = AllocateZeroPool (SadEntrySize); + + if (SadEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fix the address of Id and Data buffer and copy them, which is + // continous memory and close to the base structure of sad entry. + // + SadEntry->Id = (EFI_IPSEC_SA_ID *) ALIGN_POINTER ((SadEntry + 1), sizeof (UINTN)); + SadEntry->Data = (IPSEC_SAD_DATA *) ALIGN_POINTER ((SadEntry->Id + 1), sizeof (UINTN)); + + CopyMem (SadEntry->Id, SaId, sizeof (EFI_IPSEC_SA_ID)); + + SadEntry->Data->Mode = SaData->Mode; + SadEntry->Data->SequenceNumber = SaData->SNCount; + SadEntry->Data->AntiReplayWindowSize = SaData->AntiReplayWindows; + + ZeroMem ( + &SadEntry->Data->AntiReplayBitmap, + sizeof (SadEntry->Data->AntiReplayBitmap) + ); + + ZeroMem ( + &SadEntry->Data->AlgoInfo, + sizeof (EFI_IPSEC_ALGO_INFO) + ); + + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId = SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId; + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength = SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength; + + if (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) { + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SadEntry->Data + 1), sizeof (UINTN)); + CopyMem ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SaData->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength + ); + } + + if (SaId->Proto == EfiIPsecESP) { + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId = SaData->AlgoInfo.EspAlgoInfo.EncAlgoId; + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength = SaData->AlgoInfo.EspAlgoInfo.EncKeyLength; + + if (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) { + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (SadEntry->Data + 1) + + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength), + sizeof (UINTN) + ); + CopyMem ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey, + SaData->AlgoInfo.EspAlgoInfo.EncKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength + ); + } + } + + CopyMem ( + &SadEntry->Data->SaLifetime, + &SaData->SaLifetime, + sizeof (EFI_IPSEC_SA_LIFETIME) + ); + + SadEntry->Data->PathMTU = SaData->PathMTU; + SadEntry->Data->SpdEntry = NULL; + SadEntry->Data->ESNEnabled = FALSE; + SadEntry->Data->ManualSet = SaData->ManualSet; + + // + // Update the spd.sas list of the spd entry specified by sad.selector + // + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + for (Entry = SpdList->ForwardLink; Entry != SpdList && SaData->SpdSelector != NULL; Entry = Entry->ForwardLink) { + + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + if (CompareSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, + (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector + ) && SpdEntry->Data->Action == EfiIPsecActionProtect) { + SadEntry->Data->SpdEntry = SpdEntry; + InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd); + } + } + // + // Insert the new sad entry. + // + InsertTailList (EntryInsertBefore, &SadEntry->List); + + return EFI_SUCCESS; +} + +/** + Set the peer authorization configuration information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_PAD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL, the new entry will + be appended the end of database. + + @retval EFI_OUT_OF_RESOURCES The required system resources could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ) +{ + IPSEC_PAD_ENTRY *PadEntry; + EFI_IPSEC_PAD_ID *PadId; + EFI_IPSEC_PAD_DATA *PadData; + LIST_ENTRY *PadList; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + EFI_IPSEC_PAD_ID *InsertBefore; + LIST_ENTRY *EntryInsertBefore; + UINTN PadEntrySize; + + PadId = (Selector == NULL) ? NULL : &Selector->PadId; + PadData = (Data == NULL) ? NULL : (EFI_IPSEC_PAD_DATA *) Data; + InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->PadId; + PadList = &mConfigData[IPsecConfigDataTypePad]; + + // + // The default behavior is to insert the node ahead of the header. + // + EntryInsertBefore = PadList; + + // + // Remove the existed pad entry. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, PadList) { + + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + + if (PadId == NULL || + ComparePadId ((EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, (EFI_IPSEC_CONFIG_SELECTOR *) PadId) + ) { + // + // Record the existed entry position to keep the original order. + // + EntryInsertBefore = PadEntry->List.ForwardLink; + RemoveEntryList (&PadEntry->List); + + FreePool (PadEntry); + } + } + // + // Return success here if only want to remove the pad entry + // + if (PadData == NULL || PadId == NULL) { + return EFI_SUCCESS; + } + // + // Search the appointed entry position if InsertBefore is not NULL. + // + if (InsertBefore != NULL) { + + NET_LIST_FOR_EACH (Entry, PadList) { + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + + if (ComparePadId ( + (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, + (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore + )) { + EntryInsertBefore = Entry; + break; + } + } + } + + // + // Do PADDING for different arch. + // + PadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_PAD_ENTRY)); + PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_ID)); + PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_DATA)); + PadEntrySize = ALIGN_VARIABLE (PadEntrySize + (PadData->AuthData != NULL ? PadData->AuthDataSize : 0)); + PadEntrySize += PadData->RevocationData != NULL ? PadData->RevocationDataSize : 0; + + PadEntry = AllocateZeroPool (PadEntrySize); + + if (PadEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fix the address of Id and Data buffer and copy them, which is + // continous memory and close to the base structure of pad entry. + // + PadEntry->Id = (EFI_IPSEC_PAD_ID *) ALIGN_POINTER ((PadEntry + 1), sizeof (UINTN)); + PadEntry->Data = (EFI_IPSEC_PAD_DATA *) ALIGN_POINTER ((PadEntry->Id + 1), sizeof (UINTN)); + + CopyMem (PadEntry->Id, PadId, sizeof (EFI_IPSEC_PAD_ID)); + + PadEntry->Data->AuthProtocol = PadData->AuthProtocol; + PadEntry->Data->AuthMethod = PadData->AuthMethod; + PadEntry->Data->IkeIdFlag = PadData->IkeIdFlag; + + if (PadData->AuthData != NULL) { + PadEntry->Data->AuthDataSize = PadData->AuthDataSize; + PadEntry->Data->AuthData = (VOID *) ALIGN_POINTER (PadEntry->Data + 1, sizeof (UINTN)); + CopyMem ( + PadEntry->Data->AuthData, + PadData->AuthData, + PadData->AuthDataSize + ); + } else { + PadEntry->Data->AuthDataSize = 0; + PadEntry->Data->AuthData = NULL; + } + + if (PadData->RevocationData != NULL) { + PadEntry->Data->RevocationDataSize = PadData->RevocationDataSize; + PadEntry->Data->RevocationData = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (PadEntry->Data + 1) + PadData->AuthDataSize), + sizeof (UINTN) + ); + CopyMem ( + PadEntry->Data->RevocationData, + PadData->RevocationData, + PadData->RevocationDataSize + ); + } else { + PadEntry->Data->RevocationDataSize = 0; + PadEntry->Data->RevocationData = NULL; + } + // + // Insert the new pad entry. + // + InsertTailList (EntryInsertBefore, &PadEntry->List); + + return EFI_SUCCESS; +} + +/** + This function lookup the data entry from IPsec SPD. Return the configuration + value of the specified SPD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the SPD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + IPSEC_SPD_ENTRY *SpdEntry; + IPSEC_SAD_ENTRY *SadEntry; + EFI_IPSEC_SPD_SELECTOR *SpdSel; + EFI_IPSEC_SPD_DATA *SpdData; + LIST_ENTRY *SpdList; + LIST_ENTRY *SpdSas; + LIST_ENTRY *Entry; + UINTN RequiredSize; + + SpdSel = &Selector->SpdSelector; + SpdData = (EFI_IPSEC_SPD_DATA *) Data; + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + NET_LIST_FOR_EACH (Entry, SpdList) { + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + // + // Find the required spd entry + // + if (CompareSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel, + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector + )) { + + RequiredSize = IpSecGetSizeOfSpdData (SpdEntry->Data); + if (*DataSize < RequiredSize) { + *DataSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + if (SpdData == NULL) { + return EFI_INVALID_PARAMETER; + } + + *DataSize = RequiredSize; + + // + // Extract and fill all SaId array from the spd.sas list + // + SpdSas = &SpdEntry->Data->Sas; + SpdData->SaIdCount = 0; + + NET_LIST_FOR_EACH (Entry, SpdSas) { + SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry); + CopyMem ( + &SpdData->SaId[SpdData->SaIdCount++], + SadEntry->Id, + sizeof (EFI_IPSEC_SA_ID) + ); + } + // + // Fill the other fields in spd data. + // + CopyMem (SpdData->Name, SpdEntry->Data->Name, sizeof (SpdData->Name)); + + SpdData->PackageFlag = SpdEntry->Data->PackageFlag; + SpdData->Action = SpdEntry->Data->Action; + + if (SpdData->Action != EfiIPsecActionProtect) { + SpdData->ProcessingPolicy = NULL; + } else { + SpdData->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ((UINT8 *) SpdData + sizeof (EFI_IPSEC_SPD_DATA) + (SpdData->SaIdCount - 1) * sizeof (EFI_IPSEC_SA_ID)); + + IpSecDuplicateProcessPolicy ( + SpdData->ProcessingPolicy, + SpdEntry->Data->ProcessingPolicy + ); + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + This function lookup the data entry from IPsec SAD. Return the configuration + value of the specified SAD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the SAD entry. + @param[in, out] DataSize On output, the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + IPSEC_SAD_ENTRY *SadEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *SadList; + EFI_IPSEC_SA_ID *SaId; + EFI_IPSEC_SA_DATA *SaData; + UINTN RequiredSize; + + SaId = &Selector->SaId; + SaData = (EFI_IPSEC_SA_DATA *) Data; + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + NET_LIST_FOR_EACH (Entry, SadList) { + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + // + // Find the required sad entry. + // + if (CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) SaId, + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id + )) { + // + // Calculate the required size of the sad entry. + // Data Layout is follows: + // |EFI_IPSEC_SA_DATA + // |AuthKey + // |EncryptKey (Optional) + // |SpdSelector (Optional) + // + RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA)); + + if (SaId->Proto == EfiIPsecAH) { + RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength); + } else { + RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength); + RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength); + } + + if (SadEntry->Data->SpdEntry != NULL) { + RequiredSize += SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector); + } + + + + if (*DataSize < RequiredSize) { + *DataSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + // + // Fill the data fields of sad entry. + // + *DataSize = RequiredSize; + SaData->Mode = SadEntry->Data->Mode; + SaData->SNCount = SadEntry->Data->SequenceNumber; + SaData->AntiReplayWindows = SadEntry->Data->AntiReplayWindowSize; + + CopyMem ( + &SaData->SaLifetime, + &SadEntry->Data->SaLifetime, + sizeof (EFI_IPSEC_SA_LIFETIME) + ); + + ZeroMem ( + &SaData->AlgoInfo, + sizeof (EFI_IPSEC_ALGO_INFO) + ); + + if (SaId->Proto == EfiIPsecAH) { + // + // Copy AH alogrithm INFO to SaData + // + SaData->AlgoInfo.AhAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthAlgoId; + SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength; + if (SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength != 0) { + SaData->AlgoInfo.AhAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN)); + CopyMem ( + SaData->AlgoInfo.AhAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKey, + SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength + ); + } + } else if (SaId->Proto == EfiIPsecESP) { + // + // Copy ESP alogrithem INFO to SaData + // + SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId; + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength; + if (SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) { + SaData->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN)); + CopyMem ( + SaData->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength + ); + } + + SaData->AlgoInfo.EspAlgoInfo.EncAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId; + SaData->AlgoInfo.EspAlgoInfo.EncKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength; + + if (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) { + SaData->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (SaData + 1) + + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength), + sizeof (UINTN) + ); + CopyMem ( + SaData->AlgoInfo.EspAlgoInfo.EncKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey, + SaData->AlgoInfo.EspAlgoInfo.EncKeyLength + ); + } + } + + SaData->PathMTU = SadEntry->Data->PathMTU; + + // + // Fill the spd selector field of sad data + // + if (SadEntry->Data->SpdEntry != NULL) { + + SaData->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) ( + (UINT8 *)SaData + + RequiredSize - + SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector) + ); + + DuplicateSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector, + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdEntry->Selector, + NULL + ); + + } else { + + SaData->SpdSelector = NULL; + } + + SaData->ManualSet = SadEntry->Data->ManualSet; + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + This function lookup the data entry from IPsec PAD. Return the configuration + value of the specified PAD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the PAD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + IPSEC_PAD_ENTRY *PadEntry; + LIST_ENTRY *PadList; + LIST_ENTRY *Entry; + EFI_IPSEC_PAD_ID *PadId; + EFI_IPSEC_PAD_DATA *PadData; + UINTN RequiredSize; + + PadId = &Selector->PadId; + PadData = (EFI_IPSEC_PAD_DATA *) Data; + PadList = &mConfigData[IPsecConfigDataTypePad]; + + NET_LIST_FOR_EACH (Entry, PadList) { + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + + // + // Find the required pad entry. + // + if (ComparePadId ( + (EFI_IPSEC_CONFIG_SELECTOR *) PadId, + (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id + )) { + // + // Calculate the required size of the pad entry. + // + RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA)); + RequiredSize = ALIGN_VARIABLE (RequiredSize + PadEntry->Data->AuthDataSize); + RequiredSize += PadEntry->Data->RevocationDataSize; + + if (*DataSize < RequiredSize) { + *DataSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + // + // Fill the data fields of pad entry + // + *DataSize = RequiredSize; + PadData->AuthProtocol = PadEntry->Data->AuthProtocol; + PadData->AuthMethod = PadEntry->Data->AuthMethod; + PadData->IkeIdFlag = PadEntry->Data->IkeIdFlag; + + // + // Copy Authentication data. + // + if (PadEntry->Data->AuthData != NULL) { + + PadData->AuthDataSize = PadEntry->Data->AuthDataSize; + PadData->AuthData = (VOID *) ALIGN_POINTER ((PadData + 1), sizeof (UINTN)); + CopyMem ( + PadData->AuthData, + PadEntry->Data->AuthData, + PadData->AuthDataSize + ); + } else { + + PadData->AuthDataSize = 0; + PadData->AuthData = NULL; + } + // + // Copy Revocation Data. + // + if (PadEntry->Data->RevocationData != NULL) { + + PadData->RevocationDataSize = PadEntry->Data->RevocationDataSize; + PadData->RevocationData = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (PadData + 1) + PadData->AuthDataSize), + sizeof (UINTN) + ); + CopyMem ( + PadData->RevocationData, + PadEntry->Data->RevocationData, + PadData->RevocationDataSize + ); + } else { + + PadData->RevocationDataSize = 0; + PadData->RevocationData = NULL; + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Copy Source Process Policy to the Destination Process Policy. + + @param[in] Dst Pointer to the Source Process Policy. + @param[in] Src Pointer to the Destination Process Policy. + +**/ +VOID +IpSecDuplicateProcessPolicy ( + IN EFI_IPSEC_PROCESS_POLICY *Dst, + IN EFI_IPSEC_PROCESS_POLICY *Src + ) +{ + // + // Firstly copy the structure content itself. + // + CopyMem (Dst, Src, sizeof (EFI_IPSEC_PROCESS_POLICY)); + + // + // Recursively copy the tunnel option if needed. + // + if (Dst->Mode != EfiIPsecTunnel) { + ASSERT (Dst->TunnelOption == NULL); + } else { + Dst->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ((Dst + 1), sizeof (UINTN)); + CopyMem ( + Dst->TunnelOption, + Src->TunnelOption, + sizeof (EFI_IPSEC_TUNNEL_OPTION) + ); + } +} + +/** + Calculate the a whole size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed + to by the pointer members. + + @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA. + + @return the whole size the specified EFI_IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfEfiSpdData ( + IN EFI_IPSEC_SPD_DATA *SpdData + ) +{ + UINTN Size; + + Size = ALIGN_VARIABLE (sizeof (IPSEC_SPD_DATA)); + + if (SpdData->Action == EfiIPsecActionProtect) { + Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_PROCESS_POLICY)); + + if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) { + Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_TUNNEL_OPTION)); + } + } + + return Size; +} + +/** + Calculate the a whole size of IPSEC_SPD_DATA which includes the buffer size pointed + to by the pointer members and the buffer size used by the Sa List. + + @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA. + + @return the whole size of IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfSpdData ( + IN IPSEC_SPD_DATA *SpdData + ) +{ + UINTN Size; + LIST_ENTRY *Link; + + Size = sizeof (EFI_IPSEC_SPD_DATA) - sizeof (EFI_IPSEC_SA_ID); + + if (SpdData->Action == EfiIPsecActionProtect) { + Size += sizeof (EFI_IPSEC_PROCESS_POLICY); + + if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) { + Size += sizeof (EFI_IPSEC_TUNNEL_OPTION); + } + } + + NET_LIST_FOR_EACH (Link, &SpdData->Sas) { + Size += sizeof (EFI_IPSEC_SA_ID); + } + + return Size; +} + +/** + Get the IPsec Variable. + + Get the all variables which start with the string contained in VaraiableName. + Since all IPsec related variable store in continual space, those kinds of + variable can be searched by the EfiGetNextVariableName. Those variables also are + returned in a continual buffer. + + @param[in] VariableName Pointer to a specified Variable Name. + @param[in] VendorGuid Pointer to a specified Vendor Guid. + @param[in] Attributes Point to memory location to return the attributes + of variable. If the point is NULL, the parameter + would be ignored. + @param[in, out] DataSize As input, point to the maximum size of return + Data-Buffer. As output, point to the actual + size of the returned Data-Buffer. + @param[in] Data Point to return Data-Buffer. + + @retval EFI_ABORTED If the Variable size which contained in the variable + structure doesn't match the variable size obtained + from the EFIGetVariable. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has + been updated with the size needed to complete the request. + @retval EFI_SUCCESS The function completed successfully. + @retval others Other errors found during the variable getting. +**/ +EFI_STATUS +IpSecGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 *Attributes, OPTIONAL + IN OUT UINTN *DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + EFI_GUID VendorGuidI; + UINTN VariableNameLength; + CHAR16 *VariableNameI; + UINTN VariableNameISize; + UINTN VariableNameISizeNew; + UINTN VariableIndex; + UINTN VariableCount; + IP_SEC_VARIABLE_INFO IpSecVariableInfo; + UINTN DataSizeI; + + // + // The variable name constructor is "VariableName + Info/0001/0002/... + NULL". + // So the varialbe name is like "VariableNameInfo", "VariableName0001", ... + // "VariableNameNULL". + // + VariableNameLength = StrLen (VariableName); + VariableNameISize = (VariableNameLength + 5) * sizeof (CHAR16); + VariableNameI = AllocateZeroPool (VariableNameISize); + ASSERT (VariableNameI != NULL); + + // + // Construct the varible name of ipsecconfig meta data. + // + UnicodeSPrint (VariableNameI, VariableNameISize, L"%s%s", VariableName, L"Info"); + + DataSizeI = sizeof (IpSecVariableInfo); + + Status = gRT->GetVariable ( + VariableNameI, + VendorGuid, + Attributes, + &DataSizeI, + &IpSecVariableInfo + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (*DataSize < IpSecVariableInfo.VariableSize) { + *DataSize = IpSecVariableInfo.VariableSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + VariableCount = IpSecVariableInfo.VariableCount; + VariableNameI[0] = L'\0'; + + while (VariableCount != 0) { + // + // Get the variable name one by one in the variable database. + // + VariableNameISizeNew = VariableNameISize; + Status = gRT->GetNextVariableName ( + &VariableNameISizeNew, + VariableNameI, + &VendorGuidI + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + VariableNameI = ReallocatePool ( + VariableNameISize, + VariableNameISizeNew, + VariableNameI + ); + VariableNameISize = VariableNameISizeNew; + + Status = gRT->GetNextVariableName ( + &VariableNameISizeNew, + VariableNameI, + &VendorGuidI + ); + } + + if (EFI_ERROR (Status)) { + break; + } + // + // Check whether the current variable is the required "ipsecconfig". + // + if (StrnCmp (VariableNameI, VariableName, VariableNameLength) == 0 || + CompareGuid (VendorGuid, &VendorGuidI) + ) { + // + // Parse the variable count of the current ipsecconfig data. + // + VariableIndex = StrDecimalToUintn (VariableNameI + VariableNameLength); + if (VariableIndex!= 0 && VariableIndex <= IpSecVariableInfo.VariableCount) { + // + // Get the variable size of the current ipsecconfig data. + // + DataSizeI = 0; + Status = gRT->GetVariable ( + VariableNameI, + VendorGuid, + Attributes, + &DataSizeI, + NULL + ); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + // + // Validate the variable count and variable size. + // + if (VariableIndex != IpSecVariableInfo.VariableCount) { + // + // If the varaibe is not the last one, its size should be the max + // size of the single variable. + // + if (DataSizeI != IpSecVariableInfo.SingleVariableSize) { + return EFI_ABORTED; + } + } else { + if (DataSizeI != IpSecVariableInfo.VariableSize % IpSecVariableInfo.SingleVariableSize) { + return EFI_ABORTED; + } + } + // + // Get the variable data of the current ipsecconfig data and + // store it into user buffer continously. + // + Status = gRT->GetVariable ( + VariableNameI, + VendorGuid, + Attributes, + &DataSizeI, + (UINT8 *) Data + (VariableIndex - 1) * IpSecVariableInfo.SingleVariableSize + ); + ASSERT_EFI_ERROR (Status); + VariableCount--; + } + } + } + // + // The VariableCount in "VariableNameInfo" varaible should have the correct + // numbers of variables which name starts with VariableName. + // + if (VariableCount != 0) { + Status = EFI_ABORTED; + } + +ON_EXIT: + FreePool (VariableNameI); + return Status; +} + +/** + Set the IPsec variables. + + Set all IPsec variables which start with the specified variable name. Those variables + are set one by one. + + @param[in] VariableName The name of the vendor's variable. It is a + Null-Terminated Unicode String. + @param[in] VendorGuid Unify identifier for vendor. + @param[in] Attributes Point to memory location to return the attributes of + variable. If the point is NULL, the parameter would be ignored. + @param[in] DataSize The size in bytes of Data-Buffer. + @param[in] Data Points to the content of the variable. + + @retval EFI_SUCCESS The firmware successfully stored the variable and its data, as + defined by the Attributes. + @retval others Storing the variables failed. + +**/ +EFI_STATUS +IpSecSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + CHAR16 *VariableNameI; + UINTN VariableNameSize; + UINTN VariableIndex; + IP_SEC_VARIABLE_INFO IpSecVariableInfo; + UINT64 MaximumVariableStorageSize; + UINT64 RemainingVariableStorageSize; + UINT64 MaximumVariableSize; + + Status = gRT->QueryVariableInfo ( + Attributes, + &MaximumVariableStorageSize, + &RemainingVariableStorageSize, + &MaximumVariableSize + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // "VariableName + Info/0001/0002/... + NULL" + // + VariableNameSize = (StrLen (VariableName) + 5) * sizeof (CHAR16); + VariableNameI = AllocateZeroPool (VariableNameSize); + + if (VariableNameI == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + // + // Construct the variable of ipsecconfig general information. Like the total + // numbers of the Ipsecconfig variables, the total size of all ipsecconfig variables. + // + UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%s", VariableName, L"Info"); + MaximumVariableSize -= VariableNameSize; + + IpSecVariableInfo.VariableCount = (UINT32) ((DataSize + (UINTN) MaximumVariableSize - 1) / (UINTN) MaximumVariableSize); + IpSecVariableInfo.VariableSize = (UINT32) DataSize; + IpSecVariableInfo.SingleVariableSize = (UINT32) MaximumVariableSize; + + // + // Set the variable of ipsecconfig general information. + // + Status = gRT->SetVariable ( + VariableNameI, + VendorGuid, + Attributes, + sizeof (IpSecVariableInfo), + &IpSecVariableInfo + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error set ipsecconfig meta data with %r\n", Status)); + goto ON_EXIT; + } + + for (VariableIndex = 0; VariableIndex < IpSecVariableInfo.VariableCount; VariableIndex++) { + // + // Construct and set the variable of ipsecconfig data one by one. + // The index of variable name begin from 0001, and the varaible name + // likes "VariableName0001", "VaraiableName0002".... + // + UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%04d", VariableName, VariableIndex + 1); + Status = gRT->SetVariable ( + VariableNameI, + VendorGuid, + Attributes, + (VariableIndex == IpSecVariableInfo.VariableCount - 1) ? + (DataSize % (UINTN) MaximumVariableSize) : + (UINTN) MaximumVariableSize, + (UINT8 *) Data + VariableIndex * (UINTN) MaximumVariableSize + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error set ipsecconfig variable data with %r\n", Status)); + goto ON_EXIT; + } + } + +ON_EXIT: + if (VariableNameI != NULL) { + FreePool (VariableNameI); + } + + return Status; +} + +/** + Return the configuration value for the EFI IPsec driver. + + This function lookup the data entry from IPsec database or IKEv2 configuration + information. The expected data type and unique identification are described in + DataType and Selector parameters. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to retrieve. + @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec + configuration data entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec configuration data. + The type of the data buffer associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - Selector is NULL. + - DataSize is NULL. + - Data is NULL and *DataSize is not zero + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + if (This == NULL || Selector == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*DataSize != 0 && Data == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= IPsecConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + return mGetPolicyEntry[DataType](Selector, DataSize, Data); +} + +/** + Set the security association, security policy and peer authorization configuration + information for the EFI IPsec driver. + + This function is used to set the IPsec configuration information of type DataType for + the EFI IPsec driver. + The IPsec configuration data has a unique selector/identifier separately to identify + a data entry. The selector structure depends on DataType's definition. + Using SetData() with a Data of NULL causes the IPsec configuration data entry identified + by DataType and Selector to be deleted. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be set. + @param[in] Selector Pointer to an entry selector on operated configuration data + specified by DataType. A NULL Selector causes the entire + specified-type configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure of the data buffer is + associated with the DataType. + @param[in] InsertBefore Pointer to one entry selector which describes the expected + position the new data entry will be added. If InsertBefore is NULL, + the new entry will be appended to the end of the database. + + @retval EFI_SUCCESS The specified configuration entry data was set successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigSetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL + ) +{ + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= IPsecConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + Status = mSetPolicyEntry[DataType](Selector, Data, InsertBefore); + + if (!EFI_ERROR (Status) && !mSetBySelf) { + // + // Save the updated config data into variable. + // + IpSecConfigSave (); + } + + return Status; +} + +/** + Enumerates the current selector for IPsec configuration data entry. + + This function is called multiple times to retrieve the entry Selector in IPsec + configuration database. On each call to GetNextSelector(), the next entry + Selector are retrieved into the output interface. + + If the entire IPsec configuration database has been iterated, the error + EFI_NOT_FOUND is returned. + If the Selector buffer is too small for the next Selector copy, an + EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect + the size of buffer needed. + + On the initial call to GetNextSelector() to start the IPsec configuration database + search, a pointer to the buffer with all zero value is passed in Selector. Calls + to SetData() between calls to GetNextSelector may produce unpredictable results. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of IPsec configuration data to retrieve. + @param[in, out] SelectorSize The size of the Selector buffer. + @param[in, out] Selector On input, supplies the pointer to last Selector that was + returned by GetNextSelector(). + On output, returns one copy of the current entry Selector + of a given DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - SelectorSize is NULL. + - Selector is NULL. + @retval EFI_NOT_FOUND The next configuration data entry was not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter + has been updated with the size needed to complete the search + request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetNextSelector ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN OUT UINTN *SelectorSize, + IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + LIST_ENTRY *Link; + IPSEC_COMMON_POLICY_ENTRY *CommonEntry; + BOOLEAN IsFound; + + if (This == NULL || Selector == NULL || SelectorSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= IPsecConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + IsFound = FALSE; + + NET_LIST_FOR_EACH (Link, &mConfigData[DataType]) { + CommonEntry = BASE_CR (Link, IPSEC_COMMON_POLICY_ENTRY, List); + + if (IsFound || mIsZeroSelector[DataType](Selector)) { + // + // If found the appointed entry, then duplicate the next one and return, + // or if the appointed entry is zero, then return the first one directly. + // + return mDuplicateSelector[DataType](Selector, CommonEntry->Selector, SelectorSize); + } else { + // + // Set the flag if find the appointed entry. + // + IsFound = mCompareSelector[DataType](Selector, CommonEntry->Selector); + } + } + + return EFI_NOT_FOUND; +} + +/** + Register an event that is to be signaled whenever a configuration process on the + specified IPsec configuration information is done. + + The register function is not surpport now and always returns EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be registered the event for. + @param[in] Event The event to be registered. + + @retval EFI_SUCCESS The event is registered successfully. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. + @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigRegisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Remove the specified event that was previously registered on the specified IPsec + configuration data. + + This function is not support now and alwasy return EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The configuration data type to remove the registered event for. + @param[in] Event The event to be unregistered. + + @retval EFI_SUCCESS The event was removed successfully. + @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the + database. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigUnregisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Copy whole data in specified EFI_SIPEC_CONFIG_SELECTOR and the Data to a buffer. + + This function is a caller defined function, and it is called by the IpSecVisitConfigData(). + The orignal caller is IpSecConfigSave(), which calls the IpsecVisitConfigData() to + copy all types of IPsec Config datas into one buffer and store this buffer into firmware in + the form of several variables. + + @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE. + @param[in] Selector Points to a EFI_IPSEC_CONFIG_SELECTOR to be copied + to the buffer. + @param[in] Data Points to data to be copied to the buffer. The + Data type is related to the Type. + @param[in] SelectorSize The size of the Selector. + @param[in] DataSize The size of the Data. + @param[in, out] Buffer The buffer to store the Selector and Data. + + @retval EFI_SUCCESS Copy the Selector and Data to a buffer successfully. + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + +**/ +EFI_STATUS +IpSecCopyPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE Type, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN UINTN SelectorSize, + IN UINTN DataSize, + IN OUT IPSEC_VARIABLE_BUFFER *Buffer + ) +{ + IPSEC_VAR_ITEM_HEADER SelectorHeader; + IPSEC_VAR_ITEM_HEADER DataHeader; + UINTN EntrySize; + UINT8 *TempPoint; + + if (Type == IPsecConfigDataTypeSad) { + // + // Don't save automatically-generated sa entry into variable. + // + if (((EFI_IPSEC_SA_DATA *) Data)->ManualSet == FALSE) { + return EFI_SUCCESS; + } + } + // + // Increase the capacity size of the buffer if needed. + // + EntrySize = ALIGN_VARIABLE (sizeof (SelectorHeader)); + EntrySize = ALIGN_VARIABLE (EntrySize + SelectorSize); + EntrySize = ALIGN_VARIABLE (EntrySize + sizeof (SelectorHeader)); + EntrySize = ALIGN_VARIABLE (EntrySize + DataSize); + + //EntrySize = SelectorSize + DataSize + 2 * sizeof (SelectorHeader); + if (Buffer->Capacity - Buffer->Size < EntrySize) { + // + // Calculate the required buffer + // + Buffer->Capacity += EntrySize; + TempPoint = AllocatePool (Buffer->Capacity); + + if (Buffer->Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Copy the old Buffer to new buffer and free the old one. + // + CopyMem (TempPoint, Buffer->Ptr, Buffer->Size); + FreePool (Buffer->Ptr); + + Buffer->Ptr = TempPoint; + } + + mFixPolicyEntry[Type](Selector, Data); + + // + // Fill the selector header and copy it into buffer. + // + SelectorHeader.Type = (UINT8) (Type | IPSEC_VAR_ITEM_HEADER_LOGO_BIT); + SelectorHeader.Size = (UINT16) SelectorSize; + + CopyMem ( + Buffer->Ptr + Buffer->Size, + &SelectorHeader, + sizeof (SelectorHeader) + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (SelectorHeader)); + + // + // Copy the selector into buffer. + // + CopyMem ( + Buffer->Ptr + Buffer->Size, + Selector, + SelectorSize + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + SelectorSize); + + // + // Fill the data header and copy it into buffer. + // + DataHeader.Type = (UINT8) Type; + DataHeader.Size = (UINT16) DataSize; + + CopyMem ( + Buffer->Ptr + Buffer->Size, + &DataHeader, + sizeof (DataHeader) + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (DataHeader)); + // + // Copy the data into buffer. + // + CopyMem ( + Buffer->Ptr + Buffer->Size, + Data, + DataSize + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + DataSize); + + mUnfixPolicyEntry[Type](Selector, Data); + + return EFI_SUCCESS; +} + +/** + Visit all IPsec Configurations of specified Type and call the caller defined + interface. + + @param[in] DataType The specified IPsec Config Data Type. + @param[in] Routine The function defined by the caller. + @param[in] Context The data passed to the Routine. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated + @retval EFI_SUCCESS This function completed successfully. + +**/ +EFI_STATUS +IpSecVisitConfigData ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN IPSEC_COPY_POLICY_ENTRY Routine, + IN VOID *Context + ) +{ + EFI_STATUS GetNextStatus; + EFI_STATUS GetDataStatus; + EFI_STATUS RoutineStatus; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + UINTN SelectorSize; + UINTN DataSize; + UINTN SelectorBufferSize; + UINTN DataBufferSize; + BOOLEAN FirstGetNext; + + FirstGetNext = TRUE; + DataBufferSize = 0; + Data = NULL; + SelectorBufferSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR); + Selector = AllocateZeroPool (SelectorBufferSize); + + if (Selector == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + while (TRUE) { + // + // Get the real size of the selector. + // + SelectorSize = SelectorBufferSize; + GetNextStatus = EfiIpSecConfigGetNextSelector ( + &mIpSecConfigInstance, + DataType, + &SelectorSize, + Selector + ); + if (GetNextStatus == EFI_BUFFER_TOO_SMALL) { + FreePool (Selector); + SelectorBufferSize = SelectorSize; + // + // Allocate zero pool for the first selector, while store the last + // selector content for the other selectors. + // + if (FirstGetNext) { + Selector = AllocateZeroPool (SelectorBufferSize); + } else { + Selector = AllocateCopyPool (SelectorBufferSize, Selector); + } + + if (Selector == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get the content of the selector. + // + GetNextStatus = EfiIpSecConfigGetNextSelector ( + &mIpSecConfigInstance, + DataType, + &SelectorSize, + Selector + ); + } + + if (EFI_ERROR (GetNextStatus)) { + break; + } + + FirstGetNext = FALSE; + + // + // Get the real size of the policy entry according to the selector. + // + DataSize = DataBufferSize; + GetDataStatus = EfiIpSecConfigGetData ( + &mIpSecConfigInstance, + DataType, + Selector, + &DataSize, + Data + ); + if (GetDataStatus == EFI_BUFFER_TOO_SMALL) { + if (Data != NULL) { + FreePool (Data); + } + + DataBufferSize = DataSize; + Data = AllocateZeroPool (DataBufferSize); + + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get the content of the policy entry according to the selector. + // + GetDataStatus = EfiIpSecConfigGetData ( + &mIpSecConfigInstance, + DataType, + Selector, + &DataSize, + Data + ); + } + + if (EFI_ERROR (GetDataStatus)) { + break; + } + // + // Prepare the buffer of updated policy entry, which is stored in + // the continous memory, and then save into variable later. + // + RoutineStatus = Routine ( + DataType, + Selector, + Data, + SelectorSize, + DataSize, + Context + ); + if (EFI_ERROR (RoutineStatus)) { + break; + } + } + + if (Data != NULL) { + FreePool (Data); + } + + if (Selector != NULL) { + FreePool (Selector); + } + + return EFI_SUCCESS; +} + +/** + This function is the subfunction of EFIIpSecConfigSetData. + + This function call IpSecSetVaraible to set the IPsec Configuration into the firmware. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS Saved the configration successfully. + @retval Others Other errors were found while obtaining the variable. + +**/ +EFI_STATUS +IpSecConfigSave ( + VOID + ) +{ + IPSEC_VARIABLE_BUFFER Buffer; + EFI_STATUS Status; + EFI_IPSEC_CONFIG_DATA_TYPE Type; + + Buffer.Size = 0; + Buffer.Capacity = IPSEC_DEFAULT_VARIABLE_SIZE; + Buffer.Ptr = AllocateZeroPool (Buffer.Capacity); + + if (Buffer.Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // For each policy database, prepare the contious buffer to save into variable. + // + for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) { + IpSecVisitConfigData ( + Type, + (IPSEC_COPY_POLICY_ENTRY) IpSecCopyPolicyEntry, + &Buffer + ); + } + // + // Save the updated policy database into variable. + // + Status = IpSecSetVariable ( + IPSECCONFIG_VARIABLE_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + Buffer.Size, + Buffer.Ptr + ); + + FreePool (Buffer.Ptr); + + return Status; +} + +/** + Get the all IPSec configuration variables and store those variables + to the internal data structure. + + This founction is called by IpSecConfigInitialize() which is to intialize the + IPsecConfiguration Protocol. + + @param[in] Private Point to IPSEC_PRIVATE_DATA. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated + @retval EFI_SUCCESS Restore the IPsec Configuration successfully. + @retval others Other errors is found while obtaining the variable. + +**/ +EFI_STATUS +IpSecConfigRestore ( + IN IPSEC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + UINT8 *Buffer; + IPSEC_VAR_ITEM_HEADER *Header; + UINT8 *Ptr; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + EFI_IPSEC_CONFIG_DATA_TYPE Type; + VOID *Data; + UINT8 Value; + UINTN Size; + + Value = 0; + Size = sizeof (Value); + BufferSize = 0; + Buffer = NULL; + + Status = gRT->GetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + NULL, + &Size, + &Value + ); + + if (!EFI_ERROR (Status) && Value == IPSEC_STATUS_ENABLED) { + Private->IpSec.DisabledFlag = FALSE; + } + // + // Get the real size of policy database in variable. + // + Status = IpSecGetVariable ( + IPSECCONFIG_VARIABLE_NAME, + &gEfiIpSecConfigProtocolGuid, + NULL, + &BufferSize, + Buffer + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get the content of policy database in variable. + // + Status = IpSecGetVariable ( + IPSECCONFIG_VARIABLE_NAME, + &gEfiIpSecConfigProtocolGuid, + NULL, + &BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + for (Ptr = Buffer; Ptr < Buffer + BufferSize;) { + + Header = (IPSEC_VAR_ITEM_HEADER *) Ptr; + Type = (EFI_IPSEC_CONFIG_DATA_TYPE) (Header->Type & IPSEC_VAR_ITEM_HEADER_CONTENT_BIT); + ASSERT (((Header->Type & 0x80) == IPSEC_VAR_ITEM_HEADER_LOGO_BIT) && (Type < IPsecConfigDataTypeMaximum)); + + Selector = (EFI_IPSEC_CONFIG_SELECTOR *) ALIGN_POINTER (Header + 1, sizeof (UINTN)); + Header = (IPSEC_VAR_ITEM_HEADER *) ALIGN_POINTER ( + (UINT8 *) Selector + Header->Size, + sizeof (UINTN) + ); + ASSERT (Header->Type == Type); + + Data = ALIGN_POINTER (Header + 1, sizeof (UINTN)); + + mUnfixPolicyEntry[Type](Selector, Data); + + // + // Update each policy entry according to the content in variable. + // + mSetBySelf = TRUE; + Status = EfiIpSecConfigSetData ( + &Private->IpSecConfig, + Type, + Selector, + Data, + NULL + ); + mSetBySelf = FALSE; + + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Ptr = ALIGN_POINTER ((UINT8 *) Data + Header->Size, sizeof (UINTN)); + } + + FreePool (Buffer); + } + + return EFI_SUCCESS; +} + +/** + Install and Initialize IPsecConfig protocol + + @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish, + the pointer of IPsecConfig Protocol implementation will copy + into its IPsecConfig member. + + @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully. + @retval Others Initializing the IPsecConfig Protocol failed. +**/ +EFI_STATUS +IpSecConfigInitialize ( + IN OUT IPSEC_PRIVATE_DATA *Private + ) +{ + EFI_IPSEC_CONFIG_DATA_TYPE Type; + + CopyMem ( + &Private->IpSecConfig, + &mIpSecConfigInstance, + sizeof (EFI_IPSEC_CONFIG_PROTOCOL) + ); + + // + // Initialize the list head of policy database. + // + for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) { + InitializeListHead (&mConfigData[Type]); + } + // + // Restore the content of policy database according to the variable. + // + IpSecConfigRestore (Private); + + return gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiIpSecConfigProtocolGuid, + &Private->IpSecConfig, + NULL + ); +} diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.h b/NetworkPkg/IpSecDxe/IpSecConfigImpl.h new file mode 100644 index 0000000000..54d43bd01c --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecConfigImpl.h @@ -0,0 +1,952 @@ +/** @file + Definitions related to IPSEC_CONFIG_PROTOCOL implementations. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _IPSEC_CONFIG_IMPL_H_ +#define _IPSEC_CONFIG_IMPL_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "IpSecImpl.h" + +#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF +#define EFI_IPSEC_ANY_PORT 0 + +#define IPSEC_VAR_ITEM_HEADER_LOGO_BIT 0x80 +#define IPSEC_VAR_ITEM_HEADER_CONTENT_BIT 0x7F + +#define IPSECCONFIG_VARIABLE_NAME L"IpSecConfig" +#define IPSECCONFIG_STATUS_NAME L"IpSecStatus" + +#define SIZE_OF_SPD_SELECTOR(x) (UINTN) (sizeof (EFI_IPSEC_SPD_SELECTOR) \ + + sizeof (EFI_IP_ADDRESS_INFO) * ((x)->LocalAddressCount + (x)->RemoteAddressCount)) + +#define FIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) - (UINTN) (base)) +#define UNFIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) + (UINTN) (base)) + +// +// The data structure used to store the genernall information of IPsec configuration. +// +typedef struct { + UINT32 VariableCount; // the total number of the IPsecConfig variables. + UINT32 VariableSize; // The total size of all IpsecConfig variables. + UINT32 SingleVariableSize; // The max size of single variable +} IP_SEC_VARIABLE_INFO; + +typedef struct { + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + LIST_ENTRY List; +} IPSEC_COMMON_POLICY_ENTRY; + +typedef struct { + UINT8 *Ptr; + UINTN Size; + UINTN Capacity; +} IPSEC_VARIABLE_BUFFER; + +#pragma pack(1) +typedef struct { + UINT8 Type; + UINT16 Size; +} IPSEC_VAR_ITEM_HEADER; +#pragma pack() + +/** + The prototype of Copy Source Selector to the Destination Selector. + + @param[in out] DstSel Pointer of Destination Selector. It would be + SPD Selector, or SAD Selector or PAD Selector. + @param[in] SrcSel Pointer of Source Selector. It would be + SPD Selector, or SAD Selector or PAD Selector. + @param[in out] Size The size of the Destination Selector. If it + is not NULL and its value is less than the size of + Source Selector, the value of Source Selector's + size will be passed to the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source Selector is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source Selector. + @retval EFI_SUCCESS Copy Source Selector to the Destination + Selector successfully. + +**/ +typedef +EFI_STATUS +(*IPSEC_DUPLICATE_SELECTOR) ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + It is prototype of compare two Selectors. The Selector would be SPD Selector, + or SAD Selector, or PAD selector. + + @param[in] Selector1 Pointer of the first Selector. + @param[in] Selector2 Pointer of the second Selector. + + @retval TRUE These two Selectors have the same value in certain fields. + @retval FALSE Not all fields have the same value in these two Selectors. + +**/ +typedef +BOOLEAN +(*IPSEC_COMPARE_SELECTOR) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + The prototype of a function to check if the Selector is Zero by its certain fields. + + @param[in] Selector Pointer of the Selector. + + @retval TRUE If the Selector is Zero. + @retval FALSE If the Selector is not Zero. + +**/ +typedef +BOOLEAN +(*IPSEC_IS_ZERO_SELECTOR) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + The prototype of a function to fix the value of particular members of the Selector. + + @param[in] Selector Pointer of Selector. + @param[in] Data Pointer of Data. + +**/ +typedef +VOID +(*IPSEC_FIX_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data + ); + +/** + It is prototype function to define a routine function by the caller of IpSecVisitConfigData(). + + @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE. + @param[in] Selector Points to EFI_IPSEC_CONFIG_SELECTOR to be copied + to the buffer. + @param[in] Data Points to data to be copied to the buffer. The + Data type is related to the Type. + @param[in] SelectorSize The size of the Selector. + @param[in] DataSize The size of the Data. + @param[in out] Buffer The buffer to store the Selector and Data. + + @retval EFI_SUCCESS Copied the Selector and Data to a buffer successfully. + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + +**/ +typedef +EFI_STATUS +(*IPSEC_COPY_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_DATA_TYPE Type, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN UINTN SelectorSize, + IN UINTN DataSize, + IN OUT VOID *Context + ); + +/** + Set the security policy information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL, the new entry will + be appended to the end of the database. + + @retval EFI_INVALID_PARAMETER Certain Parameters are not correct. The Parameter + requiring a check depends on the Selector type. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +typedef +EFI_STATUS +(*IPSEC_SET_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + A prototype function definition to lookup the data entry from IPsec. Return the configuration + value of the specified Entry. + + @param[in] Selector Pointer to an entry selector that is an identifier + of the entry. + @param[in, out] DataSize On output, the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +typedef +EFI_STATUS +(*IPSEC_GET_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + IN VOID *Data + ); + +/** + Compare two SPD Selectors. + + Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/ + NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the + Local Addresses and remote Addresses. + + @param[in] Selector1 Pointer of the first SPD Selector. + @param[in] Selector2 Pointer of the second SPD Selector. + + @retval TRUE These two Selectors have the same value in above fields. + @retval FALSE Not all of the above fields have the same value in these two Selectors. + +**/ +BOOLEAN +CompareSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + + +/** + Visit all IPsec Configurations of specified Type and call the caller defined + interface. + + @param[in] DataType The specified IPsec Config Data Type. + @param[in] Routine The function caller defined. + @param[in] Context The data passed to the Routine. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS This function complete successfully. + +**/ +EFI_STATUS +IpSecVisitConfigData ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN IPSEC_COPY_POLICY_ENTRY Routine, + IN VOID *Context + ); + + +/** + This function is the subfunction of the EFIIpSecConfigSetData. + + This function call IpSecSetVaraible to set the IPsec Configuration into the firmware. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS Saved the configration successfully. + @retval Others Other errors were found while obtaining the variable. + +**/ +EFI_STATUS +IpSecConfigSave ( + VOID + ); + +/** + Initialize IPsecConfig protocol + + @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish, + the pointer of IPsecConfig Protocol implementation will copy + into its IPsecConfig member. + + @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully. + @retval Others Initializing the IPsecConfig Protocol failed. + +**/ +EFI_STATUS +IpSecConfigInitialize ( + IN OUT IPSEC_PRIVATE_DATA *Private + ); + +/** + Calculate the entire size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed + by the pointer members. + + @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA. + + @return The entire size of the specified EFI_IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfEfiSpdData ( + IN EFI_IPSEC_SPD_DATA *SpdData + ); + +/** + Calculate the a entire size of IPSEC_SPD_DATA, which includes the buffer size pointed + by the pointer members and the buffer size used by Sa List. + + @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA. + + @return The entire size of IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfSpdData ( + IN IPSEC_SPD_DATA *SpdData + ); + +/** + Copy Source Process Policy to the Destination Process Policy. + + @param[in] Dst Pointer to the Source Process Policy. + @param[in] Src Pointer to the Destination Process Policy. + +**/ +VOID +IpSecDuplicateProcessPolicy ( + IN EFI_IPSEC_PROCESS_POLICY *Dst, + IN EFI_IPSEC_PROCESS_POLICY *Src + ); + +/** + Compare two SPD Selectors. + + Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/ + NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the + Local Addresses and remote Addresses. + + @param[in] Selector1 Pointer of the first SPD Selector. + @param[in] Selector2 Pointer of the second SPD Selector. + + @retval TRUE This two Selector have the same value in above fields. + @retval FALSE Not all of the above fields have the same value in these two Selectors. + +**/ +BOOLEAN +CompareSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + Compare two SA IDs. + + @param[in] Selector1 Pointer of the first SA ID. + @param[in] Selector2 Pointer of the second SA ID. + + @retval TRUE This two Selectors have the same SA ID. + @retval FALSE This two Selecotrs don't have the same SA ID. + +**/ +BOOLEAN +CompareSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + Compare two PAD IDs. + + @param[in] Selector1 Pointer of the first PAD ID. + @param[in] Selector2 Pointer of the second PAD ID. + + @retval TRUE This two Selectors have the same PAD ID. + @retval FALSE This two Selecotrs don't have the same PAD ID. + +**/ +BOOLEAN +ComparePadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount + fields. + + @param[in] Selector Pointer of the SPD Selector. + + @retval TRUE If the SPD Selector is Zero. + @retval FALSE If the SPD Selector is not Zero. + +**/ +BOOLEAN +IsZeroSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Check if the SA ID is Zero by its DestAddress. + + @param[in] Selector Pointer of the SA ID. + + @retval TRUE If the SA ID is Zero. + @retval FALSE If the SA ID is not Zero. + +**/ +BOOLEAN +IsZeroSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Check if the PAD ID is Zero. + + @param[in] Selector Pointer of the PAD ID. + + @retval TRUE If the PAD ID is Zero. + @retval FALSE If the PAD ID is not Zero. + +**/ +BOOLEAN +IsZeroPadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Copy Source SPD Selector to the Destination SPD Selector. + + @param[in, out] DstSel Pointer of Destination SPD Selector. + @param[in] SrcSel Pointer of Source SPD Selector. + @param[in, out] Size The size of the Destination SPD Selector. If + it is not NULL and its value is less than the + size of Source SPD Selector, the value of + Source SPD Selector's size will be passed to + the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source SPD Selector. + @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD + Selector successfully. + +**/ +EFI_STATUS +DuplicateSpdSelector ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + Copy Source SA ID to the Destination SA ID. + + @param[in, out] DstSel Pointer of the Destination SA ID. + @param[in] SrcSel Pointer of the Source SA ID. + @param[in, out] Size The size of the Destination SA ID. If it + not NULL, and its value is less than the size of + Source SA ID, the value of Source SA ID's size + will be passed to the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID. + @retval EFI_SUCCESS Copied Source SA ID to the Destination SA ID successfully. + +**/ +EFI_STATUS +DuplicateSaId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + Copy Source PAD ID to the Destination PAD ID. + + @param[in, out] DstSel Pointer of Destination PAD ID. + @param[in] SrcSel Pointer of Source PAD ID. + @param[in, out] Size The size of the Destination PAD ID. If it + not NULL, and its value less than the size of + Source PAD ID, the value of Source PAD ID's size + will be passed to the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID. + @retval EFI_SUCCESS Copied Source PAD ID to the Destination PAD ID successfully. + +**/ +EFI_STATUS +DuplicatePadId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + Fix the value of some members of the SPD Selector. + + This function is called by IpSecCopyPolicyEntry(), which copies the Policy + Entry into the Variable. Since some members in SPD Selector are pointers, + a physical address to relative address conversion is required before copying + this SPD entry into the variable. + + @param[in] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +FixSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ); + +/** + Fix the value of some members of SA ID. + + This function is called by IpSecCopyPolicyEntry(), which copies the Policy + Entry into the Variable. Since some members in SA ID are pointers, + a physical address to relative address conversion is required before copying + this SAD into the variable. + + @param[in] SaId Pointer of SA ID. + @param[in, out] Data Pointer of SA Data. + +**/ +VOID +FixSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA *Data + ); + +/** + Fix the value of some members of PAD ID. + + This function is called by IpSecCopyPolicyEntry(), which copy the Policy + Entry into the Variable. Since some members in PAD ID are pointers, + a physical address to relative address conversion is required before copying + this PAD into the variable. + + @param[in] PadId Pointer of PAD ID. + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +FixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ); + +/** + Recover the value of some members of SPD Selector. + + This function is corresponding to FixSpdEntry(). It recovers the value of members + of SPD Selector which fix by the FixSpdEntry(). + + @param[in, out] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +UnfixSpdEntry ( + IN OUT EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ); + + +/** + Recover the value of some members of SA ID. + + This function is corresponding to FixSadEntry(). It recovers the value of members + of SAD ID which fix by the FixSadEntry(). + + @param[in, out] SaId Pointer of SAD ID + @param[in, out] Data Pointer of SAD Data. + +**/ +VOID +UnfixSadEntry ( + IN OUT EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA *Data + ); + +/** + Recover the value of some members of PAD ID. + + This function is corresponding to FixPadEntry(). It recovers the value of members + of PAD ID which fix by the FixPadEntry(). + + @param[in] PadId Pointer of PAD ID + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +UnfixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ); + +/** + Set the security policy information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SPD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL,the new entry will + be appended the end of database. + + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - Selector is not NULL and its LocalAddress + is NULL or its RemoteAddress is NULL. + - Data is not NULL, its Action is Protected, + and its policy is NULL. + - Data is not NULL and its Action is not protected + and its policy is not NULL. + - The Action of Data is Protected, its policy + mode is Tunnel, and its tunnel option is NULL. + - The Action of Data is protected, its policy + mode is not Tunnel, and it tunnel option is not NULL. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + Set the security association information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SA_DATA. + @param[in] Context Pointer to one entry selector which describes + the expected position the new data entry will + be added. If Context is NULL,the new entry will + be appended to the end of database. + + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + Set the peer authorization configuration information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_PAD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position where the new data entry will + be added. If Context is NULL, the new entry will + be appended the end of database. + + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + This function looks up the data entry from IPsec SPD, and returns the configuration + value of the specified SPD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the SPD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + This function looks up the data entry from IPsec SAD and returns the configuration + value of the specified SAD Entry. + + @param[in] Selector Pointer to an entry selector that is an identifier + of the SAD entry. + @param[in, out] DataSize On output, the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. This type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + This function looks up the data entry from IPsec PADand returns the configuration + value of the specified PAD Entry. + + @param[in] Selector Pointer to an entry selector that is an identifier + of the PAD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. This type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + Return the configuration value for the EFI IPsec driver. + + This function lookup the data entry from IPsec database or IKEv2 configuration + information. The expected data type and unique identification are described in + DataType and Selector parameters. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to retrieve. + @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec + configuration data entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec configuration data. + The type of the data buffer is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - Selector is NULL. + - DataSize is NULL. + - Data is NULL and *DataSize is not zero + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + Set the security association, security policy and peer authorization configuration + information for the EFI IPsec driver. + + This function is used to set the IPsec configuration information of type DataType for + the EFI IPsec driver. + The IPsec configuration data has a unique selector/identifier separately to identify + a data entry. The selector structure depends on DataType's definition. + Using SetData() with a Data of NULL causes the IPsec configuration data entry identified + by DataType and Selector to be deleted. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be set. + @param[in] Selector Pointer to an entry selector on operated configuration data + specified by DataType. A NULL Selector causes the entire + specified-type configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure of the data buffer is + associated with the DataType. + @param[in] InsertBefore Pointer to one entry selector which describes the expected + position the new data entry will be added. If InsertBefore is NULL, + the new entry will be appended the end of database. + + @retval EFI_SUCCESS The specified configuration entry data was set successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigSetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL + ); + +/** + Enumerates the current selector for IPsec configuration data entry. + + This function is called multiple times to retrieve the entry Selector in IPsec + configuration database. On each call to GetNextSelector(), the next entry + Selector are retrieved into the output interface. + + If the entire IPsec configuration database has been iterated, the error + EFI_NOT_FOUND is returned. + If the Selector buffer is too small for the next Selector copy, an + EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect + the size of buffer needed. + + On the initial call to GetNextSelector() to start the IPsec configuration database + search, a pointer to the buffer with all zero value is passed in Selector. Calls + to SetData() between calls to GetNextSelector may produce unpredictable results. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of IPsec configuration data to retrieve. + @param[in, out] SelectorSize The size of the Selector buffer. + @param[in, out] Selector On input, supplies the pointer to last Selector that was + returned by GetNextSelector(). + On output, returns one copy of the current entry Selector + of a given DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - SelectorSize is NULL. + - Selector is NULL. + @retval EFI_NOT_FOUND The next configuration data entry was not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter + has been updated with the size needed to complete the search + request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetNextSelector ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN OUT UINTN *SelectorSize, + IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Register an event that is to be signaled whenever a configuration process on the + specified IPsec configuration information is done. + + The register function is not surpport now and always returns EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be registered the event for. + @param[in] Event The event to be registered. + + @retval EFI_SUCCESS The event is registered successfully. + @retval EFI_INVALID_PARAMETER This is NULL, or Event is NULL. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. + @retval EFI_UNSUPPORTED The notify registration unsupported, or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigRegisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ); + + +/** + Remove the specified event that was previously registered on the specified IPsec + configuration data. + + This function is not supported now and always returns EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The configuration data type to remove the registered event for. + @param[in] Event The event to be unregistered. + + @retval EFI_SUCCESS The event was removed successfully. + @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the + database. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The notify registration unsupported or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigUnregisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ); + +#endif diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.c b/NetworkPkg/IpSecDxe/IpSecCryptIo.c new file mode 100644 index 0000000000..7011f98b06 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecCryptIo.c @@ -0,0 +1,133 @@ +/** @file + Common operation for Security. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecCryptIo.h" +// +// Alogrithm's informations for the Encrypt/Decrpt Alogrithm. +// +ENCRYPT_ALGORITHM mIpsecEncryptAlgorithmList[IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE] = { + {EFI_IPSEC_EALG_NULL, 0, 0, 1, NULL, NULL, NULL, NULL}, + {(UINT8)-1, 0, 0, 0, NULL, NULL, NULL, NULL} +}; +// +// Alogrithm's informations for the Authentication algorithm +// +AUTH_ALGORITHM mIpsecAuthAlgorithmList[IPSEC_AUTH_ALGORITHM_LIST_SIZE] = { + {EFI_IPSEC_AALG_NONE, 0, 0, 0, NULL, NULL, NULL, NULL}, + {EFI_IPSEC_AALG_NULL, 0, 0, 0, NULL, NULL, NULL, NULL}, + {(UINT8)-1, 0, 0, 0, NULL, NULL, NULL, NULL} +}; + + +/** + Get the block size of encrypt alogrithm. The block size is based on the algorithm used. + + @param[in] AlgorithmId The encrypt algorithm ID. + + @return The value of block size. + +**/ +UINTN +IpSecGetEncryptBlockSize ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) { + // + // The BlockSize is same with IvSize. + // + return mIpsecEncryptAlgorithmList[Index].BlockSize; + } + } + + return (UINTN) -1; +} + +/** + Get the IV size of encrypt alogrithm. The IV size is based on the algorithm used. + + @param[in] AlgorithmId The encrypt algorithm ID. + + @return The value of IV size. + +**/ +UINTN +IpSecGetEncryptIvLength ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) { + // + // The BlockSize is same with IvSize. + // + return mIpsecEncryptAlgorithmList[Index].IvLength; + } + } + + return (UINTN) -1; +} + +/** + Get the ICV size of Authenticaion alogrithm. The ICV size is based on the algorithm used. + + @param[in] AuthAlgorithmId The Authentication algorithm ID. + + @return The value of ICV size. + +**/ +UINTN +IpSecGetIcvLength ( + IN UINT8 AuthAlgorithmId + ) +{ + UINT8 Index; + for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) { + if (AuthAlgorithmId == mIpsecAuthAlgorithmList[Index].AlgorithmId) { + return mIpsecAuthAlgorithmList[Index].IcvLength; + } + } + return (UINTN) -1; +} + +/** + Generate a random data for IV. If the IvSize is zero, not needed to create + IV and return EFI_SUCCESS. + + @param[in] IvBuffer The pointer of the IV buffer. + @param[in] IvSize The IV size. + + @retval EFI_SUCCESS Create a random data for IV. + +**/ +EFI_STATUS +IpSecGenerateIv ( + IN UINT8 *IvBuffer, + IN UINTN IvSize + ) +{ + if (IvSize != 0) { + // + //TODO: return CryptGenerateRandom (IvBuffer, IvSize); + // + return EFI_SUCCESS; + } + return EFI_SUCCESS; +} diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.h b/NetworkPkg/IpSecDxe/IpSecCryptIo.h new file mode 100644 index 0000000000..d883a2ef72 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecCryptIo.h @@ -0,0 +1,322 @@ +/** @file + Definition related to the Security operation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _EFI_IPSEC_CRYPTIO_H_ +#define _EFI_IPSEC_CRYPTIO_H_ + +#include +#include + +#define IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE 2 +#define IPSEC_AUTH_ALGORITHM_LIST_SIZE 3 + +/** + Prototype of Hash GetContextSize. + + Retrieves the size, in bytes, of the context buffer required. + + @return The size, in bytes, of the context buffer required. + +**/ +typedef +UINTN +(EFIAPI *CPL_HASH_GETCONTEXTSIZE) ( + VOID + ); + +/** + Prototype of Hash Operation Initiating. + + Initialization with a new context. + + + @param[in,out] Context Input Context. + + @retval TRUE Initialization Successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *CPL_HASH_INIT) ( + IN OUT VOID *Context + ); + +/** + Prototype of HASH update. + Hash update operation. Continue an Hash message digest operation, processing + another message block, and updating the Hash context. + + If Context is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + + @param[in,out] Context The Specified Context. + @param[in,out] Data The Input Data to hash. + @param[in] DataLength The length, in bytes, of Data. + + @retval TRUE Update data successfully. + @retval FALSE The Context has been finalized. + +**/ +typedef +BOOLEAN +(EFIAPI *CPL_HASH_UPDATE) ( + IN OUT VOID *Context, + IN CONST VOID *Data, + IN UINTN DataLength + ); + +/** + Prototype of Hash finallization. + Terminate a Hash message digest operation and output the message digest. + + If Context is NULL, then ASSERT(). + If HashValue is NULL, then ASSERT(). + + @param[in,out] Context The specified Context. + @param[out] HashValue Pointer to a 16-byte message digest output buffer. + + @retval TRUE Finalized successfully. + +**/ +typedef +BOOLEAN +(EFIAPI *CPL_HASH_FINAL) ( + IN OUT VOID *Context, + OUT UINT8 *HashValue + ); + +/** + Prototype of Cipher GetContextSize. + + Retrieves the size, in bytes, of the context buffer required. + + @return The size, in bytes, of the context buffer required. + +**/ +typedef +UINTN +(EFIAPI *CPL_CIPHER_GETCONTEXTSIZE) ( + VOID + ); + +/** + Prototype of Cipher initiation. + Intializes the user-supplied key as the specifed context (key materials) for both + encryption and decryption operations. + + If Context is NULL, then ASSERT(). + If Key is NULL, then generate random key for usage. + + @param[in,out] Context The specified Context. + @param[in] Key User-supplied TDES key (64/128/192 bits). + @param[in] KeyBits Key length in bits. + + @retval TRUE TDES Initialization was successful. + +**/ +typedef +BOOLEAN +(EFIAPI *CPL_CIPHER_INIT) ( + IN OUT VOID *Context, + IN CONST UINT8 *Key, + IN CONST UINTN KeyBits + ); + + +/** + Prototype of Cipher encryption. + Encrypts plaintext message with the specified cipher. + + If Context is NULL, then ASSERT(). + if InData is NULL, then ASSERT(). + If Size of input data is not multiple of Cipher algorithm related block size, + then ASSERT(). + + @param[in] Context The specified Context. + @param[in] InData The input plaintext data to be encrypted. + @param[out] OutData The resultant encrypted ciphertext. + @param[in] DataLength Length of input data in bytes. + + @retval TRUE Encryption successful. + +**/ +typedef +BOOLEAN +(EFIAPI *CPL_CIPHER_ENCRYPT) ( + IN VOID *Context, + IN CONST UINT8 *InData, + OUT UINT8 *OutData, + IN CONST UINTN DataLength + ); + + +/** + Prototype of Cipher decryption. + Decrypts cipher message with specified cipher. + + If Context is NULL, then ASSERT(). + if InData is NULL, then ASSERT(). + If Size of input data is not a multiple of a certaion block size , then ASSERT(). + + @param[in] Context The specified Context. + @param[in] InData The input ciphertext data to be decrypted. + @param[out] OutData The resultant decrypted plaintext. + @param[in] DataLength Length of input data in bytes. + + @retval TRUE Decryption successful. + +**/ +typedef +BOOLEAN +(EFIAPI *CPL_CIPHER_DECRYPT) ( + IN CONST VOID *Context, + IN CONST UINT8 *InData, + OUT UINT8 *OutData, + IN CONST UINTN DataLength + ); + +// +// The struct used to store the informatino and operation of Cipher algorithm. +// +typedef struct _ENCRYPT_ALGORITHM { +// +// The ID of the Algorithm +// +UINT8 AlgorithmId; +// +// The Key length of the Algorithm +// +UINTN KeyLength; +// +// Iv Size of the Algorithm +// +UINTN IvLength; +// +// The Block Size of the Algorithm +// +UINTN BlockSize; +// +// The Function pointer of GetContextSize. +// +CPL_CIPHER_GETCONTEXTSIZE CipherGetContextSize; +// +// The Function pointer of Cipher intitiaion. +// +CPL_CIPHER_INIT CipherInitiate; +// +// The Function pointer of Cipher Encryption. +// +CPL_CIPHER_ENCRYPT CipherEncrypt; +// +// The Function pointer of Cipher Decrption. +// +CPL_CIPHER_DECRYPT CipherDecrypt; +} ENCRYPT_ALGORITHM; + +// +// The struct used to store the informatino and operation of Autahentication algorithm. +// +typedef struct _AUTH_ALGORITHM { + // + // ID of the Algorithm + // + UINT8 AlgorithmId; + // + // The Key length of the Algorithm + // + UINTN KeyLength; + // + // The ICV length of the Algorithm + // + UINTN IcvLength; + // + // The block size of the Algorithm + // + UINTN BlockSize; + // + // The function pointer of GetContextSize. + // + CPL_HASH_GETCONTEXTSIZE HashGetContextSize; + // + // The function pointer of Initiatoion + // + CPL_HASH_INIT HashInitiate; + // + // The function pointer of Hash Update. + // + CPL_HASH_UPDATE HashUpdate; + // + // The fucntion pointer of Hash Final + // + CPL_HASH_FINAL HashFinal; +} AUTH_ALGORITHM; + +/** + Get the IV size of encrypt alogrithm. IV size is different from different algorithm. + + @param[in] AlgorithmId The encrypt algorithm ID. + + @return The value of IV size. + +**/ +UINTN +IpSecGetEncryptIvLength ( + IN UINT8 AlgorithmId + ); + +/** + Get the block size of encrypt alogrithm. Block size is different from different algorithm. + + @param[in] AlgorithmId The encrypt algorithm ID. + + @return The value of block size. + +**/ +UINTN +IpSecGetEncryptBlockSize ( + IN UINT8 AlgorithmId + ); + +/** + Get the ICV size of Authenticaion alogrithm. ICV size is different from different algorithm. + + @param[in] AuthAlgorithmId The Authentication algorithm ID. + + @return The value of ICV size. + +**/ +UINTN +IpSecGetIcvLength ( + IN UINT8 AuthAlgorithmId + ); + +/** + Generate a random data for IV. If the IvSize is zero, not needed to create + IV and return EFI_SUCCESS. + + @param[in] IvBuffer The pointer of the IV buffer. + @param[in] IvSize The IV size. + + @retval EFI_SUCCESS Create random data for IV. + +**/ +EFI_STATUS +IpSecGenerateIv ( + IN UINT8 *IvBuffer, + IN UINTN IvSize + ); + +#endif + diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.c b/NetworkPkg/IpSecDxe/IpSecDebug.c new file mode 100644 index 0000000000..8a5811b960 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecDebug.c @@ -0,0 +1,172 @@ +/** @file + Interface of IPsec printing debug information. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecImpl.h" +#include "IpSecDebug.h" + +// +// The print title for IKEv1 variety phase. +// +CHAR8 *mStateStr[] = { + "IKEv1_MAIN_1", + "IKEv1_MAIN_2", + "IKEv1_MAIN_3", + "IKEv1_MAIN_ESTABLISHED", + "IKEv1_QUICK_1", + "IKEv1_QUICK_2", + "IKEv1_QUICK_ESTABLISHED" +}; +// +// The print title for IKEv1 variety Exchagne. +// +CHAR8 *mExchangeStr[] = { + "IKEv1 Main Exchange", + "IKEv1 Info Exchange", + "IKEv1 Quick Exchange", + "IKEv1 Unknown Exchange" +}; + +// +// The print title for IKEv1 variety Payload. +// +CHAR8 *mPayloadStr[] = { + "IKEv1 None Payload", + "IKEv1 SA Payload", + "IKEv1 Proposal Payload", + "IKEv1 Transform Payload", + "IKEv1 KE Payload", + "IKEv1 ID Payload", + "IKEv1 Certificate Payload", + "IKEv1 Certificate Request Payload", + "IKEv1 Hash Payload", + "IKEv1 Signature Payload", + "IKEv1 Nonce Payload", + "IKEv1 Notify Payload", + "IKEv1 Delete Payload", + "IKEv1 Vendor Payload" +}; + +/** + Print the IP address. + + @param[in] Level Debug print error level. Pass to DEBUG(). + @param[in] Ip Point to a specified IP address. + @param[in] IpVersion The IP Version. + +**/ +VOID +IpSecDumpAddress ( + IN UINTN Level, + IN EFI_IP_ADDRESS *Ip, + IN UINT8 IpVersion + ) +{ + if (IpVersion == IP_VERSION_6) { + DEBUG ( + (Level, + "%x%x:%x%x:%x%x:%x%x", + Ip->v6.Addr[0], + Ip->v6.Addr[1], + Ip->v6.Addr[2], + Ip->v6.Addr[3], + Ip->v6.Addr[4], + Ip->v6.Addr[5], + Ip->v6.Addr[6], + Ip->v6.Addr[7]) + ); + DEBUG ( + (Level, + ":%x%x:%x%x:%x%x:%x%x\n", + Ip->v6.Addr[8], + Ip->v6.Addr[9], + Ip->v6.Addr[10], + Ip->v6.Addr[11], + Ip->v6.Addr[12], + Ip->v6.Addr[13], + Ip->v6.Addr[14], + Ip->v6.Addr[15]) + ); + } else { + DEBUG ( + (Level, + "%d.%d.%d.%d\n", + Ip->v4.Addr[0], + Ip->v4.Addr[1], + Ip->v4.Addr[2], + Ip->v4.Addr[3]) + ); + } + +} + +/** + Print IKEv1 Current states. + + @param[in] Previous The Previous state of IKEv1. + @param[in] Current The current state of IKEv1. + +**/ +VOID +IpSecDumpState ( + IN UINT32 Previous, + IN UINT32 Current + ) +{ + if (Previous == Current) { + DEBUG ((DEBUG_INFO, "\n****Current state is %a\n", mStateStr[Previous])); + } else { + DEBUG ((DEBUG_INFO, "\n****Change state from %a to %a\n", mStateStr[Previous], mStateStr[Current])); + } + +} + +/** + Print the buffer in form of Hex. + + @param[in] Title The strings to be printed before the data of the buffer. + @param[in] Data Points to buffer to be printed. + @param[in] DataSize The size of the buffer to be printed. + +**/ +VOID +IpSecDumpBuf ( + IN CHAR8 *Title, + IN UINT8 *Data, + IN UINTN DataSize + ) +{ + UINTN Index; + UINTN DataIndex; + UINTN BytesRemaining; + UINTN BytesToPrint; + + DataIndex = 0; + BytesRemaining = DataSize; + + DEBUG ((DEBUG_INFO, "==%a %d bytes==\n", Title, DataSize)); + + while (BytesRemaining > 0) { + + BytesToPrint = (BytesRemaining > IPSEC_DEBUG_BYTE_PER_LINE) ? IPSEC_DEBUG_BYTE_PER_LINE : BytesRemaining; + + for (Index = 0; Index < BytesToPrint; Index++) { + DEBUG ((DEBUG_INFO, " 0x%02x,", Data[DataIndex++])); + } + + DEBUG ((DEBUG_INFO, "\n")); + BytesRemaining -= BytesToPrint; + } + +} diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.h b/NetworkPkg/IpSecDxe/IpSecDebug.h new file mode 100644 index 0000000000..0e6e6811c5 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecDebug.h @@ -0,0 +1,102 @@ +/** @file + The definition of functions and MACROs used for IPsec debug information print. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _EFI_IPSEC_DEBUG_H_ +#define _EFI_IPSEC_DEBUG_H_ + +#include + +#define IPSEC_DUMP_ADDRESS(Level, Ip, Version) IpSecDumpAddress (Level, Ip, Version) +#define IPSEC_DUMP_STATE(Previous, Current) IpSecDumpState (Previous, Current) +#define IPSEC_DUMP_PACKET(Packet, Direction, IpVersion) IpSecDumpPacket (Packet, Direction, IpVersion) +#define IPSEC_DUMP_PAYLOAD(IkePayload) IpSecDumpPayload (IkePayload) +#define IPSEC_DUMP_BUF(Title, Data, DataSize) IpSecDumpBuf (Title, Data, DataSize) + +#define IPSEC_DEBUG_BYTE_PER_LINE 8 + + +/** + Print the IP address. + + @param[in] Level Debug print error level. Pass to DEBUG(). + @param[in] Ip Point to specified IP address. + @param[in] IpVersion The IP Version. + +**/ +VOID +IpSecDumpAddress ( + IN UINTN Level, + IN EFI_IP_ADDRESS *Ip, + IN UINT8 IpVersion + ); + +/** + Print IKEv1 Current states. + + @param[in] Previous The Previous state of IKEv1. + @param[in] Current The current state of IKEv1. + +**/ +VOID +IpSecDumpState ( + IN UINT32 Previous, + IN UINT32 Current + ); + +/** + Print the Ike Packet. + + @param[in] Packet Point to IKE packet to be printed. + @param[in] Direction Point to the IKE packet is inbound or outbound. + @param[in] IpVersion Specified IP Version. + +**/ +/* +VOID +IpSecDumpPacket ( + IN IKE_PACKET *Packet, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN UINT8 IpVersion + ); +*/ + +/** + Print the IKE Paylolad. + + @param[in] IkePayload Points to the payload to be printed. + +**/ +/* +VOID +IpSecDumpPayload ( + IN IKE_PAYLOAD *IkePayload + ); +*/ +/** + Print the buffer in form of Hex. + + @param[in] Title The strings to be printed before the data of the buffer. + @param[in] Data Points to the buffer to be printed. + @param[in] DataSize The size of the buffer to be printed. + +**/ +VOID +IpSecDumpBuf ( + IN CHAR8 *Title, + IN UINT8 *Data, + IN UINTN DataSize + ); + +#endif diff --git a/NetworkPkg/IpSecDxe/IpSecDriver.c b/NetworkPkg/IpSecDxe/IpSecDriver.c new file mode 100644 index 0000000000..b38f2a9452 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecDriver.c @@ -0,0 +1,282 @@ +/** @file + Driver Binding Protocol for IPsec Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 +#include "IpSecConfigImpl.h" +#include "IpSecDebug.h" + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +IpSecDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + // + //TODO: Add Udp4Protocol and Udp6Protocol testing. + // + return EFI_UNSUPPORTED; +} + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. + Currently not implemented. + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +IpSecDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + // + //TODO: Add Udp4Io and Udp6Io creation for the IKE. + // + return EFI_SUCCESS; +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of a device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +IpSecDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + // + //TODO: Add UdpIo4 and UdpIo6 destruction when the Udp driver unload or stop. + // + return EFI_UNSUPPORTED; +} + +EFI_DRIVER_BINDING_PROTOCOL gIpSecDriverBinding = { + IpSecDriverBindingSupported, + IpSecDriverBindingStart, + IpSecDriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + This is a callback function when the mIpSecInstance.DisabledEvent is signaled. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +IpSecCleanupAllSa ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IPSEC_PRIVATE_DATA *Private; + UINT8 Value; + EFI_STATUS Status; + + Private = (IPSEC_PRIVATE_DATA *) Context; + + // + // Set the Status Variable + // + Value = IPSEC_STATUS_DISABLED; + Status = gRT->SetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Value), + &Value + ); + if (!EFI_ERROR (Status)) { + Private->IpSec.DisabledFlag = TRUE; + } + +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for IPsec driver which installs the driver binding, + component name protocol, IPsec Config protcolon, and IPsec protocol in + its ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_ALREADY_STARTED The IPsec driver has been already loaded. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The operation is failed. + +**/ +EFI_STATUS +EFIAPI +IpSecDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + IPSEC_PRIVATE_DATA *Private; + EFI_IPSEC_PROTOCOL *IpSec; + + // + // Check whether ipsec protocol has already been installed. + // + Status = gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &IpSec); + + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "_ModuleEntryPoint: IpSec has been already loaded\n")); + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, (VOID **) &mDpc); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to locate EfiDpcProtocol\n")); + goto ON_EXIT; + } + + Private = AllocateZeroPool (sizeof (IPSEC_PRIVATE_DATA)); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to allocate private data\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + // + // Create disable event to cleanup all sa when ipsec disabled by user. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + IpSecCleanupAllSa, + Private, + &mIpSecInstance.DisabledEvent + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to create disable event\n")); + goto ON_FREE_PRIVATE; + } + + Private->Signature = IPSEC_PRIVATE_DATA_SIGNATURE; + Private->ImageHandle = ImageHandle; + CopyMem (&Private->IpSec, &mIpSecInstance, sizeof (EFI_IPSEC_PROTOCOL)); + + // + // Initilize Private's members. Thess members is used for IKE. + // + InitializeListHead (&Private->Udp4List); + InitializeListHead (&Private->Udp6List); + InitializeListHead (&Private->Ikev1SessionList); + InitializeListHead (&Private->Ikev1EstablishedList); + InitializeListHead (&Private->Ikev2SessionList); + InitializeListHead (&Private->Ikev2EstablishedList); + + // + // Initialize the ipsec config data and restore it from variable. + // + Status = IpSecConfigInitialize (Private); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to initialize IpSecConfig\n")); + goto ON_CLOSE_EVENT; + } + // + // Install ipsec protocol which is used by ip driver to process ipsec header. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiIpSecProtocolGuid, + &Private->IpSec, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_UNINSTALL_CONFIG; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIpSecDriverBinding, + ImageHandle, + &gIpSecComponentName, + &gIpSecComponentName2 + ); + if (EFI_ERROR (Status)) { + goto ON_UNINSTALL_CONFIG; + } + + return Status; + +ON_UNINSTALL_CONFIG: + gBS->UninstallProtocolInterface ( + Private->Handle, + &gEfiIpSecConfigProtocolGuid, + &Private->IpSecConfig + ); +ON_CLOSE_EVENT: + gBS->CloseEvent (mIpSecInstance.DisabledEvent); + mIpSecInstance.DisabledEvent = NULL; +ON_FREE_PRIVATE: + FreePool (Private); +ON_EXIT: + return Status; +} + diff --git a/NetworkPkg/IpSecDxe/IpSecDxe.inf b/NetworkPkg/IpSecDxe/IpSecDxe.inf new file mode 100644 index 0000000000..250ef1cdca --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecDxe.inf @@ -0,0 +1,63 @@ +## @file +# Component description file for IpSec module. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = IpSecDxe + FILE_GUID = EE8367C0-A1D6-4565-8F89-EF628547B722 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = IpSecDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + IpSecConfigImpl.c + IpSecConfigImpl.h + IpSecCryptIo.h + IpSecCryptIo.c + IpSecDebug.h + ComponentName.c + IpSecImpl.c + IpSecDebug.c + IpSecSaEngine.c + IpSecDriver.c + IpSecImpl.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + PrintLib + DpcLib + NetLib + +[Protocols] + gEfiIp4ConfigProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIpSecConfigProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiIpSecProtocolGuid # PROTOCOL ALWAYS_PRODUCED diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.c b/NetworkPkg/IpSecDxe/IpSecImpl.c new file mode 100644 index 0000000000..15884ae403 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecImpl.c @@ -0,0 +1,856 @@ +/** @file + The implementation of IPsec Protocol + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecConfigImpl.h" + +EFI_IPSEC_PROTOCOL mIpSecInstance = { IpSecProcess, NULL, TRUE }; + +extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum]; + +/** + Check if the specified Address is the Valid Address Range. + + This function checks if the bytes after prefixed length are all Zero in this + Address. This Address is supposed to point to a range address, meaning it only + gives the correct prefixed address. + + @param[in] IpVersion The IP version. + @param[in] Address Points to EFI_IP_ADDRESS to be checked. + @param[in] PrefixLength The PrefixeLength of this address. + + @retval TRUE The address is a vaild address range. + @retval FALSE The address is not a vaild address range. + +**/ +BOOLEAN +IpSecValidAddressRange ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *Address, + IN UINT8 PrefixLength + ) +{ + UINT8 Div; + UINT8 Mod; + UINT8 Mask; + UINT8 AddrLen; + UINT8 *Addr; + EFI_IP_ADDRESS ZeroAddr; + + if (PrefixLength == 0) { + return TRUE; + } + + AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128); + + if (AddrLen <= PrefixLength) { + return FALSE; + } + + Div = (UINT8) (PrefixLength / 8); + Mod = (UINT8) (PrefixLength % 8); + Addr = (UINT8 *) Address; + ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS)); + + // + // Check whether the mod part of host scope is zero or not. + // + if (Mod > 0) { + Mask = (UINT8) (0xFF << (8 - Mod)); + + if ((Addr[Div] | Mask) != Mask) { + return FALSE; + } + + Div++; + } + // + // Check whether the div part of host scope is zero or not. + // + if (CompareMem ( + &Addr[Div], + &ZeroAddr, + sizeof (EFI_IP_ADDRESS) - Div + ) != 0) { + return FALSE; + } + + return TRUE; +} + +/** + Extrct the Address Range from a Address. + + This function keep the prefix address and zero other part address. + + @param[in] Address Point to a specified address. + @param[in] PrefixLength The prefix length. + @param[out] Range Contain the return Address Range. + +**/ +VOID +IpSecExtractAddressRange ( + IN EFI_IP_ADDRESS *Address, + IN UINT8 PrefixLength, + OUT EFI_IP_ADDRESS *Range + ) +{ + UINT8 Div; + UINT8 Mod; + UINT8 Mask; + UINT8 *Addr; + + if (PrefixLength == 0) { + return ; + } + + Div = (UINT8) (PrefixLength / 8); + Mod = (UINT8) (PrefixLength % 8); + Addr = (UINT8 *) Range; + + CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS)); + + // + // Zero the mod part of host scope. + // + if (Mod > 0) { + Mask = (UINT8) (0xFF << (8 - Mod)); + Addr[Div] = (UINT8) (Addr[Div] & Mask); + Div++; + } + // + // Zero the div part of host scope. + // + ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div); + +} + +/** + Checks if the IP Address in the address range of AddressInfos specified. + + @param[in] IpVersion The IP version. + @param[in] IpAddr Point to EFI_IP_ADDRESS to be check. + @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check + the IP Address is matched. + @param[in] AddressCount The total numbers of the AddressInfo. + + @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified. + @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified. + +**/ +BOOLEAN +IpSecMatchIpAddress ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_IP_ADDRESS_INFO *AddressInfo, + IN UINT32 AddressCount + ) +{ + EFI_IP_ADDRESS Range; + UINT32 Index; + BOOLEAN IsMatch; + + IsMatch = FALSE; + + for (Index = 0; Index < AddressCount; Index++) { + // + // Check whether the target address is in the address range + // if it's a valid range of address. + // + if (IpSecValidAddressRange ( + IpVersion, + &AddressInfo[Index].Address, + AddressInfo[Index].PrefixLength + )) { + // + // Get the range of the target address belongs to. + // + ZeroMem (&Range, sizeof (EFI_IP_ADDRESS)); + IpSecExtractAddressRange ( + IpAddr, + AddressInfo[Index].PrefixLength, + &Range + ); + + if (CompareMem ( + &Range, + &AddressInfo[Index].Address, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + // + // The target address is in the address range. + // + IsMatch = TRUE; + break; + } + } + + if (CompareMem ( + IpAddr, + &AddressInfo[Index].Address, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + // + // The target address is exact same as the address. + // + IsMatch = TRUE; + break; + } + } + + return IsMatch; +} + +/** + Check if the specified Protocol and Prot is supported by the specified SPD Entry. + + This function is the subfunction of IPsecLookUpSpdEntry() that is used to + check if the sent/received IKE packet has the related SPD entry support. + + @param[in] Protocol The Protocol to be checked. + @param[in] IpPayload Point to IP Payload to be check. + @param[in] SpdProtocol The Protocol supported by SPD. + @param[in] SpdLocalPort The Local Port in SPD. + @param[in] SpdRemotePort The Remote Port in SPD. + @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving. + + @retval TRUE The Protocol and Port are supported by the SPD Entry. + @retval FALSE The Protocol and Port are not supported by the SPD Entry. + +**/ +BOOLEAN +IpSecMatchNextLayerProtocol ( + IN UINT8 Protocol, + IN UINT8 *IpPayload, + IN UINT16 SpdProtocol, + IN UINT16 SpdLocalPort, + IN UINT16 SpdRemotePort, + IN BOOLEAN IsOutbound + ) +{ + BOOLEAN IsMatch; + + if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) { + return TRUE; + } + + IsMatch = FALSE; + + if (SpdProtocol == Protocol) { + switch (Protocol) { + case EFI_IP_PROTO_UDP: + case EFI_IP_PROTO_TCP: + // + // For udp and tcp, (0, 0) means no need to check local and remote + // port. The payload is passed from upper level, which means it should + // be in network order. + // + IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0); + IsMatch = (BOOLEAN) (IsMatch || + (IsOutbound && + (BOOLEAN)( + NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort && + NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort + ) + )); + + IsMatch = (BOOLEAN) (IsMatch || + (!IsOutbound && + (BOOLEAN)( + NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort && + NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort + ) + )); + break; + + case EFI_IP_PROTO_ICMP: + // + // For icmpv4, type code is replaced with local port and remote port, + // and (0, 0) means no need to check. + // + IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0); + IsMatch = (BOOLEAN) (IsMatch || + (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort && + ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort + ) + ); + break; + + case IP6_ICMP: + // + // For icmpv6, type code is replaced with local port and remote port, + // and (0, 0) means no need to check. + // + IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0); + + IsMatch = (BOOLEAN) (IsMatch || + (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort && + ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort + ) + ); + break; + + default: + IsMatch = TRUE; + break; + } + } + + return IsMatch; +} + +/** + Find the SAD through a specified SPD's SAD list. + + @param[in] SadList SAD list related to a specified SPD entry. + @param[in] DestAddress The destination address used to find the SAD entry. + + @return The pointer to a certain SAD entry. + +**/ +IPSEC_SAD_ENTRY * +IpSecLookupSadBySpd ( + IN LIST_ENTRY *SadList, + IN EFI_IP_ADDRESS *DestAddress + ) +{ + LIST_ENTRY *Entry; + IPSEC_SAD_ENTRY *SadEntry; + + for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) { + + SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry); + // + // Find the right sad entry which contains the appointed dest address. + // + if (CompareMem ( + &SadEntry->Id->DestAddress, + DestAddress, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + return SadEntry; + } + } + + return NULL; +} + +/** + Find the SAD through whole SAD list. + + @param[in] Spi The SPI used to search the SAD entry. + @param[in] DestAddress The destination used to search the SAD entry. + + @return the pointer to a certain SAD entry. + +**/ +IPSEC_SAD_ENTRY * +IpSecLookupSadBySpi ( + IN UINT32 Spi, + IN EFI_IP_ADDRESS *DestAddress + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *SadList; + IPSEC_SAD_ENTRY *SadEntry; + + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) { + + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + // + // Find the right sad entry which contain the appointed spi and dest addr. + // + if (SadEntry->Id->Spi == Spi && CompareMem ( + &SadEntry->Id->DestAddress, + DestAddress, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + + return SadEntry; + } + } + + return NULL; +} + +/** + Look up if there is existing SAD entry for specified IP packet sending. + + This function is called by the IPsecProcess when there is some IP packet needed to + send out. This function checks if there is an existing SAD entry that can be serviced + to this IP packet sending. If no existing SAD entry could be used, this + function will invoke an IPsec Key Exchange Negotiation. + + @param[in] Private Points to private data. + @param[in] NicHandle Points to a NIC handle. + @param[in] IpVersion The version of IP. + @param[in] IpHead The IP Header of packet to be sent out. + @param[in] IpPayload The IP Payload to be sent out. + @param[in] OldLastHead The Last protocol of the IP packet. + @param[in] SpdEntry Points to a related SPD entry. + @param[out] SadEntry Contains the Point of a related SAD entry. + + @retval EFI_DEVICE_ERROR One of following conditions is TRUE: + - If don't find related UDP service. + - Sequence Number is used up. + - Extension Sequence Number is used up. + @retval EFI_DEVICE_ERROR GC_TODO: Add description for return value. + @retval EFI_NOT_READY No existing SAD entry could be used. + @retval EFI_SUCCESS Find the related SAD entry. + +**/ +EFI_STATUS +IpSecLookupSadEntry ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE NicHandle, + IN UINT8 IpVersion, + IN VOID *IpHead, + IN UINT8 *IpPayload, + IN UINT8 OldLastHead, + IN IPSEC_SPD_ENTRY *SpdEntry, + OUT IPSEC_SAD_ENTRY **SadEntry + ) +{ + IPSEC_SAD_ENTRY *Entry; + IPSEC_SAD_DATA *Data; + EFI_IP_ADDRESS DestIp; + UINT32 SeqNum32; + + *SadEntry = NULL; + // + // Parse the destination address from ip header. + // + ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS)); + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &DestIp, + &((IP4_HEAD *) IpHead)->Dst, + sizeof (IP4_ADDR) + ); + } else { + CopyMem ( + &DestIp, + &((EFI_IP6_HEADER *) IpHead)->DestinationAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + // + // Find the sad entry in the spd.sas list according to the dest address. + // + Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp); + + if (Entry == NULL) { + + if (OldLastHead != IP6_ICMP || + (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST) + ) { + // + // TODO: Start ike negotiation process except the request packet of ping. + // + //IkeNegotiate (UdpService, SpdEntry, &DestIp); + } + + return EFI_NOT_READY; + } + + Data = Entry->Data; + + if (!Data->ManualSet) { + if (Data->ESNEnabled) { + // + // Validate the 64bit sn number if 64bit sn enabled. + // + if (Data->SequenceNumber + 1 < Data->SequenceNumber) { + // + // TODO: Re-negotiate SA + // + return EFI_DEVICE_ERROR; + } + } else { + // + // Validate the 32bit sn number if 64bit sn disabled. + // + SeqNum32 = (UINT32) Data->SequenceNumber; + if (SeqNum32 + 1 < SeqNum32) { + // + // TODO: Re-negotiate SA + // + return EFI_DEVICE_ERROR; + } + } + } + + *SadEntry = Entry; + + return EFI_SUCCESS; +} + +/** + Find a PAD entry according to a remote IP address. + + @param[in] IpVersion The version of IP. + @param[in] IpAddr Points to remote IP address. + + @return the pointer of related PAD entry. + +**/ +IPSEC_PAD_ENTRY * +IpSecLookupPadEntry ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr + ) +{ + LIST_ENTRY *PadList; + LIST_ENTRY *Entry; + EFI_IP_ADDRESS_INFO *IpAddrInfo; + IPSEC_PAD_ENTRY *PadEntry; + + PadList = &mConfigData[IPsecConfigDataTypePad]; + + for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) { + + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + IpAddrInfo = &PadEntry->Id->Id.IpAddress; + // + // Find the right pad entry which contain the appointed dest addr. + // + if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) { + return PadEntry; + } + } + + return NULL; +} + +/** + Check if the specified IP packet can be serviced by this SPD entry. + + @param[in] SpdEntry Point to SPD entry. + @param[in] IpVersion Version of IP. + @param[in] IpHead Point to IP header. + @param[in] IpPayload Point to IP payload. + @param[in] Protocol The Last protocol of IP packet. + @param[in] IsOutbound Traffic direction. + + @retval EFI_IPSEC_ACTION The support action of SPD entry. + @retval -1 If the input packet header doesn't match the SpdEntry. + +**/ +EFI_IPSEC_ACTION +IpSecLookupSpdEntry ( + IN IPSEC_SPD_ENTRY *SpdEntry, + IN UINT8 IpVersion, + IN VOID *IpHead, + IN UINT8 *IpPayload, + IN UINT8 Protocol, + IN BOOLEAN IsOutbound + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel; + IP4_HEAD *Ip4; + EFI_IP6_HEADER *Ip6; + EFI_IP_ADDRESS SrcAddr; + EFI_IP_ADDRESS DstAddr; + BOOLEAN SpdMatch; + + ASSERT (SpdEntry != NULL); + SpdSel = SpdEntry->Selector; + Ip4 = (IP4_HEAD *) IpHead; + Ip6 = (EFI_IP6_HEADER *) IpHead; + + ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS)); + + // + // Parse the source and destination address from ip header. + // + if (IpVersion == IP_VERSION_4) { + CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR)); + CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR)); + } else { + CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)); + } + // + // Check the local and remote addresses for outbound traffic + // + SpdMatch = (BOOLEAN)(IsOutbound && + IpSecMatchIpAddress ( + IpVersion, + &SrcAddr, + SpdSel->LocalAddress, + SpdSel->LocalAddressCount + ) && + IpSecMatchIpAddress ( + IpVersion, + &DstAddr, + SpdSel->RemoteAddress, + SpdSel->RemoteAddressCount + ) + ); + + // + // Check the local and remote addresses for inbound traffic + // + SpdMatch = (BOOLEAN) (SpdMatch || + (!IsOutbound && + IpSecMatchIpAddress ( + IpVersion, + &DstAddr, + SpdSel->LocalAddress, + SpdSel->LocalAddressCount + ) && + IpSecMatchIpAddress ( + IpVersion, + &SrcAddr, + SpdSel->RemoteAddress, + SpdSel->RemoteAddressCount + ) + )); + + // + // Check the next layer protocol and local and remote ports. + // + SpdMatch = (BOOLEAN) (SpdMatch && + IpSecMatchNextLayerProtocol ( + Protocol, + IpPayload, + SpdSel->NextLayerProtocol, + SpdSel->LocalPort, + SpdSel->RemotePort, + IsOutbound + ) + ); + + if (SpdMatch) { + // + // Find the right spd entry if match the 5 key elements. + // + return SpdEntry->Data->Action; + } + + return (EFI_IPSEC_ACTION) - 1; +} + +/** + Handles IPsec packet processing for inbound and outbound IP packets. + + The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet. + The behavior is that it can perform one of the following actions: + bypass the packet, discard the packet, or protect the packet. + + @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance. + @param[in] NicHandle Instance of the network interface. + @param[in] IpVersion IPV4 or IPV6. + @param[in, out] IpHead Pointer to the IP Header. + @param[in] LastHead The protocol of the next layer to be processed by IPsec. + @param[in] OptionsBuffer Pointer to the options buffer. + @param[in] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments. + @param[in] FragmentCount Number of fragments. + @param[in] TrafficDirection Traffic direction. + @param[out] RecycleSignal Event for recycling of resources. + + @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + +**/ +EFI_STATUS +EFIAPI +IpSecProcess ( + IN EFI_IPSEC_PROTOCOL *This, + IN EFI_HANDLE NicHandle, + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN UINT8 *LastHead, + IN VOID *OptionsBuffer, + IN UINT32 OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection, + OUT EFI_EVENT *RecycleSignal + ) +{ + IPSEC_PRIVATE_DATA *Private; + IPSEC_SPD_ENTRY *SpdEntry; + IPSEC_SAD_ENTRY *SadEntry; + LIST_ENTRY *SpdList; + LIST_ENTRY *Entry; + EFI_IPSEC_ACTION Action; + EFI_STATUS Status; + UINT8 *IpPayload; + UINT8 OldLastHead; + BOOLEAN IsOutbound; + + Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (This); + IpPayload = (*FragmentTable)[0].FragmentBuffer; + IsOutbound = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE); + OldLastHead = *LastHead; + *RecycleSignal = NULL; + + if (!IsOutbound) { + // + // For inbound traffic, process the ipsec header of the packet. + // + Status = IpSecProtectInboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + &SpdEntry, + RecycleSignal + ); + + if (Status == EFI_ACCESS_DENIED) { + // + // The packet is denied to access. + // + goto ON_EXIT; + } + + if (Status == EFI_SUCCESS) { + // + // Check the spd entry if the packet is accessible. + // + if (SpdEntry == NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + Action = IpSecLookupSpdEntry ( + SpdEntry, + IpVersion, + IpHead, + IpPayload, + *LastHead, + IsOutbound + ); + + if (Action != EfiIPsecActionProtect) { + // + // Discard the packet if the spd entry is not protect. + // + gBS->SignalEvent (*RecycleSignal); + *RecycleSignal = NULL; + Status = EFI_ACCESS_DENIED; + } + + goto ON_EXIT; + } + } + + Status = EFI_ACCESS_DENIED; + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + for (Entry = SpdList->ForwardLink; Entry != SpdList; Entry = Entry->ForwardLink) { + // + // For outbound and non-ipsec Inbound traffic: check the spd entry. + // + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + Action = IpSecLookupSpdEntry ( + SpdEntry, + IpVersion, + IpHead, + IpPayload, + OldLastHead, + IsOutbound + ); + + switch (Action) { + + case EfiIPsecActionProtect: + + if (IsOutbound) { + // + // For outbound traffic, lookup the sad entry. + // + Status = IpSecLookupSadEntry ( + Private, + NicHandle, + IpVersion, + IpHead, + IpPayload, + OldLastHead, + SpdEntry, + &SadEntry + ); + + if (SadEntry != NULL) { + // + // Process the packet by the found sad entry. + // + Status = IpSecProtectOutboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + SadEntry, + RecycleSignal + ); + + } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) { + // + // TODO: if no need return not ready to upper layer, change here. + // + Status = EFI_SUCCESS; + } + } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) { + // + // For inbound icmpv6 traffic except ping request, accept the packet + // although no sad entry associated with protect spd entry. + // + IpSecLookupSadEntry ( + Private, + NicHandle, + IpVersion, + IpHead, + IpPayload, + OldLastHead, + SpdEntry, + &SadEntry + ); + if (SadEntry == NULL) { + Status = EFI_SUCCESS; + } + } + + goto ON_EXIT; + + case EfiIPsecActionBypass: + Status = EFI_SUCCESS; + goto ON_EXIT; + + case EfiIPsecActionDiscard: + goto ON_EXIT; + + default: + // + // Discard the packet if no spd entry match. + // + break; + } + } + +ON_EXIT: + return Status; +} + diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.h b/NetworkPkg/IpSecDxe/IpSecImpl.h new file mode 100644 index 0000000000..644c658082 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecImpl.h @@ -0,0 +1,313 @@ +/** @file + The definitions related to IPsec protocol implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _IP_SEC_IMPL_H_ +#define _IP_SEC_IMPL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _IPSEC_PRIVATE_DATA IPSEC_PRIVATE_DATA; +typedef struct _IPSEC_SPD_ENTRY IPSEC_SPD_ENTRY; +typedef struct _IPSEC_PAD_ENTRY IPSEC_PAD_ENTRY; +typedef struct _IPSEC_SPD_DATA IPSEC_SPD_DATA; + +#define IPSEC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('I', 'P', 'S', 'E') + +#define IPSEC_PRIVATE_DATA_FROM_IPSEC(a) CR (a, IPSEC_PRIVATE_DATA, IpSec, IPSEC_PRIVATE_DATA_SIGNATURE) +#define IPSEC_PRIVATE_DATA_FROM_UDP4LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp4List, IPSEC_PRIVATE_DATA_SIGNATURE) +#define IPSEC_PRIVATE_DATA_FROM_UDP6LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp6List, IPSEC_PRIVATE_DATA_SIGNATURE) +#define IPSEC_UDP_SERVICE_FROM_LIST(a) BASE_CR (a, IKE_UDP_SERVICE, List) +#define IPSEC_SPD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SPD_ENTRY, List) +#define IPSEC_SAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SAD_ENTRY, List) +#define IPSEC_PAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_PAD_ENTRY, List) +#define IPSEC_SAD_ENTRY_FROM_SPD(a) BASE_CR (a, IPSEC_SAD_ENTRY, BySpd) + +#define IPSEC_STATUS_DISABLED 0 +#define IPSEC_STATUS_ENABLED 1 +#define IPSEC_ESP_PROTOCOL 50 +#define IPSEC_AH_PROTOCOL 51 +#define IPSEC_DEFAULT_VARIABLE_SIZE 0x100 + +// +// Internal Structure Definition +// +#pragma pack(1) +typedef struct _EFI_AH_HEADER { + UINT8 NextHeader; + UINT8 PayloadLen; + UINT16 Reserved; + UINT32 Spi; + UINT32 SequenceNumber; +} EFI_AH_HEADER; + +typedef struct _EFI_ESP_HEADER { + UINT32 Spi; + UINT32 SequenceNumber; +} EFI_ESP_HEADER; + +typedef struct _EFI_ESP_TAIL { + UINT8 PaddingLength; + UINT8 NextHeader; +} EFI_ESP_TAIL; +#pragma pack() + +struct _IPSEC_SPD_DATA { + CHAR16 Name[100]; + UINT32 PackageFlag; + EFI_IPSEC_ACTION Action; + EFI_IPSEC_PROCESS_POLICY *ProcessingPolicy; + LIST_ENTRY Sas; +}; + +struct _IPSEC_SPD_ENTRY { + EFI_IPSEC_SPD_SELECTOR *Selector; + IPSEC_SPD_DATA *Data; + LIST_ENTRY List; +}; + +typedef struct _IPSEC_SAD_DATA { + EFI_IPSEC_MODE Mode; + UINT64 SequenceNumber; + UINT8 AntiReplayWindowSize; + UINT64 AntiReplayBitmap[4]; // bitmap for received packet + EFI_IPSEC_ALGO_INFO AlgoInfo; + EFI_IPSEC_SA_LIFETIME SaLifetime; + UINT32 PathMTU; + IPSEC_SPD_ENTRY *SpdEntry; + BOOLEAN ESNEnabled; // Extended (64-bit) SN enabled + BOOLEAN ManualSet; +} IPSEC_SAD_DATA; + +typedef struct _IPSEC_SAD_ENTRY { + EFI_IPSEC_SA_ID *Id; + IPSEC_SAD_DATA *Data; + LIST_ENTRY List; + LIST_ENTRY BySpd; // Linked on IPSEC_SPD_DATA.Sas +} IPSEC_SAD_ENTRY; + +struct _IPSEC_PAD_ENTRY { + EFI_IPSEC_PAD_ID *Id; + EFI_IPSEC_PAD_DATA *Data; + LIST_ENTRY List; +}; + +typedef struct _IPSEC_RECYCLE_CONTEXT { + EFI_IPSEC_FRAGMENT_DATA *FragmentTable; + UINT8 *PayloadBuffer; +} IPSEC_RECYCLE_CONTEXT; + +struct _IPSEC_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Handle; // Virtual handle to install private prtocol + EFI_HANDLE ImageHandle; + EFI_IPSEC_PROTOCOL IpSec; + EFI_IPSEC_CONFIG_PROTOCOL IpSecConfig; + BOOLEAN SetBySelf; + LIST_ENTRY Udp4List; + UINTN Udp4Num; + LIST_ENTRY Udp6List; + UINTN Udp6Num; + LIST_ENTRY Ikev1SessionList; + LIST_ENTRY Ikev1EstablishedList; + LIST_ENTRY Ikev2SessionList; + LIST_ENTRY Ikev2EstablishedList; + BOOLEAN IsIPsecDisabling; +}; + +/** + This function processes the inbound traffic with IPsec. + + It checks the received packet security property, trims the ESP/AH header, and then + returns without an IPsec protected IP Header and FragmentTable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to IP header containing the ESP/AH header + to be trimed on input, and without ESP/AH header + on return. + @param[in] LastHead The Last Header in IP header on return. + @param[in] OptionsBuffer Pointer to the options buffer. It is optional. + @param[in] OptionsLength Length of the options buffer. It is optional. + @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec + protected on input, and without IPsec protected + on return. + @param[in] FragmentCount Number of fragments. + @param[out] SpdEntry Pointer to contain the address of SPD entry on return. + @param[out] RecycleEvent Event for recycling of resources. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectInboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN UINT8 *LastHead, + IN VOID *OptionsBuffer, OPTIONAL + IN UINT32 OptionsLength, OPTIONAL + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + OUT IPSEC_SPD_ENTRY **SpdEntry, + OUT EFI_EVENT *RecycleEvent + ); + + +/** + This fucntion processes the output traffic with IPsec. + + It protected the sending packet by encrypting it payload and inserting ESP/AH header + in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Point to IP header containing the orginal IP header + to be processed on input, and inserted ESP/AH header + on return. + @param[in] LastHead The Last Header in IP header. + @param[in] OptionsBuffer Pointer to the options buffer. It is optional. + @param[in] OptionsLength Length of the options buffer. It is optional. + @param[in, out] FragmentTable Pointer to a list of fragments to be protected by + IPsec on input, and with IPsec protected + on return. + @param[in] FragmentCount Number of fragments. + @param[in] SadEntry Related SAD entry. + @param[out] RecycleEvent Event for recycling of resources. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectOutboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN UINT8 *LastHead, + IN VOID *OptionsBuffer, OPTIONAL + IN UINT32 OptionsLength, OPTIONAL + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + IN IPSEC_SAD_ENTRY *SadEntry, + OUT EFI_EVENT *RecycleEvent + ); + +/** + Check if the IP Address in the address range of AddressInfos specified. + + @param[in] IpVersion The IP version. + @param[in] IpAddr Points to EFI_IP_ADDRESS to be check. + @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check + the IP Address is matched. + @param[in] AddressCount The total numbers of the AddressInfo. + + @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified. + @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified. + +**/ +BOOLEAN +IpSecMatchIpAddress ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_IP_ADDRESS_INFO *AddressInfo, + IN UINT32 AddressCount + ); + +/** + Find a PAD entry according to remote IP address. + + @param[in] IpVersion The version of IP. + @param[in] IpAddr Point to remote IP address. + + @return The pointer of related PAD entry. + +**/ +IPSEC_PAD_ENTRY * +IpSecLookupPadEntry ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr + ); + +/** + Find the SAD through whole SAD list. + + @param[in] Spi The SPI used to search the SAD entry. + @param[in] DestAddress The destination used to search the SAD entry. + + @return The pointer to a certain SAD entry. + +**/ +IPSEC_SAD_ENTRY * +IpSecLookupSadBySpi ( + IN UINT32 Spi, + IN EFI_IP_ADDRESS *DestAddress + ) +; + +/** + Handles IPsec packet processing for inbound and outbound IP packets. + + The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet. + The behavior is that it can perform one of the following actions: + bypass the packet, discard the packet, or protect the packet. + + @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance. + @param[in] NicHandle Instance of the network interface. + @param[in] IpVersion IPV4 or IPV6. + @param[in, out] IpHead Pointer to the IP Header. + @param[in] LastHead The protocol of the next layer to be processed by IPsec. + @param[in] OptionsBuffer Pointer to the options buffer. + @param[in] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments. + @param[in] FragmentCount Number of fragments. + @param[in] TrafficDirection Traffic direction. + @param[out] RecycleSignal Event for recycling of resources. + + @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + +**/ +EFI_STATUS +EFIAPI +IpSecProcess ( + IN EFI_IPSEC_PROTOCOL *This, + IN EFI_HANDLE NicHandle, + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN UINT8 *LastHead, + IN VOID *OptionsBuffer, + IN UINT32 OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection, + OUT EFI_EVENT *RecycleSignal + ); + +extern EFI_DPC_PROTOCOL *mDpc; +extern EFI_IPSEC_PROTOCOL mIpSecInstance; + +extern EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName; + + +#endif diff --git a/NetworkPkg/IpSecDxe/IpSecSaEngine.c b/NetworkPkg/IpSecDxe/IpSecSaEngine.c new file mode 100644 index 0000000000..8abf4d6bf4 --- /dev/null +++ b/NetworkPkg/IpSecDxe/IpSecSaEngine.c @@ -0,0 +1,934 @@ +/** @file + IPsec inbound and outbound traffic processing. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "IpSecImpl.h" +#include "IpSecDebug.h" +#include "IpSecCryptIo.h" + +extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum]; + +/** + The call back function of NetbufFromExt. + + @param[in] Arg The argument passed from the caller. + +**/ +VOID +EFIAPI +IpSecOnRecyclePacket ( + IN VOID *Arg + ) +{ +} + +/** + This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP + is released. + + @param[in] Event The related event. + @param[in] Context The data passed by the caller. + +**/ +VOID +EFIAPI +IpSecRecycleCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IPSEC_RECYCLE_CONTEXT *RecycleContext; + + RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context; + + if (RecycleContext->FragmentTable != NULL) { + FreePool (RecycleContext->FragmentTable); + } + + if (RecycleContext->PayloadBuffer != NULL) { + FreePool (RecycleContext->PayloadBuffer); + } + + FreePool (RecycleContext); + gBS->CloseEvent (Event); + +} + +/** + Calculate the extension header of IP. The return length only doesn't contain + the fixed IP header length. + + @param[in] IpHead Points to an IP head to be calculated. + @param[in] LastHead Points to the last header of the IP header. + + @return The length of the extension header. + +**/ +UINT16 +IpSecGetPlainExtHeadSize ( + IN VOID *IpHead, + IN UINT8 *LastHead + ) +{ + UINT16 Size; + + Size = (UINT16) (LastHead - (UINT8 *) IpHead); + + if (Size > sizeof (EFI_IP6_HEADER)) { + // + // * (LastHead+1) point the last header's length but not include the first + // 8 octers, so this formluation add 8 at the end. + // + Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8); + } else { + Size = 0; + } + + return Size; +} + +/** + Authenticate the IpSec Payload and store the result in the IcvBuffer. + + @param[in] BufferToAuth The buffer to be Authenticated. + @param[in] AuthSize The size of the buffer to be Authenticated. + @param[in, out] IcvBuffer The buffer to store the ICV. + @param[in] IcvSize The size of ICV. + @param[in] Key The Key passed to the CryptLib to generate a + CRYPT_HANDLE. + @param[in] AuthAlgId The Authentication Algorithm ID. + + @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list. + @retval EFI_SUCCESS Authenticated the payload successfully. + @retval otherwise Authentication of the payload failed. +**/ +EFI_STATUS +IpSecAuthPayload ( + IN UINT8 *BufferToAuth, + IN UINTN AuthSize, + IN OUT UINT8 *IcvBuffer, + IN UINTN IcvSize, + IN VOID *Key, + IN UINT8 AuthAlgId + ) +{ + switch (AuthAlgId) { + case EFI_IPSEC_AALG_NONE : + case EFI_IPSEC_AALG_NULL : + return EFI_SUCCESS; + + default: + return EFI_UNSUPPORTED; + } +} + +/** + Verify if the Authentication payload is correct. + + @param[in] EspBuffer Points to the ESP wrapped buffer. + @param[in] EspSize The size of the ESP wrapped buffer. + @param[in] SadEntry The related SAD entry to store the authentication + algorithm key. + @param[in] IcvSize The length of ICV. + + @retval EFI_SUCCESS The authentication data is correct. + @retval EFI_ACCESS_DENIED The authentication data is not correct. + +**/ +EFI_STATUS +IpSecEspAuthVerifyPayload ( + IN UINT8 *EspBuffer, + IN UINTN EspSize, + IN IPSEC_SAD_ENTRY *SadEntry, + IN UINTN *IcvSize + ) +{ + EFI_STATUS Status; + UINTN AuthSize; + UINT8 IcvBuffer[12]; + + // + // Calculate the size of authentication payload. + // + *IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId); + AuthSize = EspSize - *IcvSize; + + // + // Calculate the icv buffer and size of the payload. + // + Status = IpSecAuthPayload ( + EspBuffer, + AuthSize, + IcvBuffer, + *IcvSize, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Compare the calculated icv and the appended original icv. + // + if (CompareMem (EspBuffer + AuthSize, IcvBuffer, *IcvSize) == 0) { + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_ERROR, "Error auth verify payload\n")); + return EFI_ACCESS_DENIED; +} + +/** + ESP Decrypt the payload. + + @param[in, out] PayloadBuffer Pointer to the buffer containing the ESP wrapped; + to be decrypted on input, and plaintext on return. The + number of bytes of data to be decrypted is + specified by EncryptSize. + @param[in] EncryptSize The size of the PayloadBuffer as input. + @param[in] SadEntry The related SAD entry. + @param[in] IvSize The size of IV. + @param[out] PlainPayloadSize Contains the return value of decrypted size. + @param[out] PaddingSize Contains the return value of Padding size. + @param[out] NextHeader Contains the return value of the last protocol header + of the IP packet. + + @retval EFI_UNSUPPORTED The Algorithm pointed to by the SAD entry is not supported. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +IpSecEspDecryptPayload ( + IN OUT UINT8 *PayloadBuffer, + IN UINTN EncryptSize, + IN IPSEC_SAD_ENTRY *SadEntry, + IN UINTN *IvSize, + OUT UINTN *PlainPayloadSize, + OUT UINTN *PaddingSize, + OUT UINT8 *NextHeader + ) +{ + EFI_ESP_TAIL *EspTail; + + switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) { + case EFI_IPSEC_EALG_NULL: + EspTail = (EFI_ESP_TAIL *) (PayloadBuffer + EncryptSize - sizeof (EFI_ESP_TAIL)); + *PaddingSize = EspTail->PaddingLength; + *NextHeader = EspTail->NextHeader; + *PlainPayloadSize = EncryptSize - EspTail->PaddingLength - sizeof (EFI_ESP_TAIL); + break; + + case EFI_IPSEC_EALG_3DESCBC: + case EFI_IPSEC_EALG_AESCBC: + // + // TODO: support these algorithm + // + return EFI_UNSUPPORTED; + default : + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + ESP Encrypt the payload. + + @param[in, out] BufferToEncrypt Pointer to the buffer containing plaintext to be + encrypted on input, and ciphertext on return. The + number of bytes of data to be encrypted is + specified by EncryptSize. + @param[in, out] EncryptSize The size of the plaintext on input, and the size of the + ciphertext on return. + @param[in] IvBuffer Points to IV data. + @param[in] IvSize Size of IV. + @param[in] SadEntry Related SAD entry. + + @retval EFI_UNSUPPORTED The Algorithm pointed by SAD entry is not supported. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +IpSecEspEncryptPayload ( + IN OUT UINT8 *BufferToEncrypt, + IN OUT UINTN EncryptSize, + IN UINT8 *IvBuffer, + IN UINTN IvSize, + IN IPSEC_SAD_ENTRY *SadEntry + ) +{ + switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) { + case EFI_IPSEC_EALG_NULL: + return EFI_SUCCESS; + + case EFI_IPSEC_EALG_3DESCBC: + case EFI_IPSEC_EALG_AESCBC: + // + // TODO: support these algorithms + // + return EFI_UNSUPPORTED; + default : + return EFI_UNSUPPORTED; + + } +} + +/** + The actual entry to relative function processes the inbound traffic of ESP header. + + This function is the subfunction of IpSecProtectInboundPacket(). It checks the + received packet security property and trim the ESP header and then returns without + an IPsec protected IP Header and FramgmentTable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to the IP header containing the ESP header + to be trimed on input, and without ESP header + on return. + @param[out] LastHead The Last Header in IP header on return. + @param[in] OptionsBuffer Pointer to the options buffer. It is optional. + @param[in] OptionsLength Length of the options buffer. It is optional. + @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec + protected on input, and without IPsec protected + on return. + @param[in] FragmentCount The number of fragments. + @param[out] SpdEntry Pointer to contain the address of SPD entry on return. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_ACCESS_DENIED One or more following conditions is TRUE: + - ESP header was not found. + - The related SAD entry was not found. + - The related SAD entry does not support the ESP protocol. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + +**/ +EFI_STATUS +IpSecEspInboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + OUT UINT8 *LastHead, + IN VOID *OptionsBuffer, OPTIONAL + IN UINT32 OptionsLength, OPTIONAL + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + OUT IPSEC_SPD_ENTRY **SpdEntry, + OUT EFI_EVENT *RecycleEvent + ) +{ + EFI_STATUS Status; + NET_BUF *Payload; + UINTN EspSize; + UINTN IvSize; + UINTN PlainPayloadSize; + UINTN PaddingSize; + UINTN IcvSize; + UINT8 *ProcessBuffer; + EFI_IP_ADDRESS DestIp; + EFI_ESP_HEADER *EspHeader; + EFI_ESP_TAIL *EspTail; + EFI_IPSEC_SA_ID *SaId; + IPSEC_SAD_DATA *SadData; + IPSEC_SAD_ENTRY *SadEntry; + IPSEC_RECYCLE_CONTEXT *RecycleContext; + UINT32 Spi; + UINT8 NextHeader; + UINT16 IpSecHeadSize; + + Status = EFI_SUCCESS; + Payload = NULL; + ProcessBuffer = NULL; + RecycleContext = NULL; + *RecycleEvent = NULL; + PlainPayloadSize = 0; + NextHeader = 0; + // + // Build netbuf from fragment table first. + // + Payload = NetbufFromExt ( + (NET_FRAGMENT *) *FragmentTable, + *FragmentCount, + 0, + sizeof (EFI_ESP_HEADER), + IpSecOnRecyclePacket, + NULL + ); + if (Payload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + // + // Get the esp size and eso header from netbuf. + // + EspSize = Payload->TotalSize; + EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL); + if (EspHeader == NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + // + // Parse destination address from ip header. + // + ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS)); + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &DestIp, + &((IP4_HEAD *) IpHead)->Dst, + sizeof (IP4_ADDR) + ); + } else { + CopyMem ( + &DestIp, + &((EFI_IP6_HEADER *) IpHead)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } + // + // Lookup sad entry according to the spi and dest address. + // + Spi = NTOHL (EspHeader->Spi); + SadEntry = IpSecLookupSadBySpi (Spi, &DestIp); + if (SadEntry == NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + SaId = SadEntry->Id; + SadData = SadEntry->Data; + + // + // Only support esp protocol currently. + // + if (SaId->Proto != EfiIPsecESP) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + if (!SadData->ManualSet) { + // + // TODO: Check sa lifetime and sequence number + // + } + // + // Allocate buffer for decryption and authentication by esp. + // + ProcessBuffer = AllocateZeroPool (EspSize); + if (ProcessBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer); + + // + // Authenticate the esp wrapped buffer by the sad entry if has auth key. + // + IcvSize = 0; + if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + Status = IpSecEspAuthVerifyPayload ( + ProcessBuffer, + EspSize, + SadEntry, + &IcvSize + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + // + // Decrypt the payload by the sad entry if has decrypt key. + // + IvSize = 0; + if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + Status = IpSecEspDecryptPayload ( + ProcessBuffer + sizeof (EFI_ESP_HEADER), + EspSize - sizeof (EFI_ESP_HEADER) - IcvSize, + SadEntry, + &IvSize, + &PlainPayloadSize, + &PaddingSize, + &NextHeader + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else { + EspTail = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL)); + PaddingSize = EspTail->PaddingLength; + NextHeader = EspTail->NextHeader; + PlainPayloadSize = EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize - sizeof (EFI_ESP_TAIL) - PaddingSize; + } + // + // TODO: handle anti-replay window + // + // + // Decryption and authentication with esp has been done, so it's time to + // reload the new packet, create recycle event and fixup ip header. + // + RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT)); + if (RecycleContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IpSecRecycleCallback, + RecycleContext, + RecycleEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // TODO: Who take responsible to handle the original fragment table? + // + *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA)); + if (*FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + RecycleContext->PayloadBuffer = ProcessBuffer; + RecycleContext->FragmentTable = *FragmentTable; + (*FragmentTable)[0].FragmentBuffer = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize; + (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize; + *FragmentCount = 1; + + // + // Update the total length field in ip header since processed by esp. + // + if (IpVersion == IP_VERSION_4) { + ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + PlainPayloadSize)); + } else { + IpSecHeadSize = IpSecGetPlainExtHeadSize (IpHead, LastHead); + ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize)); + } + // + // Update the next layer field in ip header since esp header inserted. + // + *LastHead = NextHeader; + + // + // Update the spd association of the sad entry. + // + *SpdEntry = SadData->SpdEntry; + +ON_EXIT: + if (Payload != NULL) { + NetbufFree (Payload); + } + + if (EFI_ERROR (Status)) { + if (ProcessBuffer != NULL) { + FreePool (ProcessBuffer); + } + + if (RecycleContext != NULL) { + FreePool (RecycleContext); + } + + if (*RecycleEvent != NULL) { + gBS->CloseEvent (*RecycleEvent); + } + } + + return Status; +} + +/** + The actual entry to the relative function processes the output traffic using the ESP protocol. + + This function is the subfunction of IpSecProtectOutboundPacket(). It protected + the sending packet by encrypting its payload and inserting ESP header in the orginal + IP header, then return the IpHeader and IPsec protected Fragmentable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to IP header containing the orginal IP header + to be processed on input, and inserted ESP header + on return. + @param[in] LastHead The Last Header in IP header. + @param[in] OptionsBuffer Pointer to the options buffer. It is optional. + @param[in] OptionsLength Length of the options buffer. It is optional. + @param[in, out] FragmentTable Pointer to a list of fragments to be protected by + IPsec on input, and with IPsec protected + on return. + @param[in] FragmentCount The number of fragments. + @param[in] SadEntry The related SAD entry. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated. + +**/ +EFI_STATUS +IpSecEspOutboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN UINT8 *LastHead, + IN VOID *OptionsBuffer, OPTIONAL + IN UINT32 OptionsLength, OPTIONAL + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + IN IPSEC_SAD_ENTRY *SadEntry, + OUT EFI_EVENT *RecycleEvent + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_IPSEC_SA_ID *SaId; + IPSEC_SAD_DATA *SadData; + IPSEC_RECYCLE_CONTEXT *RecycleContext; + UINT8 *ProcessBuffer; + UINTN BytesCopied; + INTN EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4 + UINTN EspSize; // Total size of esp wrapped ip payload + UINTN IvSize; // Size of IV, optional, might be 0 + UINTN PlainPayloadSize;// Original IP payload size + UINTN PaddingSize; // Size of padding + UINTN EncryptSize; // Size of data to be encrypted, start after IV and + // stop before ICV + UINTN IcvSize; // Size of ICV, optional, might be 0 + UINT8 *RestOfPayload; // Start of Payload after IV + UINT8 *Padding; // Start address of padding + EFI_ESP_HEADER *EspHeader; // Start address of ESP frame + EFI_ESP_TAIL *EspTail; // Address behind padding + + Status = EFI_ACCESS_DENIED; + SaId = SadEntry->Id; + SadData = SadEntry->Data; + ProcessBuffer = NULL; + RecycleContext = NULL; + *RecycleEvent = NULL; + + if (!SadData->ManualSet && + SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL && + SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL + ) { + // + // Invalid manual sad entry configuration. + // + goto ON_EXIT; + } + // + // Calculate enctrypt block size, need iv by default and 4 bytes alignment. + // + EncryptBlockSize = 4; + + if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + EncryptBlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); + + if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) { + goto ON_EXIT; + } + } + // + // Calculate the plain payload size accroding to the fragment table. + // + PlainPayloadSize = 0; + for (Index = 0; Index < *FragmentCount; Index++) { + PlainPayloadSize += (*FragmentTable)[Index].FragmentLength; + } + // + // Calculate icv size, optional by default and 4 bytes alignment. + // + IcvSize = 0; + if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId); + if (IcvSize % 4 != 0) { + goto ON_EXIT; + } + } + // + // Calcuate the total size of esp wrapped ip payload. + // + IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); + EncryptSize = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize; + PaddingSize = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL); + EspSize = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize; + + ProcessBuffer = AllocateZeroPool (EspSize); + if (ProcessBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + // + // Calculate esp header and esp tail including header, payload and padding. + // + EspHeader = (EFI_ESP_HEADER *) ProcessBuffer; + RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize; + Padding = RestOfPayload + PlainPayloadSize; + EspTail = (EFI_ESP_TAIL *) (Padding + PaddingSize); + + // + // Fill the sn and spi fields in esp header. + // + EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1); + EspHeader->Spi = HTONL (SaId->Spi); + + // + // Copy the rest of payload (after iv) from the original fragment buffer. + // + BytesCopied = 0; + for (Index = 0; Index < *FragmentCount; Index++) { + CopyMem ( + (RestOfPayload + BytesCopied), + (*FragmentTable)[Index].FragmentBuffer, + (*FragmentTable)[Index].FragmentLength + ); + BytesCopied += (*FragmentTable)[Index].FragmentLength; + } + // + // Fill the padding buffer by natural number sequence. + // + for (Index = 0; Index < PaddingSize; Index++) { + Padding[Index] = (UINT8) (Index + 1); + } + // + // Fill the padding length and next header fields in esp tail. + // + EspTail->PaddingLength = (UINT8) PaddingSize; + EspTail->NextHeader = *LastHead; + + // + // Generate iv at random by crypt library. + // + Status = IpSecGenerateIv ( + (UINT8 *) (EspHeader + 1), + IvSize + ); + + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Encrypt the payload (after iv) by the sad entry if has encrypt key. + // + if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + Status = IpSecEspEncryptPayload ( + RestOfPayload, + EncryptSize, + (UINT8 *) (EspHeader + 1), + IvSize, + SadEntry + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + // + // Authenticate the esp wrapped buffer by the sad entry if has auth key. + // + if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + Status = IpSecAuthPayload ( + ProcessBuffer, + EspSize - IcvSize, + ProcessBuffer + EspSize - IcvSize, + IcvSize, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + // + // Encryption and authentication with esp has been done, so it's time to + // reload the new packet, create recycle event and fixup ip header. + // + RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT)); + if (RecycleContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IpSecRecycleCallback, + RecycleContext, + RecycleEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // TODO: Who take responsible to handle the original fragment table? + // + *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA)); + if (*FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + RecycleContext->FragmentTable = *FragmentTable; + RecycleContext->PayloadBuffer = ProcessBuffer; + (*FragmentTable)[0].FragmentBuffer = ProcessBuffer; + (*FragmentTable)[0].FragmentLength = (UINT32) EspSize; + *FragmentCount = 1; + + // + // Update the total length field in ip header since processed by esp. + // + if (IpVersion == IP_VERSION_4) { + ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + EspSize)); + } else { + ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize); + } + // + // Update the next layer field in ip header since esp header inserted. + // + *LastHead = IPSEC_ESP_PROTOCOL; + + // + // Increase the sn number in sad entry according to rfc4303. + // + SadData->SequenceNumber++; + +ON_EXIT: + if (EFI_ERROR (Status)) { + if (ProcessBuffer != NULL) { + FreePool (ProcessBuffer); + } + + if (RecycleContext != NULL) { + FreePool (RecycleContext); + } + + if (*RecycleEvent != NULL) { + gBS->CloseEvent (*RecycleEvent); + } + } + + return Status; +} + +/** + This function processes the inbound traffic with IPsec. + + It checks the received packet security property, trims the ESP/AH header, and then + returns without an IPsec protected IP Header and FragmentTable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to IP header containing the ESP/AH header + to be trimed on input, and without ESP/AH header + on return. + @param[in] LastHead The Last Header in IP header on return. + @param[in] OptionsBuffer Pointer to the options buffer. It is optional. + @param[in] OptionsLength Length of the options buffer. It is optional. + @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec + protected on input, and without IPsec protected + on return. + @param[in] FragmentCount The number of fragments. + @param[out] SpdEntry Pointer to contain the address of SPD entry on return. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_UNSUPPORTED The IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectInboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN UINT8 *LastHead, + IN VOID *OptionsBuffer, OPTIONAL + IN UINT32 OptionsLength, OPTIONAL + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + OUT IPSEC_SPD_ENTRY **SpdEntry, + OUT EFI_EVENT *RecycleEvent + ) +{ + if (*LastHead == IPSEC_ESP_PROTOCOL) { + // + // Process the esp ipsec header of the inbound traffic. + // + return IpSecEspInboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + SpdEntry, + RecycleEvent + ); + } + // + // The other protocols are not supported. + // + return EFI_UNSUPPORTED; +} + +/** + This function processes the output traffic with IPsec. + + It protected the sending packet by encrypting it payload and inserting ESP/AH header + in the orginal IP header, then returns the IpHeader and IPsec protected Fragmentable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to IP header containing the orginal IP header + to be processed on input, and inserted ESP/AH header + on return. + @param[in] LastHead The Last Header in the IP header. + @param[in] OptionsBuffer Pointer to the options buffer. It is optional. + @param[in] OptionsLength Length of the options buffer. It is optional. + @param[in, out] FragmentTable Pointer to a list of fragments to be protected by + IPsec on input, and with IPsec protected + on return. + @param[in] FragmentCount The number of fragments. + @param[in] SadEntry The related SAD entry. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectOutboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN UINT8 *LastHead, + IN VOID *OptionsBuffer, OPTIONAL + IN UINT32 OptionsLength, OPTIONAL + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount, + IN IPSEC_SAD_ENTRY *SadEntry, + OUT EFI_EVENT *RecycleEvent + ) +{ + if (SadEntry->Id->Proto == EfiIPsecESP) { + // + // Process the esp ipsec header of the outbound traffic. + // + return IpSecEspOutboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + SadEntry, + RecycleEvent + ); + } + // + // The other protocols are not supported. + // + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/Mtftp6Dxe/ComponentName.c b/NetworkPkg/Mtftp6Dxe/ComponentName.c new file mode 100644 index 0000000000..0f91b64c44 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/ComponentName.c @@ -0,0 +1,308 @@ +/** @file + UEFI Component Name(2) protocol implementation for Mtftp6 driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Mtftp6Impl.h" + + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for bus drivers + attempting to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName = { + Mtftp6ComponentNameGetDriverName, + Mtftp6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp6DriverNameTable[] = { + { + "eng;en", + L"MTFTP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mMtftp6DriverNameTable, + DriverName, + (BOOLEAN)(This == &gMtftp6ComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + attempting to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c new file mode 100644 index 0000000000..d448c78a34 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c @@ -0,0 +1,703 @@ +/** @file + Driver Binding functions and Service Binding functions + implementation for Mtftp6 Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Mtftp6Impl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gMtftp6DriverBinding = { + Mtftp6DriverBindingSupported, + Mtftp6DriverBindingStart, + Mtftp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gMtftp6ServiceBindingTemplate = { + Mtftp6ServiceBindingCreateChild, + Mtftp6ServiceBindingDestroyChild +}; + + +/** + Destory the MTFTP6 service. The MTFTP6 service may be partly initialized, + or partly destroyed. If a resource is destroyed, it is marked as such in + case the destroy failed and is called again later. + + @param[in] Service The MTFTP6 service to be destroyed. + +**/ +VOID +Mtftp6DestroyService ( + IN MTFTP6_SERVICE *Service + ) +{ + // + // Make sure all children instances have been already destoryed. + // + ASSERT (Service->ChildrenNum == 0); + + if (Service->DummyUdpIo != NULL) { + UdpIoFreeIo (Service->DummyUdpIo); + } + + if (Service->Timer != NULL) { + gBS->CloseEvent (Service->Timer); + } + + FreePool (Service); +} + + +/** + Create then initialize a MTFTP6 service binding instance. + + @param[in] Controller The controller to install the MTFTP6 service + binding on. + @param[in] Image The driver binding image of the MTFTP6 driver. + @param[out] Service The variable to receive the created service + binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to create the instance + @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep connection with UDP. + @retval EFI_SUCCESS The service instance is created for the controller. + +**/ +EFI_STATUS +Mtftp6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + OUT MTFTP6_SERVICE **Service + ) +{ + MTFTP6_SERVICE *Mtftp6Srv; + EFI_STATUS Status; + + ASSERT (Service != NULL); + + *Service = NULL; + Mtftp6Srv = AllocateZeroPool (sizeof (MTFTP6_SERVICE)); + + if (Mtftp6Srv == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mtftp6Srv->Signature = MTFTP6_SERVICE_SIGNATURE; + Mtftp6Srv->Controller = Controller; + Mtftp6Srv->Image = Image; + Mtftp6Srv->InDestory = FALSE; + Mtftp6Srv->ChildrenNum = 0; + + CopyMem ( + &Mtftp6Srv->ServiceBinding, + &gMtftp6ServiceBindingTemplate, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + InitializeListHead (&Mtftp6Srv->Children); + + // + // Create a internal timer for all instances. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Mtftp6OnTimerTick, + Mtftp6Srv, + &Mtftp6Srv->Timer + ); + + if (EFI_ERROR (Status)) { + FreePool (Mtftp6Srv); + return Status; + } + + // + // Create a dummy Udp6Io to build parent-child relationship between Udp6 driver + // and Mtftp6 driver. + // + Mtftp6Srv->DummyUdpIo = UdpIoCreateIo ( + Controller, + Image, + Mtftp6ConfigDummyUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + + if (Mtftp6Srv->DummyUdpIo == NULL) { + gBS->CloseEvent (Mtftp6Srv->Timer); + FreePool (Mtftp6Srv); + return EFI_DEVICE_ERROR; + } + + *Service = Mtftp6Srv; + return EFI_SUCCESS; +} + + +/** + Destroy the MTFTP6 instance and recycle the resources. + + @param[in] Instance The pointer to the MTFTP6 instance. + +**/ +VOID +Mtftp6DestroyInstance ( + IN MTFTP6_INSTANCE *Instance + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + MTFTP6_BLOCK_RANGE *Block; + + if (Instance->Config != NULL) { + FreePool (Instance->Config); + } + + if (Instance->Token != NULL && Instance->Token->Event != NULL) { + gBS->SignalEvent (Instance->Token->Event); + } + + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + if (Instance->UdpIo!= NULL) { + UdpIoFreeIo (Instance->UdpIo); + } + + if (Instance->McastUdpIo != NULL) { + UdpIoFreeIo (Instance->McastUdpIo); + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) { + Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + RemoveEntryList (Entry); + FreePool (Block); + } + + FreePool (Instance); +} + + +/** + Create the MTFTP6 instance and initialize it. + + @param[in] Service The pointer to the MTFTP6 service. + @param[out] Instance The pointer to the MTFTP6 instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The MTFTP6 instance is created. + +**/ +EFI_STATUS +Mtftp6CreateInstance ( + IN MTFTP6_SERVICE *Service, + OUT MTFTP6_INSTANCE **Instance + ) +{ + MTFTP6_INSTANCE *Mtftp6Ins; + + *Instance = NULL; + Mtftp6Ins = AllocateZeroPool (sizeof (MTFTP6_INSTANCE)); + + if (Mtftp6Ins == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mtftp6Ins->Signature = MTFTP6_INSTANCE_SIGNATURE; + Mtftp6Ins->InDestory = FALSE; + Mtftp6Ins->Service = Service; + + CopyMem ( + &Mtftp6Ins->Mtftp6, + &gMtftp6ProtocolTemplate, + sizeof (EFI_MTFTP6_PROTOCOL) + ); + + InitializeListHead (&Mtftp6Ins->Link); + InitializeListHead (&Mtftp6Ins->BlkList); + + *Instance = Mtftp6Ins; + + return EFI_SUCCESS; +} + + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + Entry point of the MTFTP6 driver to install various protocols. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gMtftp6DriverBinding, + ImageHandle, + &gMtftp6ComponentName, + &gMtftp6ComponentName2 + ); +} + + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test + @param[in] RemainingDevicePath Optional parameter use to pick a specific child. + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return gBS->OpenProtocol ( + Controller, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + MTFTP6_SERVICE *Service; + EFI_STATUS Status; + + // + // Directly return if driver is already running on this Nic handle. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiMtftp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Create Mtftp6 service for this Nic handle + // + Status = Mtftp6CreateService ( + Controller, + This->DriverBindingHandle, + &Service + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Service != NULL); + + // + // Start the internal timer to track the packet retransmission. + // + Status = gBS->SetTimer ( + Service->Timer, + TimerPeriodic, + TICKS_PER_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the Mtftp6 service on the Nic handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiMtftp6ServiceBindingProtocolGuid, + &Service->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6DestroyService (Service); + return Status; +} + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // Locate the Nic handle to retrieve the Mtftp6 private data. + // + NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp6ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Service = MTFTP6_SERVICE_FROM_THIS (ServiceBinding); + + if (Service->InDestory) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (NumberOfChildren == 0) { + // + // Destory the Mtftp6 service if there is no Mtftp6 child instance left. + // + Service->InDestory = TRUE; + + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + ServiceBinding + ); + + Mtftp6DestroyService (Service); + + } else { + // + // Destory the Mtftp6 child instance one by one. + // + while (!IsListEmpty (&Service->Children)) { + Instance = NET_LIST_HEAD (&Service->Children, MTFTP6_INSTANCE, Link); + Mtftp6ServiceBindingDestroyChild (ServiceBinding, Instance->Handle); + } + + if (Service->ChildrenNum != 0) { + Status = EFI_DEVICE_ERROR; + } + } + + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval Others The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_STATUS Status; + EFI_TPL OldTpl; + VOID *Udp6; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Service = MTFTP6_SERVICE_FROM_THIS (This); + + Status = Mtftp6CreateInstance (Service, &Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Instance != NULL); + + // + // Install the Mtftp6 protocol on the new child handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + &Instance->Mtftp6, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->Handle = *ChildHandle; + + // + // Open the Udp6 protocol by child. + // + Status = gBS->OpenProtocol ( + Service->DummyUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + gMtftp6DriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiMtftp6ProtocolGuid, + &Instance->Mtftp6, + NULL + ); + + goto ON_ERROR; + } + + // + // Add the new Mtftp6 instance into the children list of Mtftp6 service. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&Service->Children, &Instance->Link); + Service->ChildrenNum++; + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6DestroyInstance (Instance); + return Status; +} + + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle. + @retval Others The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Locate the Nic handle to retrieve the Mtftp6 private data. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + (VOID **) &Mtftp6, + gMtftp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (Mtftp6); + Service = MTFTP6_SERVICE_FROM_THIS (This); + + if (Instance->Service != Service) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the instance already in destory state. + // + if (Instance->InDestory) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->InDestory = TRUE; + + gBS->CloseProtocol ( + Service->DummyUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gMtftp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the MTFTP6 protocol first to enable a top down destruction. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + Mtftp6 + ); + + if (EFI_ERROR (Status)) { + Instance->InDestory = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Remove the Mtftp6 instance from the children list of Mtftp6 service. + // + RemoveEntryList (&Instance->Link); + Service->ChildrenNum --; + + Mtftp6DestroyInstance (Instance); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h new file mode 100644 index 0000000000..94f73a80a9 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h @@ -0,0 +1,151 @@ +/** @file + Driver Binding functions and Service Binding functions + declaration for Mtftp6 Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_MTFTP6_DRIVER_H__ +#define __EFI_MTFTP6_DRIVER_H__ + +#include + +extern EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2; + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval Others The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle. + @retval Others The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf b/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf new file mode 100644 index 0000000000..ecf1f7cf0d --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf @@ -0,0 +1,69 @@ +## @file +# Component description file for Mtftp6 module. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Mtftp6Dxe + FILE_GUID = 99F03B99-98D8-49dd-A8D3-3219D0FFE41E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Mtftp6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gMtftp6DriverBinding +# COMPONENT_NAME = gMtftp6ComponentName +# COMPONENT_NAME2 = gMtftp6ComponentName2 +# + +[Sources] + Mtftp6Driver.c + Mtftp6Driver.h + Mtftp6Impl.c + Mtftp6Impl.h + Mtftp6Option.c + Mtftp6Option.h + Mtftp6Support.h + Mtftp6Support.c + Mtftp6Rrq.c + Mtftp6Wrq.c + ComponentName.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + DebugLib + NetLib + UdpIoLib + + +[Protocols] + gEfiUdp6ServiceBindingProtocolGuid + gEfiUdp6ProtocolGuid + gEfiMtftp6ServiceBindingProtocolGuid + gEfiMtftp6ProtocolGuid + diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c new file mode 100644 index 0000000000..fcbbf11134 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c @@ -0,0 +1,634 @@ +/** @file + This EFI_MTFTP6_PROTOCOL interface implementation. + + It supports the following RFCs: + RFC1350 - THE TFTP PROTOCOL (REVISION 2) + RFC2090 - TFTP Multicast Option + RFC2347 - TFTP Option Extension + RFC2348 - TFTP Blocksize Option + RFC2349 - TFTP Timeout Interval and Transfer Size Options + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Mtftp6Impl.h" + +EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate = { + EfiMtftp6GetModeData, + EfiMtftp6Configure, + EfiMtftp6GetInfo, + EfiMtftp6ParseOptions, + EfiMtftp6ReadFile, + EfiMtftp6WriteFile, + EfiMtftp6ReadDirectory, + EfiMtftp6Poll + }; + +/** + Returns the current operating mode data for the MTFTP6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the MTFTP6 instance. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode + data is returned. + + @retval EFI_SUCCESS The configuration data was returned successfully. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + @retval EFI_INVALID_PARAMETER This is NULL or ModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetModeData ( + IN EFI_MTFTP6_PROTOCOL *This, + OUT EFI_MTFTP6_MODE_DATA *ModeData + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_TPL OldTpl; + + if (This == NULL || ModeData == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + // + // Copy back the configure data if the instance already configured. + // + if (Instance->Config != NULL) { + CopyMem ( + &ModeData->ConfigData, + Instance->Config, + sizeof (EFI_MTFTP6_CONFIG_DATA) + ); + } else { + ZeroMem ( + &ModeData->ConfigData, + sizeof (EFI_MTFTP6_CONFIG_DATA) + ); + } + + // + // Set the current support options in mode data. + // + ModeData->SupportedOptionCount = MTFTP6_SUPPORTED_OPTIONS_NUM; + ModeData->SupportedOptions = (UINT8 **) mMtftp6SupportedOptions; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Initializes, changes, or resets the default operational setting for + this EFI MTFTPv6 Protocol driver instance. + + The Configure() function is used to set and change the configuration + data for this EFI MTFTPv6 Protocol driver instance. The configuration + data can be reset to startup defaults by calling Configure() with + MtftpConfigData set to NULL. Whenever the instance is reset, any + pending operation is aborted. By changing the EFI MTFTPv6 Protocol + driver instance configuration data, the client can connect to + different MTFTPv6 servers. The configuration parameters in + MtftpConfigData are used as the default parameters in later MTFTPv6 + operations and can be overridden in later operations. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] MtftpConfigData Pointer to the configuration data structure. + + @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + - This is NULL. + - MtftpConfigData.StationIp is neither zero nor one + of the configured IP addresses in the underlying IPv6 driver. + - MtftpCofigData.ServerIp is not a valid IPv6 unicast address. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there + is some MTFTP background operation in progress. + - MtftpCofigData.LocalPort is already in use. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be + allocated. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI + MTFTPv6 Protocol driver instance is not configured. + Note: It is not defined in the UEFI 2.3 Specification. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Configure ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA Udp6Cfg; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MtftpConfigData != NULL && !NetIp6IsValidUnicast (&MtftpConfigData->ServerIp)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + Status = EFI_SUCCESS; + + if (MtftpConfigData == NULL) { + // + // Configure the instance as NULL to abort the current session. + // + Mtftp6OperationClean (Instance, EFI_ABORTED); + FreePool (Instance->Config); + Instance->Config = NULL; + } else { + // + // It's not allowed to configure one instance twice without configure null. + // + if (Instance->Config != NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + // + // Allocate the configure buffer of the instance and store the user's data. + // + Instance->Config = AllocateZeroPool (sizeof (EFI_MTFTP6_CONFIG_DATA)); + + if (Instance->Config == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (Instance->Config, MtftpConfigData, sizeof (EFI_MTFTP6_CONFIG_DATA)); + + // + // Don't configure the udpio here because each operation might override + // the configuration, so delay udpio configuration in each operation. + // + Instance->UdpIo = UdpIoCreateIo ( + Service->Controller, + Service->Image, + Mtftp6ConfigDummyUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + + if (Instance->UdpIo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Continue to configure the downside Udp6 instance by user's data. + // + ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + Udp6Cfg.AcceptPromiscuous = FALSE; + Udp6Cfg.AcceptAnyPort = FALSE; + Udp6Cfg.AllowDuplicatePort = FALSE; + Udp6Cfg.TrafficClass = 0; + Udp6Cfg.HopLimit = 128; + Udp6Cfg.ReceiveTimeout = 0; + Udp6Cfg.TransmitTimeout = 0; + Udp6Cfg.StationPort = Instance->Config->LocalPort; + Udp6Cfg.RemotePort = Instance->Config->InitialServerPort; + + CopyMem ( + &Udp6Cfg.StationAddress, + &Instance->Config->StationIp, + sizeof(EFI_IPv6_ADDRESS) + ); + + CopyMem ( + &Udp6Cfg.RemoteAddress, + &Instance->Config->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + Udp6 = Instance->UdpIo->Protocol.Udp6; + Status = Udp6->Configure (Udp6, &Udp6Cfg); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + +ON_EXIT: + if (EFI_ERROR (Status)) { + if (Instance->Config != NULL) { + FreePool (Instance->Config); + Instance->Config = NULL; + } + if (Instance->UdpIo != NULL) { + UdpIoFreeIo (Instance->UdpIo); + Instance->UdpIo = NULL; + } + } + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Get the information of the download from the server. + + The GetInfo() function assembles an MTFTPv6 request packet + with options, sends it to the MTFTPv6 server, and may return + an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries + occur only if no response packets are received from the MTFTPv6 + server before the timeout expires. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the + default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure() + function are used. + @param[in] Filename Pointer to ASCIIZ file name string. + @param[in] ModeStr Pointer to ASCIIZ mode string. If NULL, octet will be used. + @param[in] OptionCount Number of option/value string pairs in OptionList. + @param[in] OptionList Pointer to array of option/value string pairs. Ignored if + OptionCount is zero. + @param[out] PacketLength The number of bytes in the returned packet. + @param[out] Packet The pointer to the received packet. This buffer must be freed by + the caller. + + @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Filename is NULL. + - OptionCount is not zero and OptionList is NULL. + - One or more options in OptionList have wrong format. + - PacketLength is NULL. + - OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by + this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received and the Packet is set to NULL. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received and the Packet is set to NULL. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetInfo ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL, + IN UINT8 *Filename, + IN UINT8 *ModeStr OPTIONAL, + IN UINT8 OptionCount, + IN EFI_MTFTP6_OPTION *OptionList OPTIONAL, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP6_PACKET **Packet OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_MTFTP6_TOKEN Token; + MTFTP6_GETINFO_CONTEXT Context; + + if (This == NULL || + Filename == NULL || + PacketLength == NULL || + (OptionCount != 0 && OptionList == NULL) || + (OverrideData != NULL && !NetIp6IsValidUnicast (&OverrideData->ServerIp)) + ) { + return EFI_INVALID_PARAMETER; + } + + if (Packet != NULL) { + *Packet = NULL; + } + + *PacketLength = 0; + + Context.Packet = Packet; + Context.PacketLen = PacketLength; + Context.Status = EFI_SUCCESS; + + // + // Fill fields of the Token for GetInfo operation. + // + Token.Status = EFI_SUCCESS; + Token.Event = NULL; + Token.OverrideData = OverrideData; + Token.Filename = Filename; + Token.ModeStr = ModeStr; + Token.OptionCount = OptionCount; + Token.OptionList = OptionList; + Token.BufferSize = 0; + Token.Buffer = NULL; + Token.Context = &Context; + Token.CheckPacket = Mtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + // + // Start the GetInfo operation by issue the Token. + // + Status = Mtftp6OperationStart (This, &Token, EFI_MTFTP6_OPCODE_RRQ); + + if (Status == EFI_ABORTED) { + // + // Return the status if failed to issue. + // + return Context.Status; + } + + return Status; +} + + +/** + Parse the options in an MTFTPv6 OACK packet. + + The ParseOptions() function parses the option fields in an MTFTPv6 OACK + packet and returns the number of options that were found, and optionally, + a list of pointers to the options in the packet. If one or more of the + option fields are not valid, then EFI_PROTOCOL_ERROR is returned and + *OptionCount and *OptionList stop at the last valid option. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] PacketLen Length of the OACK packet to be parsed. + @param[in] Packet Pointer to the OACK packet to be parsed. + @param[out] OptionCount Pointer to the number of options in the following OptionList. + @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the + OptionList points to the corresponding MTFTP option buffer + in the Packet. Call the EFI Boot Service FreePool() to + release the OptionList if the options in this OptionList + are not needed anymore. + + @retval EFI_SUCCESS The OACK packet was valid and the OptionCount and + OptionList parameters have been updated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - PacketLen is 0. + - Packet is NULL or Packet is not a valid MTFTPv6 packet. + - OptionCount is NULL. + @retval EFI_NOT_FOUND No options were found in the OACK packet. + @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated. + @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ParseOptions ( + IN EFI_MTFTP6_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP6_PACKET *Packet, + OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ) +{ + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + return Mtftp6ParseStart (Packet, PacketLen, OptionCount, OptionList); +} + + +/** + Download a file from an MTFTPv6 server. + + The ReadFile() function is used to initialize and start an MTFTPv6 download + process, and optionally, wait for completion. When the download operation + completes, whether successfully or not, the Token.Status field is updated + by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it + is not NULL. + Data can be downloaded from the MTFTPv6 server into either of the following + locations: + - A fixed buffer that is pointed to by Token.Buffer + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The data file has been transferred successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the + downloaded data in downloading process. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ABORTED Current operation is aborted by user. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_ICMP_ERROR An ICMP ERROR packet was received. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_RRQ); +} + + +/** + Send a file to an MTFTPv6 server. + + The WriteFile() function is used to initialize an uploading operation + with the given option list and optionally wait for completion. If one + or more of the options is not supported by the server, the unsupported + options are ignored and a standard TFTP process starts instead. When + the upload process completes, whether successfully or not, Token.Event + is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status. + The caller can supply the data to be uploaded in the following two modes: + - Through the user-provided buffer + - Through a callback function + With the user-provided buffer, the Token.BufferSize field indicates + the length of the buffer, and the driver will upload the data in the + buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver + will call this callback function to get more data from the user to upload. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The upload session has started. + @retval EFI_UNSUPPORTED The operation is not supported by this implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.PacketNeeded are both NULL. + - Token.OverrideData.ServerIp is not a valid unicast IPv6 address. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6WriteFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_WRQ); +} + + +/** + Download a data file directory from an MTFTPv6 server. + + The ReadDirectory() function is used to return a list of files on the + MTFTPv6 server that are logically (or operationally) related to + Token.Filename. The directory request packet that is sent to the server + is built with the option list that was provided by the caller, if present. + The file information that the server returns is put into either of + the following locations: + - A fixed buffer that is pointed to by Token.Buffer. + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded. + @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.CheckPacket are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadDirectory ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_DIR); +} + + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function can be used by network drivers and applications + to increase the rate that data packets are moved between the + communications device and the transmit and receive queues. In some + systems, the periodic timer event in the managed network driver may + not poll the underlying communications device fast enough to transmit + and/or receive all data packets without missing incoming packets or + dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This The MTFTP6 protocol instance. + + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Poll ( + IN EFI_MTFTP6_PROTOCOL *This + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_UDP6_PROTOCOL *Udp6; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + // + // Check the instance whether configured or in destory. + // + if (Instance->Config == NULL) { + return EFI_NOT_STARTED; + } else if (Instance->InDestory) { + return EFI_DEVICE_ERROR; + } + + Udp6 = Instance->UdpIo->Protocol.Udp6; + + return Udp6->Poll (Udp6); +} diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h new file mode 100644 index 0000000000..bf924a980c --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h @@ -0,0 +1,469 @@ +/** @file + Mtftp6 internal data structure and definition declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_MTFTP6_IMPL_H__ +#define __EFI_MTFTP6_IMPL_H__ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct _MTFTP6_SERVICE MTFTP6_SERVICE; +typedef struct _MTFTP6_INSTANCE MTFTP6_INSTANCE; + +#include "Mtftp6Driver.h" +#include "Mtftp6Option.h" +#include "Mtftp6Support.h" + +#define MTFTP6_SERVICE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'S') +#define MTFTP6_INSTANCE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'I') + +#define MTFTP6_DEFAULT_SERVER_CMD_PORT 69 +#define MTFTP6_DEFAULT_TIMEOUT 3 +#define MTFTP6_GET_MAPPING_TIMEOUT 3 +#define MTFTP6_DEFAULT_MAX_RETRY 5 +#define MTFTP6_DEFAULT_BLK_SIZE 512 +#define MTFTP6_TICK_PER_SECOND 10000000U + +#define MTFTP6_SERVICE_FROM_THIS(a) CR (a, MTFTP6_SERVICE, ServiceBinding, MTFTP6_SERVICE_SIGNATURE) +#define MTFTP6_INSTANCE_FROM_THIS(a) CR (a, MTFTP6_INSTANCE, Mtftp6, MTFTP6_INSTANCE_SIGNATURE) + +extern EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate; + +typedef struct _MTFTP6_GETINFO_CONTEXT{ + EFI_MTFTP6_PACKET **Packet; + UINT32 *PacketLen; + EFI_STATUS Status; +} MTFTP6_GETINFO_CONTEXT; + +// +// Control block for MTFTP6 instance, it's per configuration data. +// +struct _MTFTP6_INSTANCE { + UINT32 Signature; + EFI_HANDLE Handle; + LIST_ENTRY Link; + EFI_MTFTP6_PROTOCOL Mtftp6; + MTFTP6_SERVICE *Service; + EFI_MTFTP6_CONFIG_DATA *Config; + + EFI_MTFTP6_TOKEN *Token; + MTFTP6_EXT_OPTION_INFO ExtInfo; + + UINT16 BlkSize; + UINT16 LastBlk; + LIST_ENTRY BlkList; + + EFI_IPv6_ADDRESS ServerIp; + UINT16 ServerCmdPort; + UINT16 ServerDataPort; + UDP_IO *UdpIo; + + EFI_IPv6_ADDRESS McastIp; + UINT16 McastPort; + UDP_IO *McastUdpIo; + + NET_BUF *LastPacket; + UINT32 CurRetry; + UINT32 MaxRetry; + UINT32 PacketToLive; + UINT32 Timeout; + + EFI_TPL OldTpl; + BOOLEAN IsTransmitted; + BOOLEAN IsMaster; + BOOLEAN InDestory; +}; + +// +// Control block for MTFTP6 service, it's per Nic handle. +// +struct _MTFTP6_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + UINT16 ChildrenNum; + LIST_ENTRY Children; + // + // It is used to be as internal calculagraph for all instances. + // + EFI_EVENT Timer; + // + // It is used to maintain the parent-child relationship between + // mtftp driver and udp driver. + // + UDP_IO *DummyUdpIo; + BOOLEAN InDestory; +}; + +/** + Returns the current operating mode data for the MTFTP6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the MTFTP6 instance. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode + data is returned. + + @retval EFI_SUCCESS The configuration data was returned successfully. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + @retval EFI_INVALID_PARAMETER This is NULL, or ModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetModeData ( + IN EFI_MTFTP6_PROTOCOL *This, + OUT EFI_MTFTP6_MODE_DATA *ModeData + ); + +/** + Initializes, changes, or resets the default operational setting for + this EFI MTFTPv6 Protocol driver instance. + + The Configure() function is used to set and change the configuration + data for this EFI MTFTPv6 Protocol driver instance. The configuration + data can be reset to startup defaults by calling Configure() with + MtftpConfigData set to NULL. Whenever the instance is reset, any + pending operation is aborted. By changing the EFI MTFTPv6 Protocol + driver instance configuration data, the client can connect to + different MTFTPv6 servers. The configuration parameters in + MtftpConfigData are used as the default parameters in later MTFTPv6 + operations and can be overridden in later operations. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] MtftpConfigData Pointer to the configuration data structure. + + @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + - This is NULL. + - MtftpConfigData.StationIp is neither zero nor one + of the configured IP addresses in the underlying IPv6 driver. + - MtftpCofigData.ServerIp is not a valid IPv6 unicast address. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there + is some MTFTP background operation in progress. + - MtftpCofigData.LocalPort is already in use. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be + allocated. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI + MTFTPv6 Protocol driver instance is not configured. + Note: It is not defined in the UEFI 2.3 Specification. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Configure ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL + ); + +/** + Get the information of the download from the server. + + The GetInfo() function assembles an MTFTPv6 request packet + with options, sends it to the MTFTPv6 server, and may return + an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries + occur only if no response packets are received from the MTFTPv6 + server before the timeout expires. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the + default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure() + function are used. + @param[in] Filename Pointer to ASCIIZ file name string. + @param[in] ModeStr Pointer to ASCIIZ mode string. If NULL, octet will be used + @param[in] OptionCount Number of option/value string pairs in OptionList. + @param[in] OptionList Pointer to array of option/value string pairs. Ignored if + OptionCount is zero. + @param[out] PacketLength The number of bytes in the returned packet. + @param[out] Packet The pointer to the received packet. This buffer must be freed by + the caller. + + @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Filename is NULL. + - OptionCount is not zero and OptionList is NULL. + - One or more options in OptionList have wrong format. + - PacketLength is NULL. + - OverrideData.ServerIp is not a valid unicast IPv6 address. + @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by + this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received, and the Packet is set to NULL. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received, and the Packet is set to NULL. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetInfo ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL, + IN UINT8 *Filename, + IN UINT8 *ModeStr OPTIONAL, + IN UINT8 OptionCount, + IN EFI_MTFTP6_OPTION *OptionList OPTIONAL, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP6_PACKET **Packet OPTIONAL + ); + +/** + Parse the options in an MTFTPv6 OACK packet. + + The ParseOptions() function parses the option fields in an MTFTPv6 OACK + packet and returns the number of options that were found, and optionally, + a list of pointers to the options in the packet. If one or more of the + option fields are not valid, then EFI_PROTOCOL_ERROR is returned and + *OptionCount and *OptionList stop at the last valid option. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] PacketLen Length of the OACK packet to be parsed. + @param[in] Packet Pointer to the OACK packet to be parsed. + @param[out] OptionCount Pointer to the number of options in the following OptionList. + @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the + OptionList points to the corresponding MTFTP option buffer + in the Packet. Call the EFI Boot Service FreePool() to + release the OptionList if the options in this OptionList + are not needed any more. + + @retval EFI_SUCCESS The OACK packet was valid and the OptionCount, and + OptionList parameters have been updated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - PacketLen is 0. + - Packet is NULL or Packet is not a valid MTFTPv6 packet. + - OptionCount is NULL. + @retval EFI_NOT_FOUND No options were found in the OACK packet. + @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated. + @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ParseOptions ( + IN EFI_MTFTP6_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP6_PACKET *Packet, + OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ); + +/** + Download a file from an MTFTPv6 server. + + The ReadFile() function is used to initialize and start an MTFTPv6 download + process and optionally wait for completion. When the download operation + completes, whether successfully or not, the Token.Status field is updated + by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it + is not NULL. + Data can be downloaded from the MTFTPv6 server into either of the following + locations: + - A fixed buffer that is pointed to by Token.Buffer. + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The data file has been transferred successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the + downloaded data in downloading process. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ABORTED Current operation is aborted by user. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_ICMP_ERROR An ICMP ERROR packet was received. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Send a file to an MTFTPv6 server. + + The WriteFile() function is used to initialize an uploading operation + with the given option list, and optionally, wait for completion. If one + or more of the options is not supported by the server, the unsupported + options are ignored and a standard TFTP process starts instead. When + the upload process completes, whether successfully or not, Token.Event + is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status. + The caller can supply the data to be uploaded in the following two modes: + - Through the user-provided buffer. + - Through a callback function. + With the user-provided buffer, the Token.BufferSize field indicates + the length of the buffer, and the driver will upload the data in the + buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver + will call this callback function to get more data from the user to upload. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The upload session has started. + @retval EFI_UNSUPPORTED The operation is not supported by this implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.PacketNeeded are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6WriteFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Download a data file directory from an MTFTPv6 server. + + The ReadDirectory() function is used to return a list of files on the + MTFTPv6 server that are logically (or operationally) related to + Token.Filename. The directory request packet that is sent to the server + is built with the option list that was provided by caller, if present. + The file information that the server returns is put into either of + the following locations: + - A fixed buffer that is pointed to by Token.Buffer. + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded. + @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.CheckPacket are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadDirectory ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function can be used by network drivers and applications + to increase the rate that data packets are moved between the + communications device and the transmit and receive queues.In some + systems, the periodic timer event in the managed network driver may + not poll the underlying communications device fast enough to transmit + and/or receive all data packets without missing incoming packets or + dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This The MTFTP6 protocol instance. + + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Poll ( + IN EFI_MTFTP6_PROTOCOL *This + ); + +#endif diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c new file mode 100644 index 0000000000..0dcf546fa8 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c @@ -0,0 +1,416 @@ +/** @file + Mtftp6 option parse functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Mtftp6Impl.h" + +CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM] = { + "blksize", + "timeout", + "tsize", + "multicast" +}; + + +/** + Parse the NULL terminated ASCII string of multicast option. + + @param[in] Str The pointer to the Ascii string of multicast option. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER The string is malformatted. + @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of + resources. + +**/ +EFI_STATUS +Mtftp6ParseMcastOption ( + IN UINT8 *Str, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ) +{ + EFI_STATUS Status; + UINT32 Num; + CHAR8 *Ip6Str; + CHAR8 *TempStr; + + // + // The multicast option is formated like "addr,port,mc" + // The server can also omit the ip and port, use ",,1" + // + if (*Str == ',') { + + ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS)); + } else { + + Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str); + if (Ip6Str == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // The IPv6 address locates before comma in the input Str. + // + TempStr = Ip6Str; + while ((*TempStr != '\0') && (*TempStr != ',')) { + TempStr++; + } + + *TempStr = '\0'; + + Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp); + FreePool (Ip6Str); + + if (EFI_ERROR (Status)) { + return Status; + } + + while ((*Str != '\0') && (*Str != ',')) { + Str++; + } + } + + if (*Str != ',') { + return EFI_INVALID_PARAMETER; + } + + Str++; + + // + // Convert the port setting. the server can send us a port number or + // empty string. such as the port in ",,1" + // + if (*Str == ',') { + + ExtInfo->McastPort = 0; + } else { + + Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); + + if (Num > 65535) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->McastPort = (UINT16) Num; + + while (NET_IS_DIGIT (*Str)) { + Str++; + } + } + + if (*Str != ',') { + return EFI_INVALID_PARAMETER; + } + + Str++; + + // + // Check the master/slave setting, 1 for master, 0 for slave. + // + Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); + + if (Num != 0 && Num != 1) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->IsMaster = (BOOLEAN) (Num == 1); + + while (NET_IS_DIGIT (*Str)) { + Str++; + } + + if (*Str != '\0') { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +/** + Parse the MTFTP6 extesion options. + + @param[in] Options The pointer to the extension options list. + @param[in] Count The num of the extension options. + @param[in] IsRequest If FALSE, the extension options is included + by a request packet. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER There is one option is malformatted at least. + @retval EFI_UNSUPPORTED There is one option is not supported at least. + +**/ +EFI_STATUS +Mtftp6ParseExtensionOption ( + IN EFI_MTFTP6_OPTION *Options, + IN UINT32 Count, + IN BOOLEAN IsRequest, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ) +{ + EFI_STATUS Status; + EFI_MTFTP6_OPTION *Opt; + UINT32 Index; + UINT32 Value; + + ExtInfo->BitMap = 0; + + for (Index = 0; Index < Count; Index++) { + + Opt = Options + Index; + + if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) { + // + // block size option, valid value is between [8, 65464] + // + Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + + if ((Value < 8) || (Value > 65464)) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->BlkSize = (UINT16) Value; + ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) { + // + // timeout option, valid value is between [1, 255] + // + Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + + if (Value < 1 || Value > 255) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->Timeout = (UINT8) Value; + ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) { + // + // tsize option, the biggest transfer supported is 4GB with block size option + // + ExtInfo->Tsize = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) { + // + // Multicast option, if it is a request, the value must be a zero string, + // otherwise, it must be like "addr,port,mc" string, mc indicates master. + // + if (!IsRequest) { + + Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo); + + if (EFI_ERROR (Status)) { + return Status; + } + } else if (*(Opt->ValueStr) != '\0') { + + return EFI_INVALID_PARAMETER; + } + + ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT; + + } else if (IsRequest) { + // + // If it's a request, unsupported; else if it's a reply, ignore. + // + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + + +/** + Go through the packet to fill the options array with the start + addresses of each MTFTP option name/value pair. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] Count The num of the Options on input. + The actual one on output. + @param[in] Options The option array to be filled. + It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_BUFFER_TOO_SMALL The Options array is too small. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received. + +**/ +EFI_STATUS +Mtftp6ParsePacketOption ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *Count, + IN EFI_MTFTP6_OPTION *Options OPTIONAL + ) +{ + UINT8 *Cur; + UINT8 *Last; + UINT8 Num; + UINT8 *Name; + UINT8 *Value; + + Num = 0; + Cur = (UINT8 *) Packet + MTFTP6_OPCODE_LEN; + Last = (UINT8 *) Packet + PacketLen - 1; + + // + // process option name and value pairs. + // The last byte is always zero. + // + while (Cur < Last) { + Name = Cur; + + while (*Cur != 0) { + Cur++; + } + + if (Cur == Last) { + return EFI_PROTOCOL_ERROR; + } + + Value = ++Cur; + + while (*Cur != 0) { + Cur++; + } + + Num++; + + if (Options != NULL && Num <= *Count) { + Options[Num - 1].OptionStr = Name; + Options[Num - 1].ValueStr = Value; + } + + Cur++; + } + + // + // Return buffer too small if the buffer passed-in isn't enough. + // + if (*Count < Num || Options == NULL) { + *Count = Num; + return EFI_BUFFER_TOO_SMALL; + } + + *Count = Num; + return EFI_SUCCESS; +} + + +/** + Go through the packet, generate option list array and fill it + by the result of parse options. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] OptionCount The num of the Options on input. + The actual one on output. + @param[out] OptionList The option list array to be generated + and filled. It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_PROTOCOL_ERROR There is one option is malformatted at least. + @retval EFI_NOT_FOUND The packet has no options. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array. + @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small. + +**/ +EFI_STATUS +Mtftp6ParseStart ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ) +{ + EFI_STATUS Status; + + if (PacketLen == 0 || Packet == NULL || OptionCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + *OptionCount = 0; + + if (OptionList != NULL) { + *OptionList = NULL; + } + + if (NTOHS (Packet->OpCode) != EFI_MTFTP6_OPCODE_OACK) { + return EFI_INVALID_PARAMETER; + } + + // + // The last byte must be zero to terminate the options. + // + if (*((UINT8 *) Packet + PacketLen - 1) != 0) { + return EFI_PROTOCOL_ERROR; + } + + // + // Parse packet with NULL buffer for the first time to get the number + // of options in the packet. + // + Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, NULL); + + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Return not found if there is no option parsed. + // + if (*OptionCount == 0) { + return EFI_NOT_FOUND; + } + + // + // Only need parse out the number of options. + // + if (OptionList == NULL) { + return EFI_SUCCESS; + } + + // + // Allocate the buffer according to the option number parsed before. + // + *OptionList = AllocateZeroPool (*OptionCount * sizeof (EFI_MTFTP6_OPTION)); + + if (*OptionList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Parse packet with allocated buffer for the second time to fill the pointer array + // of the options in the packet. + // + Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, *OptionList); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h new file mode 100644 index 0000000000..8e2671fa21 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h @@ -0,0 +1,148 @@ +/** @file + Mtftp6 option parse functions declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_MTFTP6_OPTION_H__ +#define __EFI_MTFTP6_OPTION_H__ + +#include + +#include + +#include +#include +#include +#include +#include + +#define MTFTP6_SUPPORTED_OPTIONS_NUM 4 +#define MTFTP6_OPCODE_LEN 2 +#define MTFTP6_ERRCODE_LEN 2 +#define MTFTP6_BLKNO_LEN 2 +#define MTFTP6_DATA_HEAD_LEN 4 + +// +// The bit map definition for Mtftp6 extension options. +// +#define MTFTP6_OPT_BLKSIZE_BIT 0x01 +#define MTFTP6_OPT_TIMEOUT_BIT 0x02 +#define MTFTP6_OPT_TSIZE_BIT 0x04 +#define MTFTP6_OPT_MCAST_BIT 0x08 + +extern CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM]; + +typedef struct { + UINT16 BlkSize; + UINT8 Timeout; + UINT32 Tsize; + EFI_IPv6_ADDRESS McastIp; + UINT16 McastPort; + BOOLEAN IsMaster; + UINT32 BitMap; +} MTFTP6_EXT_OPTION_INFO; + +/** + Parse the Ascii string of multi-cast option. + + @param[in] Str The pointer to the Ascii string of multi-cast option. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER The string is malformatted. + +**/ +EFI_STATUS +Mtftp6ParseMcastOption ( + IN UINT8 *Str, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ); + + +/** + Parse the MTFTP6 extesion options. + + @param[in] Options The pointer to the extension options list. + @param[in] Count The num of the extension options. + @param[in] IsRequest If FALSE, the extension options is included + by a request packet. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multi-cast option successfully. + @retval EFI_INVALID_PARAMETER An option is malformatted. + @retval EFI_UNSUPPORTED An option is not supported. + +**/ +EFI_STATUS +Mtftp6ParseExtensionOption ( + IN EFI_MTFTP6_OPTION *Options, + IN UINT32 Count, + IN BOOLEAN IsRequest, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ); + + +/** + Go through the packet to fill the options array with the start + addresses of each MTFTP option name/value pair. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] Count The num of the Options on input. + The actual one on output. + @param[in] Options The option array to be filled + it's optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted + @retval EFI_BUFFER_TOO_SMALL The Options array is too small + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received. + +**/ +EFI_STATUS +Mtftp6ParsePacketOption ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *Count, + IN EFI_MTFTP6_OPTION *Options OPTIONAL + ); + + +/** + Go through the packet, generate option list array and fill it + by the result of parse options. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] OptionCount The num of the Options on input. + The actual one on output. + @param[out] OptionList The option list array to be generated + and filled. It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_PROTOCOL_ERROR An option is malformatted. + @retval EFI_NOT_FOUND The packet has no options. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array. + @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small. + +**/ +EFI_STATUS +Mtftp6ParseStart ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ); + +#endif diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c new file mode 100644 index 0000000000..da364329fc --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c @@ -0,0 +1,900 @@ +/** @file + Mtftp6 Rrq process functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Mtftp6Impl.h" + + +/** + Build and send a ACK packet for download. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] BlockNum The block number to be acked. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. + @retval EFI_SUCCESS The ACK has been sent. + @retval Others Failed to send the ACK. + +**/ +EFI_STATUS +Mtftp6RrqSendAck ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP6_PACKET *Ack; + NET_BUF *Packet; + + // + // Allocate net buffer to create ack packet. + // + Packet = NetbufAlloc (sizeof (EFI_MTFTP6_ACK_HEADER)); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ack = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( + Packet, + sizeof (EFI_MTFTP6_ACK_HEADER), + FALSE + ); + ASSERT (Ack != NULL); + + Ack->Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); + Ack->Ack.Block[0] = HTONS (BlockNum); + + // + // Reset current retry count of the instance. + // + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, Packet); +} + + +/** + Deliver the received data block to the user, which can be saved + in the user provide buffer or through the CheckPacket callback. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The packet length. + @param[out] UdpPacket The net buf of the received packet. + + @retval EFI_SUCCESS The data was saved successfully. + @retval EFI_ABORTED The user tells to abort by return an error through + CheckPacket. + @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small, and buffer length is + updated to the actual buffer size needed. + +**/ +EFI_STATUS +Mtftp6RrqSaveBlock ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket + ) +{ + EFI_MTFTP6_TOKEN *Token; + EFI_STATUS Status; + UINT16 Block; + UINT64 Start; + UINT32 DataLen; + UINT64 TotalBlock; + BOOLEAN Completed; + + Completed = FALSE; + Token = Instance->Token; + Block = NTOHS (Packet->Data.Block); + DataLen = Len - MTFTP6_DATA_HEAD_LEN; + + // + // This is the last block, save the block num + // + if (DataLen < Instance->BlkSize) { + Completed = TRUE; + Instance->LastBlk = Block; + Mtftp6SetLastBlockNum (&Instance->BlkList, Block); + } + + // + // Remove this block number from the file hole. If Mtftp6RemoveBlockNum + // returns EFI_NOT_FOUND, the block has been saved, don't save it again. + // Note that : For bigger files, allowing the block counter to roll over + // to accept transfers of unlimited size. So TotalBlock is memorised as + // continuous block counter. + // + Status = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &TotalBlock); + + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else if (EFI_ERROR (Status)) { + return Status; + } + + if (Token->CheckPacket != NULL) { + // + // Callback to the check packet routine with the received packet. + // + Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet); + + if (EFI_ERROR (Status)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the Udp6Io might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "User aborted download" + ); + + return EFI_ABORTED; + } + } + + if (Token->Buffer != NULL) { + + Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize); + if (Start + DataLen <= Token->BufferSize) { + CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen); + // + // Update the file size when received the last block + // + if ((Instance->LastBlk == Block) && Completed) { + Token->BufferSize = Start + DataLen; + } + } else if (Instance->LastBlk != 0) { + // + // Don't save the data if the buffer is too small, return + // EFI_BUFFER_TOO_SMALL if received the last packet. This + // will give a accurate file length. + // + Token->BufferSize = Start + DataLen; + + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if no enough buffer. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_DISK_FULL, + (UINT8 *) "User provided memory block is too small" + ); + + return EFI_BUFFER_TOO_SMALL; + } + } + + return EFI_SUCCESS; +} + + +/** + Process the received data packets. It will save the block + then send back an ACK if it is active. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the download has been completed. + Otherwise, the download has not been completed. + + @retval EFI_SUCCESS The data packet was successfully processed. + @retval EFI_ABORTED The download was aborted by the user. + @retval EFI_BUFFER_TOO_SMALL The user-provided buffer is too small. + +**/ +EFI_STATUS +Mtftp6RrqHandleData ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_STATUS Status; + UINT16 BlockNum; + INTN Expected; + + *IsCompleted = FALSE; + BlockNum = NTOHS (Packet->Data.Block); + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + ASSERT (Expected >= 0); + + // + // If we are active and received an unexpected packet, retransmit + // the last ACK then restart receiving. If we are passive, save + // the block. + // + if (Instance->IsMaster && (Expected != BlockNum)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + Mtftp6TransmitPacket (Instance, Instance->LastPacket); + return EFI_SUCCESS; + } + + Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Reset the passive client's timer whenever it received a valid data packet. + // + if (!Instance->IsMaster) { + Instance->PacketToLive = Instance->Timeout * 2; + } + + // + // Check whether we have received all the blocks. Send the ACK if we + // are active (unicast client or master client for multicast download). + // If we have received all the blocks, send an ACK even if we are passive + // to tell the server that we are done. + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Instance->IsMaster || Expected < 0) { + if (Expected < 0) { + // + // If we are passive client, then the just received Block maybe + // isn't the last block. We need to send an ACK to the last block + // to inform the server that we are done. If we are active client, + // the Block == Instance->LastBlock. + // + BlockNum = Instance->LastBlk; + *IsCompleted = TRUE; + + } else { + BlockNum = (UINT16) (Expected - 1); + } + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + Mtftp6RrqSendAck (Instance, BlockNum); + } + + return EFI_SUCCESS; +} + + +/** + Validate whether the options received in the server's OACK packet is valid. + The options are valid only if: + 1. The server doesn't include options not requested by us. + 2. The server can only use smaller blksize than that is requested. + 3. The server can only use the same timeout as requested. + 4. The server doesn't change its multicast channel. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ReplyInfo The pointer to options information in reply packet. + @param[in] RequestInfo The pointer to requested options info. + + @retval TRUE If the option in the OACK is valid. + @retval FALSE If the option is invalid. + +**/ +BOOLEAN +Mtftp6RrqOackValid ( + IN MTFTP6_INSTANCE *Instance, + IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, + IN MTFTP6_EXT_OPTION_INFO *RequestInfo + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout)) + ) { + return FALSE; + } + + // + // The server can send ",,master" to client to change its master + // setting. But if it use the specific multicast channel, it can't + // change the setting. + // + if (((ReplyInfo->BitMap & MTFTP6_OPT_MCAST_BIT) != 0) && !NetIp6IsUnspecifiedAddr (&Instance->McastIp)) { + + if (!NetIp6IsUnspecifiedAddr (&ReplyInfo->McastIp) && CompareMem ( + &ReplyInfo->McastIp, + &Instance->McastIp, + sizeof (EFI_IPv6_ADDRESS) + ) != 0) { + return FALSE; + } + + if ((ReplyInfo->McastPort != 0) && (ReplyInfo->McastPort != Instance->McastPort)) { + return FALSE; + } + } + + return TRUE; +} + + +/** + Configure Udp6Io to receive a packet from a multicast address. + + @param[in] McastIo The pointer to the mcast Udp6Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The mcast Udp6Io was successfully configured. + @retval Others Failed to configure the Udp6Io. + +**/ +EFI_STATUS +EFIAPI +Mtftp6RrqConfigMcastUdpIo ( + IN UDP_IO *McastIo, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + EFI_IPv6_ADDRESS Group; + MTFTP6_INSTANCE *Instance; + + Udp6 = McastIo->Protocol.Udp6; + Udp6Cfg = &(McastIo->Config.Udp6); + Instance = (MTFTP6_INSTANCE *) Context; + + // + // Set the configure data for the mcast Udp6Io. + // + ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + Udp6Cfg->AcceptPromiscuous = FALSE; + Udp6Cfg->AcceptAnyPort = FALSE; + Udp6Cfg->AllowDuplicatePort = FALSE; + Udp6Cfg->TrafficClass = 0; + Udp6Cfg->HopLimit = 128; + Udp6Cfg->ReceiveTimeout = 0; + Udp6Cfg->TransmitTimeout = 0; + Udp6Cfg->StationPort = Instance->McastPort; + Udp6Cfg->RemotePort = 0; + + CopyMem ( + &Udp6Cfg->RemoteAddress, + &Instance->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + // + // Configure the mcast Udp6Io. + // + Status = Udp6->Configure (Udp6, Udp6Cfg); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Join the multicast group + // + CopyMem (&Group, &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS)); + + return Udp6->Groups (Udp6, TRUE, &Group); +} + + +/** + Process the OACK packet for Rrq. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the download has been completed. + Otherwise, the download has not been completed. + + @retval EFI_DEVICE_ERROR Failed to create/start a multicast Udp6 child. + @retval EFI_TFTP_ERROR An error happened during the process. + @retval EFI_SUCCESS The OACK packet successfully processed. + +**/ +EFI_STATUS +Mtftp6RrqHandleOack ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_MTFTP6_OPTION *Options; + UINT32 Count; + MTFTP6_EXT_OPTION_INFO ExtInfo; + EFI_STATUS Status; + INTN Expected; + + *IsCompleted = FALSE; + + // + // If already started the master download, don't change the + // setting. Master download always succeeds. + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + ASSERT (Expected != -1); + + if (Instance->IsMaster && Expected != 1) { + return EFI_SUCCESS; + } + + ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + + // + // Parse the options in the packet. + // + Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Parse the extensive options in the packet. + // + Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo); + + if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) { + // + // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES. + // + if (Status != EFI_OUT_OF_RESOURCES) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid packet. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Mal-formated OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) { + + // + // Save the multicast info. Always update the Master, only update the + // multicast IP address, block size, timeoute at the first time. If IP + // address is updated, create a UDP child to receive the multicast. + // + Instance->IsMaster = ExtInfo.IsMaster; + + if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) { + if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid multi-cast setting. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Illegal multicast setting" + ); + + return EFI_TFTP_ERROR; + } + + // + // Create a UDP child then start receive the multicast from it. + // + CopyMem ( + &Instance->McastIp, + &ExtInfo.McastIp, + sizeof (EFI_IP_ADDRESS) + ); + + Instance->McastPort = ExtInfo.McastPort; + Instance->McastUdpIo = UdpIoCreateIo ( + Instance->Service->Controller, + Instance->Service->Image, + Mtftp6RrqConfigMcastUdpIo, + UDP_IO_UDP6_VERSION, + Instance + ); + + if (Instance->McastUdpIo == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = UdpIoRecvDatagram ( + Instance->McastUdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + + if (EFI_ERROR (Status)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if failed to create Udp6Io to receive. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION, + (UINT8 *) "Failed to create socket to receive multicast packet" + ); + + return Status; + } + + // + // Update the parameters used. + // + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + } + + } else { + + Instance->IsMaster = TRUE; + + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + } + + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send an ACK to (Expected - 1) which is 0 for unicast download, + // or tell the server we want to receive the Expected block. + // + return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1)); +} + + +/** + The packet process callback for Mtftp6 download. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PACKET *Packet; + BOOLEAN IsCompleted; + BOOLEAN IsMcast; + EFI_STATUS Status; + UINT16 Opcode; + UINT32 TotalNum; + UINT32 Len; + + Instance = (MTFTP6_INSTANCE *) Context; + + NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); + + Status = EFI_SUCCESS; + Packet = NULL; + IsCompleted = FALSE; + IsMcast = FALSE; + TotalNum = 0; + + // + // Return error status if Udp6 instance failed to receive. + // + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Find the port this packet is from to restart receive correctly. + // + if (CompareMem ( + Ip6Swap128 (&UdpEpt->LocalAddr.v6), + &Instance->McastIp, + sizeof (EFI_IPv6_ADDRESS) + ) == 0) { + IsMcast = TRUE; + } else { + IsMcast = FALSE; + } + + // + // Client send initial request to server's listening port. Server + // will select a UDP port to communicate with the client. The server + // is required to use the same port as RemotePort to multicast the + // data. + // + if (UdpEpt->RemotePort != Instance->ServerDataPort) { + if (Instance->ServerDataPort != 0) { + goto ON_EXIT; + } else { + // + // For the subsequent exchange of requests, reconfigure the udpio as + // (serverip, serverport, localip, localport). + // Ususally, the client set serverport as 0 to receive and reset it + // once the first packet arrives to send ack. + // + Instance->ServerDataPort = UdpEpt->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + TotalNum = UdpPacket->BlockOpNum; + + if (TotalNum > 1) { + Packet = AllocateZeroPool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Callback to the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if ((Instance->Token->CheckPacket != NULL) && + (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) + ) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp6, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (UdpPacket); + UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Switch the process routines by the operation code. + // + switch (Opcode) { + case EFI_MTFTP6_OPCODE_DATA: + if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) { + goto ON_EXIT; + } + // + // Handle the data packet of Rrq. + // + Status = Mtftp6RrqHandleData ( + Instance, + Packet, + Len, + &UdpPacket, + &IsCompleted + ); + break; + + case EFI_MTFTP6_OPCODE_OACK: + if (IsMcast || Len <= MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + // + // Handle the Oack packet of Rrq. + // + Status = Mtftp6RrqHandleOack ( + Instance, + Packet, + Len, + &UdpPacket, + &IsCompleted + ); + break; + + default: + // + // Drop and return eror if received error message. + // + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status), restart the + // receive, otherwise end the session. + // + if (Packet != NULL && TotalNum > 1) { + FreePool (Packet); + } + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + if (!EFI_ERROR (Status) && !IsCompleted) { + if (IsMcast) { + Status = UdpIoRecvDatagram ( + Instance->McastUdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + } else { + Status = UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + } + } + // + // Clean up the current session if failed to continue. + // + if (EFI_ERROR (Status) || IsCompleted) { + Mtftp6OperationClean (Instance, Status); + } +} + + +/** + Start the Mtftp6 instance to download. It first initializes some + of the internal states, then builds and sends an RRQ reqeuest packet. + Finally, it starts receive for the downloading. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 is started to download. + @retval Others Failed to start to download. + +**/ +EFI_STATUS +Mtftp6RrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_STATUS Status; + + // + // The valid block number range are [1, 0xffff]. For example: + // the client sends an RRQ request to the server, the server + // transfers the DATA1 block. If option negoitation is ongoing, + // the server will send back an OACK, then client will send ACK0. + // + Status = Mtftp6InitBlockRange (&Instance->BlkList, 1, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6SendRequest (Instance, Operation); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); +} + diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c new file mode 100644 index 0000000000..24ce0e85ba --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c @@ -0,0 +1,1189 @@ +/** @file + Mtftp6 support functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Mtftp6Impl.h" + + +/** + Allocate a MTFTP block range, then init it to the range of [Start, End]. + + @param[in] Start The start block number. + @param[in] End The last block number in the range. + + @return Range The range of the allocated block buffer. + +**/ +MTFTP6_BLOCK_RANGE * +Mtftp6AllocateRange ( + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE)); + + if (Range == NULL) { + return NULL; + } + + InitializeListHead (&Range->Link); + Range->Start = Start; + Range->End = End; + Range->Bound = End; + + return Range; +} + + +/** + Initialize the block range for either RRQ or WRQ. RRQ and WRQ have + different requirements for Start and End. For example, during startup, + WRQ initializes its whole valid block range to [0, 0xffff]. This + is bacause the server will send an ACK0 to inform the user to start the + upload. When the client receives an ACK0, it will remove 0 from the range, + get the next block number, which is 1, then upload the BLOCK1. For RRQ + without option negotiation, the server will directly send the BLOCK1 + in response to the client's RRQ. When received BLOCK1, the client will + remove it from the block range and send an ACK. It also works if there + is option negotiation. + + @param[in] Head The block range head to initialize. + @param[in] Start The Start block number. + @param[in] End The last block number. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range. + @retval EFI_SUCCESS The initial block range is created. + +**/ +EFI_STATUS +Mtftp6InitBlockRange ( + IN LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + Range = Mtftp6AllocateRange (Start, End); + + if (Range == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InsertTailList (Head, &Range->Link); + return EFI_SUCCESS; +} + + +/** + Get the first valid block number on the range list. + + @param[in] Head The block range head. + + @retval ==-1 If the block range is empty. + @retval >-1 The first valid block number. + +**/ +INTN +Mtftp6GetNextBlockNum ( + IN LIST_ENTRY *Head + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + if (IsListEmpty (Head)) { + return -1; + } + + Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link); + return Range->Start; +} + + +/** + Set the last block number of the block range list. It + removes all the blocks after the Last. MTFTP initialize the + block range to the maximum possible range, such as [0, 0xffff] + for WRQ. When it gets the last block number, it calls + this function to set the last block number. + + @param[in] Head The block range list. + @param[in] Last The last block number. + +**/ +VOID +Mtftp6SetLastBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Last + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + // + // Iterate from the tail to head to remove the block number + // after the last. + // + while (!IsListEmpty (Head)) { + Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link); + + if (Range->Start > Last) { + RemoveEntryList (&Range->Link); + FreePool (Range); + continue; + } + + if (Range->End > Last) { + Range->End = Last; + } + return ; + } +} + + +/** + Remove the block number from the block range list. + + @param[in] Head The block range list to remove from. + @param[in] Num The block number to remove. + @param[in] Completed Whether Num is the last block number + @param[out] TotalBlock The continuous block number in all + + @retval EFI_NOT_FOUND The block number isn't in the block range list. + @retval EFI_SUCCESS The block number has been removed from the list. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +Mtftp6RemoveBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Num, + IN BOOLEAN Completed, + OUT UINT64 *TotalBlock + ) +{ + MTFTP6_BLOCK_RANGE *Range; + MTFTP6_BLOCK_RANGE *NewRange; + LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, Head) { + + // + // Each block represents a hole [Start, End] in the file, + // skip to the first range with End >= Num + // + Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + + if (Range->End < Num) { + continue; + } + + // + // There are three different cases for Start + // 1. (Start > Num) && (End >= Num): + // because all the holes before this one has the condition of + // End < Num, so this block number has been removed. + // + // 2. (Start == Num) && (End >= Num): + // Need to increase the Start by one, and if End == Num, this + // hole has been removed completely, remove it. + // + // 3. (Start < Num) && (End >= Num): + // if End == Num, only need to decrease the End by one because + // we have (Start < Num) && (Num == End), so (Start <= End - 1). + // if (End > Num), the hold is splited into two holes, with + // [Start, Num - 1] and [Num + 1, End]. + // + if (Range->Start > Num) { + return EFI_NOT_FOUND; + + } else if (Range->Start == Num) { + Range->Start++; + + // + // Note that: RFC 1350 does not mention block counter roll-over, + // but several TFTP hosts implement the roll-over be able to accept + // transfers of unlimited size. There is no consensus, however, whether + // the counter should wrap around to zero or to one. Many implementations + // wrap to zero, because this is the simplest to implement. Here we choose + // this solution. + // + *TotalBlock = Num; + + if (Range->Round > 0) { + *TotalBlock += Range->Bound + MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1; + } + + if (Range->Start > Range->Bound) { + Range->Start = 0; + Range->Round ++; + } + + if ((Range->Start > Range->End) || Completed) { + RemoveEntryList (&Range->Link); + FreePool (Range); + } + + return EFI_SUCCESS; + + } else { + if (Range->End == Num) { + Range->End--; + } else { + NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End); + + if (NewRange == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Range->End = Num - 1; + NetListInsertAfter (&Range->Link, &NewRange->Link); + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Configure the opened Udp6 instance until the corresponding Ip6 instance + has been configured. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] UdpCfgData The pointer to the Udp6 configure data. + + @retval EFI_SUCCESS Configure the Udp6 instance successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not + been configured yet. + +**/ +EFI_STATUS +Mtftp6GetMapping ( + IN UDP_IO *UdpIo, + IN EFI_UDP6_CONFIG_DATA *UdpCfgData + ) +{ + EFI_IP6_MODE_DATA Ip6Mode; + EFI_UDP6_PROTOCOL *Udp6; + EFI_STATUS Status; + EFI_EVENT Event; + + Event = NULL; + Udp6 = UdpIo->Protocol.Udp6; + + // + // Create a timer to check whether the Ip6 instance configured or not. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Event, + TimerRelative, + MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check the Ip6 mode data till timeout. + // + while (EFI_ERROR (gBS->CheckEvent (Event))) { + + Udp6->Poll (Udp6); + + Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL); + + if (!EFI_ERROR (Status)) { + + if (Ip6Mode.IsConfigured) { + // + // Continue to configure the Udp6 instance. + // + Status = Udp6->Configure (Udp6, UdpCfgData); + } else { + Status = EFI_NO_MAPPING; + } + } + } + +ON_EXIT: + + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + return Status; +} + + +/** + The dummy configure routine for create a new Udp6 Io. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS This value is always returned. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ConfigDummyUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + return EFI_SUCCESS; +} + + +/** + The configure routine for Mtftp6 instance to transmit/receive. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] ServerIp The pointer to the server address. + @param[in] ServerPort The pointer to the server port. + @param[in] LocalIp The pointer to the local address. + @param[in] LocalPort The pointer to the local port. + + @retval EFI_SUCCESS Configured the Udp6 Io for Mtftp6 successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been + configured yet. + +**/ +EFI_STATUS +Mtftp6ConfigUdpIo ( + IN UDP_IO *UdpIo, + IN EFI_IPv6_ADDRESS *ServerIp, + IN UINT16 ServerPort, + IN EFI_IPv6_ADDRESS *LocalIp, + IN UINT16 LocalPort + ) +{ + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + + Udp6 = UdpIo->Protocol.Udp6; + Udp6Cfg = &(UdpIo->Config.Udp6); + + ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + // + // Set the Udp6 Io configure data. + // + Udp6Cfg->AcceptPromiscuous = FALSE; + Udp6Cfg->AcceptAnyPort = FALSE; + Udp6Cfg->AllowDuplicatePort = FALSE; + Udp6Cfg->TrafficClass = 0; + Udp6Cfg->HopLimit = 128; + Udp6Cfg->ReceiveTimeout = 0; + Udp6Cfg->TransmitTimeout = 0; + Udp6Cfg->StationPort = LocalPort; + Udp6Cfg->RemotePort = ServerPort; + + CopyMem ( + &Udp6Cfg->StationAddress, + LocalIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + CopyMem ( + &Udp6Cfg->RemoteAddress, + ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + // + // Configure the Udp6 instance with current configure data. + // + Status = Udp6->Configure (Udp6, Udp6Cfg); + + if (Status == EFI_NO_MAPPING) { + + return Mtftp6GetMapping (UdpIo, Udp6Cfg); + } + + return Status; +} + + +/** + Build and transmit the request packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of this packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request. + @retval EFI_SUCCESS The request is built and sent. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendRequest ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_OPTION *Options; + EFI_MTFTP6_TOKEN *Token; + NET_BUF *Nbuf; + UINT8 *Mode; + UINT8 *Cur; + UINT32 Len1; + UINT32 Len2; + UINT32 Len; + UINTN Index; + + Token = Instance->Token; + Options = Token->OptionList; + Mode = Token->ModeStr; + + if (Mode == NULL) { + Mode = (UINT8 *) "octet"; + } + + // + // The header format of RRQ/WRQ packet is: + // + // 2 bytes string 1 byte string 1 byte + // ------------------------------------------------ + // | Opcode | Filename | 0 | Mode | 0 | + // ------------------------------------------------ + // + // The common option format is: + // + // string 1 byte string 1 byte + // --------------------------------------- + // | OptionStr | 0 | ValueStr | 0 | + // --------------------------------------- + // + + // + // Compute the size of new Mtftp6 packet. + // + Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename); + Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode); + Len = Len1 + Len2 + 4; + + for (Index = 0; Index < Token->OptionCount; Index++) { + Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr); + Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr); + Len += Len1 + Len2 + 2; + } + + // + // Allocate a packet then copy the data. + // + if ((Nbuf = NetbufAlloc (Len)) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the opcode, filename and mode into packet. + // + Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE); + ASSERT (Packet != NULL); + + Packet->OpCode = HTONS (Operation); + Cur = Packet->Rrq.Filename; + Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename); + Cur += AsciiStrLen ((CHAR8 *) Token->Filename) + 1; + Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode); + Cur += AsciiStrLen ((CHAR8 *) Mode) + 1; + + // + // Copy all the extension options into the packet. + // + for (Index = 0; Index < Token->OptionCount; ++Index) { + Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr); + Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1; + Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr); + Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1; + } + + // + // Save the packet buf for retransmit + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + Instance->LastPacket = Nbuf; + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, Nbuf); +} + + +/** + Build and send an error packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ErrCode The error code in the packet. + @param[in] ErrInfo The error message in the packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet. + @retval EFI_SUCCESS The error packet is transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendError ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ) +{ + NET_BUF *Nbuf; + EFI_MTFTP6_PACKET *TftpError; + UINT32 Len; + + // + // Allocate a packet then copy the data. + // + Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER)); + Nbuf = NetbufAlloc (Len); + + if (Nbuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE); + + if (TftpError == NULL) { + NetbufFree (Nbuf); + return EFI_OUT_OF_RESOURCES; + } + + TftpError->OpCode = HTONS (EFI_MTFTP6_OPCODE_ERROR); + TftpError->Error.ErrorCode = HTONS (ErrCode); + + AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo); + + // + // Save the packet buf for retransmit + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + Instance->LastPacket = Nbuf; + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, Nbuf); +} + + +/** + The callback function called when the packet is transmitted. + + @param[in] Packet The pointer to the packet. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The result of the transmission. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnPacketSent ( + IN NET_BUF *Packet, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + NetbufFree (Packet); + *(BOOLEAN *) Context = TRUE; +} + + +/** + Send the packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the packet to be sent. + + @retval EFI_SUCCESS The packet was sent out + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6TransmitPacket ( + IN MTFTP6_INSTANCE *Instance, + IN NET_BUF *Packet + ) +{ + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA Udp6CfgData; + EFI_STATUS Status; + UINT16 *Temp; + UINT16 Value; + UINT16 OpCode; + + ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA)); + Udp6 = Instance->UdpIo->Protocol.Udp6; + + // + // Set the live time of the packet. + // + Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2); + + Temp = (UINT16 *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Temp != NULL); + + Value = *Temp; + OpCode = NTOHS (Value); + + if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) { + // + // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as + // (serverip, 69, localip, localport) to send. + // Usually local address and local port are both default as zero. + // + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerCmdPort, + &Instance->Config->StationIp, + Instance->Config->LocalPort + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the current local address and port by get Udp6 mode data. + // + Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + NET_GET_REF (Packet); + + Instance->IsTransmitted = FALSE; + + Status = UdpIoSendDatagram ( + Instance->UdpIo, + Packet, + NULL, + NULL, + Mtftp6OnPacketSent, + &Instance->IsTransmitted + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + return Status; + } + + // + // Poll till the packet sent out from the ip6 queue. + // + gBS->RestoreTPL (Instance->OldTpl); + + while (!Instance->IsTransmitted) { + Udp6->Poll (Udp6); + } + + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // For the subsequent exchange of such requests, reconfigure the Udp6Io as + // (serverip, 0, localip, localport) to receive. + // Currently local address and local port are specified by Udp6 mode data. + // + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerDataPort, + &Udp6CfgData.StationAddress, + Udp6CfgData.StationPort + ); + } else { + // + // For the data exchange, configure the Udp6Io as (serverip, dataport, + // localip, localport) to send/receive. + // Currently local address and local port are specified by Udp6 mode data. + // + Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Udp6CfgData.RemotePort != Instance->ServerDataPort) { + + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerDataPort, + &Udp6CfgData.StationAddress, + Udp6CfgData.StationPort + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + NET_GET_REF (Packet); + + Instance->IsTransmitted = FALSE; + + Status = UdpIoSendDatagram ( + Instance->UdpIo, + Packet, + NULL, + NULL, + Mtftp6OnPacketSent, + &Instance->IsTransmitted + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + } + + // + // Poll till the packet sent out from the ip6 queue. + // + gBS->RestoreTPL (Instance->OldTpl); + + while (!Instance->IsTransmitted) { + Udp6->Poll (Udp6); + } + + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + } + + return Status; +} + + +/** + Check packet for GetInfo callback routine. + + GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect + the first packet from server, then abort the session. + + @param[in] This The pointer to the Mtftp6 protocol. + @param[in] Token The pointer to the Mtftp6 token. + @param[in] PacketLen The length of the packet. + @param[in] Packet The pointer to the received packet. + + @retval EFI_ABORTED Abort the Mtftp6 operation. + +**/ +EFI_STATUS +EFIAPI +Mtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ) +{ + MTFTP6_GETINFO_CONTEXT *Context; + UINT16 OpCode; + + Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context; + OpCode = NTOHS (Packet->OpCode); + + // + // Set the GetInfo's return status according to the OpCode. + // + switch (OpCode) { + case EFI_MTFTP6_OPCODE_ERROR: + Context->Status = EFI_TFTP_ERROR; + break; + + case EFI_MTFTP6_OPCODE_OACK: + Context->Status = EFI_SUCCESS; + break; + + default: + Context->Status = EFI_PROTOCOL_ERROR; + } + + // + // Allocate buffer then copy the packet over. Use gBS->AllocatePool + // in case NetAllocatePool will implements something tricky. + // + *(Context->Packet) = AllocateZeroPool (PacketLen); + + if (*(Context->Packet) == NULL) { + Context->Status = EFI_OUT_OF_RESOURCES; + return EFI_ABORTED; + } + + *(Context->PacketLen) = PacketLen; + CopyMem (*(Context->Packet), Packet, PacketLen); + + return EFI_ABORTED; +} + + +/** + Clean up the current Mtftp6 operation. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Result The result to be returned to the user. + +**/ +VOID +Mtftp6OperationClean ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_STATUS Result + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + MTFTP6_BLOCK_RANGE *Block; + + // + // Clean up the current token and event. + // + if (Instance->Token != NULL) { + Instance->Token->Status = Result; + if (Instance->Token->Event != NULL) { + gBS->SignalEvent (Instance->Token->Event); + } + Instance->Token = NULL; + } + + // + // Clean up the corresponding Udp6Io. + // + if (Instance->UdpIo != NULL) { + UdpIoCleanIo (Instance->UdpIo); + } + + if (Instance->McastUdpIo != NULL) { + UdpIoFreeIo (Instance->McastUdpIo); + Instance->McastUdpIo = NULL; + } + + // + // Clean up the stored last packet. + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + Instance->LastPacket = NULL; + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) { + Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + RemoveEntryList (Entry); + FreePool (Block); + } + + // + // Reinitialize the corresponding fields of the Mtftp6 operation. + // + ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS)); + + Instance->ServerCmdPort = 0; + Instance->ServerDataPort = 0; + Instance->McastPort = 0; + Instance->BlkSize = 0; + Instance->LastBlk = 0; + Instance->PacketToLive = 0; + Instance->MaxRetry = 0; + Instance->CurRetry = 0; + Instance->Timeout = 0; + Instance->IsMaster = TRUE; +} + + +/** + Start the Mtftp6 instance to perform the operation, such as read file, + write file, and read directory. + + @param[in] This The MTFTP session. + @param[in] Token The token than encapsues the user's request. + @param[in] OpCode The operation to perform. + + @retval EFI_INVALID_PARAMETER Some of the parameters are invalid. + @retval EFI_NOT_STARTED The MTFTP session hasn't been configured. + @retval EFI_ALREADY_STARTED There is pending operation for the session. + @retval EFI_SUCCESS The operation is successfully started. + +**/ +EFI_STATUS +Mtftp6OperationStart ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 OpCode + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_STATUS Status; + + if (This == NULL || + Token == NULL || + Token->Filename == NULL || + (Token->OptionCount != 0 && Token->OptionList == NULL) || + (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp)) + ) { + return EFI_INVALID_PARAMETER; + } + + // + // At least define one method to collect the data for download. + // + if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) && + Token->Buffer == NULL && + Token->CheckPacket == NULL + ) { + return EFI_INVALID_PARAMETER; + } + + // + // At least define one method to provide the data for upload. + // + if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + if (Instance->Config == NULL) { + return EFI_NOT_STARTED; + } + + if (Instance->Token != NULL) { + return EFI_ACCESS_DENIED; + } + + Status = EFI_SUCCESS; + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Parse the extension options in the request packet. + // + if (Token->OptionCount != 0) { + + Status = Mtftp6ParseExtensionOption ( + Token->OptionList, + Token->OptionCount, + TRUE, + &Instance->ExtInfo + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Initialize runtime data from config data or override data. + // + Instance->Token = Token; + Instance->ServerCmdPort = Instance->Config->InitialServerPort; + Instance->ServerDataPort = 0; + Instance->MaxRetry = Instance->Config->TryCount; + Instance->Timeout = Instance->Config->TimeoutValue; + Instance->IsMaster = TRUE; + + CopyMem ( + &Instance->ServerIp, + &Instance->Config->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + if (Token->OverrideData != NULL) { + Instance->ServerCmdPort = Token->OverrideData->ServerPort; + Instance->MaxRetry = Token->OverrideData->TryCount; + Instance->Timeout = Token->OverrideData->TimeoutValue; + + CopyMem ( + &Instance->ServerIp, + &Token->OverrideData->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + // + // Set default value for undefined parameters. + // + if (Instance->ServerCmdPort == 0) { + Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT; + } + if (Instance->BlkSize == 0) { + Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE; + } + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY; + } + if (Instance->Timeout == 0) { + Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT; + } + + Token->Status = EFI_NOT_READY; + + // + // Switch the routines by the operation code. + // + switch (OpCode) { + case EFI_MTFTP6_OPCODE_RRQ: + Status = Mtftp6RrqStart (Instance, OpCode); + break; + + case EFI_MTFTP6_OPCODE_DIR: + Status = Mtftp6RrqStart (Instance, OpCode); + break; + + case EFI_MTFTP6_OPCODE_WRQ: + Status = Mtftp6WrqStart (Instance, OpCode); + break; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Return immediately for asynchronous or poll the instance for synchronous. + // + gBS->RestoreTPL (Instance->OldTpl); + + if (Token->Event == NULL) { + while (Token->Status == EFI_NOT_READY) { + This->Poll (This); + } + return Token->Status; + } + + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6OperationClean (Instance, Status); + gBS->RestoreTPL (Instance->OldTpl); + + return Status; +} + + +/** + The timer ticking routine for the Mtftp6 instance. + + @param[in] Event The pointer to the ticking event. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + EFI_MTFTP6_TOKEN *Token; + EFI_STATUS Status; + + Service = (MTFTP6_SERVICE *) Context; + + // + // Iterate through all the children of the Mtftp service instance. Time + // out the packet. If maximum retries reached, clean the session up. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) { + + Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link); + + if (Instance->Token == NULL) { + continue; + } + + if (Instance->PacketToLive > 0) { + Instance->PacketToLive--; + continue; + } + + Instance->CurRetry++; + Token = Instance->Token; + + if (Token->TimeoutCallback != NULL) { + // + // Call the timeout callback routine if has. + // + Status = Token->TimeoutCallback (&Instance->Mtftp6, Token); + + if (EFI_ERROR (Status)) { + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer in time out" + ); + Mtftp6OperationClean (Instance, EFI_ABORTED); + continue; + } + } + + // + // Retransmit the packet if haven't reach the maxmium retry count, + // otherwise exit the transfer. + // + if (Instance->CurRetry < Instance->MaxRetry) { + Mtftp6TransmitPacket (Instance, Instance->LastPacket); + } else { + Mtftp6OperationClean (Instance, EFI_TIMEOUT); + continue; + } + } +} diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h new file mode 100644 index 0000000000..37f03fe298 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h @@ -0,0 +1,359 @@ +/** @file + Mtftp6 support functions declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_MTFTP6_SUPPORT_H__ +#define __EFI_MTFTP6_SUPPORT_H__ + +// +// The structure representing a range of block numbers, [Start, End]. +// It is used to remember the holes in the MTFTP block space. If all +// the holes are filled in, then the download or upload has completed. +// +typedef struct { + LIST_ENTRY Link; + INTN Start; + INTN End; + INTN Round; + INTN Bound; +} MTFTP6_BLOCK_RANGE; + + +/** + Initialize the block range for either RRQ or WRQ. RRQ and WRQ have + different requirements for Start and End. For example, during startup, + WRQ initializes its whole valid block range to [0, 0xffff]. This + is because the server will send an ACK0 to inform the user to start the + upload. When the client receives an ACK0, it will remove 0 from the range, + get the next block number, which is 1, then upload the BLOCK1. For RRQ + without option negotiation, the server will directly send us the BLOCK1 + in response to the client's RRQ. When BLOCK1 is received, the client will + remove it from the block range and send an ACK. It also works if there + is option negotiation. + + @param[in] Head The block range head to initialize. + @param[in] Start The Start block number. + @param[in] End The last block number. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range. + @retval EFI_SUCCESS The initial block range is created. + +**/ +EFI_STATUS +Mtftp6InitBlockRange ( + IN LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ); + + +/** + Get the first valid block number on the range list. + + @param[in] Head The block range head. + + @retval ==-1 If the block range is empty. + @retval >-1 The first valid block number. + +**/ +INTN +Mtftp6GetNextBlockNum ( + IN LIST_ENTRY *Head + ); + + +/** + Set the last block number of the block range list. It + removes all the blocks after the Last. MTFTP initialize the + block range to the maximum possible range, such as [0, 0xffff] + for WRQ. When it gets the last block number, it calls + this function to set the last block number. + + @param[in] Head The block range list. + @param[in] Last The last block number. + +**/ +VOID +Mtftp6SetLastBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Last + ); + + +/** + Remove the block number from the block range list. + + @param[in] Head The block range list to remove from. + @param[in] Num The block number to remove. + @param[in] Completed Whether Num is the last block number + @param[out] TotalBlock The continuous block number in all + + @retval EFI_NOT_FOUND The block number isn't in the block range list. + @retval EFI_SUCCESS The block number has been removed from the list. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +Mtftp6RemoveBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Num, + IN BOOLEAN Completed, + OUT UINT64 *TotalBlock + ); + + +/** + Build and transmit the request packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of this packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request. + @retval EFI_SUCCESS The request was built and sent. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendRequest ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + + +/** + Build and send an error packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ErrCode The error code in the packet. + @param[in] ErrInfo The error message in the packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet. + @retval EFI_SUCCESS The error packet was transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendError ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ); + + +/** + Send the packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the packet to be sent. + + @retval EFI_SUCCESS The packet was sent out + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6TransmitPacket ( + IN MTFTP6_INSTANCE *Instance, + IN NET_BUF *Packet + ); + + +/** + Check packet for GetInfo callback routine. + + @param[in] This The pointer to the Mtftp6 protocol. + @param[in] Token The pointer to the Mtftp6 token. + @param[in] PacketLen The length of the packet + @param[in] Packet The pointer to the received packet. + + @retval EFI_SUCCESS The check process passed successfully. + @retval EFI_ABORTED Abort the Mtftp6 operation. + +**/ +EFI_STATUS +EFIAPI +Mtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ); + + +/** + The dummy configure routine for create a new Udp6 Io. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The value is always returned. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ConfigDummyUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ); + + +/** + The configure routine for the Mtftp6 instance to transmit/receive. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] ServerIp The pointer to the server address. + @param[in] ServerPort The pointer to the server port. + @param[in] LocalIp The pointer to the local address. + @param[in] LocalPort The pointer to the local port. + + @retval EFI_SUCCESS Configure the Udp6 Io for Mtftp6 successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been + configured yet. + +**/ +EFI_STATUS +Mtftp6ConfigUdpIo ( + IN UDP_IO *UdpIo, + IN EFI_IPv6_ADDRESS *ServerIp, + IN UINT16 ServerPort, + IN EFI_IPv6_ADDRESS *LocalIp, + IN UINT16 LocalPort + ); + + +/** + Clean up the current Mtftp6 operation. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Result The result to be returned to the user. + +**/ +VOID +Mtftp6OperationClean ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_STATUS Result + ); + + +/** + Start the Mtftp6 instance to perform the operation, such as read file, + write file, and read directory. + + @param[in] This The MTFTP session + @param[in] Token The token that encapsulates the user's request. + @param[in] OpCode The operation to perform. + + @retval EFI_INVALID_PARAMETER Some of the parameters are invalid. + @retval EFI_NOT_STARTED The MTFTP session hasn't been configured. + @retval EFI_ALREADY_STARTED There is pending operation for the session. + @retval EFI_SUCCESS The operation was successfully started. + +**/ +EFI_STATUS +Mtftp6OperationStart ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 OpCode + ); + + +/** + The timer ticking routine for the Mtftp6 instance. + + @param[in] Event The pointer to the ticking event. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + The packet process callback for Mtftp6 upload. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from the Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the Mtftp6 instance to upload. It will first init some states, + then send the WRQ request packet, and start to receive the packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to upload. + @retval Others Failed to start to upload. + +**/ +EFI_STATUS +Mtftp6WrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + + +/** + The packet process callback for Mtftp6 download. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the Mtftp6 instance to download. It first initializes some + of the internal states then builds and sends an RRQ reqeuest packet. + Finally, it starts receive for the downloading. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to download. + @retval Others Failed to start to download. + +**/ +EFI_STATUS +Mtftp6RrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + +#endif diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c new file mode 100644 index 0000000000..69ef4d2093 --- /dev/null +++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c @@ -0,0 +1,602 @@ +/** @file + Mtftp6 Wrq process functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Mtftp6Impl.h" + + + +/** + Build and send a Mtftp6 data packet for upload. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] BlockNum The block num to be sent. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. + @retval EFI_SUCCESS The data packet was sent. + @retval EFI_ABORTED The user aborted this process. + +**/ +EFI_STATUS +Mtftp6WrqSendBlock ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_TOKEN *Token; + NET_BUF *UdpPacket; + EFI_STATUS Status; + UINT16 DataLen; + UINT8 *DataBuf; + UINT64 Start; + + // + // Allocate net buffer to create data packet. + // + UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN); + + if (UdpPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( + UdpPacket, + MTFTP6_DATA_HEAD_LEN, + FALSE + ); + ASSERT (Packet != NULL); + + Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA); + Packet->Data.Block = HTONS (BlockNum); + + // + // Read the block from either the buffer or PacketNeeded callback + // + Token = Instance->Token; + DataLen = Instance->BlkSize; + + if (Token->Buffer != NULL) { + Start = MultU64x32 (BlockNum - 1, Instance->BlkSize); + + if (Token->BufferSize < Start + Instance->BlkSize) { + DataLen = (UINT16) (Token->BufferSize - Start); + Instance->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen); + } + + } else { + // + // Get data from PacketNeeded + // + DataBuf = NULL; + Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf); + + if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { + if (DataBuf != NULL) { + gBS->FreePool (DataBuf); + } + // + // The received packet has already been freed. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + + return EFI_ABORTED; + } + + if (DataLen < Instance->BlkSize) { + Instance->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, DataBuf, DataLen); + gBS->FreePool (DataBuf); + } + } + + // + // Reset current retry count of the instance. + // + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, UdpPacket); +} + + +/** + Function to handle received ACK packet. If the ACK number matches the + expected block number, with more data pending, send the next + block. Otherwise, tell the caller that we are done. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The ACK packet successfully processed. + @retval EFI_TFTP_ERROR The block number loops back. + @retval Others Failed to transmit the next data packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleAck ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + UINT16 AckNum; + INTN Expected; + UINT64 TotalBlock; + + *IsCompleted = FALSE; + AckNum = NTOHS (Packet->Ack.Block[0]); + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + ASSERT (Expected >= 0); + + // + // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput + // restart receive. + // + if (Expected != AckNum) { + return EFI_SUCCESS; + } + + // + // Remove the acked block number, if this is the last block number, + // tell the Mtftp6WrqInput to finish the transfer. This is the last + // block number if the block range are empty.. + // + Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock); + + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Expected < 0) { + // + // The block range is empty. It may either because the the last + // block has been ACKed, or the sequence number just looped back, + // that is, there is more than 0xffff blocks. + // + if (Instance->LastBlk == AckNum) { + ASSERT (Instance->LastBlk >= 1); + *IsCompleted = TRUE; + return EFI_SUCCESS; + + } else { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if block number rolls back. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "Block number rolls back, not supported, try blksize option" + ); + + return EFI_TFTP_ERROR; + } + } + + // + // Free the receive buffer before send new packet since it might need + // reconfigure udpio. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + return Mtftp6WrqSendBlock (Instance, (UINT16) Expected); +} + + +/** + Check whether the received OACK is valid. The OACK is valid + only if: + 1. It only include options requested by us. + 2. It can only include a smaller block size. + 3. It can't change the proposed time out value. + 4. Other requirements of the individal MTFTP6 options as required. + + @param[in] ReplyInfo The pointer to options information in reply packet. + @param[in] RequestInfo The pointer to requested options information. + + @retval TRUE If the option in OACK is valid. + @retval FALSE If the option is invalid. + +**/ +BOOLEAN +Mtftp6WrqOackValid ( + IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, + IN MTFTP6_EXT_OPTION_INFO *RequestInfo + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout)) + ) { + + return FALSE; + } + + return TRUE; +} + + +/** + Process the OACK packet for Wrq. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The OACK packet successfully processed. + @retval EFI_TFTP_ERROR An TFTP communication error happened. + @retval Others Failed to process the OACK packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleOack ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_MTFTP6_OPTION *Options; + UINT32 Count; + MTFTP6_EXT_OPTION_INFO ExtInfo; + EFI_MTFTP6_PACKET Dummy; + EFI_STATUS Status; + INTN Expected; + + *IsCompleted = FALSE; + + // + // Ignore the OACK if already started the upload + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Expected != 0) { + return EFI_SUCCESS; + } + + // + // Parse and validate the options from server + // + ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + + Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo); + + if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) { + // + // Don't send a MTFTP error packet when out of resource, it can + // only make it worse. + // + if (Status != EFI_OUT_OF_RESOURCES) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid Oack packet received. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Mal-formated OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + + // + // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck, + // which will start the transmission of the first data block. + // + Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); + Dummy.Ack.Block[0] = 0; + + return Mtftp6WrqHandleAck ( + Instance, + &Dummy, + sizeof (EFI_MTFTP6_ACK_HEADER), + UdpPacket, + IsCompleted + ); +} + + +/** + The packet process callback for Mtftp6 upload. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PACKET *Packet; + BOOLEAN IsCompleted; + EFI_STATUS Status; + UINT32 TotalNum; + UINT32 Len; + UINT16 Opcode; + + Instance = (MTFTP6_INSTANCE *) Context; + + NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); + + IsCompleted = FALSE; + Packet = NULL; + Status = EFI_SUCCESS; + TotalNum = 0; + + // + // Return error status if Udp6 instance failed to receive. + // + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Client send initial request to server's listening port. Server + // will select a UDP port to communicate with the client. + // + if (UdpEpt->RemotePort != Instance->ServerDataPort) { + if (Instance->ServerDataPort != 0) { + goto ON_EXIT; + } else { + Instance->ServerDataPort = UdpEpt->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + TotalNum = UdpPacket->BlockOpNum; + + if (TotalNum > 1) { + Packet = AllocateZeroPool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Callback to the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if (Instance->Token->CheckPacket != NULL && + (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) + ) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp6, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (UdpPacket); + UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Switch the process routines by the operation code. + // + switch (Opcode) { + case EFI_MTFTP6_OPCODE_ACK: + if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) { + goto ON_EXIT; + } + // + // Handle the Ack packet of Wrq. + // + Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + case EFI_MTFTP6_OPCODE_OACK: + if (Len <= MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + // + // Handle the Oack packet of Wrq. + // + Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + default: + // + // Drop and return eror if received error message. + // + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status) and not completed, + // restart the receive, otherwise end the session. + // + if (Packet != NULL && TotalNum > 1) { + FreePool (Packet); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + if (!EFI_ERROR (Status) && !IsCompleted) { + Status = UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); + } + // + // Clean up the current session if failed to continue. + // + if (EFI_ERROR (Status) || IsCompleted) { + Mtftp6OperationClean (Instance, Status); + } +} + + +/** + Start the Mtftp6 instance to upload. It will first init some states, + then send the WRQ request packet, and start to receive the packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of the current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to upload. + @retval Others Failed to start to upload. + +**/ +EFI_STATUS +Mtftp6WrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_STATUS Status; + + // + // The valid block number range are [0, 0xffff]. For example: + // the client sends an WRQ request to the server, the server + // ACK with an ACK0 to let client start transfer the first + // packet. + // + Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6SendRequest (Instance, Operation); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); +} + diff --git a/NetworkPkg/NetworkPkg.dec b/NetworkPkg/NetworkPkg.dec new file mode 100644 index 0000000000..0c8df4eed8 --- /dev/null +++ b/NetworkPkg/NetworkPkg.dec @@ -0,0 +1,21 @@ +## @file +# +# This package provides network modules that conform to UEFI 2.2 specification. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = NetworkPkg + PACKAGE_GUID = 947988BE-8D5C-471a-893D-AD181C46BEBB + PACKAGE_VERSION = 0.92 diff --git a/NetworkPkg/NetworkPkg.dsc b/NetworkPkg/NetworkPkg.dsc new file mode 100644 index 0000000000..b12d5fd04a --- /dev/null +++ b/NetworkPkg/NetworkPkg.dsc @@ -0,0 +1,95 @@ +## @file +# UEFI 2.2 Network Module Package for All Architectures +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + PLATFORM_NAME = NetworkPkg + PLATFORM_GUID = 3FD34E9B-E90C-44e1-B510-1F632A509F10 + PLATFORM_VERSION = 0.92 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/NetworkPkg + SUPPORTED_ARCHITECTURES = IA32|IPF|X64|EBC|ARM + BUILD_TARGETS = DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + +[LibraryClasses] + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf + + DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf + NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf + IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf + UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf + +[LibraryClasses.common.UEFI_DRIVER] + DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf + +[LibraryClasses.common.UEFI_APPLICATION] + DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf + FileHandleLib|ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.inf + ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf + +[PcdsFeatureFlag] + gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable|TRUE + gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable|TRUE + +[PcdsFixedAtBuild] + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000 + +################################################################################################### +# +# Components Section - list of the modules and components that will be processed by compilation +# tools and the EDK II tools to generate PE32/PE32+/Coff image files. +# +# Note: The EDK II DSC file is not used to specify how compiled binary images get placed +# into firmware volume images. This section is just a list of modules to compile from +# source into UEFI-compliant binaries. +# It is the FDF file that contains information on combining binary files into firmware +# volume images, whose concept is beyond UEFI and is described in PI specification. +# Binary modules do not need to be listed in this section, as they should be +# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi), +# Logo (Logo.bmp), and etc. +# There may also be modules listed in this section that are not required in the FDF file, +# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be +# generated for it, but the binary will not be put into any firmware volume. +# +################################################################################################### + +[Components] + NetworkPkg/IpSecDxe/IpSecDxe.inf + NetworkPkg/Ip6Dxe/Ip6Dxe.inf + NetworkPkg/TcpDxe/TcpDxe.inf + NetworkPkg/Udp6Dxe/Udp6Dxe.inf + NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf + NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf + + +[Components.IA32, Components.X64, Components.IPF] + NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf + NetworkPkg/Application/Ping6/Ping6.inf + NetworkPkg/Application/IfConfig6/IfConfig6.inf + NetworkPkg/Application/IpsecConfig/IpSecConfig.inf + NetworkPkg/Application/VConfig/VConfig.inf diff --git a/NetworkPkg/TcpDxe/ComponentName.c b/NetworkPkg/TcpDxe/ComponentName.c new file mode 100644 index 0000000000..956792afec --- /dev/null +++ b/NetworkPkg/TcpDxe/ComponentName.c @@ -0,0 +1,304 @@ +/** @file + Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language or DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/// +/// EFI Component Name Protocol +/// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName = { + TcpComponentNameGetDriverName, + TcpComponentNameGetControllerName, + "eng" +}; + +/// +/// EFI Component Name 2 Protocol +/// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = { + { + "eng;en", + L"TCP Network Service Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language or DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mTcpDriverNameTable, + DriverName, + (BOOLEAN) (This == &gTcpComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/TcpDxe/SockImpl.c b/NetworkPkg/TcpDxe/SockImpl.c new file mode 100644 index 0000000000..7fad042be6 --- /dev/null +++ b/NetworkPkg/TcpDxe/SockImpl.c @@ -0,0 +1,1230 @@ +/** @file + Implementation of the Socket. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "SockImpl.h" + +/** + Get the first buffer block in the specific socket buffer. + + @param[in] Sockbuf Pointer to the socket buffer. + + @return Pointer to the first buffer in the queue. NULL if the queue is empty. + +**/ +NET_BUF * +SockBufFirst ( + IN SOCK_BUFFER *Sockbuf + ) +{ + LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if (IsListEmpty (NetbufList)) { + return NULL; + } + + return NET_LIST_HEAD (NetbufList, NET_BUF, List); +} + +/** + Get the next buffer block in the specific socket buffer. + + @param[in] Sockbuf Pointer to the socket buffer. + @param[in] SockEntry Pointer to the buffer block prior to the required one. + + @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is + the tail or head entry. + +**/ +NET_BUF * +SockBufNext ( + IN SOCK_BUFFER *Sockbuf, + IN NET_BUF *SockEntry + ) +{ + LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if ((SockEntry->List.ForwardLink == NetbufList) || + (SockEntry->List.BackLink == &SockEntry->List) || + (SockEntry->List.ForwardLink == &SockEntry->List) + ) { + + return NULL; + } + + return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List); +} + +/** + User provided callback function for NetbufFromExt. + + @param[in] Event The Event this notify function registered to, ignored. + +**/ +VOID +EFIAPI +SockFreeFoo ( + IN EFI_EVENT Event + ) +{ + return; +} + +/** + Get the length of the data that can be retrieved from the socket + receive buffer. + + @param[in] SockBuffer Pointer to the socket receive buffer. + @param[out] IsUrg Pointer to a BOOLEAN variable. + If TRUE the data is OOB. + @param[in] BufLen The maximum length of the data buffer to + store the received data in the socket layer. + + @return The length of the data can be retreived. + +**/ +UINT32 +SockTcpDataToRcv ( + IN SOCK_BUFFER *SockBuffer, + OUT BOOLEAN *IsUrg, + IN UINT32 BufLen + ) +{ + NET_BUF *RcvBufEntry; + UINT32 DataLen; + TCP_RSV_DATA *TcpRsvData; + BOOLEAN Urg; + + ASSERT ((SockBuffer != NULL) && (IsUrg != NULL) && (BufLen > 0)); + + // + // Get the first socket receive buffer + // + RcvBufEntry = SockBufFirst (SockBuffer); + ASSERT (RcvBufEntry != NULL); + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + // + // Check whether the receive data is out of bound. If yes, calculate the maximum + // allowed length of the urgent data and output it. + // + *IsUrg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) { + + DataLen = MIN (TcpRsvData->UrgLen, BufLen); + + if (DataLen < TcpRsvData->UrgLen) { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen; + } else { + TcpRsvData->UrgLen = 0; + } + + return DataLen; + + } + + // + // Process the next socket receive buffer to get the maximum allowed length + // of the received data. + // + DataLen = RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + + while ((BufLen > DataLen) && (RcvBufEntry != NULL)) { + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + Urg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg != Urg) { + break; + } + + if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) { + + if (TcpRsvData->UrgLen + DataLen < BufLen) { + TcpRsvData->UrgLen = 0; + } else { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen); + } + + return MIN (TcpRsvData->UrgLen + DataLen, BufLen); + + } + + DataLen += RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + } + + DataLen = MIN (BufLen, DataLen); + return DataLen; +} + +/** + Copy data from socket buffer to an application provided receive buffer. + + @param[in] Sock Pointer to the socket. + @param[in] TcpRxData Pointer to the application provided receive buffer. + @param[in] RcvdBytes The maximum length of the data can be copied. + @param[in] IsUrg If TRUE the data is Out of Bound, FALSE the data is normal. + +**/ +VOID +SockSetTcpRxData ( + IN SOCKET *Sock, + IN VOID *TcpRxData, + IN UINT32 RcvdBytes, + IN BOOLEAN IsUrg + ) +{ + UINT32 Index; + UINT32 CopyBytes; + UINT32 OffSet; + EFI_TCP4_RECEIVE_DATA *RxData; + EFI_TCP4_FRAGMENT_DATA *Fragment; + + RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData; + + OffSet = 0; + + ASSERT (RxData->DataLength >= RcvdBytes); + + RxData->DataLength = RcvdBytes; + RxData->UrgentFlag = IsUrg; + + // + // Copy the CopyBytes data from socket receive buffer to RxData. + // + for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) { + + Fragment = &RxData->FragmentTable[Index]; + CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes); + + NetbufQueCopy ( + Sock->RcvBuffer.DataQueue, + OffSet, + CopyBytes, + Fragment->FragmentBuffer + ); + + Fragment->FragmentLength = CopyBytes; + RcvdBytes -= CopyBytes; + OffSet += CopyBytes; + } +} + +/** + Process the send token. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockProcessSndToken ( + IN OUT SOCKET *Sock + ) +{ + UINT32 FreeSpace; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + SOCK_IO_TOKEN *SndToken; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + + ASSERT ((Sock != NULL) && (SockStream == Sock->Type)); + + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + // + // to determine if process a send token using + // socket layer flow control policy + // + while ((FreeSpace >= Sock->SndBuffer.LowWater) && !IsListEmpty (&Sock->SndTokenList)) { + + SockToken = NET_LIST_HEAD ( + &(Sock->SndTokenList), + SOCK_TOKEN, + TokenList + ); + + // + // process this token + // + RemoveEntryList (&(SockToken->TokenList)); + InsertTailList ( + &(Sock->ProcessingSndTokenList), + &(SockToken->TokenList) + ); + + // + // Proceess it in the light of SockType + // + SndToken = (SOCK_IO_TOKEN *) SockToken->Token; + TxData = SndToken->Packet.TxData; + + DataLen = TxData->DataLength; + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + goto OnError; + } + + if (DataLen >= FreeSpace) { + FreeSpace = 0; + + } else { + FreeSpace -= DataLen; + + } + } + + return; + +OnError: + + RemoveEntryList (&SockToken->TokenList); + SIGNAL_TOKEN (SockToken->Token, Status); + FreePool (SockToken); +} + +/** + Get received data from the socket layer to the receive token. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN OUT SOCKET *Sock, + IN OUT SOCK_IO_TOKEN *RcvToken + ) +{ + UINT32 TokenRcvdBytes; + EFI_TCP4_RECEIVE_DATA *RxData; + BOOLEAN IsUrg; + + ASSERT (Sock != NULL); + + ASSERT (SockStream == Sock->Type); + + RxData = RcvToken->Packet.RxData; + + TokenRcvdBytes = SockTcpDataToRcv ( + &Sock->RcvBuffer, + &IsUrg, + RxData->DataLength + ); + + // + // Copy data from RcvBuffer of socket to user + // provided RxData and set the fields in TCP RxData + // + SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg); + + NetbufQueTrim (Sock->RcvBuffer.DataQueue, TokenRcvdBytes); + SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS); + + return TokenRcvdBytes; +} + +/** + Process the TCP send data, buffer the tcp txdata, and append + the buffer to socket send buffer, then try to send it. + + @param[in] Sock Pointer to the socket. + @param[in] TcpTxData Pointer to the application provided send buffer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ) +{ + NET_BUF *SndData; + EFI_STATUS Status; + EFI_TCP4_TRANSMIT_DATA *TxData; + + TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData; + + // + // transform this TxData into a NET_BUFFER + // and insert it into Sock->SndBuffer + // + SndData = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + 0, + 0, + SockFreeFoo, + NULL + ); + + if (NULL == SndData) { + DEBUG ( + (EFI_D_ERROR, + "SockKProcessSndData: Failed to call NetBufferFromExt\n") + ); + + return EFI_OUT_OF_RESOURCES; + } + + NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData); + + // + // notify the low layer protocol to handle this send token + // + if (TxData->Urgent) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (TxData->Push) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // low layer protocol should really handle the sending + // process when catching SOCK_SND request + // + Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Flush the tokens in the specific token list. + + @param[in] Sock Pointer to the socket. + @param[in, out] PendingTokenList Pointer to the token list to be flushed. + +**/ +VOID +SockFlushPendingToken ( + IN SOCKET *Sock, + IN OUT LIST_ENTRY *PendingTokenList + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *Token; + + ASSERT ((Sock != NULL) && (PendingTokenList != NULL)); + + while (!IsListEmpty (PendingTokenList)) { + SockToken = NET_LIST_HEAD ( + PendingTokenList, + SOCK_TOKEN, + TokenList + ); + + Token = SockToken->Token; + SIGNAL_TOKEN (Token, Sock->SockError); + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + } +} + +/** + Wake up the connection token while the connection is successfully established, + then try to process any pending send token. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeConnToken ( + IN OUT SOCKET *Sock + ) +{ + ASSERT (Sock->ConnectionToken != NULL); + + SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS); + Sock->ConnectionToken = NULL; + + // + // check to see if some pending send token existed? + // + SockProcessSndToken (Sock); +} + +/** + Wake up the listen token while the connection is established successfully. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeListenToken ( + IN OUT SOCKET *Sock + ) +{ + SOCKET *Parent; + SOCK_TOKEN *SockToken; + EFI_TCP4_LISTEN_TOKEN *ListenToken; + + Parent = Sock->Parent; + + ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock)); + + if (!IsListEmpty (&Parent->ListenTokenList)) { + SockToken = NET_LIST_HEAD ( + &Parent->ListenTokenList, + SOCK_TOKEN, + TokenList + ); + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token; + ListenToken->NewChildHandle = Sock->SockHandle; + + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + RemoveEntryList (&SockToken->TokenList); + FreePool (SockToken); + + RemoveEntryList (&Sock->ConnectionList); + + Parent->ConnCnt--; + DEBUG ( + (EFI_D_INFO, + "SockWakeListenToken: accept a socket, now conncnt is %d", + Parent->ConnCnt) + ); + + Sock->Parent = NULL; + } +} + +/** + Wake up the receive token while some data is received. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeRcvToken ( + IN OUT SOCKET *Sock + ) +{ + UINT32 RcvdBytes; + UINT32 TokenRcvdBytes; + SOCK_TOKEN *SockToken; + SOCK_IO_TOKEN *RcvToken; + + ASSERT (Sock->RcvBuffer.DataQueue != NULL); + + RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize; + + ASSERT (RcvdBytes > 0); + + while (RcvdBytes > 0 && !IsListEmpty (&Sock->RcvTokenList)) { + + SockToken = NET_LIST_HEAD ( + &Sock->RcvTokenList, + SOCK_TOKEN, + TokenList + ); + + RcvToken = (SOCK_IO_TOKEN *) SockToken->Token; + TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken); + + if (0 == TokenRcvdBytes) { + return ; + } + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + RcvdBytes -= TokenRcvdBytes; + } +} + +/** + Create a socket with initial data SockInitData. + + @param[in] SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket, return NULL when an exception occurs. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + SOCKET *Parent; + EFI_STATUS Status; + EFI_GUID *TcpProtocolGuid; + UINTN ProtocolLength; + + ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL)); + ASSERT (SockInitData->Type == SockStream); + ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN)); + + if (SockInitData->IpVersion == IP_VERSION_4) { + TcpProtocolGuid = &gEfiTcp4ProtocolGuid; + ProtocolLength = sizeof (EFI_TCP4_PROTOCOL); + } else { + TcpProtocolGuid = &gEfiTcp6ProtocolGuid; + ProtocolLength = sizeof (EFI_TCP6_PROTOCOL); + } + + + Parent = SockInitData->Parent; + + if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n", + Parent->ConnCnt, + Parent->BackLog) + ); + + return NULL; + } + + Sock = AllocateZeroPool (sizeof (SOCKET)); + if (NULL == Sock) { + + DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n")); + return NULL; + } + + InitializeListHead (&Sock->Link); + InitializeListHead (&Sock->ConnectionList); + InitializeListHead (&Sock->ListenTokenList); + InitializeListHead (&Sock->RcvTokenList); + InitializeListHead (&Sock->SndTokenList); + InitializeListHead (&Sock->ProcessingSndTokenList); + + EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK); + + Sock->SndBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->SndBuffer.DataQueue) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: No resource to allocate SndBuffer for new socket\n") + ); + + goto OnError; + } + + Sock->RcvBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->RcvBuffer.DataQueue) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: No resource to allocate RcvBuffer for new socket\n") + ); + + goto OnError; + } + + Sock->Signature = SOCK_SIGNATURE; + + Sock->Parent = Parent; + Sock->BackLog = SockInitData->BackLog; + Sock->ProtoHandler = SockInitData->ProtoHandler; + Sock->SndBuffer.HighWater = SockInitData->SndBufferSize; + Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize; + Sock->Type = SockInitData->Type; + Sock->DriverBinding = SockInitData->DriverBinding; + Sock->State = SockInitData->State; + Sock->CreateCallback = SockInitData->CreateCallback; + Sock->DestroyCallback = SockInitData->DestroyCallback; + Sock->Context = SockInitData->Context; + + Sock->SockError = EFI_ABORTED; + Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER; + Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER; + + Sock->IpVersion = SockInitData->IpVersion; + + // + // Install protocol on Sock->SockHandle + // + CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength); + + // + // copy the protodata into socket + // + CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Sock->SockHandle, + TcpProtocolGuid, + &Sock->NetProtocol, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: Install TCP protocol in socket failed with %r\n", + Status) + ); + + goto OnError; + } + + if (Parent != NULL) { + ASSERT (Parent->BackLog > 0); + ASSERT (SOCK_IS_LISTENING (Parent)); + + // + // need to add it into Parent->ConnectionList + // if the Parent->ConnCnt < Parent->BackLog + // + Parent->ConnCnt++; + + DEBUG ( + (EFI_D_INFO, + "SockCreate: Create a new socket and add to parent, now conncnt is %d\n", + Parent->ConnCnt) + ); + + InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList); + } + + if (Sock->CreateCallback != NULL) { + Status = Sock->CreateCallback (Sock, Sock->Context); + if (EFI_ERROR (Status)) { + goto OnError; + } + } + + return Sock; + +OnError: + + if (Sock->SockHandle != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + Sock->SockHandle, + TcpProtocolGuid, + &Sock->NetProtocol, + NULL + ); + } + + if (NULL != Sock->SndBuffer.DataQueue) { + NetbufQueFree (Sock->SndBuffer.DataQueue); + } + + if (NULL != Sock->RcvBuffer.DataQueue) { + NetbufQueFree (Sock->RcvBuffer.DataQueue); + } + + FreePool (Sock); + + return NULL; +} + +/** + Destroy a socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockDestroy ( + IN OUT SOCKET *Sock + ) +{ + VOID *SockProtocol; + EFI_GUID *TcpProtocolGuid; + EFI_STATUS Status; + + ASSERT (SockStream == Sock->Type); + + if (Sock->DestroyCallback != NULL) { + Sock->DestroyCallback (Sock, Sock->Context); + } + + // + // Flush the completion token buffered + // by sock and rcv, snd buffer + // + if (!SOCK_IS_UNCONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + Sock->ConfigureState = SO_UNCONFIGURED; + + } + // + // Destory the RcvBuffer Queue and SendBuffer Queue + // + NetbufQueFree (Sock->RcvBuffer.DataQueue); + NetbufQueFree (Sock->SndBuffer.DataQueue); + + // + // Remove it from parent connection list if needed + // + if (Sock->Parent != NULL) { + + RemoveEntryList (&(Sock->ConnectionList)); + (Sock->Parent->ConnCnt)--; + + DEBUG ( + (EFI_D_WARN, + "SockDestory: Delete a unaccepted socket from parent now conncnt is %d\n", + Sock->Parent->ConnCnt) + ); + + Sock->Parent = NULL; + } + + // + // Set the protocol guid and driver binding handle + // in the light of Sock->SockType + // + if (Sock->IpVersion == IP_VERSION_4) { + TcpProtocolGuid = &gEfiTcp4ProtocolGuid; + } else { + TcpProtocolGuid = &gEfiTcp6ProtocolGuid; + } + + // + // Retrieve the protocol installed on this sock + // + Status = gBS->OpenProtocol ( + Sock->SockHandle, + TcpProtocolGuid, + &SockProtocol, + Sock->DriverBinding, + Sock->SockHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockDestroy: Open protocol installed on socket failed with %r\n", + Status) + ); + + goto FreeSock; + } + + // + // Uninstall the protocol installed on this sock + // in the light of Sock->SockType + // + gBS->UninstallMultipleProtocolInterfaces ( + Sock->SockHandle, + TcpProtocolGuid, + SockProtocol, + NULL + ); + +FreeSock: + + FreePool (Sock); +} + +/** + Flush the sndBuffer and rcvBuffer of socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockConnFlush ( + IN OUT SOCKET *Sock + ) +{ + SOCKET *Child; + + ASSERT (Sock != NULL); + + // + // Clear the flag in this socket + // + Sock->Flag = 0; + + // + // Flush the SndBuffer and RcvBuffer of Sock + // + NetbufQueFlush (Sock->SndBuffer.DataQueue); + NetbufQueFlush (Sock->RcvBuffer.DataQueue); + + // + // Signal the pending token + // + if (Sock->ConnectionToken != NULL) { + SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError); + Sock->ConnectionToken = NULL; + } + + if (Sock->CloseToken != NULL) { + SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError); + Sock->CloseToken = NULL; + } + + SockFlushPendingToken (Sock, &(Sock->ListenTokenList)); + SockFlushPendingToken (Sock, &(Sock->RcvTokenList)); + SockFlushPendingToken (Sock, &(Sock->SndTokenList)); + SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList)); + + // + // Destroy the pending connection, if it is a listening socket + // + if (SOCK_IS_LISTENING (Sock)) { + while (!IsListEmpty (&Sock->ConnectionList)) { + Child = NET_LIST_HEAD ( + &Sock->ConnectionList, + SOCKET, + ConnectionList + ); + + SockDestroyChild (Child); + } + + Sock->ConnCnt = 0; + } + +} + +/** + Set the state of the socket. + + @param[in, out] Sock Pointer to the socket. + @param[in] State The new socket state to be set. + +**/ +VOID +SockSetState ( + IN OUT SOCKET *Sock, + IN UINT8 State + ) +{ + Sock->State = State; +} + +/** + Clone a new socket, including its associated protocol control block. + + @param[in] Sock Pointer to the socket to be cloned. + + @return Pointer to the newly cloned socket. If NULL, an error condition occurred. + +**/ +SOCKET * +SockClone ( + IN SOCKET *Sock + ) +{ + SOCKET *ClonedSock; + SOCK_INIT_DATA InitData; + + InitData.BackLog = Sock->BackLog; + InitData.Parent = Sock; + InitData.State = Sock->State; + InitData.ProtoHandler = Sock->ProtoHandler; + InitData.Type = Sock->Type; + InitData.RcvBufferSize = Sock->RcvBuffer.HighWater; + InitData.SndBufferSize = Sock->SndBuffer.HighWater; + InitData.DriverBinding = Sock->DriverBinding; + InitData.IpVersion = Sock->IpVersion; + InitData.Protocol = &(Sock->NetProtocol); + InitData.CreateCallback = Sock->CreateCallback; + InitData.DestroyCallback = Sock->DestroyCallback; + InitData.Context = Sock->Context; + InitData.ProtoData = Sock->ProtoReserved; + InitData.DataSize = sizeof (Sock->ProtoReserved); + + ClonedSock = SockCreate (&InitData); + + if (NULL == ClonedSock) { + DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n")); + return NULL; + } + + SockSetState (ClonedSock, SO_CONNECTING); + ClonedSock->ConfigureState = Sock->ConfigureState; + + return ClonedSock; +} + +/** + Called by the low layer protocol to indicate the socket a connection is + established. + + This function just changes the socket's state to SO_CONNECTED + and signals the token used for connection establishment. + + @param[in, out] Sock Pointer to the socket associated with the + established connection. + +**/ +VOID +SockConnEstablished ( + IN OUT SOCKET *Sock + ) +{ + + ASSERT (SO_CONNECTING == Sock->State); + + SockSetState (Sock, SO_CONNECTED); + + if (NULL == Sock->Parent) { + SockWakeConnToken (Sock); + } else { + SockWakeListenToken (Sock); + } + +} + +/** + Called by the low layer protocol to indicate the connection is closed. + + This function flushes the socket, sets the state to SO_CLOSED, and signals + the close token. + + @param[in, out] Sock Pointer to the socket associated with the closed + connection. + +**/ +VOID +SockConnClosed ( + IN OUT SOCKET *Sock + ) +{ + if (Sock->CloseToken != NULL) { + SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS); + Sock->CloseToken = NULL; + } + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + if (Sock->Parent != NULL) { + SockDestroyChild (Sock); + } + +} + +/** + Called by low layer protocol to indicate that some data was sent or processed. + + This function trims the sent data in the socket send buffer, and signals the data + token if proper. + + @param[in, out] Sock Pointer to the socket. + @param[in] Count The length of the data processed or sent, in bytes. + +**/ +VOID +SockDataSent ( + IN OUT SOCKET *Sock, + IN UINT32 Count + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *SndToken; + + ASSERT (!IsListEmpty (&Sock->ProcessingSndTokenList)); + ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize); + + NetbufQueTrim (Sock->SndBuffer.DataQueue, Count); + + // + // To check if we can signal some snd token in this socket + // + while (Count > 0) { + SockToken = NET_LIST_HEAD ( + &(Sock->ProcessingSndTokenList), + SOCK_TOKEN, + TokenList + ); + + SndToken = SockToken->Token; + + if (SockToken->RemainDataLen <= Count) { + + RemoveEntryList (&(SockToken->TokenList)); + SIGNAL_TOKEN (SndToken, EFI_SUCCESS); + Count -= SockToken->RemainDataLen; + FreePool (SockToken); + } else { + + SockToken->RemainDataLen -= Count; + Count = 0; + } + } + + // + // to judge if we can process some send token in + // Sock->SndTokenList, if so process those send token + // + SockProcessSndToken (Sock); +} + +/** + Called by the low layer protocol to copy some data in the socket send + buffer starting from the specific offset to a buffer provided by + the caller. + + @param[in] Sock Pointer to the socket. + @param[in] Offset The start point of the data to be copied. + @param[in] Len The length of the data to be copied. + @param[out] Dest Pointer to the destination to copy the data. + + @return The data size copied. + +**/ +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + OUT UINT8 *Dest + ) +{ + ASSERT ((Sock != NULL) && SockStream == Sock->Type); + + return NetbufQueCopy ( + Sock->SndBuffer.DataQueue, + Offset, + Len, + Dest + ); +} + +/** + Called by the low layer protocol to deliver received data to socket layer. + + This function will append the data to the socket receive buffer, set the + urgent data length, and then check if any receive token can be signaled. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] NetBuffer Pointer to the buffer that contains the received data. + @param[in] UrgLen The length of the urgent data in the received data. + +**/ +VOID +SockDataRcvd ( + IN OUT SOCKET *Sock, + IN OUT NET_BUF *NetBuffer, + IN UINT32 UrgLen + ) +{ + ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) && + UrgLen <= NetBuffer->TotalSize); + + NET_GET_REF (NetBuffer); + + ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen; + + NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer); + + SockWakeRcvToken (Sock); +} + +/** + Get the length of the free space of the specific socket buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Which Flag to indicate which socket buffer to check: + either send buffer or receive buffer. + + @return The length of the free space, in bytes. + +**/ +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ) +{ + UINT32 BufferCC; + SOCK_BUFFER *SockBuffer; + + ASSERT (Sock != NULL && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which))); + + if (SOCK_SND_BUF == Which) { + SockBuffer = &(Sock->SndBuffer); + } else { + SockBuffer = &(Sock->RcvBuffer); + } + + BufferCC = (SockBuffer->DataQueue)->BufSize; + + if (BufferCC >= SockBuffer->HighWater) { + + return 0; + } + + return SockBuffer->HighWater - BufferCC; +} + +/** + Called by the low layer protocol to indicate that there will be no more data + from the communication peer. + + This function sets the socket's state to SO_NO_MORE_DATA and signals all queued + IO tokens with the error status EFI_CONNECTION_FIN. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockNoMoreData ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Err; + + SOCK_NO_MORE_DATA (Sock); + + if (!IsListEmpty (&Sock->RcvTokenList)) { + + ASSERT (0 == GET_RCV_DATASIZE (Sock)); + + Err = Sock->SockError; + + SOCK_ERROR (Sock, EFI_CONNECTION_FIN); + + SockFlushPendingToken (Sock, &Sock->RcvTokenList); + + SOCK_ERROR (Sock, Err); + + } +} + diff --git a/NetworkPkg/TcpDxe/SockImpl.h b/NetworkPkg/TcpDxe/SockImpl.h new file mode 100644 index 0000000000..bb4f6c2085 --- /dev/null +++ b/NetworkPkg/TcpDxe/SockImpl.h @@ -0,0 +1,103 @@ +/** @file + The function declaration that provided for Socket Interface. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _SOCK_IMPL_H_ +#define _SOCK_IMPL_H_ + +#include "Socket.h" + +/** + Signal a event with the given status. + + @param[in] Token The token's event is to be signaled. + @param[in] TokenStatus The status to be sent with the event. + +**/ +#define SIGNAL_TOKEN(Token, TokenStatus) \ + do { \ + (Token)->Status = (TokenStatus); \ + gBS->SignalEvent ((Token)->Event); \ + } while (0) + +#define SOCK_HEADER_SPACE (60 + 60 + 72) + +/** + Process the TCP send data, buffer the tcp txdata and append + the buffer to socket send buffer, then try to send it. + + @param[in] Sock Pointer to the socket. + @param[in] TcpTxData Pointer to the application provided send buffer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ); + +/** + Get received data from the socket layer to the receive token. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN OUT SOCKET *Sock, + IN OUT SOCK_IO_TOKEN *RcvToken + ); + +/** + Flush the sndBuffer and rcvBuffer of socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockConnFlush ( + IN OUT SOCKET *Sock + ); + +/** + Create a socket with initial data SockInitData. + + @param[in] SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket, return NULL when exception occured. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ); + +/** + Destroy a socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockDestroy ( + IN OUT SOCKET *Sock + ); + +#endif diff --git a/NetworkPkg/TcpDxe/SockInterface.c b/NetworkPkg/TcpDxe/SockInterface.c new file mode 100644 index 0000000000..e36c0e97c8 --- /dev/null +++ b/NetworkPkg/TcpDxe/SockInterface.c @@ -0,0 +1,999 @@ +/** @file + Interface function of the Socket. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "SockImpl.h" + +/** + Check whether the Event is in the List. + + @param[in] List Pointer to the token list to be searched. + @param[in] Event The event to be checked. + + @retval TRUE The specific Event exists in the List. + @retval FALSE The specific Event is not in the List. + +**/ +BOOLEAN +SockTokenExistedInList ( + IN LIST_ENTRY *List, + IN EFI_EVENT Event + ) +{ + LIST_ENTRY *ListEntry; + SOCK_TOKEN *SockToken; + + NET_LIST_FOR_EACH (ListEntry, List) { + SockToken = NET_LIST_USER_STRUCT ( + ListEntry, + SOCK_TOKEN, + TokenList + ); + + if (Event == SockToken->Token->Event) { + return TRUE; + } + } + + return FALSE; +} + +/** + Call SockTokenExistedInList() to check whether the Event is + in the related socket's lists. + + @param[in] Sock Pointer to the instance's socket. + @param[in] Event The event to be checked. + + @retval TRUE The Event exists in related socket's lists. + @retval FALSE The Event is not in related socket's lists. + +**/ +BOOLEAN +SockTokenExisted ( + IN SOCKET *Sock, + IN EFI_EVENT Event + ) +{ + + if (SockTokenExistedInList (&Sock->SndTokenList, Event) || + SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) || + SockTokenExistedInList (&Sock->RcvTokenList, Event) || + SockTokenExistedInList (&Sock->ListenTokenList, Event) + ) { + + return TRUE; + } + + if ((Sock->ConnectionToken != NULL) && (Sock->ConnectionToken->Event == Event)) { + + return TRUE; + } + + if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) { + return TRUE; + } + + return FALSE; +} + +/** + Buffer a token into the specific list of the socket Sock. + + @param[in] Sock Pointer to the instance's socket. + @param[in] List Pointer to the list to store the token. + @param[in] Token Pointer to the token to be buffered. + @param[in] DataLen The data length of the buffer contained in Token. + + @return Pointer to the token that wraps Token. If NULL, an error condition occurred. + +**/ +SOCK_TOKEN * +SockBufferToken ( + IN SOCKET *Sock, + IN LIST_ENTRY *List, + IN VOID *Token, + IN UINT32 DataLen + ) +{ + SOCK_TOKEN *SockToken; + + SockToken = AllocateZeroPool (sizeof (SOCK_TOKEN)); + if (NULL == SockToken) { + + DEBUG ( + (EFI_D_ERROR, + "SockBufferIOToken: No Memory to allocate SockToken\n") + ); + + return NULL; + } + + SockToken->Sock = Sock; + SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token; + SockToken->RemainDataLen = DataLen; + InsertTailList (List, &SockToken->TokenList); + + return SockToken; +} + +/** + Destory the socket Sock and its associated protocol control block. + + @param[in, out] Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock was destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL)); + + if (Sock->IsDestroyed) { + return EFI_SUCCESS; + } + + Sock->IsDestroyed = TRUE; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockDestroyChild: Get the lock to access socket failed with %r\n", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + // + // force protocol layer to detach the PCB + // + Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockDestroyChild: Protocol detach socket failed with %r\n", + Status) + ); + + Sock->IsDestroyed = FALSE; + } else if (SOCK_IS_CONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + } + + EfiReleaseLock (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + return Status; + } + + SockDestroy (Sock); + return EFI_SUCCESS; +} + +/** + Create a socket and its associated protocol control block + with the intial data SockInitData and protocol specific + data ProtoData. + + @param[in] SockInitData Inital data to setting the socket. + + @return Pointer to the newly created socket. If NULL, an error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + // + // create a new socket + // + Sock = SockCreate (SockInitData); + if (NULL == Sock) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: No resource to create a new socket\n") + ); + + return NULL; + } + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: Get the lock to access socket failed with %r\n", + Status) + ); + + SockDestroy (Sock); + return NULL; + } + // + // inform the protocol layer to attach the socket + // with a new protocol control block + // + Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: Protocol failed to attach a socket with %r\n", + Status) + ); + + SockDestroy (Sock); + Sock = NULL; + } + + EfiReleaseLock (&(Sock->Lock)); + return Sock; +} + +/** + Configure the specific socket Sock using configuration data ConfigData. + + @param[in] Sock Pointer to the socket to be configured. + @param[in] ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket configured successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is already configured. + +**/ +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ) +{ + EFI_STATUS Status; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockConfigure: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_CONFIGURED (Sock)) { + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + ASSERT (Sock->State == SO_CLOSED); + + Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData); + +OnExit: + EfiReleaseLock (&(Sock->Lock)); + + return Status; +} + +/** + Initiate a connection establishment process. + + @param[in] Sock Pointer to the socket to initiate the initate the + connection. + @param[in] Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection initialized successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be an active one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockConnect: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto OnExit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto OnExit; + } + + if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token; + SockSetState (Sock, SO_CONNECTING); + Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL); + +OnExit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Issue a listen token to get an existed connected network instance + or wait for a connection if there is none. + + @param[in] Sock Pointer to the socket to accept connections. + @param[in] Token The token to accept a connection. + + @retval EFI_SUCCESS Either a connection is accpeted or the Token is + buffered for further acception. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be a passive one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limits. + +**/ +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_TCP4_LISTEN_TOKEN *ListenToken; + LIST_ENTRY *ListEntry; + EFI_STATUS Status; + SOCKET *Socket; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockAccept: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!SOCK_IS_LISTENING (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token; + + // + // Check if a connection has already in this Sock->ConnectionList + // + NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) { + + Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList); + + if (SOCK_IS_CONNECTED (Socket)) { + ListenToken->NewChildHandle = Socket->SockHandle; + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + RemoveEntryList (ListEntry); + + ASSERT (Socket->Parent != NULL); + + Socket->Parent->ConnCnt--; + + DEBUG ( + (EFI_D_INFO, + "SockAccept: Accept a socket, now conncount is %d", + Socket->Parent->ConnCnt) + ); + Socket->Parent = NULL; + + goto Exit; + } + } + + // + // Buffer this token for latter incoming connection request + // + if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) { + + Status = EFI_OUT_OF_RESOURCES; + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + + return Status; +} + +/** + Issue a token with data to the socket to send out. + + @param[in] Sock Pointer to the socket to process the token with + data. + @param[in] Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limits. + +**/ +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *SndToken; + EFI_EVENT Event; + UINT32 FreeSpace; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockSend: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + SndToken = (SOCK_IO_TOKEN *) Token; + TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData; + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // check if a token is already in the token buffer + // + Event = SndToken->Token.Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + DataLen = TxData->DataLength; + + // + // process this sending token now or buffer it only? + // + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) { + + SockToken = SockBufferToken ( + Sock, + &Sock->SndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + Status = EFI_OUT_OF_RESOURCES; + } + } else { + + SockToken = SockBufferToken ( + Sock, + &Sock->ProcessingSndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + DEBUG ( + (EFI_D_ERROR, + "SockSend: Failed to buffer IO token into socket processing SndToken List\n", + Status) + ); + + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockSend: Failed to process Snd Data\n", + Status) + ); + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + } + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Issue a token to get data from the socket. + + @param[in] Sock Pointer to the socket to get data from. + @param[in] Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_CONNECTION_FIN The connection is closed and there is no more data. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit. + +**/ +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *RcvToken; + UINT32 RcvdBytes; + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockRcv: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + + // + // check if a token is already in the token buffer of this socket + // + Event = RcvToken->Token.Event; + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + RcvdBytes = GET_RCV_DATASIZE (Sock); + + // + // check whether an error has happened before + // + if (EFI_ABORTED != Sock->SockError) { + + SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError); + Sock->SockError = EFI_ABORTED; + goto Exit; + } + + // + // check whether can not receive and there is no any + // data buffered in Sock->RcvBuffer + // + if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) { + + Status = EFI_CONNECTION_FIN; + goto Exit; + } + + if (RcvdBytes != 0) { + Status = SockProcessRcvToken (Sock, RcvToken); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL); + } else { + + if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) { + Status = EFI_OUT_OF_RESOURCES; + } + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Reset the socket and its associated protocol control block. + + @param[in, out] Sock Pointer to the socket to be flushed. + + @retval EFI_SUCCESS The socket is flushed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockFlush ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockFlush: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (!SOCK_IS_CONFIGURED (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockFlush: Protocol failed handling SOCK_FLUSH with %r", + Status) + ); + + goto Exit; + } + + SOCK_ERROR (Sock, EFI_ABORTED); + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Close or abort the socket associated connection. + + @param[in, out] Sock Pointer to the socket of the connection to close + or abort. + @param[in] Token The token for a close operation. + @param[in] OnAbort TRUE for aborting the connection; FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation initialized + successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockClose ( + IN OUT SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockClose: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (SOCK_IS_DISCONNECTING (Sock)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Sock->CloseToken = Token; + SockSetState (Sock, SO_DISCONNECTING); + + if (OnAbort) { + Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL); + } else { + Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL); + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Get the mode data of the low layer protocol. + + @param[in] Sock Pointer to the socket to get mode data from. + @param[in, out] Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data was obtained successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN OUT VOID *Mode + ) +{ + return Sock->ProtoHandler (Sock, SOCK_MODE, Mode); +} + +/** + Configure the low level protocol to join a multicast group for + this socket's connection. + + @param[in] Sock Pointer to the socket of the connection to join the + specific multicast group. + @param[in] GroupInfo Pointer to the multicast group info. + + @retval EFI_SUCCESS The configuration completed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ) +{ + EFI_STATUS Status; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockGroup: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo); + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Add or remove route information in IP route table associated + with this socket. + + @param[in] Sock Pointer to the socket associated with the IP route + table to operate on. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table updated successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ) +{ + EFI_STATUS Status; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockRoute: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo); + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + diff --git a/NetworkPkg/TcpDxe/Socket.h b/NetworkPkg/TcpDxe/Socket.h new file mode 100644 index 0000000000..a00625244e --- /dev/null +++ b/NetworkPkg/TcpDxe/Socket.h @@ -0,0 +1,924 @@ +/** @file + Common head file for TCP socket. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCK_SND_BUF 0 +#define SOCK_RCV_BUF 1 + +#define SOCK_BUFF_LOW_WATER (2 * 1024) +#define SOCK_RCV_BUFF_SIZE (8 * 1024) +#define SOCK_SND_BUFF_SIZE (8 * 1024) +#define SOCK_BACKLOG 5 + +#define PROTO_RESERVED_LEN 20 + +#define SO_NO_MORE_DATA 0x0001 + +// +// +// +// When a socket is created it enters into SO_UNCONFIGURED, +// no actions can be taken on this socket, only after calling +// SockConfigure. The state transition diagram of socket is +// as following: +// +// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING +// ^ | | +// | ---> SO_LISTENING | +// | | +// |------------------SO_DISCONNECTING<-- SO_CONNECTED +// +// A passive socket can only go into SO_LISTENING and +// SO_UNCONFIGURED state. SO_XXXING state is a middle state +// when a socket is undergoing a protocol procedure such +// as requesting a TCP connection. +// +// +// + +/// +/// Socket state +/// +#define SO_CLOSED 0 +#define SO_LISTENING 1 +#define SO_CONNECTING 2 +#define SO_CONNECTED 3 +#define SO_DISCONNECTING 4 + +/// +/// Socket configure state +/// +#define SO_UNCONFIGURED 0 +#define SO_CONFIGURED_ACTIVE 1 +#define SO_CONFIGURED_PASSIVE 2 +#define SO_NO_MAPPING 3 + +/// +/// The request issued from socket layer to protocol layer. +/// +#define SOCK_ATTACH 0 ///< Attach current socket to a new PCB +#define SOCK_DETACH 1 ///< Detach current socket from the PCB +#define SOCK_CONFIGURE 2 ///< Configure attached PCB +#define SOCK_FLUSH 3 ///< Flush attached PCB +#define SOCK_SND 4 ///< Need protocol to send something +#define SOCK_SNDPUSH 5 ///< Need protocol to send pushed data +#define SOCK_SNDURG 6 ///< Need protocol to send urgent data +#define SOCK_CONSUMED 7 ///< Application has retrieved data from socket +#define SOCK_CONNECT 8 ///< Need to connect to a peer +#define SOCK_CLOSE 9 ///< Need to close the protocol process +#define SOCK_ABORT 10 ///< Need to reset the protocol process +#define SOCK_POLL 11 ///< Need to poll to the protocol layer +#define SOCK_ROUTE 12 ///< Need to add a route information +#define SOCK_MODE 13 ///< Need to get the mode data of the protocol +#define SOCK_GROUP 14 ///< Need to join a mcast group + +/** + Set socket SO_NO_MORE_DATA flag. + + @param[in] Sock Pointer to the socket + +**/ +#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA) + +/** + Check whether the socket is unconfigured. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is unconfigued. + @retval FALSE The socket is not unconfigued. + +**/ +#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED) + +/** + Check whether the socket is configured. + + @param[in] Sock Pointer to the socket + + @retval TRUE The socket is configued + @retval FALSE The socket is not configued + +**/ +#define SOCK_IS_CONFIGURED(Sock) \ + (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \ + ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)) + +/** + Check whether the socket is configured to active mode. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is configued to active mode. + @retval FALSE The socket is not configued to active mode. + +**/ +#define SOCK_IS_CONFIGURED_ACTIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) + +/** + Check whether the socket is configured to passive mode. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is configued to passive mode. + @retval FALSE The socket is not configued to passive mode. + +**/ +#define SOCK_IS_CONNECTED_PASSIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE) + +/** + Check whether the socket is mapped. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is not mapping. + @retval FALSE The socket is mapped. + +**/ +#define SOCK_IS_NO_MAPPING(Sock) ((Sock)->ConfigureState == SO_NO_MAPPING) + +/** + Check whether the socket is closed. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is closed. + @retval FALSE The socket is not closed. + +**/ +#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED) + +/** + Check whether the socket is listening. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is listening. + @retval FALSE The socket is not listening. + +**/ +#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING) + +/** + Check whether the socket is connecting. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is connecting. + @retval FALSE The socket is not connecting. + +**/ +#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING) + +/** + Check whether the socket has connected. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket has connected. + @retval FALSE The socket has not connected. + +**/ +#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED) + +/** + Check whether the socket is disconnecting. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is disconnecting. + @retval FALSE The socket is not disconnecting. + +**/ +#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING) + +/** + Check whether the socket is no more data. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is no more data. + @retval FALSE The socket still has data. + +**/ +#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA)) + +/** + Set the size of the receive buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Size The size to set. + +**/ +#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size)) + +/** + Get the size of the receive buffer. + + @param[in] Sock Pointer to the socket. + + @return The receive buffer size. + +**/ +#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater) + +/** + Get the size of the receive data. + + @param[in] Sock Pointer to the socket. + + @return The received data size. + +**/ +#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize) + +/** + Set the size of the send buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Size The size to set. + +**/ +#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size)) + +/** + Get the size of the send buffer. + + @param[in] Sock Pointer to the socket. + + @return The send buffer size. + +**/ +#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater) + +/** + Get the size of the send data. + + @param[in] Sock Pointer to the socket. + + @return The send data size. + +**/ +#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize) + +/** + Set the backlog value of the socket. + + @param[in] Sock Pointer to the socket. + @param[in] Value The value to set. + +**/ +#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value)) + +/** + Get the backlog value of the socket. + + @param[in] Sock Pointer to the socket. + + @return The backlog value. + +**/ +#define GET_BACKLOG(Sock) ((Sock)->BackLog) + +/** + Set the socket with error state. + + @param[in] Sock Pointer to the socket. + @param[in] Error The error state. + +**/ +#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error)) + +#define SOCK_SIGNATURE SIGNATURE_32 ('S', 'O', 'C', 'K') + +#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE) + +#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock) + +#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) ((Type *) (((SOCK_TOKEN *) (SockToken))->Token)) + +typedef struct _TCP_SOCKET SOCKET; + +/// +/// Socket completion token +/// +typedef struct _SOCK_COMPLETION_TOKEN { + EFI_EVENT Event; ///< The event to be issued + EFI_STATUS Status; ///< The status to be issued +} SOCK_COMPLETION_TOKEN; + +typedef union { + VOID *RxData; + VOID *TxData; +} SOCK_IO_DATA; + +/// +/// The application token with data packet +/// +typedef struct _SOCK_IO_TOKEN { + SOCK_COMPLETION_TOKEN Token; + SOCK_IO_DATA Packet; +} SOCK_IO_TOKEN; + +/// +/// The socket type. +/// +typedef enum { + SockDgram, ///< This socket providing datagram service + SockStream ///< This socket providing stream service +} SOCK_TYPE; + +/// +/// The buffer structure of rcvd data and send data used by socket. +/// +typedef struct _SOCK_BUFFER { + UINT32 HighWater; ///< The buffersize upper limit of sock_buffer + UINT32 LowWater; ///< The low warter mark of sock_buffer + NET_BUF_QUEUE *DataQueue; ///< The queue to buffer data +} SOCK_BUFFER; + +/** + The handler of protocol for request from socket. + + @param[in] Socket The socket issuing the request to protocol. + @param[in] Request The request issued by socket. + @param[in] RequestData The request related data. + + @retval EFI_SUCCESS The socket request is completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +typedef +EFI_STATUS +(*SOCK_PROTO_HANDLER) ( + IN SOCKET *Socket, + IN UINT8 Request, + IN VOID *RequestData + ); + +/** + The Callback funtion called after the TCP socket is created. + + @param[in] This Pointer to the socket just created. + @param[in] Context Context of the socket. + + @retval EFI_SUCCESS This protocol installed successfully. + @retval other Some error occured. + +**/ +typedef +EFI_STATUS +(*SOCK_CREATE_CALLBACK) ( + IN SOCKET *This, + IN VOID *Context + ); + +/** + The callback function called before the TCP socket is to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context. + +**/ +typedef +VOID +(*SOCK_DESTROY_CALLBACK) ( + IN SOCKET *This, + IN VOID *Context + ); + +/// +/// The initialize data for create a new socket. +/// +typedef struct _SOCK_INIT_DATA { + SOCK_TYPE Type; + UINT8 State; + + SOCKET *Parent; ///< The parent of this socket + UINT32 BackLog; ///< The connection limit for listening socket + UINT32 SndBufferSize; ///< The high warter mark of send buffer + UINT32 RcvBufferSize; ///< The high warter mark of receive buffer + UINT8 IpVersion; + VOID *Protocol; ///< The pointer to protocol function template + ///< wanted to install on socket + + // + // Callbacks after socket is created and before socket is to be destroyed. + // + SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created + SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied + VOID *Context; ///< The context of the callback + + // + // Opaque protocol data. + // + VOID *ProtoData; + UINT32 DataSize; + + SOCK_PROTO_HANDLER ProtoHandler; ///< The handler of protocol for socket request + + EFI_HANDLE DriverBinding; ///< The driver binding handle +} SOCK_INIT_DATA; + +/// +/// The union type of TCP and UDP protocol. +/// +typedef union _NET_PROTOCOL { + EFI_TCP4_PROTOCOL Tcp4Protocol; ///< Tcp4 protocol + EFI_TCP6_PROTOCOL Tcp6Protocol; ///< Tcp6 protocol +} NET_PROTOCOL; +/// +/// The socket structure representing a network service access point. +/// +struct _TCP_SOCKET { + // + // Socket description information + // + UINT32 Signature; ///< Signature of the socket + EFI_HANDLE SockHandle; ///< The virtual handle of the socket + EFI_HANDLE DriverBinding; ///< Socket's driver binding protocol + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + LIST_ENTRY Link; + UINT8 ConfigureState; + SOCK_TYPE Type; + UINT8 State; + UINT16 Flag; + EFI_LOCK Lock; ///< The lock of socket + SOCK_BUFFER SndBuffer; ///< Send buffer of application's data + SOCK_BUFFER RcvBuffer; ///< Receive buffer of received data + EFI_STATUS SockError; ///< The error returned by low layer protocol + BOOLEAN IsDestroyed; + + // + // Fields used to manage the connection request + // + UINT32 BackLog; ///< the limit of connection to this socket + UINT32 ConnCnt; ///< the current count of connections to it + SOCKET *Parent; ///< listening parent that accept the connection + LIST_ENTRY ConnectionList; ///< the connections maintained by this socket + // + // The queue to buffer application's asynchronous token + // + LIST_ENTRY ListenTokenList; + LIST_ENTRY RcvTokenList; + LIST_ENTRY SndTokenList; + LIST_ENTRY ProcessingSndTokenList; + + SOCK_COMPLETION_TOKEN *ConnectionToken; ///< app's token to signal if connected + SOCK_COMPLETION_TOKEN *CloseToken; ///< app's token to signal if closed + // + // Interface for low level protocol + // + SOCK_PROTO_HANDLER ProtoHandler; ///< The request handler of protocol + UINT8 ProtoReserved[PROTO_RESERVED_LEN]; ///< Data fields reserved for protocol + UINT8 IpVersion; + NET_PROTOCOL NetProtocol; ///< TCP or UDP protocol socket used + // + // Callbacks after socket is created and before socket is to be destroyed. + // + SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created + SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied + VOID *Context; ///< The context of the callback +}; + +/// +/// The token structure buffered in socket layer. +/// +typedef struct _SOCK_TOKEN { + LIST_ENTRY TokenList; ///< The entry to add in the token list + SOCK_COMPLETION_TOKEN *Token; ///< The application's token + UINT32 RemainDataLen; ///< Unprocessed data length + SOCKET *Sock; ///< The poninter to the socket this token + ///< belongs to +} SOCK_TOKEN; + +/// +/// Reserved data to access the NET_BUF delivered by TCP driver. +/// +typedef struct _TCP_RSV_DATA { + UINT32 UrgLen; +} TCP_RSV_DATA; + +// +// Socket provided oprerations for low layer protocol implemented in SockImpl.c +// + +/** + Set the state of the socket. + + @param[in, out] Sock Pointer to the socket. + @param[in] State The new socket state to be set. + +**/ +VOID +SockSetState ( + IN OUT SOCKET *Sock, + IN UINT8 State + ); + +/** + Clone a new socket including its associated protocol control block. + + @param[in] Sock Pointer to the socket to be cloned. + + @return Pointer to the newly cloned socket. If NULL, an error condition occurred. + +**/ +SOCKET * +SockClone ( + IN SOCKET *Sock + ); + +/** + Called by the low layer protocol to indicate the socket a connection is + established. + + This function just changes the socket's state to SO_CONNECTED + and signals the token used for connection establishment. + + @param[in, out] Sock Pointer to the socket associated with the + established connection. + +**/ +VOID +SockConnEstablished ( + IN OUT SOCKET *Sock + ); + +/** + Called by the low layer protocol to indicate that the connection is closed. + + This function flushes the socket, sets the state to SO_CLOSED, and signals + the close token. + + @param[in, out] Sock Pointer to the socket associated with the closed + connection. + +**/ +VOID +SockConnClosed ( + IN OUT SOCKET *Sock + ); + +/** + Called by low layer protocol to indicate that some data is sent or processed. + + This function trims the sent data in the socket send buffer and signals the data + token, if proper. + + @param[in, out] Sock Pointer to the socket. + @param[in] Count The length of the data processed or sent, in bytes. + +**/ +VOID +SockDataSent ( + IN OUT SOCKET *Sock, + IN UINT32 Count + ); + +/** + Called by the low layer protocol to copy some data in socket send + buffer starting from the specific offset to a buffer provided by + the caller. + + @param[in] Sock Pointer to the socket. + @param[in] Offset The start point of the data to be copied. + @param[in] Len The length of the data to be copied. + @param[out] Dest Pointer to the destination to copy the data. + + @return The data size copied. + +**/ +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + OUT UINT8 *Dest + ); + +/** + Called by the low layer protocol to deliver received data to socket layer. + + This function appends the data to the socket receive buffer, set the + urgent data length, then checks if any receive token can be signaled. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] NetBuffer Pointer to the buffer that contains the received data. + @param[in] UrgLen The length of the urgent data in the received data. + +**/ +VOID +SockDataRcvd ( + IN OUT SOCKET *Sock, + IN OUT NET_BUF *NetBuffer, + IN UINT32 UrgLen + ); + +/** + Get the length of the free space of the specific socket buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Which Flag to indicate which socket buffer to check: + either send buffer or receive buffer. + + @return The length of the free space, in bytes. + +**/ +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ); + +/** + Called by the low layer protocol to indicate that there will be no more data + from the communication peer. + + This function sets the socket's state to SO_NO_MORE_DATA and signals all queued + IO tokens with the error status EFI_CONNECTION_FIN. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockNoMoreData ( + IN OUT SOCKET *Sock + ); + +// +// Socket provided operations for user interface implemented in SockInterface.c +// + +/** + Create a socket and its associated protocol control block + with the intial data SockInitData and protocol specific + data ProtoData. + + @param[in] SockInitData Inital data to setting the socket. + + @return Pointer to the newly created socket. If NULL, an error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData + ); + +/** + Destory the socket Sock and its associated protocol control block. + + @param[in, out] Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock was destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN OUT SOCKET *Sock + ); + +/** + Configure the specific socket Sock using configuration data ConfigData. + + @param[in] Sock Pointer to the socket to be configured. + @param[in] ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket configured successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is already configured. + +**/ +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ); + +/** + Initiate a connection establishment process. + + @param[in] Sock Pointer to the socket to initiate the initate the + connection. + @param[in] Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection initialized successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be an active one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Issue a listen token to get an existed connected network instance, + or wait for a connection if there is none. + + @param[in] Sock Pointer to the socket to accept connections. + @param[in] Token The token to accept a connection. + + @retval EFI_SUCCESS Either a connection is accepted or the Token is + buffered for further acceptance. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be a passive one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit. + +**/ +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Issue a token with data to the socket to send out. + + @param[in] Sock Pointer to the socket to process the token with + data. + @param[in] Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit. + +**/ +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Issue a token to get data from the socket. + + @param[in] Sock Pointer to the socket to get data from. + @param[in] Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_CONNECTION_FIN The connection is closed and there is no more data. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit. + +**/ +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Reset the socket and its associated protocol control block. + + @param[in, out] Sock Pointer to the socket to be flushed. + + @retval EFI_SUCCESS The socket flushed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockFlush ( + IN OUT SOCKET *Sock + ); + +/** + Close or abort the socket associated connection. + + @param[in, out] Sock Pointer to the socket of the connection to close + or abort. + @param[in] Token The token for close operation. + @param[in] OnAbort TRUE for aborting the connection, FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation initialized + successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockClose ( + IN OUT SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ); + +/** + Get the mode data of the low layer protocol. + + @param[in] Sock Pointer to the socket to get mode data from. + @param[in, out] Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data was obtained successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN OUT VOID *Mode + ); + +/** + Configure the low level protocol to join a multicast group for + this socket's connection. + + @param[in] Sock Pointer to the socket of the connection to join the + specific multicast group. + @param[in] GroupInfo Pointer to the multicast group information. + + @retval EFI_SUCCESS The configuration completed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ); + +/** + Add or remove route information in IP route table associated + with this socket. + + @param[in] Sock Pointer to the socket associated with the IP route + table to operate on. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table updated successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpDispatcher.c b/NetworkPkg/TcpDxe/TcpDispatcher.c new file mode 100644 index 0000000000..eaa75a4ec4 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDispatcher.c @@ -0,0 +1,861 @@ +/** @file + The implementation of a dispatch routine for processing TCP requests. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +/** + Add or remove a route entry in the IP route table associated with this TCP instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration(DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table + (when RouteInfo->DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table + (when RouteInfo->DeleteRoute is FALSE). +**/ +EFI_STATUS +Tcp4Route ( + IN TCP_CB *Tcb, + IN TCP4_ROUTE_INFO *RouteInfo + ) +{ + IP_IO_IP_PROTOCOL Ip; + + Ip = Tcb->IpInfo->Ip; + + ASSERT (Ip.Ip4!= NULL); + + return Ip.Ip4->Routes ( + Ip.Ip4, + RouteInfo->DeleteRoute, + RouteInfo->SubnetAddress, + RouteInfo->SubnetMask, + RouteInfo->GatewayAddress + ); + +} + +/** + Get the operational settings of this TCPv4 instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +EFI_STATUS +Tcp4GetMode ( + IN TCP_CB *Tcb, + IN OUT TCP4_MODE_DATA *Mode + ) +{ + SOCKET *Sock; + EFI_TCP4_CONFIG_DATA *ConfigData; + EFI_TCP4_ACCESS_POINT *AccessPoint; + EFI_TCP4_OPTION *Option; + EFI_IP4_PROTOCOL *Ip; + + Sock = Tcb->Sk; + + if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + if (Mode->Tcp4State != NULL) { + *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State; + } + + if (Mode->Tcp4ConfigData != NULL) { + + ConfigData = Mode->Tcp4ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TypeOfService = Tcb->Tos; + ConfigData->TimeToLive = Tcb->Ttl; + + AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr; + + CopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + + AccessPoint->SubnetMask = Tcb->SubnetMask; + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + + CopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + + AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); + AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); + + if (Option != NULL) { + Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk); + Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk); + Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk); + + Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ; + Option->DataRetries = Tcb->MaxRexmit; + Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ; + Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ; + Option->KeepAliveProbes = Tcb->MaxKeepAlive; + Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ; + Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ; + + Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)); + Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)); + Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip.Ip4; + ASSERT (Ip != NULL); + + return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + +/** + Get the operational settings of this TCPv6 instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +EFI_STATUS +Tcp6GetMode ( + IN TCP_CB *Tcb, + IN OUT TCP6_MODE_DATA *Mode + ) +{ + SOCKET *Sock; + EFI_TCP6_CONFIG_DATA *ConfigData; + EFI_TCP6_ACCESS_POINT *AccessPoint; + EFI_TCP6_OPTION *Option; + EFI_IP6_PROTOCOL *Ip; + + Sock = Tcb->Sk; + + if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + if (Mode->Tcp6State != NULL) { + *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State); + } + + if (Mode->Tcp6ConfigData != NULL) { + + ConfigData = Mode->Tcp6ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TrafficClass = Tcb->Tos; + ConfigData->HopLimit = Tcb->Ttl; + + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); + AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); + + IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip); + IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip); + + if (Option != NULL) { + Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk); + Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk); + Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk); + + Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ; + Option->DataRetries = Tcb->MaxRexmit; + Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ; + Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ; + Option->KeepAliveProbes = Tcb->MaxKeepAlive; + Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ; + Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ; + + Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)); + Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)); + Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip.Ip6; + ASSERT (Ip != NULL); + + return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + +/** + If TcpAp->StationPort isn't zero, check whether the access point + is registered, else generate a random station port for this + access point. + + @param[in] TcpAp Pointer to the access point. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6 + + @retval EFI_SUCCESS The check passed or the port is assigned. + @retval EFI_INVALID_PARAMETER The non-zero station port is already used. + @retval EFI_OUT_OF_RESOURCES No port can be allocated. + +**/ +EFI_STATUS +TcpBind ( + IN TCP_ACCESS_POINT *TcpAp, + IN UINT8 IpVersion + ) +{ + BOOLEAN Cycle; + EFI_IP_ADDRESS Local; + UINT16 *Port; + UINT16 *RandomPort; + + if (IpVersion == IP_VERSION_4) { + CopyMem (&Local, &TcpAp->Tcp4Ap.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + Port = &TcpAp->Tcp4Ap.StationPort; + RandomPort = &mTcp4RandomPort; + } else { + IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress); + Port = &TcpAp->Tcp6Ap.StationPort; + RandomPort = &mTcp6RandomPort; + } + + if (0 != *Port) { + // + // Check if a same endpoing is bound. + // + if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) { + + return EFI_INVALID_PARAMETER; + } + } else { + // + // generate a random port + // + Cycle = FALSE; + + if (TCP_PORT_USER_RESERVED == *RandomPort) { + *RandomPort = TCP_PORT_KNOWN; + } + + (*RandomPort)++; + + while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) { + (*RandomPort)++; + + if (*RandomPort <= TCP_PORT_KNOWN) { + if (Cycle) { + DEBUG ( + (EFI_D_ERROR, + "TcpBind: no port can be allocated for this pcb\n") + ); + return EFI_OUT_OF_RESOURCES; + } + + *RandomPort = TCP_PORT_KNOWN + 1; + + Cycle = TRUE; + } + } + + *Port = *RandomPort; + } + + return EFI_SUCCESS; +} + +/** + Flush the Tcb add its associated protocols. + + @param[in, out] Tcb Pointer to the TCP_CB to be flushed. + +**/ +VOID +TcpFlushPcb ( + IN OUT TCP_CB *Tcb + ) +{ + SOCKET *Sock; + TCP_PROTO_DATA *TcpProto; + + IpIoConfigIp (Tcb->IpInfo, NULL); + + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + + if (SOCK_IS_CONFIGURED (Sock)) { + RemoveEntryList (&Tcb->List); + + if (Sock->DevicePath != NULL) { + // + // Uninstall the device path protocl. + // + gBS->UninstallProtocolInterface ( + Sock->SockHandle, + &gEfiDevicePathProtocolGuid, + Sock->DevicePath + ); + + FreePool (Sock->DevicePath); + Sock->DevicePath = NULL; + } + + TcpSetVariableData (TcpProto->TcpService); + } + + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + Tcb->State = TCP_CLOSED; +} + +/** + Attach a Pcb to the socket. + + @param[in] Sk Pointer to the socket of this TCP instance. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +TcpAttachPcb ( + IN SOCKET *Sk + ) +{ + TCP_CB *Tcb; + TCP_PROTO_DATA *ProtoData; + IP_IO *IpIo; + + Tcb = AllocateZeroPool (sizeof (TCP_CB)); + + if (Tcb == NULL) { + + DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n")); + + return EFI_OUT_OF_RESOURCES; + } + + ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved; + IpIo = ProtoData->TcpService->IpIo; + + // + // Create an IpInfo for this Tcb. + // + Tcb->IpInfo = IpIoAddIp (IpIo); + if (Tcb->IpInfo == NULL) { + + FreePool (Tcb); + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&Tcb->List); + InitializeListHead (&Tcb->SndQue); + InitializeListHead (&Tcb->RcvQue); + + Tcb->State = TCP_CLOSED; + Tcb->Sk = Sk; + ProtoData->TcpPcb = Tcb; + + return EFI_SUCCESS; +} + +/** + Detach the Pcb of the socket. + + @param[in, out] Sk Pointer to the socket of this TCP instance. + +**/ +VOID +TcpDetachPcb ( + IN OUT SOCKET *Sk + ) +{ + TCP_PROTO_DATA *ProtoData; + TCP_CB *Tcb; + + ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + ASSERT (Tcb != NULL); + + TcpFlushPcb (Tcb); + + IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo); + + FreePool (Tcb); + + ProtoData->TcpPcb = NULL; +} + +/** + Configure the Pcb using CfgData. + + @param[in] Sk Pointer to the socket of this TCP instance. + @param[in] CfgData Pointer to the TCP configuration data. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A same access point has been configured in + another TCP instance. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +TcpConfigurePcb ( + IN SOCKET *Sk, + IN TCP_CONFIG_DATA *CfgData + ) +{ + IP_IO_IP_CONFIG_DATA IpCfgData; + EFI_STATUS Status; + EFI_TCP4_OPTION *Option; + TCP_PROTO_DATA *TcpProto; + TCP_CB *Tcb; + TCP_ACCESS_POINT *TcpAp; + + ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL)); + + TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved; + Tcb = TcpProto->TcpPcb; + + ASSERT (Tcb != NULL); + + if (Sk->IpVersion == IP_VERSION_4) { + // + // Add Ip for send pkt to the peer + // + CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA)); + IpCfgData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + IpCfgData.Ip4CfgData.TypeOfService = CfgData->Tcp4CfgData.TypeOfService; + IpCfgData.Ip4CfgData.TimeToLive = CfgData->Tcp4CfgData.TimeToLive; + IpCfgData.Ip4CfgData.UseDefaultAddress = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress; + IpCfgData.Ip4CfgData.SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask; + IpCfgData.Ip4CfgData.ReceiveTimeout = (UINT32) (-1); + CopyMem ( + &IpCfgData.Ip4CfgData.StationAddress, + &CfgData->Tcp4CfgData.AccessPoint.StationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + + } else { + ASSERT (Sk->IpVersion == IP_VERSION_6); + + CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA)); + IpCfgData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + IpCfgData.Ip6CfgData.TrafficClass = CfgData->Tcp6CfgData.TrafficClass; + IpCfgData.Ip6CfgData.HopLimit = CfgData->Tcp6CfgData.HopLimit; + IpCfgData.Ip6CfgData.ReceiveTimeout = (UINT32) (-1); + IP6_COPY_ADDRESS ( + &IpCfgData.Ip6CfgData.StationAddress, + &CfgData->Tcp6CfgData.AccessPoint.StationAddress + ); + IP6_COPY_ADDRESS ( + &IpCfgData.Ip6CfgData.DestinationAddress, + &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress + ); + } + + // + // Configure the IP instance this Tcb consumes. + // + Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData); + if (EFI_ERROR (Status)) { + goto OnExit; + } + + if (Sk->IpVersion == IP_VERSION_4) { + // + // Get the default address information if the instance is configured to use default address. + // + CfgData->Tcp4CfgData.AccessPoint.StationAddress = IpCfgData.Ip4CfgData.StationAddress; + CfgData->Tcp4CfgData.AccessPoint.SubnetMask = IpCfgData.Ip4CfgData.SubnetMask; + + TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint; + } else { + IP6_COPY_ADDRESS ( + &CfgData->Tcp6CfgData.AccessPoint.StationAddress, + &IpCfgData.Ip6CfgData.StationAddress + ); + + TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint; + } + + // + // check if we can bind this endpoint in CfgData + // + Status = TcpBind (TcpAp, Sk->IpVersion); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "TcpConfigurePcb: Bind endpoint failed with %r\n", + Status) + ); + + goto OnExit; + } + + // + // Initalize the operating information in this Tcb + // + ASSERT (Tcb->State == TCP_CLOSED && + IsListEmpty (&Tcb->SndQue) && + IsListEmpty (&Tcb->RcvQue)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + Tcb->State = TCP_CLOSED; + + Tcb->SndMss = 536; + Tcb->RcvMss = TcpGetRcvMss (Sk); + + Tcb->SRtt = 0; + Tcb->Rto = 3 * TCP_TICK_HZ; + + Tcb->CWnd = Tcb->SndMss; + Tcb->Ssthresh = 0xffffffff; + + Tcb->CongestState = TCP_CONGEST_OPEN; + + Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN; + Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD; + Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE; + Tcb->MaxRexmit = TCP_MAX_LOSS; + Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME; + Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME; + Tcb->ConnectTimeout = TCP_CONNECT_TIME; + + if (Sk->IpVersion == IP_VERSION_4) { + // + // initialize Tcb in the light of CfgData + // + Tcb->Ttl = CfgData->Tcp4CfgData.TimeToLive; + Tcb->Tos = CfgData->Tcp4CfgData.TypeOfService; + + Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress; + + CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR)); + Tcb->LocalEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort); + Tcb->SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask; + + CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); + Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort); + + Option = CfgData->Tcp4CfgData.ControlOption; + } else { + Tcb->Ttl = CfgData->Tcp6CfgData.HopLimit; + Tcb->Tos = CfgData->Tcp6CfgData.TrafficClass; + + IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress); + Tcb->LocalEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort); + + IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress); + Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort); + + // + // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same. + // + Option = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption; + } + + if (Option != NULL) { + SET_RCV_BUFFSIZE ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_RCV_BUF_SIZE_MIN, + TCP_RCV_BUF_SIZE, + TCP_RCV_BUF_SIZE, + Option->ReceiveBufferSize + ) + ) + ); + SET_SND_BUFFSIZE ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_SND_BUF_SIZE_MIN, + TCP_SND_BUF_SIZE, + TCP_SND_BUF_SIZE, + Option->SendBufferSize + ) + ) + ); + + SET_BACKLOG ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_BACKLOG_MIN, + TCP_BACKLOG, + TCP_BACKLOG, + Option->MaxSynBackLog + ) + ) + ); + + Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL ( + TCP_MAX_LOSS_MIN, + TCP_MAX_LOSS, + TCP_MAX_LOSS, + Option->DataRetries + ); + Tcb->FinWait2Timeout = TCP_COMP_VAL ( + TCP_FIN_WAIT2_TIME, + TCP_FIN_WAIT2_TIME_MAX, + TCP_FIN_WAIT2_TIME, + (UINT32) (Option->FinTimeout * TCP_TICK_HZ) + ); + + if (Option->TimeWaitTimeout != 0) { + Tcb->TimeWaitTimeout = TCP_COMP_VAL ( + TCP_TIME_WAIT_TIME, + TCP_TIME_WAIT_TIME_MAX, + TCP_TIME_WAIT_TIME, + (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ) + ); + } else { + Tcb->TimeWaitTimeout = 0; + } + + if (Option->KeepAliveProbes != 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + + Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL ( + TCP_MAX_KEEPALIVE_MIN, + TCP_MAX_KEEPALIVE, + TCP_MAX_KEEPALIVE, + Option->KeepAliveProbes + ); + Tcb->KeepAliveIdle = TCP_COMP_VAL ( + TCP_KEEPALIVE_IDLE_MIN, + TCP_KEEPALIVE_IDLE_MAX, + TCP_KEEPALIVE_IDLE_MIN, + (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ) + ); + Tcb->KeepAlivePeriod = TCP_COMP_VAL ( + TCP_KEEPALIVE_PERIOD_MIN, + TCP_KEEPALIVE_PERIOD, + TCP_KEEPALIVE_PERIOD, + (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ) + ); + } + + Tcb->ConnectTimeout = TCP_COMP_VAL ( + TCP_CONNECT_TIME_MIN, + TCP_CONNECT_TIME, + TCP_CONNECT_TIME, + (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ) + ); + + if (!Option->EnableNagle) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); + } + + if (!Option->EnableTimeStamp) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS); + } + + if (!Option->EnableWindowScaling) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS); + } + } + + // + // The socket is bound, the is + // determined, construct the IP device path and install it. + // + Status = TcpInstallDevicePath (Sk); + if (EFI_ERROR (Status)) { + goto OnExit; + } + + // + // update state of Tcb and socket + // + if (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) || + ((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag) + ) { + + TcpSetState (Tcb, TCP_LISTEN); + SockSetState (Sk, SO_LISTENING); + + Sk->ConfigureState = SO_CONFIGURED_PASSIVE; + } else { + + Sk->ConfigureState = SO_CONFIGURED_ACTIVE; + } + + if (Sk->IpVersion == IP_VERSION_6) { + Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK; + } + + TcpInsertTcb (Tcb); + +OnExit: + + return Status; +} + +/** + The procotol handler provided to the socket layer, which is used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param[in] Sock Pointer to the socket of this TCP instance. + @param[in] Request The code of this operation request. + @param[in] Data Pointer to the operation specific data passed in + together with the operation request. This is an + optional parameter that may be NULL. + + @retval EFI_SUCCESS The socket request completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +TcpDispatcher ( + IN SOCKET *Sock, + IN UINT8 Request, + IN VOID *Data OPTIONAL + ) +{ + TCP_CB *Tcb; + TCP_PROTO_DATA *ProtoData; + + ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + switch (Request) { + case SOCK_POLL: + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4); + } else { + ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6); + } + + break; + + case SOCK_CONSUMED: + // + // After user received data from socket buffer, socket will + // notify TCP using this message to give it a chance to send out + // window update information + // + ASSERT (Tcb != NULL); + TcpOnAppConsume (Tcb); + break; + + case SOCK_SND: + + ASSERT (Tcb != NULL); + TcpOnAppSend (Tcb); + break; + + case SOCK_CLOSE: + + TcpOnAppClose (Tcb); + + break; + + case SOCK_ABORT: + + TcpOnAppAbort (Tcb); + + break; + + case SOCK_SNDPUSH: + Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + break; + + case SOCK_SNDURG: + Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + + break; + + case SOCK_CONNECT: + + TcpOnAppConnect (Tcb); + + break; + + case SOCK_ATTACH: + + return TcpAttachPcb (Sock); + + break; + + case SOCK_FLUSH: + + TcpFlushPcb (Tcb); + + break; + + case SOCK_DETACH: + + TcpDetachPcb (Sock); + + break; + + case SOCK_CONFIGURE: + + return TcpConfigurePcb ( + Sock, + (TCP_CONFIG_DATA *) Data + ); + + break; + + case SOCK_MODE: + + ASSERT ((Data != NULL) && (Tcb != NULL)); + + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + + return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data); + } else { + + return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data); + } + + break; + + case SOCK_ROUTE: + + ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4)); + + return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data); + + default: + + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/TcpDxe/TcpDriver.c b/NetworkPkg/TcpDxe/TcpDriver.c new file mode 100644 index 0000000000..37e53af4b8 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDriver.c @@ -0,0 +1,891 @@ +/** @file + The driver binding and service binding protocol for the TCP driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +UINT16 mTcp4RandomPort; +UINT16 mTcp6RandomPort; + +TCP_HEARTBEAT_TIMER mTcpTimer = { + NULL, + 0 +}; + +EFI_TCP4_PROTOCOL gTcp4ProtocolTemplate = { + Tcp4GetModeData, + Tcp4Configure, + Tcp4Routes, + Tcp4Connect, + Tcp4Accept, + Tcp4Transmit, + Tcp4Receive, + Tcp4Close, + Tcp4Cancel, + Tcp4Poll +}; + +EFI_TCP6_PROTOCOL gTcp6ProtocolTemplate = { + Tcp6GetModeData, + Tcp6Configure, + Tcp6Connect, + Tcp6Accept, + Tcp6Transmit, + Tcp6Receive, + Tcp6Close, + Tcp6Cancel, + Tcp6Poll +}; + +SOCK_INIT_DATA mTcpDefaultSockData = { + SockStream, + SO_CLOSED, + NULL, + TCP_BACKLOG, + TCP_SND_BUF_SIZE, + TCP_RCV_BUF_SIZE, + IP_VERSION_4, + NULL, + TcpCreateSocketCallback, + TcpDestroySocketCallback, + NULL, + NULL, + 0, + TcpDispatcher, + NULL, +}; + +EFI_DRIVER_BINDING_PROTOCOL gTcpDriverBinding = { + TcpDriverBindingSupported, + TcpDriverBindingStart, + TcpDriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gTcpServiceBinding = { + TcpServiceBindingCreateChild, + TcpServiceBindingDestroyChild +}; + + +/** + Create and start the heartbeat timer for the TCP driver. + + @retval EFI_SUCCESS The timer was successfully created and started. + @retval other The timer was not created. + +**/ +EFI_STATUS +TcpCreateTimer ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (mTcpTimer.RefCnt == 0) { + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + TcpTicking, + NULL, + &mTcpTimer.TimerEvent + ); + if (!EFI_ERROR (Status)) { + + Status = gBS->SetTimer ( + mTcpTimer.TimerEvent, + TimerPeriodic, + (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ) + ); + } + } + + if (!EFI_ERROR (Status)) { + + mTcpTimer.RefCnt++; + } + + return Status; +} + +/** + Stop and destroy the heartbeat timer for TCP driver. + +**/ +VOID +TcpDestroyTimer ( + VOID + ) +{ + ASSERT (mTcpTimer.RefCnt > 0); + + mTcpTimer.RefCnt--; + + if (mTcpTimer.RefCnt > 0) { + return; + } + + gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0); + gBS->CloseEvent (mTcpTimer.TimerEvent); + mTcpTimer.TimerEvent = NULL; +} + +/** + The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS The driver loaded. + @retval other The driver did not load. + +**/ +EFI_STATUS +EFIAPI +TcpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINT32 Seed; + + // + // Install the TCP Driver Binding Protocol + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gTcpDriverBinding, + ImageHandle, + &gTcpComponentName, + &gTcpComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize ISS and random port. + // + Seed = NetRandomInitSeed (); + mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss; + mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN)); + mTcp6RandomPort = mTcp4RandomPort; + + return EFI_SUCCESS; +} + +/** + Create a new TCP4 or TCP6 driver service binding protocol + + @param[in] Controller Controller handle of device to bind driver to. + @param[in] ImageHandle The TCP driver's image handle. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new IP6 service binding private was created. + +**/ +EFI_STATUS +TcpCreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *IpServiceBindingGuid; + EFI_GUID *TcpServiceBindingGuid; + TCP_SERVICE_DATA *TcpServiceData; + IP_IO_OPEN_DATA OpenData; + + if (IpVersion == IP_VERSION_4) { + IpServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + IpServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + Controller, + TcpServiceBindingGuid, + NULL, + Image, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + Status = gBS->OpenProtocol ( + Controller, + IpServiceBindingGuid, + NULL, + Image, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Create the TCP service data. + // + TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA)); + if (TcpServiceData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TcpServiceData->Signature = TCP_DRIVER_SIGNATURE; + TcpServiceData->ControllerHandle = Controller; + TcpServiceData->DriverBindingHandle = Image; + TcpServiceData->IpVersion = IpVersion; + CopyMem ( + &TcpServiceData->ServiceBinding, + &gTcpServiceBinding, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion); + if (TcpServiceData->IpIo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + + InitializeListHead (&TcpServiceData->SocketList); + ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA)); + + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &OpenData.IpConfigData.Ip4CfgData, + &mIp4IoDefaultIpConfigData, + sizeof (EFI_IP4_CONFIG_DATA) + ); + OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + } else { + CopyMem ( + &OpenData.IpConfigData.Ip6CfgData, + &mIp6IoDefaultIpConfigData, + sizeof (EFI_IP6_CONFIG_DATA) + ); + OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + } + + OpenData.PktRcvdNotify = TcpRxCallback; + Status = IpIoOpen (TcpServiceData->IpIo, &OpenData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = TcpCreateTimer (); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + TcpServiceBindingGuid, + &TcpServiceData->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + TcpDestroyTimer (); + + goto ON_ERROR; + } + + TcpSetVariableData (TcpServiceData); + + return EFI_SUCCESS; + +ON_ERROR: + + if (TcpServiceData->IpIo != NULL) { + IpIoDestroy (TcpServiceData->IpIo); + } + + FreePool (TcpServiceData); + + return Status; +} + +/** + Destroy a TCP6 or TCP4 service binding instance. It will release all + the resources allocated by the instance. + + @param[in] Controller Controller handle of device to bind driver to. + @param[in] ImageHandle The TCP driver's image handle. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6 + + @retval EFI_SUCCESS The resources used by the instance were cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +TcpDestroyService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + IN UINTN NumberOfChildren, + IN UINT8 IpVersion + ) +{ + EFI_HANDLE NicHandle; + EFI_GUID *IpProtocolGuid; + EFI_GUID *ServiceBindingGuid; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + TCP_SERVICE_DATA *TcpServiceData; + EFI_STATUS Status; + SOCKET *Sock; + + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + if (IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid); + if (NicHandle == NULL) { + return EFI_NOT_FOUND; + } + + Status = gBS->OpenProtocol ( + NicHandle, + ServiceBindingGuid, + (VOID **) &ServiceBinding, + ImageHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding); + + if (NumberOfChildren == 0) { + // + // Uninstall TCP servicebinding protocol + // + gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + ServiceBindingGuid, + ServiceBinding, + NULL + ); + + // + // Destroy the IpIO consumed by TCP driver + // + IpIoDestroy (TcpServiceData->IpIo); + + // + // Destroy the heartbeat timer. + // + TcpDestroyTimer (); + + // + // Clear the variable. + // + TcpClearVariableData (TcpServiceData); + + // + // Release the TCP service data + // + FreePool (TcpServiceData); + } else { + + while (!IsListEmpty (&TcpServiceData->SocketList)) { + Sock = NET_LIST_HEAD (&TcpServiceData->SocketList, SOCKET, Link); + + ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle); + } + } + + return EFI_SUCCESS; +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + BOOLEAN IsTcp4Started; + + // + // Test for the Tcp4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Test for the Ip4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + IsTcp4Started = FALSE; + } else { + IsTcp4Started = TRUE; + } + + // + // Check the Tcp6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Test for the Ip6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + } else if (IsTcp4Started) { + return EFI_ALREADY_STARTED; + } + + return EFI_UNSUPPORTED; +} + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Tcp4Status; + EFI_STATUS Tcp6Status; + + Tcp4Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4); + if ((Tcp4Status == EFI_ALREADY_STARTED) || (Tcp4Status == EFI_UNSUPPORTED)) { + Tcp4Status = EFI_SUCCESS; + } + + Tcp6Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6); + if ((Tcp6Status == EFI_ALREADY_STARTED) || (Tcp6Status == EFI_UNSUPPORTED)) { + Tcp6Status = EFI_SUCCESS; + } + + if (!EFI_ERROR (Tcp4Status) || !EFI_ERROR (Tcp6Status)) { + return EFI_SUCCESS; + } else if (EFI_ERROR (Tcp4Status)) { + return Tcp4Status; + } else { + return Tcp6Status; + } +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Tcp4Status; + EFI_STATUS Tcp6Status; + + Tcp4Status = TcpDestroyService ( + ControllerHandle, + This->DriverBindingHandle, + NumberOfChildren, + IP_VERSION_4 + ); + + Tcp6Status = TcpDestroyService ( + ControllerHandle, + This->DriverBindingHandle, + NumberOfChildren, + IP_VERSION_6 + ); + + if (EFI_ERROR (Tcp4Status) && EFI_ERROR (Tcp6Status)) { + return EFI_DEVICE_ERROR; + } else { + return EFI_SUCCESS; + } +} + +/** + The Callback funtion called after the TCP socket was created. + + @param[in] This Pointer to the socket just created + @param[in] Context Context of the socket + + @retval EFI_SUCCESS This protocol installed successfully. + @retval other An error occured. + +**/ +EFI_STATUS +TcpCreateSocketCallback ( + IN SOCKET *This, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TCP_SERVICE_DATA *TcpServiceData; + EFI_GUID *IpProtocolGuid; + VOID *Ip; + + if (This->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService; + + // + // Open the default IP protocol of IP_IO BY_DRIVER. + // + Status = gBS->OpenProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + &Ip, + TcpServiceData->DriverBindingHandle, + This->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the device path on the handle where service binding resides on. + // + Status = gBS->OpenProtocol ( + TcpServiceData->ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &This->ParentDevicePath, + TcpServiceData->DriverBindingHandle, + This->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); + } else { + // + // Insert this socket into the SocketList. + // + InsertTailList (&TcpServiceData->SocketList, &This->Link); + } + + return Status; +} + +/** + The callback function called before the TCP socket was to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context of the socket. + +**/ +VOID +TcpDestroySocketCallback ( + IN SOCKET *This, + IN VOID *Context + ) +{ + TCP_SERVICE_DATA *TcpServiceData; + EFI_GUID *IpProtocolGuid; + + if (This->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService; + + // + // Remove this node from the list. + // + RemoveEntryList (&This->Link); + + // + // Close the device path protocol + // + gBS->CloseProtocol ( + TcpServiceData->ControllerHandle, + &gEfiDevicePathProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); + + // + // Close the IP protocol. + // + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); +} + +/** + Creates a child handle with a set of TCP services. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + SOCKET *Sock; + TCP_SERVICE_DATA *TcpServiceData; + TCP_PROTO_DATA TcpProto; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = EFI_SUCCESS; + TcpServiceData = TCP_SERVICE_FROM_THIS (This); + TcpProto.TcpService = TcpServiceData; + TcpProto.TcpPcb = NULL; + + // + // Create a tcp instance with defualt Tcp default + // sock init data and TcpProto + // + mTcpDefaultSockData.ProtoData = &TcpProto; + mTcpDefaultSockData.DataSize = sizeof (TCP_PROTO_DATA); + mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle; + mTcpDefaultSockData.IpVersion = TcpServiceData->IpVersion; + + if (TcpServiceData->IpVersion == IP_VERSION_4) { + mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate; + } else { + mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate; + } + + Sock = SockCreateChild (&mTcpDefaultSockData); + if (NULL == Sock) { + DEBUG ( + (EFI_D_ERROR, + "TcpDriverBindingCreateChild: No resource to create a Tcp Child\n") + ); + + Status = EFI_OUT_OF_RESOURCES; + } else { + *ChildHandle = Sock->SockHandle; + } + + mTcpDefaultSockData.ProtoData = NULL; + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Destroys a child handle with a set of TCP services. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to be destroyed. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + VOID *Tcp; + SOCKET *Sock; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // retrieve the Tcp4 protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp4ProtocolGuid, + &Tcp, + gTcpDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // No Tcp4, try the Tcp6 protocol + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp6ProtocolGuid, + &Tcp, + gTcpDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + } + } + + if (!EFI_ERROR (Status)) { + // + // destroy this sock and related Tcp protocol control + // block + // + Sock = SOCK_FROM_THIS (Tcp); + + SockDestroyChild (Sock); + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/NetworkPkg/TcpDxe/TcpDriver.h b/NetworkPkg/TcpDxe/TcpDriver.h new file mode 100644 index 0000000000..9de4be617d --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDriver.h @@ -0,0 +1,230 @@ +/** @file + The prototype of driver binding and service binding protocol for TCP driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _TCP_DRIVER_H_ +#define _TCP_DRIVER_H_ + +#define TCP_DRIVER_SIGNATURE SIGNATURE_32 ('T', 'C', 'P', 'D') + +#define TCP_PORT_KNOWN 1024 +#define TCP_PORT_USER_RESERVED 65535 + +typedef struct _TCP_HEARTBEAT_TIMER { + EFI_EVENT TimerEvent; + INTN RefCnt; +} TCP_HEARTBEAT_TIMER; + +typedef struct _TCP_SERVICE_DATA { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + EFI_HANDLE DriverBindingHandle; + UINT8 IpVersion; + IP_IO *IpIo; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + CHAR16 *MacString; + LIST_ENTRY SocketList; +} TCP_SERVICE_DATA; + +typedef struct _TCP_PROTO_DATA { + TCP_SERVICE_DATA *TcpService; + TCP_CB *TcpPcb; +} TCP_PROTO_DATA; + +#define TCP_SERVICE_FROM_THIS(a) \ + CR ( \ + (a), \ + TCP_SERVICE_DATA, \ + ServiceBinding, \ + TCP_DRIVER_SIGNATURE \ + ) + +// +// Function prototype for the driver's entry point +// + +/** + The entry point for Tcp driver, used to install Tcp driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS The driver loaded. + @retval other The driver did not load. + +**/ +EFI_STATUS +EFIAPI +TcpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Driver Binding Protocol +// + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of the device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver was added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + The Callback funtion called after the TCP socket is created. + + @param[in] This Pointer to the socket just created. + @param[in] Context The context of the socket. + + @retval EFI_SUCCESS This protocol is installed successfully. + @retval other An error occured. + +**/ +EFI_STATUS +TcpCreateSocketCallback ( + IN SOCKET *This, + IN VOID *Context + ); + +/** + The callback function called before the TCP socket is to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context of the socket. + +**/ +VOID +TcpDestroySocketCallback ( + IN SOCKET *This, + IN VOID *Context + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// + +/** + Creates a child handle with a set of TCP services. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of TCP services. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER The child handle is not a valid UEFI Handle. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpDxe.inf b/NetworkPkg/TcpDxe/TcpDxe.inf new file mode 100644 index 0000000000..67615388df --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDxe.inf @@ -0,0 +1,83 @@ +## @file TcpDxe.inf +# Component description file for Tcp module. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TcpDxe + FILE_GUID = 1A7E4468-2F55-4a56-903C-01265EB7622B + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TcpDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + TcpDriver.c + SockImpl.c + SockInterface.c + TcpDispatcher.c + TcpOutput.c + TcpMain.c + SockImpl.h + TcpMisc.c + TcpProto.h + TcpOption.c + TcpInput.c + TcpFunc.h + TcpOption.h + TcpTimer.c + TcpMain.h + Socket.h + ComponentName.c + TcpIo.c + TcpDriver.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + BaseMemoryLib + DevicePathLib + DebugLib + MemoryAllocationLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DpcLib + NetLib + IpIoLib + + +[Protocols] + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/NetworkPkg/TcpDxe/TcpFunc.h b/NetworkPkg/TcpDxe/TcpFunc.h new file mode 100644 index 0000000000..c23ba94e44 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpFunc.h @@ -0,0 +1,724 @@ +/** @file + Declaration of external functions shared in TCP driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _TCP_FUNC_H_ +#define _TCP_FUNC_H_ + +#include "TcpOption.h" + +#define TCP_COMP_VAL(Min, Max, Default, Val) \ + ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default)) + +/** + Timeout handler prototype. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +typedef +VOID +(*TCP_TIMER_HANDLER) ( + IN OUT TCP_CB *Tcb + ); + +// +// Functions in TcpMisc.c +// + +/** + Initialize the Tcb locally related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpInitTcbLocal ( + IN OUT TCP_CB *Tcb + ); + +/** + 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 information. + @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 + ); + +/** + Try to find one Tcb whose equals to . + + @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 pairs exists. + @retval FALSE Otherwise + +**/ +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IP_ADDRESS *Addr, + IN TCP_PORTNO Port, + IN UINT8 Version + ); + +/** + 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 + ); + +/** + 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 An error condition occurred. + +**/ +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ); + +/** + 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, an error condition occurred. + +**/ +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ); + +/** + Compute an ISS to be used by a new connection. + + @return The result ISS. + +**/ +TCP_SEQNO +TcpGetIss ( + VOID + ); + +/** + Get the local mss. + + @param[in] Sock Pointer to the socket to get mss. + + @return The mss size. + +**/ +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Translate the information from the head of the received TCP + segment Nbuf contains, 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Clear the variable and free the resource. + + @param[in] TcpService Tcp service data. + +**/ +VOID +TcpClearVariableData ( + IN TCP_SERVICE_DATA *TcpService + ); + +/** + 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 installed. + @retval other Failed to install the device path protocol. + +**/ +EFI_STATUS +TcpInstallDevicePath ( + IN SOCKET *Sock + ); + + +// +// Functions in TcpOutput.c +// + +/** + Compute the sequence space left in the old receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence space left in the old receive window. + +**/ +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ); + +/** + Compute the current receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The size of the current receive window, in bytes. + +**/ +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ); + +/** + Get the maximum SndNxt. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence number of the maximum SndNxt. + +**/ +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ); + +/** + Compute how much data to send. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The length of the data that can be sent. If 0, no data can be sent. + +**/ +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ); + +/** + Retransmit the segment from sequence Seq. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment to be retransmitted. + + @retval 0 The retransmission succeeded. + @retval -1 An error condition occurred. + +**/ +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ); + +/** + Check whether to send data/SYN/FIN and piggyback an ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN OUT TCP_CB *Tcb, + IN INTN Force + ); + +/** + Check whether to send an ACK or delayed ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpToSendAck ( + IN OUT TCP_CB *Tcb + ); + +/** + Send an ACK immediately. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSendAck ( + IN OUT TCP_CB *Tcb + ); + +/** + Send a zero probe segment. It can be used by keepalive and zero window probe. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other An error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN OUT TCP_CB *Tcb + ); + +/** + Send a RESET segment in response to the segment received. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance, may be NULL. + @param[in] Head TCP header of the segment that triggers the reset. + @param[in] Len Length of the segment that triggers the reset. + @param[in] Local Local IP address. + @param[in] Remote Remote peer's 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. + + @retval 0 A reset is sent or no need to send it. + @retval -1 No reset is sent. + +**/ +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN EFI_IP_ADDRESS *Local, + IN EFI_IP_ADDRESS *Remote, + IN UINT8 Version + ); + +/** + Verify that the segment is in good shape. + + @param[in] Nbuf Buffer that contains the segment to be checked. + + @retval 0 The segment is broken. + @retval 1 The segment is in good shape. + +**/ +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ); + +// +// Functions from TcpInput.c +// + +/** + Process the received ICMP error messages for TCP. + + @param[in] Nbuf Buffer that contains part of the TCP segment without IP header + truncated from the ICMP error packet. + @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet. + @param[in] Src Source address of the ICMP error message. + @param[in] Dst Destination address of the ICMP error message. + @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates + IP6 stack. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN UINT8 IcmpErr, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ); + +/** + Process the received TCP segments. + + @param[in] Nbuf Buffer that contains received TCP segment without an IP header. + @param[in] Src Source address of the segment, or the peer's IP address. + @param[in] Dst Destination address of the segment, or the local end's IP + address. + @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates + IP6 stack. + + @retval 0 The segment processed successfully. It is either accepted or + discarded. But no connection is reset by the segment. + @retval -1 A connection is reset by the segment. + +**/ +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ); + +// +// Functions in TcpTimer.c +// + +/** + Close the TCP connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClose ( + IN OUT TCP_CB *Tcb + ); + +/** + Heart beat timer handler, queues the DPC at TPL_CALLBACK. + + @param[in] Event Timer event signaled, ignored. + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Enable a TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be enabled. + @param[in] TimeOut The timeout value of this timer. + +**/ +VOID +TcpSetTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ); + +/** + Clear one TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be cleared. + +**/ +VOID +TcpClearTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer + ); + +/** + Clear all TCP timers. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClearAllTimer ( + IN OUT TCP_CB *Tcb + ); + +/** + Enable the window prober timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetProbeTimer ( + IN OUT TCP_CB *Tcb + ); + +/** + Enable the keepalive timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN OUT TCP_CB *Tcb + ); + +// +// Functions in TcpIo.c +// + +/** + Packet receive callback function provided to IP_IO. Used to call + the proper function to handle the packet received by IP. + + @param[in] Status Result of the receive request. + @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR. + @param[in] NetSession The IP session for the received packet. + @param[in] Pkt Packet received. + @param[in] Context The data provided by the user for the received packet when + the callback is registered in IP_IO_OPEN_DATA::RcvdContext. + This is an optional parameter that may be NULL. + +**/ +VOID +EFIAPI +TcpRxCallback ( + IN EFI_STATUS Status, + IN UINT8 IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ); + +/** + Send the segment to IP via IpIo function. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the TCP segment to be sent. + @param[in] Src Source address of the TCP segment. + @param[in] Dest Destination address of the TCP segment. + @param[in] Version IP_VERSION_4 or IP_VERSION_6 + + @retval 0 The segment was sent out successfully. + @retval -1 The segment failed to be sent. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dest, + IN UINT8 Version + ); + +/** + Refresh the remote peer's Neighbor Cache State if already exists. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Neighbor Source address of the TCP segment. + @param[in] Timeout Time in 100-ns units that this entry will remain + in the neighbor cache. A value of zero means that + the entry is permanent. A value of non-zero means + that the entry is dynamic and will be deleted + after Timeout. + + @retval EFI_SUCCESS Successfully updated the neighbor relationship. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_NOT_FOUND This entry is not in the neighbor table. + +**/ +EFI_STATUS +Tcp6RefreshNeighbor ( + IN TCP_CB *Tcb, + IN EFI_IP_ADDRESS *Neighbor, + IN UINT32 Timeout + ); + +// +// Functions in TcpDispatcher.c +// + +/** + The procotol handler provided to the socket layer, used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param[in] Sock Pointer to the socket of this TCP instance. + @param[in] Request The code of this operation request. + @param[in] Data Pointer to the operation specific data passed in + together with the operation request. This is an + optional parameter that may be NULL. + + @retval EFI_SUCCESS The socket request completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +TcpDispatcher ( + IN SOCKET *Sock, + IN UINT8 Request, + IN VOID *Data OPTIONAL + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpInput.c b/NetworkPkg/TcpDxe/TcpInput.c new file mode 100644 index 0000000000..e63469adb9 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpInput.c @@ -0,0 +1,1592 @@ +/** @file + TCP input process routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +/** + Check whether the sequence number of the incoming segment is acceptable. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Pointer to the incoming segment. + + @retval 1 The sequence number is acceptable. + @retval 0 The sequence number is not acceptable. + +**/ +INTN +TcpSeqAcceptable ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) && + TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd)); +} + +/** + NewReno fast recovery defined in RFC3782. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Segment that triggers the fast recovery. + +**/ +VOID +TcpFastRecover ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + UINT32 FlightSize; + UINT32 Acked; + + // + // Step 1: Three duplicate ACKs and not in fast recovery + // + if (Tcb->CongestState != TCP_CONGEST_RECOVER) { + + // + // Step 1A: Invoking fast retransmission. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss)); + Tcb->Recover = Tcb->SndNxt; + + Tcb->CongestState = TCP_CONGEST_RECOVER; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + + // + // Step 2: Entering fast retransmission + // + TcpRetransmit (Tcb, Tcb->SndUna); + Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss; + + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: enter fast retransmission for TCB %p, recover point is %d\n", + Tcb, + Tcb->Recover) + ); + return; + } + + // + // During fast recovery, execute Step 3, 4, 5 of RFC3782 + // + if (Seg->Ack == Tcb->SndUna) { + + // + // Step 3: Fast Recovery, + // If this is a duplicated ACK, increse Cwnd by SMSS. + // + + // Step 4 is skipped here only to be executed later + // by TcpToSendData + // + Tcb->CWnd += Tcb->SndMss; + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received another duplicated ACK (%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } else { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) { + + // + // Step 5 - Full ACK: + // deflate the congestion window, and exit fast recovery + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->CWnd = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss); + + Tcb->CongestState = TCP_CONGEST_OPEN; + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received a full ACK(%d) for TCB %p, exit fast recovery\n", + Seg->Ack, + Tcb) + ); + + } else { + + // + // Step 5 - Partial ACK: + // fast retransmit the first unacknowledge field + // , then deflate the CWnd + // + TcpRetransmit (Tcb, Seg->Ack); + Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna); + + // + // Deflate the CWnd by the amount of new data + // ACKed by SEG.ACK. If more than one SMSS data + // is ACKed, add back SMSS byte to CWnd after + // + if (Acked >= Tcb->SndMss) { + Acked -= Tcb->SndMss; + + } + + Tcb->CWnd -= Acked; + + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received a partial ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } + } +} + +/** + NewReno fast loss recovery defined in RFC3792. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Segment that triggers the fast loss recovery. + +**/ +VOID +TcpFastLossRecover ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) { + + // + // Full ACK: exit the loss recovery. + // + Tcb->LossTimes = 0; + Tcb->CongestState = TCP_CONGEST_OPEN; + + DEBUG ( + (EFI_D_INFO, + "TcpFastLossRecover: received a full ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } else { + + // + // Partial ACK: + // fast retransmit the first unacknowledge field. + // + TcpRetransmit (Tcb, Seg->Ack); + DEBUG ( + (EFI_D_INFO, + "TcpFastLossRecover: received a partial ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + } + } +} + +/** + Compute the RTT as specified in RFC2988. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Measure Currently measured RTT in heartbeats. + +**/ +VOID +TcpComputeRtt ( + IN OUT TCP_CB *Tcb, + IN UINT32 Measure + ) +{ + INT32 Var; + + // + // Step 2.3: Compute the RTO for subsequent RTT measurement. + // + if (Tcb->SRtt != 0) { + + Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT); + + if (Var < 0) { + Var = -Var; + } + + Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2; + Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure; + + } else { + // + // Step 2.2: compute the first RTT measure + // + Tcb->SRtt = Measure << TCP_RTT_SHIFT; + Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1); + } + + Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT; + + // + // Step 2.4: Limit the RTO to at least 1 second + // Step 2.5: Limit the RTO to a maxium value that + // is at least 60 second + // + if (Tcb->Rto < TCP_RTO_MIN) { + Tcb->Rto = TCP_RTO_MIN; + + } else if (Tcb->Rto > TCP_RTO_MAX) { + Tcb->Rto = TCP_RTO_MAX; + + } + + DEBUG ( + (EFI_D_INFO, + "TcpComputeRtt: new RTT for TCB %p computed SRTT: %d RTTVAR: %d RTO: %d\n", + Tcb, + Tcb->SRtt, + Tcb->RttVar, + Tcb->Rto) + ); + +} + +/** + Trim the data; SYN and FIN to fit into the window defined by Left and Right. + + @param[in] Nbuf The buffer that contains a received TCP segment without an IP header. + @param[in] Left The sequence number of the window's left edge. + @param[in] Right The sequence number of the window's right edge. + +**/ +VOID +TcpTrimSegment ( + IN NET_BUF *Nbuf, + IN TCP_SEQNO Left, + IN TCP_SEQNO Right + ) +{ + TCP_SEG *Seg; + TCP_SEQNO Urg; + UINT32 Drop; + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // If the segment is completely out of window, + // truncate every thing, include SYN and FIN. + // + if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + + Seg->Seq = Seg->End; + NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD); + return; + } + + // + // Adjust the buffer header + // + if (TCP_SEQ_LT (Seg->Seq, Left)) { + + Drop = TCP_SUB_SEQ (Left, Seg->Seq); + Urg = Seg->Seq + Seg->Urg; + Seg->Seq = Left; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + Drop--; + } + + // + // Adjust the urgent point + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) { + + if (TCP_SEQ_LT (Urg, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + } else { + Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq); + } + } + + if (Drop != 0) { + NetbufTrim (Nbuf, Drop, NET_BUF_HEAD); + } + } + + // + // Adjust the buffer tail + // + if (TCP_SEQ_GT (Seg->End, Right)) { + + Drop = TCP_SUB_SEQ (Seg->End, Right); + Seg->End = Right; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + Drop--; + } + + if (Drop != 0) { + NetbufTrim (Nbuf, Drop, NET_BUF_TAIL); + } + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); +} + +/** + Trim off the data outside the tcb's receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the NET_BUF containing the received tcp segment. + +**/ +VOID +TcpTrimInWnd ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd); +} + +/** + Process the data and FIN flag, and check whether to deliver + data to the socket layer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 No error occurred to deliver data. + @retval -1 An error condition occurred. The proper response is to reset the + connection. + +**/ +INTN +TcpDeliverData ( + IN OUT TCP_CB *Tcb + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + TCP_SEG *Seg; + UINT32 Urgent; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + // + // make sure there is some data queued, + // and TCP is in a proper state + // + if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) { + + return 0; + } + + // + // Deliver data to the socket layer + // + Entry = Tcb->RcvQue.ForwardLink; + Seq = Tcb->RcvNxt; + + while (Entry != &Tcb->RcvQue) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seg = TCPSEG_NETBUF (Nbuf); + + ASSERT (TcpVerifySegment (Nbuf) != 0); + ASSERT (Nbuf->Tcp == NULL); + + if (TCP_SEQ_GT (Seg->Seq, Seq)) { + break; + } + + Entry = Entry->ForwardLink; + Seq = Seg->End; + Tcb->RcvNxt = Seq; + + RemoveEntryList (&Nbuf->List); + + // + // RFC793 Eighth step: process FIN in sequence + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + // + // The peer sends to us junky data after FIN, + // reset the connection. + // + if (!IsListEmpty (&Tcb->RcvQue)) { + DEBUG ( + (EFI_D_ERROR, + "TcpDeliverData: data received after FIN from peer of TCB %p, reset connection\n", + Tcb) + ); + + NetbufFree (Nbuf); + return -1; + } + + DEBUG ( + (EFI_D_INFO, + "TcpDeliverData: processing FIN from peer of TCB %p\n", + Tcb) + ); + + switch (Tcb->State) { + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + + TcpSetState (Tcb, TCP_CLOSE_WAIT); + break; + + case TCP_FIN_WAIT_1: + + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSING); + break; + } + + // + // fall through + // + case TCP_FIN_WAIT_2: + + TcpSetState (Tcb, TCP_TIME_WAIT); + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\n", + Tcb) + ); + + TcpSendAck (Tcb); + TcpClose (Tcb); + } + break; + + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + // + // The peer sends to us junk FIN byte. Discard + // the buffer then reset the connection + // + NetbufFree (Nbuf); + return -1; + break; + default: + break; + } + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + Seg->End--; + } + + // + // Don't delay the ack if PUSH flag is on. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + if (Nbuf->TotalSize != 0) { + Urgent = 0; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp) + ) { + + if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) { + Urgent = Nbuf->TotalSize; + } else { + Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1; + } + } + + SockDataRcvd (Tcb->Sk, Nbuf, Urgent); + } + + if (TCP_FIN_RCVD (Tcb->State)) { + + SockNoMoreData (Tcb->Sk); + } + + NetbufFree (Nbuf); + } + + return 0; +} + +/** + Store the data into the reassemble queue. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer containing the data to be queued. + +**/ +VOID +TcpQueueData ( + IN OUT TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + LIST_ENTRY *Head; + LIST_ENTRY *Prev; + LIST_ENTRY *Cur; + NET_BUF *Node; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + + NET_GET_REF (Nbuf); + + Seg = TCPSEG_NETBUF (Nbuf); + Head = &Tcb->RcvQue; + + // + // Fast path to process normal case. That is, + // no out-of-order segments are received. + // + if (IsListEmpty (Head)) { + + InsertTailList (Head, &Nbuf->List); + return; + } + + // + // Find the point to insert the buffer + // + for (Prev = Head, Cur = Head->ForwardLink; + Cur != Head; + Prev = Cur, Cur = Cur->ForwardLink + ) { + + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) { + break; + } + } + + // + // Check whether the current segment overlaps with the + // previous segment. + // + if (Prev != Head) { + Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) { + + if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) { + + NetbufFree (Nbuf); + return; + } + + TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End); + } + } + + InsertHeadList (Prev, &Nbuf->List); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + // + // Check the segments after the insert point. + // + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) { + + Cur = Cur->ForwardLink; + + RemoveEntryList (&Node->List); + NetbufFree (Node); + continue; + } + + if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) { + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) { + + RemoveEntryList (&Nbuf->List); + NetbufFree (Nbuf); + return; + } + + TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq); + break; + } + + Cur = Cur->ForwardLink; + } +} + + +/** + Adjust the send queue or the retransmit queue. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Ack The acknowledge seuqence number of the received segment. + +**/ +VOID +TcpAdjustSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Ack + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + + Head = &Tcb->SndQue; + Cur = Head->ForwardLink; + + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_GEQ (Seg->Seq, Ack)) { + break; + } + + // + // Remove completely ACKed segments + // + if (TCP_SEQ_LEQ (Seg->End, Ack)) { + Cur = Cur->ForwardLink; + + RemoveEntryList (&Node->List); + NetbufFree (Node); + continue; + } + + TcpTrimSegment (Node, Ack, Seg->End); + break; + } +} + +/** + Process the received TCP segments. + + @param[in] Nbuf Buffer that contains received a TCP segment without an IP header. + @param[in] Src Source address of the segment, or the peer's IP address. + @param[in] Dst Destination address of the segment, or the local end's IP + address. + @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates + IP6 stack. + + @retval 0 Segment processed successfully. It is either accepted or + discarded. However, no connection is reset by the segment. + @retval -1 A connection is reset by the segment. + +**/ +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ) +{ + TCP_CB *Tcb; + TCP_CB *Parent; + TCP_OPTION Option; + TCP_HEAD *Head; + INT32 Len; + TCP_SEG *Seg; + TCP_SEQNO Right; + TCP_SEQNO Urg; + UINT16 Checksum; + + ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6)); + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Parent = NULL; + Tcb = NULL; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != NULL); + Len = Nbuf->TotalSize - (Head->HeadLen << 2); + + if ((Head->HeadLen < 5) || (Len < 0)) { + + DEBUG ((EFI_D_INFO, "TcpInput: received an mal-formated packet\n")); + goto DISCARD; + } + + if (Version == IP_VERSION_4) { + Checksum = NetPseudoHeadChecksum (Src->Addr[0], Dst->Addr[0], 6, 0); + } else { + Checksum = NetIp6PseudoHeadChecksum (&Src->v6, &Dst->v6, 6, 0); + } + + Checksum = TcpChecksum (Nbuf, Checksum); + + if (Checksum != 0) { + DEBUG ((EFI_D_ERROR, "TcpInput: received a checksum error packet\n")); + goto DISCARD; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) { + Len++; + } + + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + Version, + (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN) + ); + + if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) { + DEBUG ((EFI_D_INFO, "TcpInput: send reset because no TCB find\n")); + + Tcb = NULL; + goto SEND_RESET; + } + + Seg = TcpFormatNetbuf (Tcb, Nbuf); + + // + // RFC1122 recommended reaction to illegal option + // (in fact, an illegal option length) is reset. + // + if (TcpParseOption (Nbuf->Tcp, &Option) == -1) { + DEBUG ( + (EFI_D_ERROR, + "TcpInput: reset the peer because of mal-format option for Tcb %p\n", + Tcb) + ); + + goto SEND_RESET; + } + + // + // From now on, the segment is headless + // + NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + // + // Process the segment in LISTEN state. + // + if (Tcb->State == TCP_LISTEN) { + // + // First step: Check RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard a reset segment for TCB %p in listening\n", + Tcb) + ); + + goto DISCARD; + } + + // + // Second step: Check ACK. + // Any ACK sent to TCP in LISTEN is reseted. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of segment with ACK for TCB %p in listening\n", + Tcb) + ); + + goto SEND_RESET; + } + + // + // Third step: Check SYN + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + // + // create a child TCB to handle the data + // + Parent = Tcb; + + Tcb = TcpCloneTcb (Parent); + if (Tcb == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpInput: discard a segment because failed to clone a child for TCB%p\n", + Tcb) + ); + + goto DISCARD; + } + + DEBUG ( + (EFI_D_INFO, + "TcpInput: create a child for TCB %p in listening\n", + Tcb) + ); + + // + // init the TCB structure + // + IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, Dst); + IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, Src); + Tcb->LocalEnd.Port = Head->DstPort; + Tcb->RemoteEnd.Port = Head->SrcPort; + + TcpInitTcbLocal (Tcb); + TcpInitTcbPeer (Tcb, Seg, &Option); + + TcpSetState (Tcb, TCP_SYN_RCVD); + TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout); + TcpTrimInWnd (Tcb, Nbuf); + + goto StepSix; + } + + goto DISCARD; + + } else if (Tcb->State == TCP_SYN_SENT) { + // + // First step: Check ACK bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of wrong ACK received for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto SEND_RESET; + } + + // + // Second step: Check RST bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset by peer for TCB %p in SYN_SENT\n", + Tcb) + ); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto DROP_CONNECTION; + } else { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard a reset segment because of no ACK for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto DISCARD; + } + } + + // + // Third step: Check security and precedence. Skipped + // + + // + // Fourth step: Check SYN. Pay attention to sitimulatous open + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + TcpInitTcbPeer (Tcb, Seg, &Option); + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + Tcb->SndUna = Seg->Ack; + } + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + + if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) { + + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON) + ) { + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + TcpTrimInWnd (Tcb, Nbuf); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + DEBUG ( + (EFI_D_INFO, + "TcpInput: connection established for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto StepSix; + } else { + // + // Received a SYN segment without ACK, simultanous open. + // + TcpSetState (Tcb, TCP_SYN_RCVD); + + ASSERT (Tcb->SndNxt == Tcb->Iss + 1); + TcpAdjustSndQue (Tcb, Tcb->SndNxt); + + TcpTrimInWnd (Tcb, Nbuf); + + DEBUG ( + (EFI_D_WARN, + "TcpInput: simultanous open for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto StepSix; + } + } + + goto DISCARD; + } + + // + // Process segment in SYN_RCVD or TCP_CONNECTED states + // + + // + // Clear probe timer since the RecvWindow is opened. + // + if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) { + TcpClearTimer (Tcb, TCP_TIMER_PROBE); + Tcb->ProbeTimerOn = FALSE; + } + + // + // First step: Check whether SEG.SEQ is acceptable + // + if (TcpSeqAcceptable (Tcb, Seg) == 0) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: sequence acceptance test failed for segment of TCB %p\n", + Tcb) + ); + + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + TcpSendAck (Tcb); + } + + goto DISCARD; + } + + if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) && + (Tcb->RcvWl2 == Seg->End) && + !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN) + ) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + // + // Second step: Check the RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb)); + + if (Tcb->State == TCP_SYN_RCVD) { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED); + + // + // This TCB comes from either a LISTEN TCB, + // or active open TCB with simultanous open. + // Do NOT signal user CONNECTION refused + // if it comes from a LISTEN TCB. + // + } else if ((Tcb->State == TCP_ESTABLISHED) || + (Tcb->State == TCP_FIN_WAIT_1) || + (Tcb->State == TCP_FIN_WAIT_2) || + (Tcb->State == TCP_CLOSE_WAIT) + ) { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + + } else { + } + + goto DROP_CONNECTION; + } + + // + // Trim the data and flags. + // + TcpTrimInWnd (Tcb, Nbuf); + + // + // Third step: Check security and precedence, Ignored + // + + // + // Fourth step: Check the SYN bit. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because received extra SYN for TCB %p\n", + Tcb) + ); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto RESET_THEN_DROP; + } + // + // Fifth step: Check the ACK + // + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: segment discard because of no ACK for connected TCB %p\n", + Tcb) + ); + + goto DISCARD; + } else { + if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick == 0) { + Tcp6RefreshNeighbor (Tcb, Src, TCP6_KEEP_NEIGHBOR_TIME * TICKS_PER_SECOND); + Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK; + } + } + + if (Tcb->State == TCP_SYN_RCVD) { + + if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) { + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + DEBUG ( + (EFI_D_INFO, + "TcpInput: connection established for TCB %p in SYN_RCVD\n", + Tcb) + ); + + // + // Continue the process as ESTABLISHED state + // + } else { + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of wrong ACK for TCB %p in SYN_RCVD\n", + Tcb) + ); + + goto SEND_RESET; + } + } + + if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: ignore the out-of-data ACK for connected TCB %p\n", + Tcb) + ); + + goto StepSix; + + } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard segment for future ACK for connected TCB %p\n", + Tcb) + ); + + TcpSendAck (Tcb); + goto DISCARD; + } + + // + // From now on: SND.UNA <= SEG.ACK <= SND.NXT. + // + if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) { + // + // update TsRecent as specified in page 16 RFC1323. + // RcvWl2 equals to the variable "LastAckSent" + // defined there. + // + if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) { + + Tcb->TsRecent = Option.TSVal; + Tcb->TsRecentAge = mTcpTick; + } + + TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr)); + + } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN); + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + if (Seg->Ack == Tcb->SndNxt) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + } else { + + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Count duplicate acks. + // + if ((Seg->Ack == Tcb->SndUna) && + (Tcb->SndUna != Tcb->SndNxt) && + (Seg->Wnd == Tcb->SndWnd) && + (0 == Len) + ) { + + Tcb->DupAck++; + } else { + + Tcb->DupAck = 0; + } + + // + // Congestion avoidance, fast recovery and fast retransmission. + // + if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) || + (Tcb->CongestState == TCP_CONGEST_LOSS) + ) { + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + if (Tcb->CWnd < Tcb->Ssthresh) { + + Tcb->CWnd += Tcb->SndMss; + } else { + + Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1); + } + + Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale); + } + + if (Tcb->CongestState == TCP_CONGEST_LOSS) { + TcpFastLossRecover (Tcb, Seg); + } + } else { + + TcpFastRecover (Tcb, Seg); + } + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + TcpAdjustSndQue (Tcb, Seg->Ack); + Tcb->SndUna = Seg->Ack; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && + TCP_SEQ_LT (Tcb->SndUp, Seg->Ack) + ) { + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + } + } + + // + // Update window info + // + if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) || + ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack)) + ) { + + Right = Seg->Ack + Seg->Wnd; + + if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) { + + if ((Tcb->SndWl1 == Seg->Seq) && + (Tcb->SndWl2 == Seg->Ack) && + (Len == 0) + ) { + + goto NO_UPDATE; + } + + DEBUG ( + (EFI_D_WARN, + "TcpInput: peer shrinks the window for connected TCB %p\n", + Tcb) + ); + + if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && (TCP_SEQ_LT (Right, Tcb->Recover))) { + + Tcb->Recover = Right; + } + + if ((Tcb->CongestState == TCP_CONGEST_LOSS) && (TCP_SEQ_LT (Right, Tcb->LossRecover))) { + + Tcb->LossRecover = Right; + } + + if (TCP_SEQ_LT (Right, Tcb->SndNxt)) { + + Tcb->SndNxt = Right; + + if (Right == Tcb->SndUna) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + TcpSetProbeTimer (Tcb); + } + } + } + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + } + +NO_UPDATE: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && (Tcb->SndUna == Tcb->SndNxt)) { + + DEBUG ( + (EFI_D_INFO, + "TcpInput: local FIN is ACKed by peer for connected TCB %p\n", + Tcb) + ); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED); + } + + // + // Transit the state if proper. + // + switch (Tcb->State) { + case TCP_FIN_WAIT_1: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_FIN_WAIT_2); + + TcpClearAllTimer (Tcb); + TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout); + } + + case TCP_FIN_WAIT_2: + + break; + + case TCP_CLOSE_WAIT: + break; + + case TCP_CLOSING: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_TIME_WAIT); + + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\n", + Tcb) + ); + + TcpClose (Tcb); + } + } + break; + + case TCP_LAST_ACK: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSED); + } + + break; + + case TCP_TIME_WAIT: + + TcpSendAck (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\n", + Tcb) + ); + + TcpClose (Tcb); + } + break; + + default: + break; + } + // + // Sixth step: Check the URG bit.update the Urg point + // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact. + // +StepSix: + + Tcb->Idle = 0; + TcpSetKeepaliveTimer (Tcb); + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && !TCP_FIN_RCVD (Tcb->State)) { + + DEBUG ( + (EFI_D_INFO, + "TcpInput: received urgent data from peer for connected TCB %p\n", + Tcb) + ); + + Urg = Seg->Seq + Seg->Urg; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && TCP_SEQ_GT (Urg, Tcb->RcvUp)) { + + Tcb->RcvUp = Urg; + } else { + + Tcb->RcvUp = Urg; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG); + } + } + // + // Seventh step: Process the segment data + // + if (Seg->End != Seg->Seq) { + + if (TCP_FIN_RCVD (Tcb->State)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because data is lost for connected TCB %p\n", + Tcb) + ); + + goto RESET_THEN_DROP; + } + + if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because data is lost for connected TCB %p\n", + Tcb) + ); + + goto RESET_THEN_DROP; + } + + TcpQueueData (Tcb, Nbuf); + if (TcpDeliverData (Tcb) == -1) { + goto RESET_THEN_DROP; + } + + if (!IsListEmpty (&Tcb->RcvQue)) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + } + + // + // Eighth step: check the FIN. + // This step is moved to TcpDeliverData. FIN will be + // processed in sequence there. Check the comments in + // the beginning of the file header for information. + // + + // + // Tcb is a new child of the listening Parent, + // commit it. + // + if (Parent != NULL) { + Tcb->Parent = Parent; + TcpInsertTcb (Tcb); + } + + if ((Tcb->State != TCP_CLOSED) && + (TcpToSendData (Tcb, 0) == 0) && + (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0)) + ) { + + TcpToSendAck (Tcb); + } + + NetbufFree (Nbuf); + return 0; + +RESET_THEN_DROP: + TcpSendReset (Tcb, Head, Len, Dst, Src, Version); + +DROP_CONNECTION: + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + NetbufFree (Nbuf); + TcpClose (Tcb); + + return -1; + +SEND_RESET: + + TcpSendReset (Tcb, Head, Len, Dst, Src, Version); + +DISCARD: + + // + // Tcb is a child of Parent, and it doesn't survive + // + DEBUG ((EFI_D_WARN, "TcpInput: Discard a packet\n")); + NetbufFree (Nbuf); + + if ((Parent != NULL) && (Tcb != NULL)) { + + ASSERT (Tcb->Sk != NULL); + TcpClose (Tcb); + } + + return 0; +} + +/** + Process the received ICMP error messages for TCP. + + @param[in] Nbuf The buffer that contains part of the TCP segment without an IP header + truncated from the ICMP error packet. + @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet. + @param[in] Src Source address of the ICMP error message. + @param[in] Dst Destination address of the ICMP error message. + @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates + IP6 stack. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN UINT8 IcmpErr, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ) +{ + TCP_HEAD *Head; + TCP_CB *Tcb; + TCP_SEQNO Seq; + EFI_STATUS IcmpErrStatus; + BOOLEAN IcmpErrIsHard; + BOOLEAN IcmpErrNotify; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != NULL); + + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + Version, + FALSE + ); + if (Tcb == NULL || Tcb->State == TCP_CLOSED) { + + goto CLEAN_EXIT; + } + + // + // Validate the sequence number. + // + Seq = NTOHL (Head->Seq); + if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) { + + goto CLEAN_EXIT; + } + + IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, Tcb->Sk->IpVersion, &IcmpErrIsHard, &IcmpErrNotify); + + if (IcmpErrNotify) { + + SOCK_ERROR (Tcb->Sk, IcmpErrStatus); + } + + if (IcmpErrIsHard) { + + TcpClose (Tcb); + } + +CLEAN_EXIT: + + NetbufFree (Nbuf); +} diff --git a/NetworkPkg/TcpDxe/TcpIo.c b/NetworkPkg/TcpDxe/TcpIo.c new file mode 100644 index 0000000000..cecb6d19c5 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpIo.c @@ -0,0 +1,190 @@ +/** @file + Implementation of I/O interfaces between TCP and IpIoLib. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +/** + Packet receive callback function provided to IP_IO, used to call + the proper function to handle the packet received by IP. + + @param[in] Status Result of the receive request. + @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR. + @param[in] NetSession The IP session for the received packet. + @param[in] Pkt Packet received. + @param[in] Context The data provided by the user for the received packet when + the callback is registered in IP_IO_OPEN_DATA::RcvdContext. + This is an optional parameter that may be NULL. + +**/ +VOID +EFIAPI +TcpRxCallback ( + IN EFI_STATUS Status, + IN UINT8 IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ) +{ + if (EFI_SUCCESS == Status) { + TcpInput (Pkt, &NetSession->Source, &NetSession->Dest, NetSession->IpVersion); + } else { + TcpIcmpInput ( + Pkt, + IcmpErr, + &NetSession->Source, + &NetSession->Dest, + NetSession->IpVersion + ); + } +} + +/** + Send the segment to IP via IpIo function. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the TCP segment to be sent. + @param[in] Src Source address of the TCP segment. + @param[in] Dest Destination address of the TCP segment. + @param[in] Version IP_VERSION_4 or IP_VERSION_6 + + @retval 0 The segment was sent out successfully. + @retval -1 The segment failed to send. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dest, + IN UINT8 Version + ) +{ + EFI_STATUS Status; + IP_IO *IpIo; + IP_IO_OVERRIDE Override; + SOCKET *Sock; + VOID *IpSender; + TCP_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + + IpIo = NULL; + IpSender = IpIoFindSender (&IpIo, Version, Src); + + if (IpSender == NULL) { + DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n")); + return -1; + } + + if (Version == IP_VERSION_6) { + // + // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the + // destination address if the dest is already specified through the + // configuration data. Here we get the IpIo we need and use the default IP + // instance in this IpIo to send the packet. The dest address is configured + // to be the unspecified address for the default IP instance. + // + IpSender = NULL; + } + } else { + + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + IpSender = Tcb->IpInfo; + + if (Version == IP_VERSION_6) { + // + // It's IPv6 and this TCP segment belongs to a solid TCB, in such case + // the destination address can't be overridden, so reset the Dest to NULL. + // + Dest = NULL; + } + } + + ASSERT (Version == IpIo->IpVersion); + + if (Version == IP_VERSION_4) { + Override.Ip4OverrideData.TypeOfService = 0; + Override.Ip4OverrideData.TimeToLive = 255; + Override.Ip4OverrideData.DoNotFragment = FALSE; + Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_TCP; + ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS)); + } else { + Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_TCP; + Override.Ip6OverrideData.HopLimit = 255; + Override.Ip6OverrideData.FlowLabel = 0; + } + + Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status)); + return -1; + } + + return 0; +} + +/** + Refresh the remote peer's Neighbor Cache State if already exists. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Neighbor Source address of the TCP segment. + @param[in] Timeout Time in 100-ns units that this entry will remain + in the neighbor cache. A value of zero means that + the entry is permanent. A value of non-zero means + that the entry is dynamic and will be deleted + after Timeout. + + @retval EFI_SUCCESS Successfully updated the neighbor relationship. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_NOT_FOUND This entry is not in the neighbor table. + +**/ +EFI_STATUS +Tcp6RefreshNeighbor ( + IN TCP_CB *Tcb, + IN EFI_IP_ADDRESS *Neighbor, + IN UINT32 Timeout + ) +{ + IP_IO *IpIo; + SOCKET *Sock; + TCP_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + IpIo = NULL; + IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor); + + if (IpIo == NULL) { + DEBUG ((EFI_D_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n")); + return EFI_NOT_STARTED; + } + + } else { + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + } + + return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout); +} + diff --git a/NetworkPkg/TcpDxe/TcpMain.c b/NetworkPkg/TcpDxe/TcpMain.c new file mode 100644 index 0000000000..55a6d5b7d2 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpMain.c @@ -0,0 +1,1074 @@ +/** @file + Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +/** + Check the integrity of the data buffer. + + @param[in] DataLen The total length of the data buffer. + @param[in] FragmentCount The fragment count of the fragment table. + @param[in] FragmentTable Pointer to the fragment table of the data + buffer. + + @retval EFI_SUCCESS The integrity check passed. + @retval EFI_INVALID_PARAMETER The integrity check failed. + +**/ +EFI_STATUS +TcpChkDataBuf ( + IN UINT32 DataLen, + IN UINT32 FragmentCount, + IN EFI_TCP4_FRAGMENT_DATA *FragmentTable + ) +{ + UINT32 Index; + + UINT32 Len; + + for (Index = 0, Len = 0; Index < FragmentCount; Index++) { + Len = Len + FragmentTable[Index].FragmentLength; + } + + if (DataLen != Len) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Get the current operational status. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[out] Tcp4State Pointer to the buffer to receive the current TCP + state. Optional parameter that may be NULL. + @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. Optional parameter that may be NULL. + @param[out] Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. Optional parameter that may be NULL. + @param[out] MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + @param[out] SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN CONST EFI_TCP4_PROTOCOL *This, + OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + TCP4_MODE_DATA TcpMode; + SOCKET *Sock; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + TcpMode.Tcp4State = Tcp4State; + TcpMode.Tcp4ConfigData = Tcp4ConfigData; + TcpMode.Ip4ModeData = Ip4ModeData; + TcpMode.MnpConfigData = MnpConfigData; + TcpMode.SnpModeData = SnpModeData; + + return SockGetMode (Sock, &TcpMode); +} + +/** + Initialize or brutally reset the operational parameters for + this EFI TCPv4 instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] TcpConfigData Pointer to the configure data to configure the + instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The operational settings were set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already + configured. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + +**/ +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ) +{ + EFI_TCP4_OPTION *Option; + SOCKET *Sock; + EFI_STATUS Status; + IP4_ADDR Ip; + IP4_ADDR SubnetMask; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != TcpConfigData) { + + CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); + if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) { + return EFI_INVALID_PARAMETER; + } + + if (TcpConfigData->AccessPoint.ActiveFlag && (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) { + return EFI_INVALID_PARAMETER; + } + + if (!TcpConfigData->AccessPoint.UseDefaultAddress) { + + CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR)); + CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR)); + if (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (SubnetMask))) { + return EFI_INVALID_PARAMETER; + } + } + + Option = TcpConfigData->ControlOption; + if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { + return EFI_UNSUPPORTED; + } + } + + Sock = SOCK_FROM_THIS (This); + + if (NULL == TcpConfigData) { + return SockFlush (Sock); + } + + Status = SockConfigure (Sock, TcpConfigData); + + if (EFI_NO_MAPPING == Status) { + Sock->ConfigureState = SO_NO_MAPPING; + } + + return Status; +} + +/** + Add or delete routing entries. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param[in] SubnetAddress The destination network. + @param[in] SubnetMask The subnet mask for the destination network. + @param[in] GatewayAddress The gateway address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the + entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED This route is already in the routing table. + @retval EFI_UNSUPPORTED The TCP driver does not support this operation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ) +{ + SOCKET *Sock; + TCP4_ROUTE_INFO RouteInfo; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + RouteInfo.DeleteRoute = DeleteRoute; + RouteInfo.SubnetAddress = SubnetAddress; + RouteInfo.SubnetMask = SubnetMask; + RouteInfo.GatewayAddress = GatewayAddress; + + return SockRoute (Sock, &RouteInfo); +} + +/** + Initiate a non-blocking TCP connection request for an active TCP instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when + the TCP three way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully + initiated. + @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instance is not configured as an active one, + or it is not in Tcp4StateClosed state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to + initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockConnect (Sock, ConnectionToken); +} + +/** + Listen on the passive instance to accept an incoming connection request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when + operation finishes. + + @retval EFI_SUCCESS The listen token was queued successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not + in Tcp4StateListen state or a same listen token + has already existed in the listen token queue of + this TCP instance. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish + the operation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockAccept (Sock, ListenToken); +} + +/** + Queues outgoing data into the transmit queue + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to the completion token to queue to the + transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A transmit completion token with the same + Token-> CompletionToken.Event was already in the + transmission queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a + resource shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or + address. + +**/ +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.TxData || + 0 == Token->Packet.TxData->FragmentCount || + 0 == Token->Packet.TxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.TxData->DataLength, + Token->Packet.TxData->FragmentCount, + Token->Packet.TxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockSend (Sock, Token); +} + +/** + Place an asynchronous receive request into the receiving queue. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A receive completion token with the same + Token->CompletionToken.Event was already in the + receive queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection, + and there is no any buffered data in the receive + buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.RxData || + 0 == Token->Packet.RxData->FragmentCount || + 0 == Token->Packet.RxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.RxData->DataLength, + Token->Packet.RxData->FragmentCount, + Token->Packet.RxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockRcv (Sock, Token); + +} + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when + operation finishes. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: * + Configure() has been called with TcpConfigData + set to NULL, and this function has not returned. + * Previous Close() call on this instance has not + finished. + @retval EFI_INVALID_PARAMETER One ore more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the + operation. + @retval EFI_DEVICE_ERROR Any unexpected category error not belonging to those + listed above. + +**/ +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockClose (Sock, CloseToken, CloseToken->AbortOnClose); +} + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by the four + functions listed above will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in the current + implementation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or + receive queue. Consider increasing the polling + rate. + +**/ +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL); + + return Status; +} + +/** + Get the current operational status. + + The GetModeData() function copies the current operational settings of this EFI TCPv6 + Protocol instance into user-supplied buffers. This function can also be used to retrieve + the operational setting of underlying drivers such as IPv6, MNP, or SNP. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[out] Tcp6State The buffer in which the current TCP state is + returned. Optional parameter that may be NULL. + @param[out] Tcp6ConfigData The buffer in which the current TCP configuration + is returned. Optional parameter that may be NULL. + @param[out] Ip6ModeData The buffer in which the current IPv6 configuration + data used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] MnpConfigData The buffer in which the current MNP configuration + data indirectly used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] SnpModeData The buffer in which the current SNP mode data + indirectly used by the TCP instance is returned. + Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp6GetModeData ( + IN EFI_TCP6_PROTOCOL *This, + OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL, + OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + TCP6_MODE_DATA TcpMode; + SOCKET *Sock; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + TcpMode.Tcp6State = Tcp6State; + TcpMode.Tcp6ConfigData = Tcp6ConfigData; + TcpMode.Ip6ModeData = Ip6ModeData; + TcpMode.MnpConfigData = MnpConfigData; + TcpMode.SnpModeData = SnpModeData; + + return SockGetMode (Sock, &TcpMode); +} + +/** + Initialize or brutally reset the operational parameters for this EFI TCPv6 instance. + + The Configure() function does the following: + - Initialize this TCP instance, i.e., initialize the communication end settings and + specify active open or passive open for an instance. + - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush + transmission and receiving buffer directly without informing the communication peer. + + No other TCPv6 Protocol operation except Poll() can be executed by this instance until + it is configured properly. For an active TCP instance, after a proper configuration it + may call Connect() to initiate a three-way handshake. For a passive TCP instance, + its state transits to Tcp6StateListen after configuration, and Accept() may be + called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL, + the instance is reset. The resetting process will be done brutally, the state machine will + be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed, + and no traffic is allowed through this instance. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance. + If Tcp6ConfigData is set to NULL, the instance is reset. + + @retval EFI_SUCCESS The operational settings were set, changed, or reset + successfully. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for + use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE: + - This is NULL. + - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor + one of the configured IP addresses in the underlying IPv6 driver. + - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast + IPv6 address. + - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or + Tcp6ConfigData->AccessPoint.RemotePort is zero when + Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE. + - A same access point has been configured in other TCP + instance properly. + @retval EFI_ACCESS_DENIED Configuring a TCP instance when it is configured without + calling Configure() with NULL to reset it. + @retval EFI_UNSUPPORTED One or more of the control options are not supported in + the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Configure ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL + ) +{ + EFI_TCP6_OPTION *Option; + SOCKET *Sock; + EFI_STATUS Status; + EFI_IPv6_ADDRESS *Ip; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != Tcp6ConfigData) { + + Ip = &Tcp6ConfigData->AccessPoint.RemoteAddress; + if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) { + return EFI_INVALID_PARAMETER; + } + + if (Tcp6ConfigData->AccessPoint.ActiveFlag && + (0 == Tcp6ConfigData->AccessPoint.RemotePort || NetIp6IsUnspecifiedAddr (Ip)) + ) { + return EFI_INVALID_PARAMETER; + } + + Ip = &Tcp6ConfigData->AccessPoint.StationAddress; + if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) { + return EFI_INVALID_PARAMETER; + } + + Option = Tcp6ConfigData->ControlOption; + if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { + return EFI_UNSUPPORTED; + } + } + + Sock = SOCK_FROM_THIS (This); + + if (NULL == Tcp6ConfigData) { + return SockFlush (Sock); + } + + Status = SockConfigure (Sock, Tcp6ConfigData); + + if (EFI_NO_MAPPING == Status) { + Sock->ConfigureState = SO_NO_MAPPING; + } + + return Status; +} + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + The Connect() function will initiate an active open to the remote peer configured + in a current TCP instance if it is configured active. If the connection succeeds or + fails due to any error, the ConnectionToken->CompletionToken.Event will be signaled + and ConnectionToken->CompletionToken.Status will be updated accordingly. This + function can only be called for the TCP instance in the Tcp6StateClosed state. The + instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS. + If a TCP three-way handshake succeeds, its state will become Tcp6StateEstablished. + Otherwise, the state will return to Tcp6StateClosed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when the TCP three + way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully initiated and the state of + this TCP instance has been changed to Tcp6StateSynSent. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - This instance is not configured as an active one. + - This instance is not in Tcp6StateClosed state. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ConnectionToken is NULL. + - ConnectionToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Connect ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockConnect (Sock, ConnectionToken); +} + +/** + Listen on the passive instance to accept an incoming connection request. This is a + nonblocking operation. + + The Accept() function initiates an asynchronous accept request to wait for an incoming + connection on the passive TCP instance. If a remote peer successfully establishes a + connection with this instance, a new TCP instance will be created and its handle will + be returned in ListenToken->NewChildHandle. The newly created instance is configured + by inheriting the passive instance's configuration and is ready for use upon return. + The new instance is in the Tcp6StateEstablished state. + + The ListenToken->CompletionToken.Event will be signaled when a new connection is + accepted, when a user aborts the listen or when a connection is reset. + + This function only can be called when a current TCP instance is in Tcp6StateListen state. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when operation finishes. + + + @retval EFI_SUCCESS The listen token queued successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - This instance is not a passive instance. + - This instance is not in Tcp6StateListen state. + - The same listen token has already existed in the listen + token queue of this TCP instance. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ListenToken is NULL. + - ListentToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to a category listed above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Accept ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_LISTEN_TOKEN *ListenToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockAccept (Sock, ListenToken); +} + +/** + Queues outgoing data into the transmit queue. + + The Transmit() function queues a sending request to this TCP instance along with the + user data. The status of the token is updated and the event in the token will be + signaled once the data is sent out or an error occurs. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token to queue to the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a + source address for this instance, but no source address was + available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.TxData is NULL. + - Token->Packet.FragmentCount is zero. + - Token->Packet.DataLength is not equal to the sum of fragment lengths. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - A transmit completion token with the same Token-> + CompletionToken.Event was already in the + transmission queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_NOT_READY The completion token could not be queued because the + transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of resource + shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address. + +**/ +EFI_STATUS +EFIAPI +Tcp6Transmit ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.TxData || + 0 == Token->Packet.TxData->FragmentCount || + 0 == Token->Packet.TxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.TxData->DataLength, + Token->Packet.TxData->FragmentCount, + (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.TxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockSend (Sock, Token); +} + +/** + Places an asynchronous receive request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. This + function is always asynchronous. The caller must allocate the Token->CompletionToken.Event + and the FragmentBuffer used to receive data. The caller also must fill the DataLength that + represents the whole length of all FragmentBuffer. When the receive operation completes, the + EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData + fields, and the Token->CompletionToken.Event is signaled. If data obtained, the data and its length + will be copied into the FragmentTable; at the same time the full length of received data will + be recorded in the DataLength fields. Providing a proper notification function and context + for the event enables the user to receive the notification and receiving status. That + notification function is guaranteed to not be re-entered. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the receive data + descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.RxData is NULL. + - Token->Packet.RxData->DataLength is 0. + - The Token->Packet.RxData->DataLength is not the + sum of all FragmentBuffer length in FragmentTable. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of + system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI TCPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + - A receive completion token with the same Token->CompletionToken.Event + was already in the receive queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection and there is no + buffered data in the receive buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp6Receive ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.RxData || + 0 == Token->Packet.RxData->FragmentCount || + 0 == Token->Packet.RxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.RxData->DataLength, + Token->Packet.RxData->FragmentCount, + (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.RxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockRcv (Sock, Token); +} + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a + nonblocking operation. + + Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered + transmission data will be sent by the TCP driver, and the current instance will have a graceful close + working flow described as RFC 793 if AbortOnClose is set to FALSE. Otherwise, a rest packet + will be sent by TCP driver to fast disconnect this connection. When the close operation completes + successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous + operations are signaled, and any buffers used for TCP network traffic are flushed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when operation finishes. + + @retval EFI_SUCCESS The Close() was called successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - CloseToken or CloseToken->CompletionToken.Event is already in use. + - Previous Close() call on this instance has not finished. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - CloseToken is NULL. + - CloseToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to error categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Close ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CLOSE_TOKEN *CloseToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockClose (Sock, CloseToken, CloseToken->AbortOnClose); +} + +/** + Abort an asynchronous connection, listen, transmission, or receive request. + + The Cancel() function aborts a pending connection, listen, transmit, or + receive request. + + If Token is not NULL and the token is in the connection, listen, transmission, + or receive queue when it is being cancelled, its Token->Status will be set + to EFI_ABORTED, and then Token->Event will be signaled. + + If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. + + If Token is NULL all asynchronous token issued by Connect(), Accept(), + Transmit(), and Receive() will be aborted. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_TCP6_PROTOCOL.Connect(), + EFI_TCP6_PROTOCOL.Accept(), + EFI_TCP6_PROTOCOL.Transmit() or + EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending + tokens issued by above four functions will be aborted. Type + EFI_TCP6_COMPLETION_TOKEN is defined in + EFI_TCP_PROTOCOL.Connect(). + + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +Tcp6Cancel ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Poll to receive incoming data and transmit outgoing segments. + + The Poll() function increases the rate that data is moved between the network + and application, and can be called when the TCP instance is created successfully. + Its use is optional. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Tcp6Poll ( + IN EFI_TCP6_PROTOCOL *This + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL); + + return Status; +} + diff --git a/NetworkPkg/TcpDxe/TcpMain.h b/NetworkPkg/TcpDxe/TcpMain.h new file mode 100644 index 0000000000..fabffc21d7 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpMain.h @@ -0,0 +1,758 @@ +/** @file + Declaration of protocol interfaces in EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL. + It is the common head file for all Tcp*.c in TCP driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _TCP_MAIN_H_ +#define _TCP_MAIN_H_ + +#include +#include +#include +#include + +#include "Socket.h" +#include "TcpProto.h" +#include "TcpDriver.h" +#include "TcpFunc.h" + +extern UINT16 mTcp4RandomPort; +extern UINT16 mTcp6RandomPort; +extern CHAR16 *mTcpStateName[]; +extern EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2; + +extern LIST_ENTRY mTcpRunQue; +extern LIST_ENTRY mTcpListenQue; +extern TCP_SEQNO mTcpGlobalIss; +extern UINT32 mTcpTick; + +/// +/// 30 seconds. +/// +#define TCP6_KEEP_NEIGHBOR_TIME 30 +/// +/// 5 seconds, since 1 tick equals 200ms. +/// +#define TCP6_REFRESH_NEIGHBOR_TICK 25 + +#define TCP_EXPIRE_TIME 65535 + +/// +/// The implementation selects the initial send sequence number and the unit to +/// be added when it is increased. +/// +#define TCP_BASE_ISS 0x4d7e980b +#define TCP_ISS_INCREMENT_1 2048 +#define TCP_ISS_INCREMENT_2 100 + +typedef union { + EFI_TCP4_CONFIG_DATA Tcp4CfgData; + EFI_TCP6_CONFIG_DATA Tcp6CfgData; +} TCP_CONFIG_DATA; + +typedef union { + EFI_TCP4_ACCESS_POINT Tcp4Ap; + EFI_TCP6_ACCESS_POINT Tcp6Ap; +} TCP_ACCESS_POINT; + +typedef struct _TCP4_MODE_DATA { + EFI_TCP4_CONNECTION_STATE *Tcp4State; + EFI_TCP4_CONFIG_DATA *Tcp4ConfigData; + EFI_IP4_MODE_DATA *Ip4ModeData; + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData; + EFI_SIMPLE_NETWORK_MODE *SnpModeData; +} TCP4_MODE_DATA; + +typedef struct _TCP6_MODE_DATA { + EFI_TCP6_CONNECTION_STATE *Tcp6State; + EFI_TCP6_CONFIG_DATA *Tcp6ConfigData; + EFI_IP6_MODE_DATA *Ip6ModeData; + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData; + EFI_SIMPLE_NETWORK_MODE *SnpModeData; +} TCP6_MODE_DATA; + +typedef struct _TCP4_ROUTE_INFO { + BOOLEAN DeleteRoute; + EFI_IPv4_ADDRESS *SubnetAddress; + EFI_IPv4_ADDRESS *SubnetMask; + EFI_IPv4_ADDRESS *GatewayAddress; +} TCP4_ROUTE_INFO; + +// +// EFI_TCP4_PROTOCOL definitions. +// + +/** + Get the current operational status. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[out] Tcp4State Pointer to the buffer to receive the current TCP + state. Optional parameter that may be NULL. + @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. Optional parameter that may be NULL. + @param[out] Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. Optional parameter that may be NULL. + @param[out] MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + @param[out] SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN CONST EFI_TCP4_PROTOCOL *This, + OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Initialize or brutally reset the operational parameters for + this EFI TCPv4 instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] TcpConfigData Pointer to the configure data to configure the + instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The operational settings are set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED Configuring the TCP instance when it is already + configured. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + +**/ +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ); + +/** + Add or delete routing entries. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param[in] SubnetAddress The destination network. + @param[in] SubnetMask The subnet mask for the destination network. + @param[in] GatewayAddress The gateway address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the + entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED This route is already in the routing table. + @retval EFI_UNSUPPORTED The TCP driver does not support this operation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when + the TCP three way handshake finishes. + + @retval EFI_SUCCESS The connection request is successfully + initiated. + @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instance is not configured as an active one + or it is not in Tcp4StateClosed state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to + initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ); + +/** + Listen on the passive instance to accept an incoming connection request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when + operation finishes. + + @retval EFI_SUCCESS The listen token has been queued successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not + in Tcp4StateListen state, or a same listen token + has already existed in the listen token queue of + this TCP instance. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish + the operation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ); + +/** + Queues outgoing data into the transmit queue + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance + @param[in] Token Pointer to the completion token to queue to the + transmit queue + + @retval EFI_SUCCESS The data has been queued for transmission + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A transmit completion token with the same + Token-> CompletionToken.Event was already in the + transmission queue. * The current instance is in + Tcp4StateClosed state * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a + resource shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or + address. + +**/ +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +/** + Place an asynchronous receive request into the receiving queue. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A receive completion token with the same + Token->CompletionToken.Event was already in the + receive queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection + and there is no buffered data in the receive + buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when + operation finishes. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: * + Configure() has been called with TcpConfigData + set to NULL and this function has not returned. + * Previous Close() call on this instance has not + finished. + @retval EFI_INVALID_PARAMETER One ore more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the + operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error + categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ); + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by the above four + functions will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in the current + implementation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or + receive queue. Consider increasing the polling + rate. + +**/ +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ); + +// +// EFI_TCP6_PROTOCOL definitions. +// + +/** + Get the current operational status. + + The GetModeData() function copies the current operational settings of this EFI TCPv6 + Protocol instance into user-supplied buffers. This function can also be used to retrieve + the operational setting of underlying drivers such as IPv6, MNP, or SNP. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[out] Tcp6State The buffer in which the current TCP state is + returned. Optional parameter that may be NULL. + @param[out] Tcp6ConfigData The buffer in which the current TCP configuration + is returned. Optional parameter that may be NULL. + @param[out] Ip6ModeData The buffer in which the current IPv6 configuration + data used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] MnpConfigData The buffer in which the current MNP configuration + data used indirectly by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] SnpModeData The buffer in which the current SNP mode data + used indirectly by the TCP instance is returned. + Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp6GetModeData ( + IN EFI_TCP6_PROTOCOL *This, + OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL, + OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Initialize or brutally reset the operational parameters for this EFI TCPv6 instance. + + The Configure() function does the following: + - Initialize this TCP instance, i.e., initialize the communication end settings and + specify active open or passive open for an instance. + - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush + transmission and receiving buffer directly without informing the communication peer. + + No other TCPv6 Protocol operation except Poll() can be executed by this instance until + it is configured properly. For an active TCP instance, after a proper configuration it + may call Connect() to initiates the three-way handshake. For a passive TCP instance, + its state will transit to Tcp6StateListen after configuration, and Accept() may be + called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL, + the instance is reset. Resetting process will be done brutally, the state machine will + be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed, + and no traffic is allowed through this instance. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance. + If Tcp6ConfigData is set to NULL, the instance is reset. + + @retval EFI_SUCCESS The operational settings were set, changed, or reset + successfully. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for + use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE: + - This is NULL. + - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor + one of the configured IP addresses in the underlying IPv6 driver. + - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast + IPv6 address. + - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or + Tcp6ConfigData->AccessPoint.RemotePort is zero when + Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE. + - A same access point has been configured in other TCP + instance properly. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is configured without + calling Configure() with NULL to reset it. + @retval EFI_UNSUPPORTED One or more of the control options are not supported in + the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Configure ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL + ); + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + The Connect() function will initiate an active open to the remote peer configured + in current TCP instance if it is configured active. If the connection succeeds or + fails due to an error, the ConnectionToken->CompletionToken.Event will be signaled, + and ConnectionToken->CompletionToken.Status will be updated accordingly. This + function can only be called for the TCP instance in Tcp6StateClosed state. The + instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS. + If TCP three-way handshake succeeds, its state will become Tcp6StateEstablished; + otherwise, the state will return to Tcp6StateClosed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when the TCP + three-way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully initiated and the state of + this TCP instance has been changed to Tcp6StateSynSent. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - This instance is not configured as an active instance. + - This instance is not in Tcp6StateClosed state. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ConnectionToken is NULL. + - ConnectionToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Connect ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken + ); + +/** + Listen on the passive instance to accept an incoming connection request. This is a + nonblocking operation. + + The Accept() function initiates an asynchronous accept request to wait for an incoming + connection on the passive TCP instance. If a remote peer successfully establishes a + connection with this instance, a new TCP instance will be created and its handle will + be returned in ListenToken->NewChildHandle. The newly created instance is configured + by inheriting the passive instance's configuration, and is ready for use upon return. + The new instance is in the Tcp6StateEstablished state. + + The ListenToken->CompletionToken.Event will be signaled when a new connection is + accepted, user aborts the listen or connection is reset. + + This function only can be called when the current TCP instance is in Tcp6StateListen state. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when the operation finishes. + + + @retval EFI_SUCCESS The listen token was been queued successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - This instance is not a passive instance. + - This instance is not in Tcp6StateListen state. + - The same listen token has already existed in the listen + token queue of this TCP instance. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ListenToken is NULL. + - ListentToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error + categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Accept ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_LISTEN_TOKEN *ListenToken + ); + +/** + Queues outgoing data into the transmit queue. + + The Transmit() function queues a sending request to this TCP instance along with the + user data. The status of the token is updated and the event in the token will be + signaled once the data is sent out or some error occurs. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token to queue to the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a + source address for this instance, but no source address was + available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.TxData is NULL. + - Token->Packet.FragmentCount is zero. + - Token->Packet.DataLength is not equal to the sum of fragment lengths. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - A transmit completion token with the same Token-> + CompletionToken.Event was already in the + transmission queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_NOT_READY The completion token could not be queued because the + transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a resource + shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address. + +**/ +EFI_STATUS +EFIAPI +Tcp6Transmit ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ); + +/** + Places an asynchronous receive request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. This + function is always asynchronous. The caller must allocate the Token->CompletionToken.Event + and the FragmentBuffer used to receive data. The caller also must fill the DataLength, which + represents the whole length of all FragmentBuffer. When the receive operation completes, the + EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData + fields, and the Token->CompletionToken.Event is signaled. If data is obtained, the data and its length + will be copied into the FragmentTable. At the same time the full length of received data will + be recorded in the DataLength fields. Providing a proper notification function and context + for the event enables the user to receive the notification and receiving status. That + notification function is guaranteed to not be re-entered. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the receive data + descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.RxData is NULL. + - Token->Packet.RxData->DataLength is 0. + - The Token->Packet.RxData->DataLength is not the + sum of all FragmentBuffer length in FragmentTable. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of + system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI TCPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + - A receive completion token with the same Token->CompletionToken.Event + was already in the receive queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - The user has called Close() to disconnect this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection, and there is no + buffered data in the receive buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp6Receive ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ); + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a + nonblocking operation. + + Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered + transmission data will be sent by the TCP driver, and the current instance will have a graceful close + working flow described as RFC 793 if AbortOnClose is set to FALSE, otherwise, a rest packet + will be sent by TCP driver to fast disconnect this connection. When the close operation completes + successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous + operations are signaled, and any buffers used for TCP network traffic are flushed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when operation finishes. + + @retval EFI_SUCCESS The Close() was called successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - CloseToken or CloseToken->CompletionToken.Event is already in use. + - Previous Close() call on this instance has not finished. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - CloseToken is NULL. + - CloseToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Close ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CLOSE_TOKEN *CloseToken + ); + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + The Cancel() function aborts a pending connection, listen, transmit or + receive request. + + If Token is not NULL and the token is in the connection, listen, transmission + or receive queue when it is being cancelled, its Token->Status will be set + to EFI_ABORTED and then Token->Event will be signaled. + + If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. + + If Token is NULL all asynchronous token issued by Connect(), Accept(), + Transmit() and Receive() will be aborted. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_TCP6_PROTOCOL.Connect(), + EFI_TCP6_PROTOCOL.Accept(), + EFI_TCP6_PROTOCOL.Transmit() or + EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending + tokens issued by above four functions will be aborted. Type + EFI_TCP6_COMPLETION_TOKEN is defined in + EFI_TCP_PROTOCOL.Connect(). + + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +Tcp6Cancel ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Poll to receive incoming data and transmit outgoing segments. + + The Poll() function increases the rate that data is moved between the network + and application and can be called when the TCP instance is created successfully. + Its use is optional. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Tcp6Poll ( + IN EFI_TCP6_PROTOCOL *This + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpMisc.c b/NetworkPkg/TcpDxe/TcpMisc.c new file mode 100644 index 0000000000..492ec35fb8 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpMisc.c @@ -0,0 +1,1281 @@ +/** @file + Misc support routines for TCP driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 equals to . + + @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 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; +} + diff --git a/NetworkPkg/TcpDxe/TcpOption.c b/NetworkPkg/TcpDxe/TcpOption.c new file mode 100644 index 0000000000..bacce1070d --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpOption.c @@ -0,0 +1,374 @@ +/** @file + Routines to process TCP option. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +/** + Get a UINT16 value from buffer. + + @param[in] Buf Pointer to input buffer. + + @return The UINT16 value obtained from the buffer. + +**/ +UINT16 +TcpGetUint16 ( + IN UINT8 *Buf + ) +{ + UINT16 Value; + CopyMem (&Value, Buf, sizeof (UINT16)); + return NTOHS (Value); +} + +/** + Get a UINT32 value from buffer. + + @param[in] Buf Pointer to input buffer. + + @return The UINT32 value obtained from the buffer. + +**/ +UINT32 +TcpGetUint32 ( + IN UINT8 *Buf + ) +{ + UINT32 Value; + CopyMem (&Value, Buf, sizeof (UINT32)); + return NTOHL (Value); +} + +/** + Put a UINT32 value in buffer. + + @param[out] Buf Pointer to the buffer. + @param[in] Data The UINT32 Date to put in the buffer. + +**/ +VOID +TcpPutUint32 ( + OUT UINT8 *Buf, + IN UINT32 Data + ) +{ + Data = HTONL (Data); + CopyMem (Buf, &Data, sizeof (UINT32)); +} + +/** + Compute the window scale value according to the given buffer size. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ) +{ + UINT8 Scale; + UINT32 BufSize; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + BufSize = GET_RCV_BUFFSIZE (Tcb->Sk); + + Scale = 0; + while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) { + + Scale++; + } + + return Scale; +} + +/** + Build the TCP option in three-way handshake. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT8 *Data; + UINT16 Len; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + + Len = 0; + + // + // Add a timestamp option if not disabled by the application + // and it is the first SYN segment, or the peer has sent + // us its timestamp. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS)) + ) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data != NULL); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, 0); + } + + // + // Build window scale option, only when configured + // to send WS option, and either we are doing active + // open or we have received WS option from peer. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS)) + ) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_WS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data != NULL); + + Len += TCP_OPTION_WS_ALIGNED_LEN; + TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb)); + } + + // + // Build the MSS option. + // + Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1); + ASSERT (Data != NULL); + + Len += TCP_OPTION_MSS_LEN; + TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss); + + return Len; +} + +/** + Build the TCP option in synchronized states. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT8 *Data; + UINT16 Len; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + Len = 0; + + // + // Build the Timestamp option. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) && + !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST) + ) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data != NULL); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, Tcb->TsRecent); + } + + return Len; +} + +/** + Parse the supported options. + + @param[in] Tcp Pointer to the TCP_CB of this TCP instance. + @param[in, out] Option Pointer to the TCP_OPTION used to store the + successfully pasrsed options. + + @retval 0 The options are successfully pasrsed. + @retval -1 Ilegal option was found. + +**/ +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN OUT TCP_OPTION *Option + ) +{ + UINT8 *Head; + UINT8 TotalLen; + UINT8 Cur; + UINT8 Type; + UINT8 Len; + + ASSERT ((Tcp != NULL) && (Option != NULL)); + + Option->Flag = 0; + + TotalLen = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD)); + if (TotalLen <= 0) { + return 0; + } + + Head = (UINT8 *) (Tcp + 1); + + // + // Fast process of the timestamp option. + // + if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) { + + Option->TSVal = TcpGetUint32 (Head + 4); + Option->TSEcr = TcpGetUint32 (Head + 8); + Option->Flag = TCP_OPTION_RCVD_TS; + + return 0; + } + // + // Slow path to process the options. + // + Cur = 0; + + while (Cur < TotalLen) { + Type = Head[Cur]; + + switch (Type) { + case TCP_OPTION_MSS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) { + + return -1; + } + + Option->Mss = TcpGetUint16 (&Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS); + + Cur += TCP_OPTION_MSS_LEN; + break; + + case TCP_OPTION_WS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) { + + return -1; + } + + Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS); + + Cur += TCP_OPTION_WS_LEN; + break; + + case TCP_OPTION_TS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) { + + return -1; + } + + Option->TSVal = TcpGetUint32 (&Head[Cur + 2]); + Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS); + + Cur += TCP_OPTION_TS_LEN; + break; + + case TCP_OPTION_NOP: + Cur++; + break; + + case TCP_OPTION_EOP: + Cur = TotalLen; + break; + + default: + Len = Head[Cur + 1]; + + if ((TotalLen - Cur) < Len || Len < 2) { + return -1; + } + + Cur = (UINT8) (Cur + Len); + break; + } + + } + + return 0; +} + +/** + Check the segment against PAWS. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] TSVal The timestamp value. + + @retval 1 The segment passed the PAWS check. + @retval 0 The segment failed to pass the PAWS check. + +**/ +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ) +{ + // + // PAWS as defined in RFC1323, buggy... + // + if (TCP_TIME_LT (TSVal, Tcb->TsRecent) && + TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick) + ) { + + return 0; + + } + + return 1; +} diff --git a/NetworkPkg/TcpDxe/TcpOption.h b/NetworkPkg/TcpDxe/TcpOption.h new file mode 100644 index 0000000000..0ccadb9536 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpOption.h @@ -0,0 +1,145 @@ +/** @file + Tcp option's routine header file. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _TCP_OPTION_H_ +#define _TCP_OPTION_H_ + +// +// Supported TCP option types and their length. +// +#define TCP_OPTION_EOP 0 ///< End Of oPtion +#define TCP_OPTION_NOP 1 ///< No-Option. +#define TCP_OPTION_MSS 2 ///< Maximum Segment Size +#define TCP_OPTION_WS 3 ///< Window scale +#define TCP_OPTION_TS 8 ///< Timestamp +#define TCP_OPTION_MSS_LEN 4 ///< Length of MSS option +#define TCP_OPTION_WS_LEN 3 ///< Length of window scale option +#define TCP_OPTION_TS_LEN 10 ///< Length of timestamp option +#define TCP_OPTION_WS_ALIGNED_LEN 4 ///< Length of window scale option, aligned +#define TCP_OPTION_TS_ALIGNED_LEN 12 ///< Length of timestamp option, aligned + +// +// recommend format of timestamp window scale +// option for fast process. +// +#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \ + (TCP_OPTION_NOP << 16) | \ + (TCP_OPTION_TS << 8) | \ + (TCP_OPTION_TS_LEN)) + +#define TCP_OPTION_WS_FAST ((TCP_OPTION_NOP << 24) | \ + (TCP_OPTION_WS << 16) | \ + (TCP_OPTION_WS_LEN << 8)) + +#define TCP_OPTION_MSS_FAST ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16)) + +// +// Other misc definations +// +#define TCP_OPTION_RCVD_MSS 0x01 +#define TCP_OPTION_RCVD_WS 0x02 +#define TCP_OPTION_RCVD_TS 0x04 +#define TCP_OPTION_MAX_WS 14 ///< Maxium window scale value +#define TCP_OPTION_MAX_WIN 0xffff ///< Max window size in TCP header + +/// +/// The structure to store the parse option value. +/// ParseOption only parses the options, doesn't process them. +/// +typedef struct _TCP_OPTION { + UINT8 Flag; ///< Flag such as TCP_OPTION_RCVD_MSS + UINT8 WndScale; ///< The WndScale received + UINT16 Mss; ///< The Mss received + UINT32 TSVal; ///< The TSVal field in a timestamp option + UINT32 TSEcr; ///< The TSEcr field in a timestamp option +} TCP_OPTION; + +/** + Compute the window scale value according to the given buffer size. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ); + +/** + Build the TCP option in three-way handshake. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +/** + Build the TCP option in synchronized states. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +/** + Parse the supported options. + + @param[in] Tcp Pointer to the TCP_CB of this TCP instance. + @param[in, out] Option Pointer to the TCP_OPTION used to store the + successfully pasrsed options. + + @retval 0 The options successfully pasrsed. + @retval -1 Ilegal option was found. + +**/ +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN OUT TCP_OPTION *Option + ); + +/** + Check the segment against PAWS. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] TSVal The timestamp value. + + @retval 1 The segment passed the PAWS check. + @retval 0 The segment failed to pass the PAWS check. + +**/ +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpOutput.c b/NetworkPkg/TcpDxe/TcpOutput.c new file mode 100644 index 0000000000..c038213484 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpOutput.c @@ -0,0 +1,1219 @@ +/** @file + TCP output process routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +UINT8 mTcpOutFlag[] = { + 0, // TCP_CLOSED + 0, // TCP_LISTEN + TCP_FLG_SYN, // TCP_SYN_SENT + TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD + TCP_FLG_ACK, // TCP_ESTABLISHED + TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1 + TCP_FLG_ACK, // TCP_FIN_WAIT_2 + TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING + TCP_FLG_ACK, // TCP_TIME_WAIT + TCP_FLG_ACK, // TCP_CLOSE_WAIT + TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK +}; + +/** + Compute the sequence space left in the old receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence space left in the old receive window. + +**/ +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ) +{ + UINT32 OldWin; + + OldWin = 0; + + if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) { + + OldWin = TCP_SUB_SEQ ( + Tcb->RcvWl2 + Tcb->RcvWnd, + Tcb->RcvNxt + ); + } + + return OldWin; +} + +/** + Compute the current receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The size of the current receive window, in bytes. + +**/ +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Increase; + UINT32 OldWin; + + Sk = Tcb->Sk; + ASSERT (Sk != NULL); + + OldWin = TcpRcvWinOld (Tcb); + + Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF); + + Increase = 0; + if (Win > OldWin) { + Increase = Win - OldWin; + } + + // + // Receiver's SWS: don't advertise a bigger window + // unless it can be increased by at least one Mss or + // half of the receive buffer. + // + if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) { + + return Win; + } + + return OldWin; +} + +/** + Compute the value to fill in the window size field of the outgoing segment. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Syn The flag to indicate whether the outgoing segment + is a SYN segment. + + @return The value of the local receive window size used to fill the outgoing segment. + +**/ +UINT16 +TcpComputeWnd ( + IN OUT TCP_CB *Tcb, + IN BOOLEAN Syn + ) +{ + UINT32 Wnd; + + // + // RFC requires that initial window not be scaled + // + if (Syn) { + + Wnd = GET_RCV_BUFFSIZE (Tcb->Sk); + } else { + + Wnd = TcpRcvWinNow (Tcb); + + Tcb->RcvWnd = Wnd; + } + + Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff); + return NTOHS ((UINT16) Wnd); +} + +/** + Get the maximum SndNxt. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence number of the maximum SndNxt. + +**/ +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + + if (IsListEmpty (&Tcb->SndQue)) { + return Tcb->SndNxt; + } + + Entry = Tcb->SndQue.BackLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt)); + return TCPSEG_NETBUF (Nbuf)->End; +} + +/** + Compute how much data to send. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, to ignore the sender's SWS avoidance algorithm and send + out data by force. + + @return The length of the data can be sent. If 0, no data can be sent. + +**/ +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Len; + UINT32 Left; + UINT32 Limit; + + Sk = Tcb->Sk; + ASSERT (Sk != NULL); + + // + // TCP should NOT send data beyond the send window + // and congestion window. The right edge of send + // window is defined as SND.WL2 + SND.WND. The right + // edge of congestion window is defined as SND.UNA + + // CWND. + // + Win = 0; + Limit = Tcb->SndWl2 + Tcb->SndWnd; + + if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) { + + Limit = Tcb->SndUna + Tcb->CWnd; + } + + if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) { + Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt); + } + + // + // The data to send contains two parts: the data on the + // socket send queue, and the data on the TCB's send + // buffer. The later can be non-zero if the peer shrinks + // its advertised window. + // + Left = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt); + + Len = MIN (Win, Left); + + if (Len > Tcb->SndMss) { + Len = Tcb->SndMss; + } + + if ((Force != 0)|| (Len == 0 && Left == 0)) { + return Len; + } + + if (Len == 0 && Left != 0) { + goto SetPersistTimer; + } + + // + // Sender's SWS avoidance: Don't send a small segment unless + // a)A full-sized segment can be sent, + // b)At least one-half of the maximum sized windows that + // the other end has ever advertised. + // c)It can send everything it has, and either it isn't + // expecting an ACK, or the Nagle algorithm is disabled. + // + if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) { + + return Len; + } + + if ((Len == Left) && + ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)) + ) { + + return Len; + } + + // + // RFC1122 suggests to set a timer when SWSA forbids TCP + // sending more data, and combines it with a probe timer. + // +SetPersistTimer: + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + + DEBUG ( + (EFI_D_WARN, + "TcpDataToSend: enter persistent state for TCB %p\n", + Tcb) + ); + + if (!Tcb->ProbeTimerOn) { + TcpSetProbeTimer (Tcb); + } + } + + return 0; +} + +/** + Build the TCP header of the TCP segment and transmit the segment by IP. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer containing the segment to be + sent out. + + @retval 0 The segment was sent out successfully. + @retval -1 An error condition occurred. + +**/ +INTN +TcpTransmitSegment ( + IN OUT TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT16 Len; + TCP_HEAD *Head; + TCP_SEG *Seg; + BOOLEAN Syn; + UINT32 DataLen; + + ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0)); + + DataLen = Nbuf->TotalSize; + + Seg = TCPSEG_NETBUF (Nbuf); + Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN); + + if (Syn) { + + Len = TcpSynBuildOption (Tcb, Nbuf); + } else { + + Len = TcpBuildOption (Tcb, Nbuf); + } + + ASSERT ((Len % 4 == 0) && (Len <= 40)); + + Len += sizeof (TCP_HEAD); + + Head = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_HEAD + ); + + ASSERT (Head != NULL); + + Nbuf->Tcp = Head; + + Head->SrcPort = Tcb->LocalEnd.Port; + Head->DstPort = Tcb->RemoteEnd.Port; + Head->Seq = NTOHL (Seg->Seq); + Head->Ack = NTOHL (Tcb->RcvNxt); + Head->HeadLen = (UINT8) (Len >> 2); + Head->Res = 0; + Head->Wnd = TcpComputeWnd (Tcb, Syn); + Head->Checksum = 0; + + // + // Check whether to set the PSH flag. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH); + + if (DataLen != 0) { + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) && + TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End) + ) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + } + } + + // + // Check whether to set the URG flag and the urgent pointer. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) { + + Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq); + } else { + + Seg->Urg = (UINT16) MIN ( + TCP_SUB_SEQ (Tcb->SndUp, + Seg->Seq), + 0xffff + ); + } + } + + Head->Flag = Seg->Flag; + Head->Urg = NTOHS (Seg->Urg); + Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum); + + // + // Update the TCP session's control information. + // + Tcb->RcvWl2 = Tcb->RcvNxt; + if (Syn) { + Tcb->RcvWnd = NTOHS (Head->Wnd); + } + + // + // Clear the delayedack flag. + // + Tcb->DelayedAck = 0; + + return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion); +} + +/** + Get a segment from the Tcb's SndQue. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] Len The maximum length of the segment. + + @return Pointer to the segment. If NULL, some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + NET_BUF *Nbuf; + TCP_SEQNO End; + UINT8 *Data; + UINT8 Flag; + INT32 Offset; + INT32 CopyLen; + + ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0)); + + // + // Find the segment that contains the Seq. + // + Head = &Tcb->SndQue; + + Node = NULL; + Seg = NULL; + + NET_LIST_FOR_EACH (Cur, Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) { + + break; + } + } + + if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) { + return NULL; + } + + // + // Return the buffer if it can be returned without + // adjustment: + // + if ((Seg->Seq == Seq) && + TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) && + !NET_BUF_SHARED (Node) + ) { + + NET_GET_REF (Node); + return Node; + } + + // + // Create a new buffer and copy data there. + // + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Flag = Seg->Flag; + End = Seg->End; + + if (TCP_SEQ_LT (Seq + Len, Seg->End)) { + End = Seq + Len; + } + + CopyLen = TCP_SUB_SEQ (End, Seq); + Offset = TCP_SUB_SEQ (Seq, Seg->Seq); + + // + // If SYN is set and out of the range, clear the flag. + // Becuase the sequence of the first byte is SEG.SEQ+1, + // adjust Offset by -1. If SYN is in the range, copy + // one byte less. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + if (TCP_SEQ_LT (Seg->Seq, Seq)) { + + TCP_CLEAR_FLG (Flag, TCP_FLG_SYN); + Offset--; + } else { + + CopyLen--; + } + } + + // + // If FIN is set and in the range, copy one byte less, + // and if it is out of the range, clear the flag. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + if (Seg->End == End) { + + CopyLen--; + } else { + + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + ASSERT (CopyLen >= 0); + + // + // Copy data to the segment + // + if (CopyLen != 0) { + Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL); + ASSERT (Data != NULL); + + if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) { + goto OnError; + } + } + + CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG)); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = End; + TCPSEG_NETBUF (Nbuf)->Flag = Flag; + + return Nbuf; + +OnError: + NetbufFree (Nbuf); + return NULL; +} + +/** + Get a segment from the Tcb's socket buffer. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] Len The maximum length of the segment. + + @return Pointer to the segment. If NULL, some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSock ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + UINT8 *Data; + UINT32 DataGet; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n", + Tcb) + ); + + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + DataGet = 0; + + if (Len != 0) { + // + // copy data to the segment. + // + Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL); + ASSERT (Data != NULL); + + DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data); + } + + NET_GET_REF (Nbuf); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = Seq + Len; + + InsertTailList (&(Tcb->SndQue), &(Nbuf->List)); + + if (DataGet != 0) { + + SockDataSent (Tcb->Sk, DataGet); + } + + return Nbuf; +} + +/** + Get a segment starting from sequence Seq of a maximum + length of Len. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] Len The maximum length of the segment. + + @return Pointer to the segment. If NULL, some error occurred. + +**/ +NET_BUF * +TcpGetSegment ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + + ASSERT (Tcb != NULL); + + // + // Compare the SndNxt with the max sequence number sent. + // + if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) { + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + } else { + + Nbuf = TcpGetSegmentSock (Tcb, Seq, Len); + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); + return Nbuf; +} + +/** + Retransmit the segment from sequence Seq. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment to be retransmitted. + + @retval 0 Retransmission succeeded. + @retval -1 Error condition occurred. + +**/ +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ) +{ + NET_BUF *Nbuf; + UINT32 Len; + + // + // Compute the maxium length of retransmission. It is + // limited by three factors: + // 1. Less than SndMss + // 2. Must in the current send window + // 3. Will not change the boundaries of queued segments. + // + if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) { + DEBUG ( + (EFI_D_WARN, + "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n", + Tcb) + ); + + return 0; + } + + Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq); + Len = MIN (Len, Tcb->SndMss); + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + if (Nbuf == NULL) { + return -1; + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + goto OnError; + } + + // + // The retransmitted buffer may be on the SndQue, + // trim TCP head because all the buffers on SndQue + // are headless. + // + ASSERT (Nbuf->Tcp != NULL); + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + return 0; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return -1; +} + +/** + Verify that all the segments in SndQue are in good shape. + + @param[in] Head Pointer to the head node of the SndQue. + + @retval 0 At least one segment is broken. + @retval 1 All segments in the specific queue are in good shape. + +**/ +INTN +TcpCheckSndQue ( + IN LIST_ENTRY *Head + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + + if (IsListEmpty (Head)) { + return 1; + } + // + // Initialize the Seq. + // + Entry = Head->ForwardLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seq = TCPSEG_NETBUF (Nbuf)->Seq; + + NET_LIST_FOR_EACH (Entry, Head) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + if (TcpVerifySegment (Nbuf) == 0) { + return 0; + } + + // + // All the node in the SndQue should has: + // SEG.SEQ = LAST_SEG.END + // + if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) { + return 0; + } + + Seq = TCPSEG_NETBUF (Nbuf)->End; + } + + return 1; +} + +/** + Check whether to send data/SYN/FIN and piggyback an ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN OUT TCP_CB *Tcb, + IN INTN Force + ) +{ + UINT32 Len; + INTN Sent; + UINT8 Flag; + NET_BUF *Nbuf; + TCP_SEG *Seg; + TCP_SEQNO Seq; + TCP_SEQNO End; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN)); + + Sent = 0; + + if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) { + + return 0; + } + + do { + // + // Compute how much data can be sent + // + Len = TcpDataToSend (Tcb, Force); + Seq = Tcb->SndNxt; + + ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0]))); + Flag = mTcpOutFlag[Tcb->State]; + + if ((Flag & TCP_FLG_SYN) != 0) { + + Seq = Tcb->Iss; + Len = 0; + } + + // + // Only send a segment without data if SYN or + // FIN is set. + // + if ((Len == 0) && ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) { + return Sent; + } + + Nbuf = TcpGetSegment (Tcb, Seq, Len); + + if (Nbuf == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpToSendData: failed to get a segment for TCB %p\n", + Tcb) + ); + + goto OnError; + } + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // Set the TcpSeg in Nbuf. + // + Len = Nbuf->TotalSize; + End = Seq + Len; + if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) { + End++; + } + + if ((Flag & TCP_FLG_FIN) != 0) { + // + // Send FIN if all data is sent, and FIN is + // in the window + // + if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) && + (GET_SND_DATASIZE (Tcb->Sk) == 0) && + TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2) + ) { + DEBUG ( + (EFI_D_INFO, + "TcpToSendData: send FIN to peer for TCB %p in state %s\n", + Tcb, + mTcpStateName[Tcb->State]) + ); + + End++; + } else { + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + Seg->Seq = Seq; + Seg->End = End; + Seg->Flag = Flag; + + ASSERT (TcpVerifySegment (Nbuf) != 0); + ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0); + + // + // Don't send an empty segment here. + // + if (Seg->End == Seg->Seq) { + DEBUG ( + (EFI_D_WARN, + "TcpToSendData: created a empty segment for TCB %p, free it now\n", + Tcb) + ); + + NetbufFree (Nbuf); + return Sent; + } + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + if ((Flag & TCP_FLG_FIN) != 0) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + goto OnError; + } + + Sent += TCP_SUB_SEQ (End, Seq); + + // + // All the buffers in the SndQue are headless. + // + ASSERT (Nbuf->Tcp != NULL); + + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + + // + // Update the status in TCB. + // + Tcb->DelayedAck = 0; + + if ((Flag & TCP_FLG_FIN) != 0) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + if (TCP_SEQ_GT (End, Tcb->SndNxt)) { + Tcb->SndNxt = End; + } + + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Enable RTT measurement only if not in retransmit. + // Karn's algorithm requires not to update RTT when in loss. + // + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + DEBUG ( + (EFI_D_INFO, + "TcpToSendData: set RTT measure sequence %d for TCB %p\n", + Seq, + Tcb) + ); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + Tcb->RttSeq = Seq; + Tcb->RttMeasure = 0; + } + + } while (Len == Tcb->SndMss); + + return Sent; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return Sent; +} + +/** + Send an ACK immediately. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSendAck ( + IN OUT TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt; + Seg->End = Tcb->SndNxt; + Seg->Flag = TCP_FLG_ACK; + + if (TcpTransmitSegment (Tcb, Nbuf) == 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + Tcb->DelayedAck = 0; + } + + NetbufFree (Nbuf); +} + +/** + Send a zero probe segment. It can be used by keepalive and zero window probe. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other An error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN OUT TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + INTN Result; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + // + // SndNxt-1 is out of window. The peer should respond + // with an ACK. + // + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt - 1; + Seg->End = Tcb->SndNxt - 1; + Seg->Flag = TCP_FLG_ACK; + + Result = TcpTransmitSegment (Tcb, Nbuf); + NetbufFree (Nbuf); + + return Result; +} + +/** + Check whether to send an ACK or delayed ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpToSendAck ( + IN OUT TCP_CB *Tcb + ) +{ + UINT32 TcpNow; + + // + // Generally, TCP should send a delayed ACK unless: + // 1. ACK at least every other FULL sized segment received. + // 2. Packets received out of order. + // 3. Receiving window is open. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Tcb->DelayedAck >= 1)) { + TcpSendAck (Tcb); + return; + } + + TcpNow = TcpRcvWinNow (Tcb); + + if (TcpNow > TcpRcvWinOld (Tcb)) { + TcpSendAck (Tcb); + return; + } + + DEBUG ( + (EFI_D_INFO, + "TcpToSendAck: scheduled a delayed ACK for TCB %p\n", + Tcb) + ); + + // + // Schedule a delayed ACK. + // + Tcb->DelayedAck++; +} + +/** + Send a RESET segment in response to the segment received. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. May be NULL. + @param[in] Head TCP header of the segment that triggers the reset. + @param[in] Len Length of the segment that triggers the reset. + @param[in] Local Local IP address. + @param[in] Remote Remote peer's 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. + + @retval 0 A reset was sent or there is no need to send it. + @retval -1 No reset is sent. + +**/ +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN EFI_IP_ADDRESS *Local, + IN EFI_IP_ADDRESS *Remote, + IN UINT8 Version + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + UINT16 HeadSum; + + // + // Don't respond to a Reset with reset. + // + if ((Head->Flag & TCP_FLG_RST) != 0) { + return 0; + } + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + Nhead = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_TAIL + ); + + ASSERT (Nhead != NULL); + + Nbuf->Tcp = Nhead; + Nhead->Flag = TCP_FLG_RST; + + // + // Derive Seq/ACK from the segment if no TCB + // is associated with it, otherwise derive from the Tcb. + // + if (Tcb == NULL) { + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) { + Nhead->Seq = Head->Ack; + Nhead->Ack = 0; + } else { + Nhead->Seq = 0; + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len); + } + } else { + + Nhead->Seq = HTONL (Tcb->SndNxt); + Nhead->Ack = HTONL (Tcb->RcvNxt); + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + } + + Nhead->SrcPort = Head->DstPort; + Nhead->DstPort = Head->SrcPort; + Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + + if (Version == IP_VERSION_4) { + HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0); + } else { + HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0); + } + + Nhead->Checksum = TcpChecksum (Nbuf, HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version); + + NetbufFree (Nbuf); + + return 0; +} + +/** + Verify that the segment is in good shape. + + @param[in] Nbuf The buffer that contains the segment to be checked. + + @retval 0 The segment is broken. + @retval 1 The segment is in good shape. + +**/ +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ) +{ + TCP_HEAD *Head; + TCP_SEG *Seg; + UINT32 Len; + + if (Nbuf == NULL) { + return 1; + } + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Seg = TCPSEG_NETBUF (Nbuf); + Len = Nbuf->TotalSize; + Head = Nbuf->Tcp; + + if (Head != NULL) { + if (Head->Flag != Seg->Flag) { + return 0; + } + + Len -= (Head->HeadLen << 2); + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + Len++; + } + + if (Seg->Seq + Len != Seg->End) { + return 0; + } + + return 1; +} + diff --git a/NetworkPkg/TcpDxe/TcpProto.h b/NetworkPkg/TcpDxe/TcpProto.h new file mode 100644 index 0000000000..88dfbb9d5c --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpProto.h @@ -0,0 +1,342 @@ +/** @file + TCP protocol header file. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _TCP_PROTO_H_ +#define _TCP_PROTO_H_ + +/// +/// Tcp states don't change their order. It is used as an +/// index to mTcpOutFlag and other macros. +/// +#define TCP_CLOSED 0 +#define TCP_LISTEN 1 +#define TCP_SYN_SENT 2 +#define TCP_SYN_RCVD 3 +#define TCP_ESTABLISHED 4 +#define TCP_FIN_WAIT_1 5 +#define TCP_FIN_WAIT_2 6 +#define TCP_CLOSING 7 +#define TCP_TIME_WAIT 8 +#define TCP_CLOSE_WAIT 9 +#define TCP_LAST_ACK 10 + + +/// +/// Flags in the TCP header +/// +#define TCP_FLG_FIN 0x01 +#define TCP_FLG_SYN 0x02 +#define TCP_FLG_RST 0x04 +#define TCP_FLG_PSH 0x08 +#define TCP_FLG_ACK 0x10 +#define TCP_FLG_URG 0x20 + + // + // mask for all the flags + // +#define TCP_FLG_FLAG 0x3F + + +#define TCP_CONNECT_REFUSED (-1) ///< TCP error status +#define TCP_CONNECT_RESET (-2) ///< TCP error status +#define TCP_CONNECT_CLOSED (-3) ///< TCP error status + +// +// Current congestion status as suggested by RFC3782. +// +#define TCP_CONGEST_RECOVER 1 ///< During the NewReno fast recovery. +#define TCP_CONGEST_LOSS 2 ///< Retxmit because of retxmit time out. +#define TCP_CONGEST_OPEN 3 ///< TCP is opening its congestion window. + +// +// TCP control flags +// +#define TCP_CTRL_NO_NAGLE 0x0001 ///< Disable Nagle algorithm +#define TCP_CTRL_NO_KEEPALIVE 0x0002 ///< Disable keepalive timer. +#define TCP_CTRL_NO_WS 0x0004 ///< Disable window scale option. +#define TCP_CTRL_RCVD_WS 0x0008 ///< Received a wnd scale option in syn. +#define TCP_CTRL_NO_TS 0x0010 ///< Disable Timestamp option. +#define TCP_CTRL_RCVD_TS 0x0020 ///< Received a Timestamp option in syn. +#define TCP_CTRL_SND_TS 0x0040 ///< Send Timestamp option to remote. +#define TCP_CTRL_SND_URG 0x0080 ///< In urgent send mode. +#define TCP_CTRL_RCVD_URG 0x0100 ///< In urgent receive mode. +#define TCP_CTRL_SND_PSH 0x0200 ///< In PUSH send mode. +#define TCP_CTRL_FIN_SENT 0x0400 ///< FIN is sent. +#define TCP_CTRL_FIN_ACKED 0x0800 ///< FIN is ACKed. +#define TCP_CTRL_TIMER_ON 0x1000 ///< At least one of the timer is on. +#define TCP_CTRL_RTT_ON 0x2000 ///< The RTT measurement is on. +#define TCP_CTRL_ACK_NOW 0x4000 ///< Send the ACK now, don't delay. + +// +// Timer related values +// +#define TCP_TIMER_CONNECT 0 ///< Connection establishment timer. +#define TCP_TIMER_REXMIT 1 ///< Retransmit timer. +#define TCP_TIMER_PROBE 2 ///< Window probe timer. +#define TCP_TIMER_KEEPALIVE 3 ///< Keepalive timer. +#define TCP_TIMER_FINWAIT2 4 ///< FIN_WAIT_2 timer. +#define TCP_TIMER_2MSL 5 ///< TIME_WAIT timer. +#define TCP_TIMER_NUMBER 6 ///< The total number of the TCP timer. +#define TCP_TICK 200 ///< Every TCP tick is 200ms. +#define TCP_TICK_HZ 5 ///< The frequence of TCP tick. +#define TCP_RTT_SHIFT 3 ///< SRTT & RTTVAR scaled by 8. +#define TCP_RTO_MIN TCP_TICK_HZ ///< The minium value of RTO. +#define TCP_RTO_MAX (TCP_TICK_HZ * 60) ///< The maxium value of RTO. +#define TCP_FOLD_RTT 4 ///< Timeout threshod to fold RTT. + +// +// Default values for some timers +// +#define TCP_MAX_LOSS 12 ///< Default max times to retxmit. +#define TCP_KEEPALIVE_IDLE_MIN (TCP_TICK_HZ * 60 * 60 * 2) ///< First keepalive. +#define TCP_KEEPALIVE_PERIOD (TCP_TICK_HZ * 60) +#define TCP_MAX_KEEPALIVE 8 +#define TCP_FIN_WAIT2_TIME (2 * TCP_TICK_HZ) +#define TCP_TIME_WAIT_TIME (2 * TCP_TICK_HZ) +#define TCP_PAWS_24DAY (24 * 24 * 60 * 60 * TCP_TICK_HZ) +#define TCP_CONNECT_TIME (75 * TCP_TICK_HZ) + +// +// The header space to be reserved before TCP data to accomodate : +// 60byte IP head + 60byte TCP head + link layer head +// +#define TCP_MAX_HEAD 192 + +// +// Value ranges for some control option +// +#define TCP_RCV_BUF_SIZE (2 * 1024 * 1024) +#define TCP_RCV_BUF_SIZE_MIN (8 * 1024) +#define TCP_SND_BUF_SIZE (2 * 1024 * 1024) +#define TCP_SND_BUF_SIZE_MIN (8 * 1024) +#define TCP_BACKLOG 10 +#define TCP_BACKLOG_MIN 5 +#define TCP_MAX_LOSS_MIN 6 +#define TCP_CONNECT_TIME_MIN (60 * TCP_TICK_HZ) +#define TCP_MAX_KEEPALIVE_MIN 4 +#define TCP_KEEPALIVE_IDLE_MAX (TCP_TICK_HZ * 60 * 60 * 4) +#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30) +#define TCP_FIN_WAIT2_TIME_MAX (4 * TCP_TICK_HZ) +#define TCP_TIME_WAIT_TIME_MAX (60 * TCP_TICK_HZ) + +/// +/// TCP_CONNECTED: both ends have synchronized their ISN. +/// +#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD) + +#define TCP_FIN_RCVD(State) \ + ( \ + ((State) == TCP_CLOSE_WAIT) || \ + ((State) == TCP_LAST_ACK) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT) \ + ) + +#define TCP_LOCAL_CLOSED(State) \ + ( \ + ((State) == TCP_FIN_WAIT_1) || \ + ((State) == TCP_FIN_WAIT_2) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT) || \ + ((State) == TCP_LAST_ACK) \ + ) + +// +// Get the TCP_SEG point from a net buffer's ProtoData. +// +#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData)) + +// +// Macros to compare sequence no +// +#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0) +#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0) +#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0) +#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0) + +// +// TCP_SEQ_BETWEEN return whether b <= m <= e +// +#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b)) + +// +// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2 +// +#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2))) + +// +// Check whether Flag is on +// +#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0)) +// +// Set and Clear operation on a Flag +// +#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag)) +#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag)) + +// +// Test whether two peers are equal +// +#define TCP_PEER_EQUAL(Pa, Pb, Ver) \ + (((Pa)->Port == (Pb)->Port) && TcpIsIpEqual(&((Pa)->Ip), &((Pb)->Ip), Ver)) + +// +// Test whether Pa matches Pb, or Pa is more specific +// than pb. Zero means wildcard. +// +#define TCP_PEER_MATCH(Pa, Pb, Ver) \ + ( \ + (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)) && \ + (TcpIsIpZero (&((Pb)->Ip), Ver) || TcpIsIpEqual (&((Pb)->Ip), &((Pa)->Ip), Ver)) \ + ) + +#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer))) +#define TCP_SET_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) | (1 << (Timer)))) +#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) & (~(1 << (Timer))))) + + +#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0) +#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0) +#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb))) + +#define TCP_MAX_WIN 0xFFFFU + +/// +/// TCP segmentation data. +/// +typedef struct _TCP_SEG { + TCP_SEQNO Seq; ///< Starting sequence number. + TCP_SEQNO End; ///< The sequence of the last byte + 1, include SYN/FIN. End-Seq = SEG.LEN. + TCP_SEQNO Ack; ///< ACK field in the segment. + UINT8 Flag; ///< TCP header flags. + UINT16 Urg; ///< Valid if URG flag is set. + UINT32 Wnd; ///< TCP window size field. +} TCP_SEG; + +/// +/// Network endpoint, IP plus Port structure. +/// +typedef struct _TCP_PEER { + EFI_IP_ADDRESS Ip; ///< IP address, in network byte order. + TCP_PORTNO Port; ///< Port number, in network byte order. +} TCP_PEER; + +typedef struct _TCP_CONTROL_BLOCK TCP_CB; + +/// +/// TCP control block: it includes various states. +/// +struct _TCP_CONTROL_BLOCK { + LIST_ENTRY List; ///< Back and forward link entry + TCP_CB *Parent; ///< The parent TCP_CB structure + + SOCKET *Sk; ///< The socket it controled. + TCP_PEER LocalEnd; ///< Local endpoint. + TCP_PEER RemoteEnd;///< Remote endpoint. + + LIST_ENTRY SndQue; ///< Retxmission queue. + LIST_ENTRY RcvQue; ///< Reassemble queue. + UINT32 CtrlFlag; ///< Control flags, such as NO_NAGLE. + INT32 Error; ///< Soft error status, such as TCP_CONNECT_RESET. + + // + // RFC793 and RFC1122 defined variables + // + UINT8 State; ///< TCP state, such as SYN_SENT, LISTEN. + UINT8 DelayedAck; ///< Number of delayed ACKs. + UINT16 HeadSum; ///< Checksum of the fixed parts of pesudo + ///< header: Src IP, Dst IP, 0, Protocol, + ///< do not include the TCP length. + + TCP_SEQNO Iss; ///< Initial Sending Sequence. + TCP_SEQNO SndUna; ///< First unacknowledged data. + TCP_SEQNO SndNxt; ///< Next data sequence to send. + TCP_SEQNO SndPsh; ///< Send PUSH point. + TCP_SEQNO SndUp; ///< Send urgent point. + UINT32 SndWnd; ///< Window advertised by the remote peer. + UINT32 SndWndMax; ///< Max send window advertised by the peer. + TCP_SEQNO SndWl1; ///< Seq number used for last window update. + TCP_SEQNO SndWl2; ///< Ack no of last window update. + UINT16 SndMss; ///< Max send segment size. + TCP_SEQNO RcvNxt; ///< Next sequence no to receive. + UINT32 RcvWnd; ///< Window advertised by the local peer. + TCP_SEQNO RcvWl2; ///< The RcvNxt (or ACK) of last window update. + ///< It is necessary because of delayed ACK. + + TCP_SEQNO RcvUp; ///< Urgent point; + TCP_SEQNO Irs; ///< Initial Receiving Sequence. + UINT16 RcvMss; ///< Max receive segment size. + UINT16 EnabledTimer; ///< Which timer is currently enabled. + UINT32 Timer[TCP_TIMER_NUMBER]; ///< When the timer will expire. + INT32 NextExpire; ///< Countdown offset for the nearest timer. + UINT32 Idle; ///< How long the connection is in idle. + UINT32 ProbeTime; ///< The time out value for current window prober. + BOOLEAN ProbeTimerOn;///< If TRUE, the probe time is on. + + // + // RFC1323 defined variables, about window scale, + // timestamp and PAWS + // + UINT8 SndWndScale; ///< Wndscale received from the peer. + UINT8 RcvWndScale; ///< Wndscale used to scale local buffer. + UINT32 TsRecent; ///< TsRecent to echo to the remote peer. + UINT32 TsRecentAge; ///< When this TsRecent is updated. + + // + // RFC2988 defined variables. about RTT measurement + // + TCP_SEQNO RttSeq; ///< The seq of measured segment now. + UINT32 RttMeasure; ///< Currently measured RTT in heartbeats. + UINT32 SRtt; ///< Smoothed RTT, scaled by 8. + UINT32 RttVar; ///< RTT variance, scaled by 8. + UINT32 Rto; ///< Current RTO, not scaled. + + // + // RFC2581, and 3782 variables. + // Congestion control + NewReno fast recovery. + // + UINT32 CWnd; ///< Sender's congestion window. + UINT32 Ssthresh; ///< Slow start threshold. + TCP_SEQNO Recover; ///< Recover point for NewReno. + UINT16 DupAck; ///< Number of duplicate ACKs. + UINT8 CongestState; ///< The current congestion state(RFC3782). + UINT8 LossTimes; ///< Number of retxmit timeouts in a row. + TCP_SEQNO LossRecover; ///< Recover point for retxmit. + + // + // configuration parameters, for EFI_TCP4_PROTOCOL specification + // + UINT32 KeepAliveIdle; ///< Idle time before sending first probe. + UINT32 KeepAlivePeriod; ///< Interval for subsequent keep alive probe. + UINT8 MaxKeepAlive; ///< Maxium keep alive probe times. + UINT8 KeepAliveProbes; ///< The number of keep alive probe. + UINT16 MaxRexmit; ///< The maxium number of retxmit before abort. + UINT32 FinWait2Timeout; ///< The FIN_WAIT_2 timeout. + UINT32 TimeWaitTimeout; ///< The TIME_WAIT timeout. + UINT32 ConnectTimeout; ///< The connect establishment timeout. + + // + // configuration for tcp provided by user + // + BOOLEAN UseDefaultAddr; + UINT8 Tos; + UINT8 Ttl; + EFI_IPv4_ADDRESS SubnetMask; + + IP_IO_IP_INFO *IpInfo; ///< Pointer reference to Ip used to send pkt + UINT32 Tick; ///< 1 tick = 200ms +}; + +#endif diff --git a/NetworkPkg/TcpDxe/TcpTimer.c b/NetworkPkg/TcpDxe/TcpTimer.c new file mode 100644 index 0000000000..cc52ad924c --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpTimer.c @@ -0,0 +1,593 @@ +/** @file + TCP timer related functions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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" + +UINT32 mTcpTick = 1000; + +/** + Connect timeout handler. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpConnectTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for TCP retransmission timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpRexmitTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for window probe timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpProbeTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for keepalive timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpKeepaliveTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for FIN_WAIT_2 timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpFinwait2Timeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for 2MSL timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +Tcp2MSLTimeout ( + IN OUT TCP_CB *Tcb + ); + +TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = { + TcpConnectTimeout, + TcpRexmitTimeout, + TcpProbeTimeout, + TcpKeepaliveTimeout, + TcpFinwait2Timeout, + Tcp2MSLTimeout, +}; + +/** + Close the TCP connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClose ( + IN OUT TCP_CB *Tcb + ) +{ + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + + TcpSetState (Tcb, TCP_CLOSED); +} + +/** + Backoff the RTO. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpBackoffRto ( + IN OUT TCP_CB *Tcb + ) +{ + // + // Fold the RTT estimate if too many times, the estimate + // may be wrong, fold it. So the next time a valid + // measurement is sampled, we can start fresh. + // + if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) { + Tcb->RttVar += Tcb->SRtt >> 2; + Tcb->SRtt = 0; + } + + Tcb->Rto <<= 1; + + if (Tcb->Rto < TCP_RTO_MIN) { + + Tcb->Rto = TCP_RTO_MIN; + } else if (Tcb->Rto > TCP_RTO_MAX) { + + Tcb->Rto = TCP_RTO_MAX; + } +} + +/** + Connect timeout handler. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpConnectTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + if (!TCP_CONNECTED (Tcb->State)) { + DEBUG ( + (EFI_D_ERROR, + "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n", + Tcb) + ); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + if (TCP_SYN_RCVD == Tcb->State) { + DEBUG ( + (EFI_D_WARN, + "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n", + Tcb) + ); + + TcpResetConnection (Tcb); + + } + + TcpClose (Tcb); + } +} + + +/** + Timeout handler for TCP retransmission timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpRexmitTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + UINT32 FlightSize; + + DEBUG ( + (EFI_D_WARN, + "TcpRexmitTimeout: transmission timeout for TCB %p\n", + Tcb) + ); + + // + // Set the congestion window. FlightSize is the + // amount of data that has been sent but not + // yet ACKed. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2); + + Tcb->CWnd = Tcb->SndMss; + Tcb->LossRecover = Tcb->SndNxt; + + Tcb->LossTimes++; + if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) { + + DEBUG ( + (EFI_D_ERROR, + "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n", + Tcb) + ); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpBackoffRto (Tcb); + TcpRetransmit (Tcb, Tcb->SndUna); + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + + Tcb->CongestState = TCP_CONGEST_LOSS; + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); +} + +/** + Timeout handler for window probe timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpProbeTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + // + // This is the timer for sender's SWSA. RFC1122 requires + // a timer set for sender's SWSA, and suggest combine it + // with window probe timer. If data is sent, don't set + // the probe timer, since retransmit timer is on. + // + if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) { + + ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0); + Tcb->ProbeTimerOn = FALSE; + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetProbeTimer (Tcb); +} + +/** + Timeout handler for keepalive timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpKeepaliveTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + Tcb->KeepAliveProbes++; + + // + // Too many Keep-alive probes, drop the connection + // + if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) { + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetKeepaliveTimer (Tcb); +} + +/** + Timeout handler for FIN_WAIT_2 timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpFinwait2Timeout ( + IN OUT TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n", + Tcb) + ); + + TcpClose (Tcb); +} + +/** + Timeout handler for 2MSL timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +Tcp2MSLTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n", + Tcb) + ); + + TcpClose (Tcb); +} + +/** + Update the timer status and the next expire time according to the timers + to expire in a specific future time slot. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpUpdateTimer ( + IN OUT TCP_CB *Tcb + ) +{ + UINT16 Index; + + // + // Don't use a too large value to init NextExpire + // since mTcpTick wraps around as sequence no does. + // + Tcb->NextExpire = TCP_EXPIRE_TIME; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && + TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire) + ) { + + Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + } + } +} + +/** + Enable a TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be enabled. + @param[in] TimeOut The timeout value of this timer. + +**/ +VOID +TcpSetTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ) +{ + TCP_SET_TIMER (Tcb->EnabledTimer, Timer); + Tcb->Timer[Timer] = mTcpTick + TimeOut; + + TcpUpdateTimer (Tcb); +} + +/** + Clear one TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be cleared. + +**/ +VOID +TcpClearTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer + ) +{ + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer); + TcpUpdateTimer (Tcb); +} + +/** + Clear all TCP timers. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClearAllTimer ( + IN OUT TCP_CB *Tcb + ) +{ + Tcb->EnabledTimer = 0; + TcpUpdateTimer (Tcb); +} + +/** + Enable the window prober timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetProbeTimer ( + IN OUT TCP_CB *Tcb + ) +{ + if (!Tcb->ProbeTimerOn) { + Tcb->ProbeTime = Tcb->Rto; + Tcb->ProbeTimerOn = TRUE; + + } else { + Tcb->ProbeTime <<= 1; + } + + if (Tcb->ProbeTime < TCP_RTO_MIN) { + + Tcb->ProbeTime = TCP_RTO_MIN; + } else if (Tcb->ProbeTime > TCP_RTO_MAX) { + + Tcb->ProbeTime = TCP_RTO_MAX; + } + + TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime); +} + +/** + Enable the keepalive timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN OUT TCP_CB *Tcb + ) +{ + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) { + return ; + + } + + // + // Set the timer to KeepAliveIdle if either + // 1. the keepalive timer is off + // 2. The keepalive timer is on, but the idle + // is less than KeepAliveIdle, that means the + // connection is alive since our last probe. + // + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) || + (Tcb->Idle < Tcb->KeepAliveIdle) + ) { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle); + Tcb->KeepAliveProbes = 0; + + } else { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod); + } +} + +/** + Heart beat timer handler. + + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTickingDpc ( + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + TCP_CB *Tcb; + INT16 Index; + + mTcpTick++; + mTcpGlobalIss += TCP_ISS_INCREMENT_2; + + // + // Don't use LIST_FOR_EACH, which isn't delete safe. + // + for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) { + + Next = Entry->ForwardLink; + + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (Tcb->State == TCP_CLOSED) { + continue; + } + // + // The connection is doing RTT measurement. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + Tcb->RttMeasure++; + } + + Tcb->Idle++; + + if (Tcb->DelayedAck != 0) { + TcpSendAck (Tcb); + } + + if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) { + Tcb->Tick--; + } + + // + // No timer is active or no timer expired + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) { + + continue; + } + + // + // Call the timeout handler for each expired timer. + // + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) { + // + // disable the timer before calling the handler + // in case the handler enables it again. + // + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index); + mTcpTimerHandler[Index](Tcb); + + // + // The Tcb may have been deleted by the timer, or + // no other timer is set. + // + if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) { + break; + } + } + } + + // + // If the Tcb still exist or some timer is set, update the timer + // + if (Index == TCP_TIMER_NUMBER) { + TcpUpdateTimer (Tcb); + } + } +} + +/** + Heart beat timer handler, queues the DPC at TPL_CALLBACK. + + @param[in] Event Timer event signaled, ignored. + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context); +} + diff --git a/NetworkPkg/Udp6Dxe/ComponentName.c b/NetworkPkg/Udp6Dxe/ComponentName.c new file mode 100644 index 0000000000..d7df0965a0 --- /dev/null +++ b/NetworkPkg/Udp6Dxe/ComponentName.c @@ -0,0 +1,313 @@ +/** @file + UEFI Component Name(2) protocol implementation for UDP6 driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Udp6Impl.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName = { + Udp6ComponentNameGetDriverName, + Udp6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Udp6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Udp6ComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdp6DriverNameTable[] = { + { + "eng;en", + L"UDP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUdp6DriverNameTable, + DriverName, + (BOOLEAN) (This == &gUdp6ComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.c b/NetworkPkg/Udp6Dxe/Udp6Driver.c new file mode 100644 index 0000000000..1cfd5f1b4d --- /dev/null +++ b/NetworkPkg/Udp6Dxe/Udp6Driver.c @@ -0,0 +1,556 @@ +/** @file + Driver Binding functions and Service Binding functions for the Network driver module. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Udp6Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gUdp6DriverBinding = { + Udp6DriverBindingSupported, + Udp6DriverBindingStart, + Udp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding = { + Udp6ServiceBindingCreateChild, + Udp6ServiceBindingDestroyChild +}; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + // + // Test for the Udp6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + // + // Test for the Ip6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind the driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_SERVICE_DATA *Udp6Service; + + // + // Allocate Private Context Data Structure. + // + Udp6Service = AllocateZeroPool (sizeof (UDP6_SERVICE_DATA)); + if (Udp6Service == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Status = Udp6CreateService (Udp6Service, This->DriverBindingHandle, ControllerHandle); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Install the Udp6ServiceBindingProtocol on the ControllerHandle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Udp6Service->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + Udp6CleanService (Udp6Service); + goto EXIT; + } else { + Status = Udp6SetVariableData (Udp6Service); + } + +EXIT: + if (EFI_ERROR (Status)) { + if (Udp6Service != NULL) { + FreePool (Udp6Service); + } + } + return Status; +} + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number + of children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional. + + @retval EFI_SUCCES This driver is removed ControllerHandle. + @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UDP6_SERVICE_DATA *Udp6Service; + UDP6_INSTANCE_DATA *Instance; + + // + // Find the NicHandle where UDP6 ServiceBinding Protocol is installed. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid); + if (NicHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Retrieve the UDP6 ServiceBinding Protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (ServiceBinding); + + if (NumberOfChildren == 0) { + + gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Udp6Service->ServiceBinding, + NULL + ); + + Udp6ClearVariableData (Udp6Service); + + Udp6CleanService (Udp6Service); + + FreePool (Udp6Service); + } else { + + while (!IsListEmpty (&Udp6Service->ChildrenList)) { + Instance = NET_LIST_HEAD (&Udp6Service->ChildrenList, UDP6_INSTANCE_DATA, Link); + + Status = ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle); + } + } + + return Status; +} + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + EFI_STATUS Status; + UDP6_SERVICE_DATA *Udp6Service; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + VOID *Ip6; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This); + + // + // Allocate the instance private data structure. + // + Instance = AllocateZeroPool (sizeof (UDP6_INSTANCE_DATA)); + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Udp6InitInstance (Udp6Service, Instance); + + // + // Add an IpInfo for this instance. + // + Instance->IpInfo = IpIoAddIp (Udp6Service->IpIo); + if (Instance->IpInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Install the Udp6Protocol for this instance. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiUdp6ProtocolGuid, + &Instance->Udp6Proto, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->ChildHandle = *ChildHandle; + + // + // Open the default Ip6 protocol in the IP_IO BY_CHILD. + // + Status = gBS->OpenProtocol ( + Udp6Service->IpIo->ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Ip6, + gUdp6DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Link this instance into the service context data and increase the ChildrenNumber. + // + InsertTailList (&Udp6Service->ChildrenList, &Instance->Link); + Udp6Service->ChildrenNumber++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + if (Instance->ChildHandle != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->ChildHandle, + &gEfiUdp6ProtocolGuid, + &Instance->Udp6Proto, + NULL + ); + } + + if (Instance->IpInfo != NULL) { + IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo); + } + + Udp6CleanInstance (Instance); + + FreePool (Instance); + + return Status; +} + +/** + Destroys a child handle with a set of I/O services. + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + UDP6_SERVICE_DATA *Udp6Service; + EFI_UDP6_PROTOCOL *Udp6Proto; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This); + + // + // Try to get the Udp6 protocol from the ChildHandle. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6Proto, + gUdp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (Udp6Proto); + + if (Instance->Destroyed) { + return EFI_SUCCESS; + } + + // + // Use the Destroyed flag to avoid the re-entering of the following code. + // + Instance->Destroyed = TRUE; + + // + // Close the Ip6 protocol. + // + gBS->CloseProtocol ( + Udp6Service->IpIo->ChildHandle, + &gEfiIp6ProtocolGuid, + gUdp6DriverBinding.DriverBindingHandle, + Instance->ChildHandle + ); + + // + // Uninstall the Udp6Protocol previously installed on the ChildHandle. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiUdp6ProtocolGuid, + (VOID *) &Instance->Udp6Proto, + NULL + ); + if (EFI_ERROR (Status)) { + Instance->Destroyed = FALSE; + return Status; + } + + // + // Reset the configuration in case the instance's consumer forgets to do this. + // + Udp6Proto->Configure (Udp6Proto, NULL); + + // + // Remove the IpInfo this instance consumes. + // + IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Remove this instance from the service context data's ChildrenList. + // + RemoveEntryList (&Instance->Link); + Udp6Service->ChildrenNumber--; + + // + // Clean the instance. + // + Udp6CleanInstance (Instance); + + gBS->RestoreTPL (OldTpl); + + FreePool (Instance); + + return EFI_SUCCESS; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for Udp6 driver that installs the driver binding + and component name protocol on its ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install the Udp6DriverBinding and Udp6ComponentName protocols. + // + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUdp6DriverBinding, + ImageHandle, + &gUdp6ComponentName, + &gUdp6ComponentName2 + ); + if (!EFI_ERROR (Status)) { + // + // Initialize the UDP random port. + // + mUdp6RandomPort = (UINT16)( + ((UINT16) NetRandomInitSeed ()) % + UDP6_PORT_KNOWN + + UDP6_PORT_KNOWN + ); + } + + return Status; +} + + diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.h b/NetworkPkg/Udp6Dxe/Udp6Driver.h new file mode 100644 index 0000000000..e5a923be68 --- /dev/null +++ b/NetworkPkg/Udp6Dxe/Udp6Driver.h @@ -0,0 +1,182 @@ +/** @file + Driver Binding functions and Service Binding functions for the Network driver module. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _UDP6_DRIVER_H_ +#define _UDP6_DRIVER_H_ + +#include +#include +#include + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind a driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional. + + @retval EFI_SUCCESS This driver removed ControllerHandle. + @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of I/O services. + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif + diff --git a/NetworkPkg/Udp6Dxe/Udp6Dxe.inf b/NetworkPkg/Udp6Dxe/Udp6Dxe.inf new file mode 100644 index 0000000000..30b2bc1cc7 --- /dev/null +++ b/NetworkPkg/Udp6Dxe/Udp6Dxe.inf @@ -0,0 +1,63 @@ +## @file Udp6Dxe.inf +# Component description file for Udp6 module. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Udp6Dxe + FILE_GUID = D912C7BC-F098-4367-92BA-E911083C7B0E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = Udp6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + Udp6Driver.h + Udp6Driver.c + Udp6Impl.c + Udp6Impl.h + ComponentName.c + Udp6Main.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + UefiLib + DebugLib + IpIoLib + NetLib + DpcLib + + +[Protocols] + gEfiIp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUdp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUdp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.c b/NetworkPkg/Udp6Dxe/Udp6Impl.c new file mode 100644 index 0000000000..8e259319b9 --- /dev/null +++ b/NetworkPkg/Udp6Dxe/Udp6Impl.c @@ -0,0 +1,2131 @@ +/** @file + Udp6 driver's whole implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Udp6Impl.h" + +UINT16 mUdp6RandomPort; + +/** + This function checks and timeouts the I/O datagrams holding by the corresponding + service context. + + @param[in] Event The event this function is registered to. + @param[in] Context The context data registered during the creation of + the Event. + +**/ +VOID +EFIAPI +Udp6CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This function finds the udp instance by the specified pair. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] Address Pointer to the specified IPv6 address. + @param[in] Port The udp port number. + + @retval TRUE The specified pair is found. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp6FindInstanceByPort ( + IN LIST_ENTRY *InstanceList, + IN EFI_IPv6_ADDRESS *Address, + IN UINT16 Port + ); + +/** + This function is the packet transmitting notify function registered to the IpIo + interface. It's called to signal the udp TxToken when the IpIo layer completes + transmitting of the udp datagram. + + @param[in] Status The completion status of the output udp datagram. + @param[in] Context Pointer to the context data. + @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending. + @param[in] NotifyData Pointer to the notify data. + +**/ +VOID +EFIAPI +Udp6DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN IP_IO_IP_PROTOCOL Sender, + IN VOID *NotifyData + ); + +/** + This function processes the received datagram passed up by the IpIo layer. + + @param[in] Status The status of this udp datagram. + @param[in] IcmpError The IcmpError code, only available when Status is + EFI_ICMP_ERROR. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA. + @param[in] Packet Pointer to the NET_BUF containing the received udp + datagram. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6DgramRcvd ( + IN EFI_STATUS Status, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ); + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled, if NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token + is not the same as that in the Item if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Udp6CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +/** + This function check if the received udp datagram matches with the Instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted + from the received udp datagram. + + @retval TRUE The udp datagram matches the receiving requirements of the Instance. + @retval FALSE The udp datagram doe not match the receiving requirements of the Instance. + +**/ +BOOLEAN +Udp6MatchDgram ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_SESSION_DATA *Udp6Session + ); + +/** + This function removes the Wrap specified by Context and releases relevant resources. + + @param[in] Event The Event this notify function is registered to. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This function wraps the Packet into RxData. + + @param[in] Instance Pointer to the instance context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return Pointer to the structure wrapping the RxData and the Packet. + +**/ +UDP6_RXDATA_WRAP * +Udp6WrapRxData ( + IN UDP6_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ); + +/** + This function enqueues the received datagram into the instances' receiving queues. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return The times this datagram is enqueued. + +**/ +UINTN +Udp6EnqueueDgram ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ); + +/** + This function delivers the datagrams enqueued in the instances. + + @param[in] Udp6Service Pointer to the udp service context data. + +**/ +VOID +Udp6DeliverDgram ( + IN UDP6_SERVICE_DATA *Udp6Service + ); + +/** + This function demultiplexes the received udp datagram to the apropriate instances. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from + the received datagram. + @param[in] Packet Pointer to the buffer containing the received udp + datagram. + +**/ +VOID +Udp6Demultiplex ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ); + +/** + This function handles the received Icmp Error message and demultiplexes it to the + instance. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] IcmpError The icmp error code. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted + from the received Icmp Error packet. + @param[in, out] Packet Pointer to the Icmp Error packet. + +**/ +VOID +Udp6IcmpHandler ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN OUT NET_BUF *Packet + ); + +/** + This function builds and sends out a icmp port unreachable message. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet + causes this icmp error message. + @param[in] Udp6Header Pointer to the udp header of the datagram causes + this icmp error message. + +**/ +VOID +Udp6SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp6Header + ); + +/** + Find the key in the netmap + + @param[in] Map The netmap to search within. + @param[in] Key The key to search. + + @return The point to the item contains the Key, or NULL if Key isn't in the map. + +**/ +NET_MAP_ITEM * +Udp6MapMultiCastAddr ( + IN NET_MAP *Map, + IN VOID *Key + ); + +/** + Create the Udp service context data. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in] ImageHandle The image handle of this udp6 driver. + @param[in] ControllerHandle The controller handle this udp6 driver binds on. + + @retval EFI_SUCCESS The udp6 service context data was created and + initialized. + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory. + @retval Others An error condition occurred. + +**/ +EFI_STATUS +Udp6CreateService ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_STATUS Status; + IP_IO_OPEN_DATA OpenData; + + ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA)); + + Udp6Service->Signature = UDP6_SERVICE_DATA_SIGNATURE; + Udp6Service->ServiceBinding = mUdp6ServiceBinding; + Udp6Service->ImageHandle = ImageHandle; + Udp6Service->ControllerHandle = ControllerHandle; + Udp6Service->ChildrenNumber = 0; + + InitializeListHead (&Udp6Service->ChildrenList); + + // + // Create the IpIo for this service context. + // + Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6); + if (Udp6Service->IpIo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the OpenData used to open the IpIo. + // + CopyMem ( + &OpenData.IpConfigData.Ip6CfgData, + &mIp6IoDefaultIpConfigData, + sizeof (EFI_IP6_CONFIG_DATA) + ); + OpenData.RcvdContext = (VOID *) Udp6Service; + OpenData.SndContext = NULL; + OpenData.PktRcvdNotify = Udp6DgramRcvd; + OpenData.PktSentNotify = Udp6DgramSent; + + // + // Configure and start the IpIo. + // + Status = IpIoOpen (Udp6Service->IpIo, &OpenData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create the event for Udp timeout checking. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Udp6CheckTimeout, + Udp6Service, + &Udp6Service->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Start the timeout timer event. + // + Status = gBS->SetTimer ( + Udp6Service->TimeoutEvent, + TimerPeriodic, + UDP6_TIMEOUT_INTERVAL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (Udp6Service->TimeoutEvent != NULL) { + gBS->CloseEvent (Udp6Service->TimeoutEvent); + } + + IpIoDestroy (Udp6Service->IpIo); + + return Status; +} + + +/** + Clean the Udp service context data. + + @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA. + +**/ +VOID +Udp6CleanService ( + IN OUT UDP6_SERVICE_DATA *Udp6Service + ) +{ + // + // Close the TimeoutEvent timer. + // + gBS->CloseEvent (Udp6Service->TimeoutEvent); + + // + // Destroy the IpIo. + // + IpIoDestroy (Udp6Service->IpIo); +} + + +/** + This function checks and times out the I/O datagrams listed in the + UDP6_SERVICE_DATA which is specified by the input parameter Context. + + + @param[in] Event The event this function registered to. + @param[in] Context The context data registered during the creation of + the Event. + +**/ +VOID +EFIAPI +Udp6CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP6_SERVICE_DATA *Udp6Service; + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + LIST_ENTRY *WrapEntry; + LIST_ENTRY *NextEntry; + UDP6_RXDATA_WRAP *Wrap; + + Udp6Service = (UDP6_SERVICE_DATA *) Context; + NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE); + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate all the instances belonging to this service context. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + NET_CHECK_SIGNATURE (Instance, UDP6_INSTANCE_DATA_SIGNATURE); + + if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) { + // + // Skip this instance if it's not configured or no receive timeout. + // + continue; + } + + NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) { + // + // Iterate all the rxdatas belonging to this udp instance. + // + Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP6_RXDATA_WRAP, Link); + + if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) { + // + // Remove this RxData if it timeouts. + // + Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap); + } else { + Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10; + } + } + } +} + + +/** + This function intializes the new created udp instance. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA. + +**/ +VOID +Udp6InitInstance ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN OUT UDP6_INSTANCE_DATA *Instance + ) +{ + // + // Set the signature. + // + Instance->Signature = UDP6_INSTANCE_DATA_SIGNATURE; + + // + // Init the lists. + // + InitializeListHead (&Instance->Link); + InitializeListHead (&Instance->RcvdDgramQue); + InitializeListHead (&Instance->DeliveredDgramQue); + + // + // Init the NET_MAPs. + // + NetMapInit (&Instance->TxTokens); + NetMapInit (&Instance->RxTokens); + NetMapInit (&Instance->McastIps); + + // + // Save the pointer to the UDP6_SERVICE_DATA, and initialize other members. + // + Instance->Udp6Service = Udp6Service; + CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL)); + Instance->IcmpError = EFI_SUCCESS; + Instance->Configured = FALSE; + Instance->IsNoMapping = FALSE; + Instance->Destroyed = FALSE; +} + + +/** + This function cleans the udp instance. + + @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean. + +**/ +VOID +Udp6CleanInstance ( + IN OUT UDP6_INSTANCE_DATA *Instance + ) +{ + NetMapClean (&Instance->McastIps); + NetMapClean (&Instance->RxTokens); + NetMapClean (&Instance->TxTokens); +} + + +/** + This function finds the udp instance by the specified pair. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] Address Pointer to the specified IPv6 address. + @param[in] Port The udp port number. + + @retval TRUE The specified pair is found. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp6FindInstanceByPort ( + IN LIST_ENTRY *InstanceList, + IN EFI_IPv6_ADDRESS *Address, + IN UINT16 Port + ) +{ + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + EFI_UDP6_CONFIG_DATA *ConfigData; + + NET_LIST_FOR_EACH (Entry, InstanceList) { + // + // Iterate all the udp instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + ConfigData = &Instance->ConfigData; + + if (!Instance->Configured || ConfigData->AcceptAnyPort) { + // + // If the instance is not configured, or the configdata of the instance indicates + // this instance accepts any port, skip it. + // + continue; + } + + if (EFI_IP6_EQUAL (&ConfigData->StationAddress, Address) && + (ConfigData->StationPort == Port) + ) { + // + // If both the address and the port are the same, return TRUE. + // + return TRUE; + } + } + + // + // Return FALSE when matching fails. + // + return FALSE; +} + + +/** + This function tries to bind the udp instance according to the configured port + allocation stragety. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] ConfigData Pointer to the ConfigData of the instance to be + bound. + + @retval EFI_SUCCESS The bound operation completed successfully. + @retval EFI_ACCESS_DENIED The specified by the ConfigData is + already used by other instance. + @retval EFI_OUT_OF_RESOURCES No available port resources. + +**/ +EFI_STATUS +Udp6Bind ( + IN LIST_ENTRY *InstanceList, + IN EFI_UDP6_CONFIG_DATA *ConfigData + ) +{ + EFI_IPv6_ADDRESS *StationAddress; + UINT16 StartPort; + + if (ConfigData->AcceptAnyPort) { + return EFI_SUCCESS; + } + + StationAddress = &ConfigData->StationAddress; + + if (ConfigData->StationPort != 0) { + + if (!ConfigData->AllowDuplicatePort && + Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort) + ) { + // + // Do not allow duplicate ports and the port is already used by other instance. + // + return EFI_ACCESS_DENIED; + } + } else { + // + // Select a random port for this instance. + // + if (ConfigData->AllowDuplicatePort) { + // + // Just pick up the random port if the instance allows duplicate port. + // + ConfigData->StationPort = mUdp6RandomPort; + } else { + + StartPort = mUdp6RandomPort; + + while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) { + + mUdp6RandomPort++; + if (mUdp6RandomPort == 0) { + mUdp6RandomPort = UDP6_PORT_KNOWN; + } + + if (mUdp6RandomPort == StartPort) { + // + // No available port. + // + return EFI_OUT_OF_RESOURCES; + } + } + + ConfigData->StationPort = mUdp6RandomPort; + } + + mUdp6RandomPort++; + if (mUdp6RandomPort == 0) { + mUdp6RandomPort = UDP6_PORT_KNOWN; + } + } + return EFI_SUCCESS; +} + + +/** + This function is used to check whether the NewConfigData has any un-reconfigurable + parameters changed compared to the OldConfigData. + + @param[in] OldConfigData Pointer to the current ConfigData the udp instance + uses. + @param[in] NewConfigData Pointer to the new ConfigData. + + @retval TRUE The instance is reconfigurable according to the NewConfigData. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp6IsReconfigurable ( + IN EFI_UDP6_CONFIG_DATA *OldConfigData, + IN EFI_UDP6_CONFIG_DATA *NewConfigData + ) +{ + if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) || + (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) || + (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort) + ) { + // + // The receiving filter parameters cannot be changed. + // + return FALSE; + } + + if ((!NewConfigData->AcceptAnyPort) && + (NewConfigData->StationPort != OldConfigData->StationPort) + ) { + // + // The port is not changeable. + // + return FALSE; + } + + if (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) { + // + // The StationAddress is not the same. + // + return FALSE; + } + + + if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) { + // + // The remoteaddress is not the same. + // + return FALSE; + } + + if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) && + (NewConfigData->RemotePort != OldConfigData->RemotePort) + ) { + // + // The RemotePort differs if it's designated in the configdata. + // + return FALSE; + } + + // + // All checks pass, return TRUE. + // + return TRUE; +} + + +/** + This function builds the Ip6 configdata from the Udp6ConfigData. + + @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA. + @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA. + +**/ +VOID +Udp6BuildIp6ConfigData ( + IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData, + IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData + ) +{ + CopyMem ( + Ip6ConfigData, + &mIp6IoDefaultIpConfigData, + sizeof (EFI_IP6_CONFIG_DATA) + ); + Ip6ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP; + Ip6ConfigData->AcceptPromiscuous = Udp6ConfigData->AcceptPromiscuous; + IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress); + IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress); + // + // Use the -1 magic number to disable the receiving process of the ip instance. + // + Ip6ConfigData->ReceiveTimeout = (UINT32) (-1); +} + + +/** + This function validates the TxToken. It returns the error code according to the spec. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] TxToken Pointer to the token to be checked. + + @retval EFI_SUCCESS The TxToken is valid. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + Token.Event is NULL; + Token.Packet.TxData is NULL; + Token.Packet.TxData.FragmentCount is zero; + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths; + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentLength + fields is zero; + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentBuffer + fields is NULL; + UdpSessionData.DestinationAddress are not valid + unicast IPv6 addresses if the UdpSessionData is + not NULL; + UdpSessionData.DestinationPort and + ConfigData.RemotePort are all zero if the + UdpSessionData is not NULL. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. + +**/ +EFI_STATUS +Udp6ValidateTxToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *TxToken + ) +{ + EFI_UDP6_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 TotalLen; + EFI_UDP6_CONFIG_DATA *ConfigData; + EFI_UDP6_SESSION_DATA *UdpSessionData; + + + if (TxToken->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + TxData = TxToken->Packet.TxData; + + if ((TxData == NULL) || (TxData->FragmentCount == 0)) { + return EFI_INVALID_PARAMETER; + } + + TotalLen = 0; + for (Index = 0; Index < TxData->FragmentCount; Index++) { + + if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) || + (TxData->FragmentTable[Index].FragmentLength == 0) + ) { + // + // If the FragmentBuffer is NULL, or the FragmentLeng is zero. + // + return EFI_INVALID_PARAMETER; + } + + TotalLen += TxData->FragmentTable[Index].FragmentLength; + } + + if (TotalLen != TxData->DataLength) { + // + // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the + // DataLength. + // + return EFI_INVALID_PARAMETER; + } + + ConfigData = &Instance->ConfigData; + UdpSessionData = TxData->UdpSessionData; + + if (UdpSessionData != NULL) { + + if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) { + // + // Ambiguous; no avalaible DestinationPort for this token. + // + return EFI_INVALID_PARAMETER; + } + + if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) && + NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) + ) { + // + // The DestinationAddress is not specificed. + // + return EFI_INVALID_PARAMETER; + } + + if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) && + !NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) + ) { + // + // The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress + // is not zero too. + // + return EFI_INVALID_PARAMETER; + } + } else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) { + // + // The configured RemoteAddress is all zero, and the user doesn't override the + // destination address. + // + return EFI_INVALID_PARAMETER; + } + + if (TxData->DataLength > UDP6_MAX_DATA_SIZE) { + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + + +/** + This function checks whether the specified Token duplicates the one in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to + the Token. + @param[in] Context Pointer to the Token to be checked. + + @retval EFI_SUCCESS The Token specified by Context differs from the + one in the Item. + @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item. + +**/ +EFI_STATUS +EFIAPI +Udp6TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_UDP6_COMPLETION_TOKEN *Token; + EFI_UDP6_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_UDP6_COMPLETION_TOKEN *) Context; + TokenInItem = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key; + + if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) { + // + // The Token duplicates with the TokenInItem in case either the two pointers are the + // same, or the Events of these two tokens are the same. + // + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + This function calculates the checksum for the Packet, utilizing the pre-calculated + pseudo HeadSum to reduce some overhead. + + @param[in] Packet Pointer to the NET_BUF contains the udp datagram. + @param[in] HeadSum Checksum of the pseudo header, execpt the length + field. + + @return The 16-bit checksum of this udp datagram. + +**/ +UINT16 +Udp6Checksum ( + IN NET_BUF *Packet, + IN UINT16 HeadSum + ) +{ + UINT16 Checksum; + + Checksum = NetbufChecksum (Packet); + Checksum = NetAddChecksum (Checksum, HeadSum); + + Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize)); + Checksum = (UINT16) (~Checksum); + return Checksum; +} + + +/** + This function removes the specified Token from the TokenMap. + + @param[in] TokenMap Pointer to the NET_MAP containing the tokens. + @param[in] Token Pointer to the Token to be removed. + + @retval EFI_SUCCESS The specified Token is removed from the TokenMap. + @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap. + +**/ +EFI_STATUS +Udp6RemoveToken ( + IN NET_MAP *TokenMap, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ) +{ + NET_MAP_ITEM *Item; + + // + // Find the Token first. + // + Item = NetMapFindKey (TokenMap, (VOID *) Token); + + if (Item != NULL) { + // + // Remove the token if it's found in the map. + // + NetMapRemoveItem (TokenMap, Item, NULL); + + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; +} + + +/** + This function is the packet transmitting notify function registered to the IpIo + interface. It's called to signal the udp TxToken when IpIo layer completes the + transmitting of the udp datagram. + + @param[in] Status The completion status of the output udp datagram. + @param[in] Context Pointer to the context data. + @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending. + @param[in] NotifyData Pointer to the notify data. + +**/ +VOID +EFIAPI +Udp6DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN IP_IO_IP_PROTOCOL Sender, + IN VOID *NotifyData + ) +{ + UDP6_INSTANCE_DATA *Instance; + EFI_UDP6_COMPLETION_TOKEN *Token; + + Instance = (UDP6_INSTANCE_DATA *) Context; + Token = (EFI_UDP6_COMPLETION_TOKEN *) NotifyData; + + if (Udp6RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) { + // + // The token may be cancelled. Only signal it if the remove operation succeeds. + // + Token->Status = Status; + gBS->SignalEvent (Token->Event); + DispatchDpc (); + } +} + + +/** + This function processes the received datagram passed up by the IpIo layer. + + @param[in] Status The status of this udp datagram. + @param[in] IcmpError The IcmpError code, only available when Status is + EFI_ICMP_ERROR. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA. + @param[in] Packet Pointer to the NET_BUF containing the received udp + datagram. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6DgramRcvd ( + IN EFI_STATUS Status, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ) +{ + NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE); + + // + // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR. + // + if (Status == EFI_SUCCESS) { + + // + // Demultiplex the received datagram. + // + Udp6Demultiplex ((UDP6_SERVICE_DATA *) Context, NetSession, Packet); + } else { + // + // Handle the ICMP6 Error packet. + // + Udp6IcmpHandler ((UDP6_SERVICE_DATA *) Context, IcmpError, NetSession, Packet); + } + + // + // Dispatch the DPC queued by the NotifyFunction of the rx token's events + // that are signaled with received data. + // + DispatchDpc (); +} + + +/** + This function removes the multicast group specified by Arg from the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the Arg, it's the pointer to a + multicast IPv6 Address. This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The multicast address is removed. + @retval EFI_ABORTED The specified multicast address is removed, and the + Arg is not NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6LeaveGroup ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_IPv6_ADDRESS *McastIp; + + McastIp = Arg; + + if ((McastIp != NULL) && + !EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key)) + ) { + // + // McastIp is not NULL and the multicast address contained in the Item + // is not the same as McastIp. + // + return EFI_SUCCESS; + } + + FreePool (Item->Key); + + // + // Remove this Item. + // + NetMapRemoveItem (Map, Item, NULL); + + if (McastIp != NULL) { + // + // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled. If NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token + is not the same as that in the Item, if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Udp6CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_UDP6_COMPLETION_TOKEN *TokenToCancel; + NET_BUF *Packet; + IP_IO *IpIo; + + if ((Arg != NULL) && (Item->Key != Arg)) { + return EFI_SUCCESS; + } + + if (Item->Value != NULL) { + // + // If the token is a transmit token, the corresponding Packet is recorded in + // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken + // will invoke Udp6DgramSent, the token will be signaled and this Item will + // be removed from the Map there. + // + Packet = (NET_BUF *) (Item->Value); + IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0])); + + IpIoCancelTxToken (IpIo, Packet); + } else { + // + // The token is a receive token. Abort it and remove it from the Map. + // + TokenToCancel = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key; + NetMapRemoveItem (Map, Item, NULL); + + TokenToCancel->Status = EFI_ABORTED; + gBS->SignalEvent (TokenToCancel->Event); + } + + if (Arg != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function removes all the Wrap datas in the RcvdDgramQue. + + @param[in] Instance Pointer to the Udp6 Instance. + +**/ +VOID +Udp6FlushRcvdDgram ( + IN UDP6_INSTANCE_DATA *Instance + ) +{ + UDP6_RXDATA_WRAP *Wrap; + + while (!IsListEmpty (&Instance->RcvdDgramQue)) { + // + // Iterate all the Wraps in the RcvdDgramQue. + // + Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link); + + // + // The Wrap will be removed from the RcvdDgramQue by this function call. + // + Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap); + } +} + + + +/** + Cancel Udp6 tokens from the Udp6 instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Udp6InstanceCancelToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Cancel this token from the TxTokens map. + // + Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_ABORTED)) { + // + // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from + // the TxTokens and returns success. + // + return EFI_SUCCESS; + } + + // + // Try to cancel this token from the RxTokens map in condition either the Token + // is NULL or the specified Token is not in TxTokens. + // + Status = NetMapIterate (&Instance->RxTokens, Udp6CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_SUCCESS)) { + // + // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the + // TxTokens nor the RxTokens, or say, it's not found. + // + return EFI_NOT_FOUND; + } + + ASSERT ((Token != NULL) || + ((0 == NetMapGetCount (&Instance->TxTokens)) && + (0 == NetMapGetCount (&Instance->RxTokens))) + ); + + return EFI_SUCCESS; +} + + +/** + This function checks if the received udp datagram matches with the Instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted + from the received udp datagram. + + @retval TRUE The udp datagram matches the receiving requirements of the Instance. + @retval FALSE The udp datagram does not matche the receiving requirements of the Instance. + +**/ +BOOLEAN +Udp6MatchDgram ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_SESSION_DATA *Udp6Session + ) +{ + EFI_UDP6_CONFIG_DATA *ConfigData; + EFI_IPv6_ADDRESS Destination; + + ConfigData = &Instance->ConfigData; + + if (ConfigData->AcceptPromiscuous) { + // + // Always matches if this instance is in the promiscuous state. + // + return TRUE; + } + + if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) || + ((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort)) + ) { + // + // The local port or the remote port doesn't match. + // + return FALSE; + } + + if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) && + !EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress) + ) { + // + // This datagram doesn't come from the instance's specified sender. + // + return FALSE; + } + + if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) || + EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress) + ) { + // + // The instance is configured to receive datagrams destinated to any station IP or + // the destination address of this datagram matches the configured station IP. + // + return TRUE; + } + + IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress); + + if (IP6_IS_MULTICAST (&Destination) && + (NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination)) + ) { + // + // It's a multicast packet and the multicast address is accepted by this instance. + // + return TRUE; + } + + return FALSE; +} + + +/** + This function removes the Wrap specified by Context and release relevant resources. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP6_RXDATA_WRAP *Wrap; + + Wrap = (UDP6_RXDATA_WRAP *) Context; + + // + // Remove the Wrap from the list it belongs to. + // + RemoveEntryList (&Wrap->Link); + + // + // Free the Packet associated with this Wrap. + // + NetbufFree (Wrap->Packet); + + // + // Close the event. + // + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + + FreePool (Wrap); +} + + +/** + This function wraps the Packet into RxData. + + @param[in] Instance Pointer to the instance context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return Pointer to the structure wrapping the RxData and the Packet. + +**/ +UDP6_RXDATA_WRAP * +Udp6WrapRxData ( + IN UDP6_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ) +{ + EFI_STATUS Status; + UDP6_RXDATA_WRAP *Wrap; + + // + // Allocate buffer for the Wrap. + // + Wrap = AllocateZeroPool (sizeof (UDP6_RXDATA_WRAP) + + (Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA)); + if (Wrap == NULL) { + return NULL; + } + + InitializeListHead (&Wrap->Link); + + CopyMem (&Wrap->RxData, RxData, sizeof(EFI_UDP6_RECEIVE_DATA)); + // + // Create the Recycle event. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Udp6RecycleRxDataWrap, + Wrap, + &Wrap->RxData.RecycleSignal + ); + if (EFI_ERROR (Status)) { + FreePool (Wrap); + return NULL; + } + + Wrap->Packet = Packet; + Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout; + + return Wrap; +} + + +/** + This function enqueues the received datagram into the instances' receiving queues. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return The times this datagram is enqueued. + +**/ +UINTN +Udp6EnqueueDgram ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ) +{ + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + UDP6_RXDATA_WRAP *Wrap; + UINTN Enqueued; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + if (Udp6MatchDgram (Instance, &RxData->UdpSession)) { + // + // Wrap the RxData and put this Wrap into the instances RcvdDgramQue. + // + Wrap = Udp6WrapRxData (Instance, Packet, RxData); + if (Wrap == NULL) { + continue; + } + + NET_GET_REF (Packet); + + InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link); + + Enqueued++; + } + } + + return Enqueued; +} + + +/** + This function delivers the received datagrams to the specified instance. + + @param[in] Instance Pointer to the instance context data. + +**/ +VOID +Udp6InstanceDeliverDgram ( + IN UDP6_INSTANCE_DATA *Instance + ) +{ + UDP6_RXDATA_WRAP *Wrap; + EFI_UDP6_COMPLETION_TOKEN *Token; + NET_BUF *Dup; + EFI_UDP6_RECEIVE_DATA *RxData; + EFI_TPL OldTpl; + + if (!IsListEmpty (&Instance->RcvdDgramQue) && + !NetMapIsEmpty (&Instance->RxTokens) + ) { + + Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link); + + if (NET_BUF_SHARED (Wrap->Packet)) { + // + // Duplicate the Packet if it is shared between instances. + // + Dup = NetbufDuplicate (Wrap->Packet, NULL, 0); + if (Dup == NULL) { + return; + } + + NetbufFree (Wrap->Packet); + + Wrap->Packet = Dup; + } + + NetListRemoveHead (&Instance->RcvdDgramQue); + + Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + // + // Build the FragmentTable and set the FragmentCount in RxData. + // + RxData = &Wrap->RxData; + RxData->FragmentCount = Wrap->Packet->BlockOpNum; + + NetbufBuildExt ( + Wrap->Packet, + (NET_FRAGMENT *) RxData->FragmentTable, + &RxData->FragmentCount + ); + + Token->Status = EFI_SUCCESS; + Token->Packet.RxData = &Wrap->RxData; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link); + gBS->RestoreTPL (OldTpl); + + gBS->SignalEvent (Token->Event); + } +} + + +/** + This function delivers the datagrams enqueued in the instances. + + @param[in] Udp6Service Pointer to the udp service context data. + +**/ +VOID +Udp6DeliverDgram ( + IN UDP6_SERVICE_DATA *Udp6Service + ) +{ + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + // + // Deliver the datagrams of this instance. + // + Udp6InstanceDeliverDgram (Instance); + } +} + + +/** + This function demultiplexes the received udp datagram to the appropriate instances. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from + the received datagram. + @param[in] Packet Pointer to the buffer containing the received udp + datagram. + +**/ +VOID +Udp6Demultiplex ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ) +{ + EFI_UDP_HEADER *Udp6Header; + UINT16 HeadSum; + EFI_UDP6_RECEIVE_DATA RxData; + EFI_UDP6_SESSION_DATA *Udp6Session; + UINTN Enqueued; + + // + // Get the datagram header from the packet buffer. + // + Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Udp6Header != NULL); + + if (Udp6Header->Checksum != 0) { + // + // check the checksum. + // + HeadSum = NetIp6PseudoHeadChecksum ( + &NetSession->Source.v6, + &NetSession->Dest.v6, + EFI_IP_PROTO_UDP, + 0 + ); + + if (Udp6Checksum (Packet, HeadSum) != 0) { + // + // Wrong checksum. + // + return; + } + } + + gRT->GetTime (&RxData.TimeStamp, NULL); + + Udp6Session = &RxData.UdpSession; + Udp6Session->SourcePort = NTOHS (Udp6Header->SrcPort); + Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort); + + IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source); + IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest); + + // + // Trim the UDP header. + // + NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE); + + RxData.DataLength = (UINT32) Packet->TotalSize; + + // + // Try to enqueue this datagram into the instances. + // + Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData); + + if (Enqueued == 0) { + // + // Send the port unreachable ICMP packet before we free this NET_BUF + // + Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header); + } + + // + // Try to free the packet before deliver it. + // + NetbufFree (Packet); + + if (Enqueued > 0) { + // + // Deliver the datagram. + // + Udp6DeliverDgram (Udp6Service); + } +} + + +/** + This function builds and sends out a icmp port unreachable message. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet + causes this icmp error message. + @param[in] Udp6Header Pointer to the udp header of the datagram causes + this icmp error message. + +**/ +VOID +Udp6SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp6Header + ) +{ + NET_BUF *Packet; + UINT32 Len; + IP6_ICMP_ERROR_HEAD *IcmpErrHdr; + UINT8 *Ptr; + IP_IO_OVERRIDE Override; + IP_IO_IP_INFO *IpSender; + EFI_IP6_MODE_DATA *Ip6ModeData; + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6Protocol; + + Ip6ModeData = NULL; + + // + // An ICMPv6 error message MUST NOT be originated as A packet destined to + // 1) an IPv6 multicast address 2) The IPv6 Unspecified Address + // + if (NetSession->IpVersion == IP_VERSION_6) { + if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) || + IP6_IS_MULTICAST (&NetSession->Dest.v6) + ) { + goto EXIT; + } + } + + + IpSender = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest); + + // + // Get the Ipv6 Mode Data. + // + Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA)); + ASSERT (Ip6ModeData != NULL); + + // + // If not finding the related IpSender use the default IpIo to send out + // the port unreachable ICMP message. + // + if (IpSender == NULL) { + Ip6Protocol = IpIo->Ip.Ip6; + } else { + Ip6Protocol = IpSender->Ip.Ip6; + } + + Status = Ip6Protocol->GetModeData ( + Ip6Protocol, + Ip6ModeData, + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + // + // The ICMP6 packet length, includes whole invoking packet and ICMP6 error header. + // + Len = NetSession->IpHdrLen + + NTOHS(((EFI_UDP_HEADER *) Udp6Header)->Length) + + sizeof (IP6_ICMP_ERROR_HEAD); + + // + // If the ICMP6 packet length larger than IP MTU, adjust its length to MTU. + // + if (Ip6ModeData->MaxPacketSize < Len) { + Len = Ip6ModeData->MaxPacketSize; + } + + // + // Allocate buffer for the icmp error message. + // + Packet = NetbufAlloc (Len); + if (Packet == NULL) { + goto EXIT; + } + + // + // Allocate space for the IP6_ICMP_ERROR_HEAD. + // + IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE); + ASSERT (IcmpErrHdr != NULL); + + // + // Set the required fields for the icmp port unreachable message. + // + IcmpErrHdr->Head.Type = ICMP_V6_DEST_UNREACHABLE; + IcmpErrHdr->Head.Code = ICMP_V6_PORT_UNREACHABLE; + IcmpErrHdr->Head.Checksum = 0; + IcmpErrHdr->Fourth = 0; + + // + // Copy as much of invoking Packet as possible without the ICMPv6 packet + // exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains + // the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD + // for pointer movement that fact should be considered. + // + Ptr = (VOID *) &IcmpErrHdr->Head; + Ptr = (UINT8 *) (UINTN) ((UINTN) Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER)); + CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen); + CopyMem ( + Ptr + NetSession->IpHdrLen, + Udp6Header, + Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER) + ); + + // + // Set the checksum as zero, and IP6 driver will calcuate it with pseudo header. + // + IcmpErrHdr->Head.Checksum = 0; + + // + // Fill the override data. + // + Override.Ip6OverrideData.FlowLabel = 0; + Override.Ip6OverrideData.HopLimit = 255; + Override.Ip6OverrideData.Protocol = IP6_ICMP; + + // + // Send out this icmp packet. + // + IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override); + + NetbufFree (Packet); + +EXIT: + if (Ip6ModeData != NULL) { + FreePool (Ip6ModeData); + } +} + + +/** + This function handles the received Icmp Error message and de-multiplexes it to the + instance. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] IcmpError The icmp error code. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted + from the received Icmp Error packet. + @param[in, out] Packet Pointer to the Icmp Error packet. + +**/ +VOID +Udp6IcmpHandler ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN OUT NET_BUF *Packet + ) +{ + EFI_UDP_HEADER *Udp6Header; + EFI_UDP6_SESSION_DATA Udp6Session; + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + + Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Udp6Header != NULL); + + IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source); + IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest); + + Udp6Session.SourcePort = NTOHS (Udp6Header->DstPort); + Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort); + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate all the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + if (Udp6MatchDgram (Instance, &Udp6Session)) { + // + // Translate the Icmp Error code according to the udp spec. + // + Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL); + + if (IcmpError > ICMP_ERR_UNREACH_PORT) { + Instance->IcmpError = EFI_ICMP_ERROR; + } + + // + // Notify the instance with the received Icmp Error. + // + Udp6ReportIcmpError (Instance); + + break; + } + } + + NetbufFree (Packet); +} + + +/** + This function reports the received ICMP error. + + @param[in] Instance Pointer to the udp instance context data. + +**/ +VOID +Udp6ReportIcmpError ( + IN UDP6_INSTANCE_DATA *Instance + ) +{ + EFI_UDP6_COMPLETION_TOKEN *Token; + + if (NetMapIsEmpty (&Instance->RxTokens)) { + // + // There are no receive tokens to deliver the ICMP error. + // + return; + } + + if (EFI_ERROR (Instance->IcmpError)) { + // + // Try to get a RxToken from the RxTokens map. + // + Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + if (Token != NULL) { + // + // Report the error through the Token. + // + Token->Status = Instance->IcmpError; + gBS->SignalEvent (Token->Event); + + // + // Clear the IcmpError. + // + Instance->IcmpError = EFI_SUCCESS; + } + } +} + + +/** + This function is a dummy ext-free function for the NET_BUF created for the output + udp datagram. + + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6NetVectorExtFree ( + IN VOID *Context + ) +{ +} + + +/** + Set the Udp6 variable data. + + @param[in] Udp6Service Udp6 service data. + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the + variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +Udp6SetVariableData ( + IN UDP6_SERVICE_DATA *Udp6Service + ) +{ + UINT32 NumConfiguredInstance; + LIST_ENTRY *Entry; + UINTN VariableDataSize; + EFI_UDP6_VARIABLE_DATA *Udp6VariableData; + EFI_UDP6_SERVICE_POINT *Udp6ServicePoint; + UDP6_INSTANCE_DATA *Udp6Instance; + CHAR16 *NewMacString; + EFI_STATUS Status; + + NumConfiguredInstance = 0; + + // + // Go through the children list to count the configured children. + // + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + Udp6Instance = NET_LIST_USER_STRUCT_S ( + Entry, + UDP6_INSTANCE_DATA, + Link, + UDP6_INSTANCE_DATA_SIGNATURE + ); + + if (Udp6Instance->Configured) { + NumConfiguredInstance++; + } + } + + // + // Calculate the size of the Udp6VariableData. As there may be no Udp6 child, + // we should add extra buffer for the service points only if the number of configured + // children is more than 1. + // + VariableDataSize = sizeof (EFI_UDP6_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_UDP6_SERVICE_POINT) * (NumConfiguredInstance - 1); + } + + Udp6VariableData = AllocateZeroPool (VariableDataSize); + if (Udp6VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Udp6VariableData->DriverHandle = Udp6Service->ImageHandle; + Udp6VariableData->ServiceCount = NumConfiguredInstance; + + Udp6ServicePoint = &Udp6VariableData->Services[0]; + + // + // Go through the children list to fill the configured children's address pairs. + // + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + Udp6Instance = NET_LIST_USER_STRUCT_S ( + Entry, + UDP6_INSTANCE_DATA, + Link, + UDP6_INSTANCE_DATA_SIGNATURE + ); + + if (Udp6Instance->Configured) { + Udp6ServicePoint->InstanceHandle = Udp6Instance->ChildHandle; + Udp6ServicePoint->LocalPort = Udp6Instance->ConfigData.StationPort; + Udp6ServicePoint->RemotePort = Udp6Instance->ConfigData.RemotePort; + + IP6_COPY_ADDRESS ( + &Udp6ServicePoint->LocalAddress, + &Udp6Instance->ConfigData.StationAddress + ); + IP6_COPY_ADDRESS ( + &Udp6ServicePoint->RemoteAddress, + &Udp6Instance->ConfigData.RemoteAddress + ); + Udp6ServicePoint++; + } + } + + // + // Get the MAC string. + // + Status = NetLibGetMacString ( + Udp6Service->ControllerHandle, + Udp6Service->ImageHandle, + &NewMacString + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + if (Udp6Service->MacString != NULL) { + // + // The variable is set already, we're going to update it. + // + if (StrCmp (Udp6Service->MacString, NewMacString) != 0) { + // + // The MAC address is changed, delete the previous variable first. + // + gRT->SetVariable ( + Udp6Service->MacString, + &gEfiUdp6ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + } + + FreePool (Udp6Service->MacString); + } + + Udp6Service->MacString = NewMacString; + + Status = gRT->SetVariable ( + Udp6Service->MacString, + &gEfiUdp6ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableDataSize, + (VOID *) Udp6VariableData + ); + +EXIT: + + FreePool (Udp6VariableData); + + return Status; +} + + +/** + Clear the variable and free the resource. + + @param[in, out] Udp6Service Udp6 service data. + +**/ +VOID +Udp6ClearVariableData ( + IN OUT UDP6_SERVICE_DATA *Udp6Service + ) +{ + ASSERT (Udp6Service->MacString != NULL); + + gRT->SetVariable ( + Udp6Service->MacString, + &gEfiUdp6ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + + FreePool (Udp6Service->MacString); + Udp6Service->MacString = NULL; +} + + +/** + Find the key in the netmap. + + @param[in] Map The netmap to search within. + @param[in] Key The key to search. + + @return The point to the item contains the Key, or NULL, if Key isn't in the map. + +**/ +NET_MAP_ITEM * +Udp6MapMultiCastAddr ( + IN NET_MAP *Map, + IN VOID *Key + ) +{ + LIST_ENTRY *Entry; + NET_MAP_ITEM *Item; + EFI_IPv6_ADDRESS *Addr; + + ASSERT (Map != NULL); + NET_LIST_FOR_EACH (Entry, &Map->Used) { + Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); + Addr = (EFI_IPv6_ADDRESS *) Item->Key; + if (EFI_IP6_EQUAL (Addr, Key)) { + return Item; + } + } + return NULL; +} + diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.h b/NetworkPkg/Udp6Dxe/Udp6Impl.h new file mode 100644 index 0000000000..108e30b71c --- /dev/null +++ b/NetworkPkg/Udp6Dxe/Udp6Impl.h @@ -0,0 +1,673 @@ +/** @file + Udp6 driver's whole implementation and internal data structures. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _UDP6_IMPL_H_ +#define _UDP6_IMPL_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Udp6Driver.h" + +extern EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName; +extern EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding; +extern EFI_UDP6_PROTOCOL mUdp6Protocol; +extern UINT16 mUdp6RandomPort; + +// +// Define time out 50 milliseconds +// +#define UDP6_TIMEOUT_INTERVAL (50 * TICKS_PER_MS) +#define UDP6_HEADER_SIZE sizeof (EFI_UDP_HEADER) +#define UDP6_MAX_DATA_SIZE 65507 +#define UDP6_PORT_KNOWN 1024 + +#define UDP6_SERVICE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', '6') +#define UDP6_INSTANCE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', 'S') + +#define UDP6_SERVICE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + UDP6_SERVICE_DATA, \ + ServiceBinding, \ + UDP6_SERVICE_DATA_SIGNATURE \ + ) + +#define UDP6_INSTANCE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + UDP6_INSTANCE_DATA, \ + Udp6Proto, \ + UDP6_INSTANCE_DATA_SIGNATURE \ + ) +// +// Udp6 service contest data +// +typedef struct _UDP6_SERVICE_DATA { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE ImageHandle; + EFI_HANDLE ControllerHandle; + LIST_ENTRY ChildrenList; + UINTN ChildrenNumber; + IP_IO *IpIo; + EFI_EVENT TimeoutEvent; + CHAR16 *MacString; +} UDP6_SERVICE_DATA; + +typedef struct _UDP6_INSTANCE_DATA { + UINT32 Signature; + LIST_ENTRY Link; + UDP6_SERVICE_DATA *Udp6Service; + EFI_UDP6_PROTOCOL Udp6Proto; + EFI_UDP6_CONFIG_DATA ConfigData; + EFI_HANDLE ChildHandle; + BOOLEAN Configured; + BOOLEAN IsNoMapping; + NET_MAP TxTokens; + NET_MAP RxTokens; + NET_MAP McastIps; + LIST_ENTRY RcvdDgramQue; + LIST_ENTRY DeliveredDgramQue; + UINT16 HeadSum; + EFI_STATUS IcmpError; + IP_IO_IP_INFO *IpInfo; + BOOLEAN Destroyed; +} UDP6_INSTANCE_DATA; + +typedef struct _UDP6_RXDATA_WRAP { + LIST_ENTRY Link; + NET_BUF *Packet; + UINT32 TimeoutTick; + EFI_UDP6_RECEIVE_DATA RxData; +} UDP6_RXDATA_WRAP; + +/** + Clean the Udp service context data. + + @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA. + +**/ +VOID +Udp6CleanService ( + IN OUT UDP6_SERVICE_DATA *Udp6Service + ); + +/** + Create the Udp service context data. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in] ImageHandle The image handle of this udp6 driver. + @param[in] ControllerHandle The controller handle this udp6 driver binds on. + + @retval EFI_SUCCESS The udp6 service context data was created and + initialized. + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory. + @retval Others An error condition occurred. + +**/ +EFI_STATUS +Udp6CreateService ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ); + +/** + Set the Udp6 variable data. + + @param[in] Udp6Service Udp6 service data. + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the + variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +Udp6SetVariableData ( + IN UDP6_SERVICE_DATA *Udp6Service + ); + +/** + This function cleans the udp instance. + + @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean. + +**/ +VOID +Udp6CleanInstance ( + IN OUT UDP6_INSTANCE_DATA *Instance + ); + +/** + Clear the variable and free the resource. + + @param[in, out] Udp6Service Udp6 service data. + +**/ +VOID +Udp6ClearVariableData ( + IN OUT UDP6_SERVICE_DATA *Udp6Service + ); + +/** + This function intializes the new created udp instance. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA. + +**/ +VOID +Udp6InitInstance ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN OUT UDP6_INSTANCE_DATA *Instance + ); + +/** + This function reports the received ICMP error. + + @param[in] Instance Pointer to the udp instance context data. + +**/ +VOID +Udp6ReportIcmpError ( + IN UDP6_INSTANCE_DATA *Instance + ); + +/** + This function copies the current operational settings of this EFI UDPv6 Protocol + instance into user-supplied buffers. This function is used optionally to retrieve + the operational mode data of underlying networks or drivers. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[out] Udp6ConfigData The buffer in which the current UDP configuration + data is returned. This parameter is optional and + may be NULL. + @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol + mode data is returned. This parameter is optional + and may be NULL. + @param[out] MnpConfigData The buffer in which the current managed network + configuration data is returned. This parameter + is optional and may be NULL. + @param[out] SnpModeData The buffer in which the simple network mode data + is returned. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration + data is available because this instance has not + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6GetModeData ( + IN EFI_UDP6_PROTOCOL *This, + OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + This function is used to do the following: + Initialize and start this instance of the EFI UDPv6 Protocol. + Change the filtering rules and operational parameters. + Reset this instance of the EFI UDPv6 Protocol. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] UdpConfigData Pointer to the buffer to set the configuration + data. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The configuration settings were set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set + to true and there is no address available for IP6 + driver to binding source address to this + instance. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + This is NULL. + UdpConfigData.StationAddress is not a valid + unicast IPv6 address. + UdpConfigData.RemoteAddress is not a valid unicast + IPv6 address, if it is not zero. + @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already + started/configured and must be stopped/reset + before it can be reconfigured. Only TrafficClass, + HopLimit, ReceiveTimeout, and TransmitTimeout can + be reconfigured without stopping the current + instance of the EFI UDPv6 Protocol. + @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE, and + UdpConfigData.StationPort is already used by another + instance. + @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate + memory for this EFI UDPv6 Protocol instance. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and + this instance was not opened. + +**/ +EFI_STATUS +EFIAPI +Udp6Configure ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL + ); + +/** + This function places a sending request to this instance of the EFI UDPv6 Protocol, + alongside the transmit data that was filled by the user. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token that will be + placed into the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for + choosing a source address for this instance, but + no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + This is NULL. Token is NULL. Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. + One or more of the + Token.Packet.TxData.FragmentTable[] + .FragmentLength fields is zero. + One or more of the + Token.Packet.TxData.FragmentTable[] + .FragmentBuffer fields is NULL. + One or more of the + Token.Packet.TxData.UdpSessionData. + DestinationAddres are not valid unicast IPv6 + addresses, if the UdpSessionData is not NULL. + Token.Packet.TxData.UdpSessionData. + DestinationAddres is NULL + Token.Packet.TxData.UdpSessionData. + DestinatioPort is zero. + Token.Packet.TxData.UdpSessionData is + NULL and this instance's + UdpConfigData.RemoteAddress is unspecified. + @retval EFI_ACCESS_DENIED The transmit completion token with the same + Token.Event is already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_NOT_FOUND There is no route to the destination network or + address. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. Or the length of the IP header + UDP + header + data length is greater than MTU if + DoNotFragment is TRUE. + +**/ +EFI_STATUS +EFIAPI +Udp6Transmit ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ); + +/** + This function places a completion token into the receive packet queue. This function + is always asynchronous. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token is cached. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources (usually + memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI UDPv6 Protocol instance has been reset to + startup defaults. + @retval EFI_ACCESS_DENIED A receive completion token with the same + Token.Event is already in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Udp6Receive ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ); + +/** + This function is used to abort a pending transmit or receive request. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_UDP6_PROTOCOL.Transmit() or + EFI_UDP6_PROTOCOL.Receive(). This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The asynchronous I/O request is aborted and + Token.Event is signaled. When Token is NULL, all + pending requests are aborted and their events are + signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O + request is not found in the transmit or receive + queue. It either completed or was not issued by + Transmit() or Receive(). + +**/ +EFI_STATUS +EFIAPI +Udp6Cancel ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + This function can be used by network drivers and applications to increase the rate that + data packets are moved between the communications device and the transmit/receive queues. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or + receive queue. + +**/ +EFI_STATUS +EFIAPI +Udp6Poll ( + IN EFI_UDP6_PROTOCOL *This + ); + +/** + This function is used to enable and disable the multicast group filtering. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join a multicast group. Set to + FALSE to leave one or all multicast groups. + @param[in] MulticastAddress Pointer to multicast group address to join or + leave. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been + started. + @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. JoinFlag is TRUE and + MulticastAddress is NULL. JoinFlag is TRUE and + *MulticastAddress is not a valid multicast + address. + @retval EFI_ALREADY_STARTED The group address is already in the group table + (when JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when + JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Udp6Groups ( + IN EFI_UDP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL + ); + +/** + This function tries to bind the udp instance according to the configured port + allocation stragety. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] ConfigData Pointer to the ConfigData of the instance to be + bound. + + @retval EFI_SUCCESS The bound operation completed successfully. + @retval EFI_ACCESS_DENIED The specified by the ConfigData is + already used by another instance. + @retval EFI_OUT_OF_RESOURCES No available port resources. + +**/ +EFI_STATUS +Udp6Bind ( + IN LIST_ENTRY *InstanceList, + IN EFI_UDP6_CONFIG_DATA *ConfigData + ); + +/** + This function builds the Ip6 configdata from the Udp6ConfigData. + + @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA. + @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA. + +**/ +VOID +Udp6BuildIp6ConfigData ( + IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData, + IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData + ); + +/** + This function checks whether the specified Token duplicates with the one in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to + the Token. + @param[in] Context Pointer to the Token to be checked. + + @retval EFI_SUCCESS The Token specified by Context differs from the + one in the Item. + @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item. + +**/ +EFI_STATUS +EFIAPI +Udp6TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + This function removes the specified Token from the TokenMap. + + @param[in] TokenMap Pointer to the NET_MAP containing the tokens. + @param[in] Token Pointer to the Token to be removed. + + @retval EFI_SUCCESS The specified Token is removed from the TokenMap. + @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap. + +**/ +EFI_STATUS +Udp6RemoveToken ( + IN NET_MAP *TokenMap, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ); + +/** + This function is used to check whether the NewConfigData has any un-reconfigurable + parameters changed compared to the OldConfigData. + + @param[in] OldConfigData Pointer to the current ConfigData the udp instance + uses. + @param[in] NewConfigData Pointer to the new ConfigData. + + @retval TRUE The instance is reconfigurable according to NewConfigData. + @retval FALSE The instance is not reconfigurable according to NewConfigData. + +**/ +BOOLEAN +Udp6IsReconfigurable ( + IN EFI_UDP6_CONFIG_DATA *OldConfigData, + IN EFI_UDP6_CONFIG_DATA *NewConfigData + ); + +/** + This function removes the multicast group specified by Arg from the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the Arg. It is the pointer to a + multicast IPv6 Address. This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The multicast address is removed. + @retval EFI_ABORTED The specified multicast address is removed, and the + Arg is not NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6LeaveGroup ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +/** + This function validates the TxToken, it returns the error code according to the spec. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] TxToken Pointer to the token to be checked. + + @retval EFI_SUCCESS The TxToken is valid. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentLength + fields is zero. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentBuffer + fields is NULL. + UdpSessionData.DestinationAddress are not valid + unicast IPv6 addresses if the UdpSessionData is + not NULL. + UdpSessionData.DestinationPort and + ConfigData.RemotePort are all zero if the + UdpSessionData is not NULL. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. + +**/ +EFI_STATUS +Udp6ValidateTxToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *TxToken + ); + +/** + This function is a dummy ext-free function for the NET_BUF created for the output + udp datagram. + + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6NetVectorExtFree ( + IN VOID *Context + ); + +/** + This function calculates the checksum for the Packet, utilizing the pre-calculated + pseudo header to reduce overhead. + + @param[in] Packet Pointer to the NET_BUF contains the udp datagram. + @param[in] HeadSum Checksum of the pseudo header execpt the length + field. + + @return The 16-bit checksum of this udp datagram. + +**/ +UINT16 +Udp6Checksum ( + IN NET_BUF *Packet, + IN UINT16 HeadSum + ); + +/** + This function delivers the received datagrams to the specified instance. + + @param[in] Instance Pointer to the instance context data. + +**/ +VOID +Udp6InstanceDeliverDgram ( + IN UDP6_INSTANCE_DATA *Instance + ); + +/** + Cancel Udp6 tokens from the Udp6 instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Udp6InstanceCancelToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + This function removes all the Wrap datas in the RcvdDgramQue. + + @param[in] Instance Pointer to the Udp6 Instance. + +**/ +VOID +Udp6FlushRcvdDgram ( + IN UDP6_INSTANCE_DATA *Instance + ); + +#endif + diff --git a/NetworkPkg/Udp6Dxe/Udp6Main.c b/NetworkPkg/Udp6Dxe/Udp6Main.c new file mode 100644 index 0000000000..0cad596276 --- /dev/null +++ b/NetworkPkg/Udp6Dxe/Udp6Main.c @@ -0,0 +1,855 @@ +/** @file + Contains all EFI_UDP6_PROTOCOL interfaces. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "Udp6Impl.h" + +EFI_UDP6_PROTOCOL mUdp6Protocol = { + Udp6GetModeData, + Udp6Configure, + Udp6Groups, + Udp6Transmit, + Udp6Receive, + Udp6Cancel, + Udp6Poll +}; + + +/** + This function copies the current operational settings of this EFI UDPv6 Protocol + instance into user-supplied buffers. This function is used optionally to retrieve + the operational mode data of underlying networks or drivers. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[out] Udp6ConfigData The buffer in which the current UDP configuration + data is returned. This parameter is optional and + may be NULL. + @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol + mode data is returned. This parameter is optional + and may be NULL. + @param[out] MnpConfigData The buffer in which the current managed network + configuration data is returned. This parameter is + optional and may be NULL. + @param[out] SnpModeData The buffer in which the simple network mode data + is returned. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration + data is available because this instance has not + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6GetModeData ( + IN EFI_UDP6_PROTOCOL *This, + OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + UDP6_INSTANCE_DATA *Instance; + EFI_IP6_PROTOCOL *Ip; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured && (Udp6ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (Udp6ConfigData != NULL) { + // + // Set the Udp6ConfigData. + // + CopyMem (Udp6ConfigData, &Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA)); + } + + Ip = Instance->IpInfo->Ip.Ip6; + + // + // Get the underlying Ip6ModeData, MnpConfigData and SnpModeData. + // + Status = Ip->GetModeData (Ip, Ip6ModeData, MnpConfigData, SnpModeData); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function is used to do the following: + Initialize and start this instance of the EFI UDPv6 Protocol. + Change the filtering rules and operational parameters. + Reset this instance of the EFI UDPv6 Protocol. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] UdpConfigData Pointer to the buffer to set the configuration + data. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The configuration settings were set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set + to true and there is no address available for the IP6 + driver to bind a source address to this instance. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + This is NULL. + UdpConfigData.StationAddress is not a valid + unicast IPv6 address. + UdpConfigData.RemoteAddress is not a valid unicast + IPv6 address if it is not zero. + @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already + started/configured and must be stopped/reset + before it can be reconfigured. Only TrafficClass, + HopLimit, ReceiveTimeout, and TransmitTimeout can + be reconfigured without stopping the current + instance of the EFI UDPv6 Protocol. + @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE and + UdpConfigData.StationPort is already used by another + instance. + @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate + memory for this EFI UDPv6 Protocol instance. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and + this instance was not opened. + +**/ +EFI_STATUS +EFIAPI +Udp6Configure ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + UDP6_SERVICE_DATA *Udp6Service; + EFI_TPL OldTpl; + EFI_IPv6_ADDRESS StationAddress; + EFI_IPv6_ADDRESS RemoteAddress; + EFI_IP6_CONFIG_DATA Ip6ConfigData; + EFI_IPv6_ADDRESS LocalAddr; + EFI_IPv6_ADDRESS RemoteAddr; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured && (UdpConfigData == NULL)) { + return EFI_SUCCESS; + } + + Udp6Service = Instance->Udp6Service; + Status = EFI_SUCCESS; + ASSERT (Udp6Service != NULL); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (UdpConfigData != NULL) { + + IP6_COPY_ADDRESS (&StationAddress, &UdpConfigData->StationAddress); + IP6_COPY_ADDRESS (&RemoteAddress, &UdpConfigData->RemoteAddress); + + if ((!NetIp6IsUnspecifiedAddr (&StationAddress) && !NetIp6IsValidUnicast (&StationAddress)) || + (!NetIp6IsUnspecifiedAddr (&RemoteAddress) && !NetIp6IsValidUnicast (&RemoteAddress)) + ){ + // + // If not use default address, and StationAddress is not a valid unicast + // if it is not IPv6 address or RemoteAddress is not a valid unicast IPv6 + // address if it is not 0. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (Instance->Configured) { + // + // The instance is already configured, try to do the re-configuration. + // + if (!Udp6IsReconfigurable (&Instance->ConfigData, UdpConfigData)) { + // + // If the new configuration data wants to change some unreconfigurable + // settings, return EFI_ALREADY_STARTED. + // + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + // + // Save the reconfigurable parameters. + // + Instance->ConfigData.TrafficClass = UdpConfigData->TrafficClass; + Instance->ConfigData.HopLimit = UdpConfigData->HopLimit; + Instance->ConfigData.ReceiveTimeout = UdpConfigData->ReceiveTimeout; + Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout; + } else { + // + // Construct the Ip configuration data from the UdpConfigData. + // + Udp6BuildIp6ConfigData (UdpConfigData, &Ip6ConfigData); + + // + // Configure the Ip instance wrapped in the IpInfo. + // + Status = IpIoConfigIp (Instance->IpInfo, &Ip6ConfigData); + if (EFI_ERROR (Status)) { + if (Status == EFI_NO_MAPPING) { + Instance->IsNoMapping = TRUE; + } + + goto ON_EXIT; + } + + Instance->IsNoMapping = FALSE; + + // + // Save the configuration data. + // + CopyMem ( + &Instance->ConfigData, + UdpConfigData, + sizeof (EFI_UDP6_CONFIG_DATA) + ); + IP6_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip6ConfigData.StationAddress); + // + // Try to allocate the required port resource. + // + Status = Udp6Bind (&Udp6Service->ChildrenList, &Instance->ConfigData); + if (EFI_ERROR (Status)) { + // + // Reset the ip instance if bind fails. + // + IpIoConfigIp (Instance->IpInfo, NULL); + goto ON_EXIT; + } + + // + // Pre calculate the checksum for the pseudo head, ignore the UDP length first. + // + IP6_COPY_ADDRESS (&LocalAddr, &Instance->ConfigData.StationAddress); + IP6_COPY_ADDRESS (&RemoteAddr, &Instance->ConfigData.RemoteAddress); + + Instance->HeadSum = NetIp6PseudoHeadChecksum ( + &LocalAddr, + &RemoteAddr, + EFI_IP_PROTO_UDP, + 0 + ); + + Instance->Configured = TRUE; + } + } else { + // + // UdpConfigData is NULL, reset the instance. + // + Instance->Configured = FALSE; + Instance->IsNoMapping = FALSE; + + // + // Reset the Ip instance wrapped in the IpInfo. + // + IpIoConfigIp (Instance->IpInfo, NULL); + + // + // Cancel all the user tokens. + // + Status = Instance->Udp6Proto.Cancel (&Instance->Udp6Proto, NULL); + + // + // Remove the buffered RxData for this instance. + // + Udp6FlushRcvdDgram (Instance); + + ASSERT (IsListEmpty (&Instance->DeliveredDgramQue)); + } + + Status = Udp6SetVariableData (Instance->Udp6Service); + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function is used to enable and disable the multicast group filtering. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join a multicast group. Set to + FALSE to leave one or all multicast groups. + @param[in] MulticastAddress Pointer to multicast group address to join or + leave. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been + started. + @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + JoinFlag is TRUE and MulticastAddress is NULL. + JoinFlag is TRUE and *MulticastAddress is not a + valid multicast address. + @retval EFI_ALREADY_STARTED The group address is already in the group table + (when JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when + JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Udp6Groups ( + IN EFI_UDP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_IP6_PROTOCOL *Ip; + EFI_TPL OldTpl; + EFI_IPv6_ADDRESS *McastIp; + + if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) { + return EFI_INVALID_PARAMETER; + } + + McastIp = NULL; + + if (JoinFlag) { + if (!IP6_IS_MULTICAST (MulticastAddress)) { + return EFI_INVALID_PARAMETER; + } + + McastIp = AllocateCopyPool (sizeof (EFI_IPv6_ADDRESS), MulticastAddress); + if (McastIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + Ip = Instance->IpInfo->Ip.Ip6; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Invoke the Ip instance the Udp6 instance consumes to do the group operation. + // + Status = Ip->Groups (Ip, JoinFlag, MulticastAddress); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Keep a local copy of the configured multicast IPs because IpIo receives + // datagrams from the 0 station address IP instance and then UDP delivers to + // the matched instance. This copy of multicast IPs is used to avoid receive + // the mutlicast datagrams destinated to multicast IPs the other instances configured. + // + if (JoinFlag) { + + Status = NetMapInsertTail (&Instance->McastIps, (VOID *) McastIp, NULL); + } else { + + NetMapIterate (&Instance->McastIps, Udp6LeaveGroup, MulticastAddress); + } + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + if (EFI_ERROR (Status)) { + if (McastIp != NULL) { + FreePool (McastIp); + } + } + + return Status; +} + + + +/** + This function places a sending request to this instance of the EFI UDPv6 Protocol, + alongside the transmit data that was filled by the user. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token that will be + placed into the transmit queue. + + @retval EFI_SUCCESS The data was queued for transmission. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for + choosing a source address for this instance, but + no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + This is NULL. + Token is NULL. Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentLength + fields is zero. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentBuffer + fields is NULL. One or more of the + Token.Packet.TxData.UdpSessionData.DestinationAddres + are not valid unicast IPv6 + addresses if the UdpSessionData is not NULL. + Token.Packet.TxData.UdpSessionData. + DestinationAddress is NULL + Token.Packet.TxData.UdpSessionData. + DestinatioPort + is zero. + Token.Packet.TxData.UdpSessionData is NULL and this + instance's UdpConfigData.RemoteAddress is unspecified. + @retval EFI_ACCESS_DENIED The transmit completion token with the same + Token.Event is already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_NOT_FOUND There is no route to the destination network or + address. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. Or, the length of the IP header + UDP + header + data length is greater than MTU if + DoNotFragment is TRUE. + +**/ +EFI_STATUS +EFIAPI +Udp6Transmit ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + NET_BUF *Packet; + EFI_UDP_HEADER *Udp6Header; + EFI_UDP6_CONFIG_DATA *ConfigData; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS Destination; + EFI_UDP6_TRANSMIT_DATA *TxData; + EFI_UDP6_SESSION_DATA *UdpSessionData; + UDP6_SERVICE_DATA *Udp6Service; + IP_IO_OVERRIDE Override; + UINT16 HeadSum; + EFI_IP_ADDRESS IpDestAddr; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Validate the Token, if the token is invalid return the error code. + // + Status = Udp6ValidateTxToken (Instance, Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) || + EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) + ){ + // + // Try to find a duplicate token in the two token maps, if found, return + // EFI_ACCESS_DENIED. + // + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + TxData = Token->Packet.TxData; + + // + // Create a net buffer to hold the user buffer and the udp header. + // + Packet = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + UDP6_HEADER_SIZE, + 0, + Udp6NetVectorExtFree, + NULL + ); + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Store the IpIo in ProtoData. + // + Udp6Service = Instance->Udp6Service; + *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp6Service->IpIo); + + Udp6Header = (EFI_UDP_HEADER *) NetbufAllocSpace (Packet, UDP6_HEADER_SIZE, TRUE); + ASSERT (Udp6Header != NULL); + ConfigData = &Instance->ConfigData; + + // + // Fill the udp header. + // + Udp6Header->SrcPort = HTONS (ConfigData->StationPort); + Udp6Header->DstPort = HTONS (ConfigData->RemotePort); + Udp6Header->Length = HTONS ((UINT16) Packet->TotalSize); + Udp6Header->Checksum = 0; + // + // Set the UDP Header in NET_BUF, this UDP header is for IP6 can fast get the + // Udp header for pseudoHeadCheckSum. + // + Packet->Udp = Udp6Header; + UdpSessionData = TxData->UdpSessionData; + + if (UdpSessionData != NULL) { + // + // Set the Destination according to the specified + // UdpSessionData. + // + + if (UdpSessionData->DestinationPort != 0) { + Udp6Header->DstPort = HTONS (UdpSessionData->DestinationPort); + } + + IP6_COPY_ADDRESS (&Source, &ConfigData->StationAddress); + if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress)) { + IP6_COPY_ADDRESS (&Destination, &UdpSessionData->DestinationAddress); + } else { + IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress); + } + + // + //Calculate the pseudo head checksum using the overridden parameters. + // + if (!NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress)) { + HeadSum = NetIp6PseudoHeadChecksum ( + &Source, + &Destination, + EFI_IP_PROTO_UDP, + 0 + ); + + // + // calculate the checksum. + // + Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum); + if (Udp6Header->Checksum == 0) { + // + // If the calculated checksum is 0, fill the Checksum field with all ones. + // + Udp6Header->Checksum = 0XFFFF; + } + } else { + // + // Set the checksum is zero if the ConfigData->StationAddress is unspcified + // and the Ipv6 will fill the correct value of this checksum. + // + Udp6Header->Checksum = 0; + + } + } else { + // + // UdpSessionData is NULL, use the address and port information previously configured. + // + IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress); + + HeadSum = Instance->HeadSum; + // + // calculate the checksum. + // + Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum); + if (Udp6Header->Checksum == 0) { + // + // If the calculated checksum is 0, fill the Checksum field with all ones. + // + Udp6Header->Checksum = 0xffff; + } + } + + + + // + // Fill the IpIo Override data. + // + Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_UDP; + Override.Ip6OverrideData.HopLimit = ConfigData->HopLimit; + Override.Ip6OverrideData.FlowLabel = 0; + + // + // Save the token into the TxToken map. + // + Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet); + if (EFI_ERROR (Status)) { + goto FREE_PACKET; + } + + // + // Send out this datagram through IpIo. + // + if (UdpSessionData != NULL){ + IP6_COPY_ADDRESS (&(IpDestAddr.v6), &Destination); + } else { + ZeroMem (&IpDestAddr.v6, sizeof (EFI_IPv6_ADDRESS)); + } + + Status = IpIoSend ( + Udp6Service->IpIo, + Packet, + Instance->IpInfo, + Instance, + Token, + &IpDestAddr, + &Override + ); + if (EFI_ERROR (Status)) { + // + // Remove this token from the TxTokens. + // + Udp6RemoveToken (&Instance->TxTokens, Token); + } + +FREE_PACKET: + + NetbufFree (Packet); + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function places a completion token into the receive packet queue. This function + is always asynchronous. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. Token is NULL. Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources (usually + memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI UDPv6 Protocol instance has been reset to + startup defaults. + @retval EFI_ACCESS_DENIED A receive completion token with the same + Token.Event is already in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Udp6Receive ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) || + EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) + ){ + // + // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or + // RxTokens map. + // + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + Token->Packet.RxData = NULL; + + // + // Save the token into the RxTokens map. + // + Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + // + // If there is an icmp error, report it. + // + Udp6ReportIcmpError (Instance); + + // + // Try to delivered the received datagrams. + // + Udp6InstanceDeliverDgram (Instance); + + // + // Dispatch the DPC queued by the NotifyFunction of Token->Event. + // + DispatchDpc (); + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function is used to abort a pending transmit or receive request. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_UDP6_PROTOCOL.Transmit() or + EFI_UDP6_PROTOCOL.Receive(). This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The asynchronous I/O request was aborted, and + Token.Event was signaled. When Token is NULL, all + pending requests are aborted and their events are + signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O + request is not found in the transmit or receive + queue. It is either completed or not issued by + Transmit() or Receive(). + +**/ +EFI_STATUS +EFIAPI +Udp6Cancel ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Cancle the tokens specified by Token for this instance. + // + Status = Udp6InstanceCancelToken (Instance, Token); + + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function can be used by network drivers and applications to increase the rate that + data packets are moved between the communications device and the transmit/receive queues. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or + receive queue. + +**/ +EFI_STATUS +EFIAPI +Udp6Poll ( + IN EFI_UDP6_PROTOCOL *This + ) +{ + UDP6_INSTANCE_DATA *Instance; + EFI_IP6_PROTOCOL *Ip; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + Ip = Instance->IpInfo->Ip.Ip6; + + // + // Invode the Ip instance consumed by the udp instance to do the poll operation. + // + return Ip->Poll (Ip); +} diff --git a/NetworkPkg/UefiPxeBcDxe/ComponentName.c b/NetworkPkg/UefiPxeBcDxe/ComponentName.c new file mode 100644 index 0000000000..4228519d18 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/ComponentName.c @@ -0,0 +1,312 @@ +/** @file + UEFI Component Name(2) protocol implementation for UefiPxeBc driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = { + PxeBcComponentNameGetDriverName, + PxeBcComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = { + { + "eng;en", + L"UEFI PXE Base Code Driver" + }, + { + NULL, + NULL + } +}; + + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2( + Language, + This->SupportedLanguages, + mPxeBcDriverNameTable, + DriverName, + (BOOLEAN)(This == &gPxeBcComponentName) + ); +} + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c new file mode 100644 index 0000000000..dd1d76d820 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c @@ -0,0 +1,1170 @@ +/** @file + Boot functions implementation for UefiPxeBc Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + + +/** + Display the string of the boot item. + + If the length of the boot item string beyond 70 Char, just display 70 Char. + + @param[in] Str The pointer to the string. + @param[in] Len The length of the string. + +**/ +VOID +PxeBcDisplayBootItem ( + IN UINT8 *Str, + IN UINT8 Len + ) +{ + UINT8 Tmp; + + // + // Cut off the chars behind 70th. + // + Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len); + Tmp = Str[Len]; + Str[Len] = 0; + AsciiPrint ("%a \n", Str); + + // + // Restore the original 70th char. + // + Str[Len] = Tmp; +} + + +/** + Select and maintain the boot prompt if needed. + + @param[in] Private Pointer to PxeBc private data. + + @retval EFI_SUCCESS Selected boot prompt done. + @retval EFI_TIMEOUT Selected boot prompt timed out. + @retval EFI_NOT_FOUND The proxy offer is not Pxe10. + @retval EFI_ABORTED User cancelled the operation. + @retval EFI_NOT_READY Reading the input key from the keyboard has not finish. + +**/ +EFI_STATUS +PxeBcSelectBootPrompt ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP_PACKET_CACHE *Cache; + PXEBC_VENDOR_OPTION *VendorOpt; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_EVENT TimeoutEvent; + EFI_EVENT DescendEvent; + EFI_INPUT_KEY InputKey; + EFI_STATUS Status; + UINT32 OfferType; + UINT8 Timeout; + UINT8 *Prompt; + UINT8 PromptLen; + INT32 SecCol; + INT32 SecRow; + + TimeoutEvent = NULL; + DescendEvent = NULL; + Mode = Private->PxeBc.Mode; + Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck; + OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType; + + // + // Only ProxyPxe10 offer needs boot prompt. + // + if (OfferType != PxeOfferTypeProxyPxe10) { + return EFI_NOT_FOUND; + } + + // + // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec. + // + ASSERT (!Mode->UsingIpv6); + + VendorOpt = &Cache->Dhcp4.VendorOpt; + if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) { + return EFI_SUCCESS; + } + + Timeout = VendorOpt->MenuPrompt->Timeout; + Prompt = VendorOpt->MenuPrompt->Prompt; + PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1); + + // + // The valid scope of Timeout refers to PXE2.1 spec. + // + if (Timeout == 0) { + return EFI_SUCCESS; + } + if (Timeout == 255) { + return EFI_TIMEOUT; + } + + // + // Create and start a timer as timeout event. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Timeout * TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Create and start a periodic timer as descend event by second. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &DescendEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + DescendEvent, + TimerPeriodic, + TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Display the boot item and cursor on the screen. + // + SecCol = gST->ConOut->Mode->CursorColumn; + SecRow = gST->ConOut->Mode->CursorRow; + + PxeBcDisplayBootItem (Prompt, PromptLen); + + gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow); + AsciiPrint ("(%d) ", Timeout--); + + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) { + gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow); + AsciiPrint ("(%d) ", Timeout--); + } + if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) { + gBS->Stall (10 * TICKS_PER_MS); + continue; + } + // + // Parse the input key by user. + // + if (InputKey.ScanCode == 0) { + + switch (InputKey.UnicodeChar) { + + case CTRL ('c'): + Status = EFI_ABORTED; + break; + + case CTRL ('m'): + case 'm': + case 'M': + Status = EFI_TIMEOUT; + break; + + default: + continue; + } + + } else { + + switch (InputKey.ScanCode) { + + case SCAN_F8: + Status = EFI_TIMEOUT; + break; + + case SCAN_ESC: + Status = EFI_ABORTED; + break; + + default: + continue; + } + } + + break; + } + + // + // Reset the cursor on the screen. + // + gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1); + +ON_EXIT: + if (DescendEvent != NULL) { + gBS->CloseEvent (DescendEvent); + } + if (TimeoutEvent != NULL) { + gBS->CloseEvent (TimeoutEvent); + } + + return Status; +} + + +/** + Select the boot menu by user's input. + + @param[in] Private Pointer to PxeBc private data. + @param[out] Type The type of the menu. + @param[in] UseDefaultItem Use default item or not. + + @retval EFI_ABORTED User cancel operation. + @retval EFI_SUCCESS Select the boot menu success. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcSelectBootMenu ( + IN PXEBC_PRIVATE_DATA *Private, + OUT UINT16 *Type, + IN BOOLEAN UseDefaultItem + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP_PACKET_CACHE *Cache; + PXEBC_VENDOR_OPTION *VendorOpt; + EFI_INPUT_KEY InputKey; + UINT32 OfferType; + UINT8 MenuSize; + UINT8 MenuNum; + INT32 TopRow; + UINT16 Select; + UINT16 LastSelect; + UINT8 Index; + BOOLEAN Finish; + CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE]; + PXEBC_BOOT_MENU_ENTRY *MenuItem; + PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM]; + + Finish = FALSE; + Select = 1; + Index = 0; + *Type = 0; + Mode = Private->PxeBc.Mode; + Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck; + OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType; + + // + // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec. + // + ASSERT (!Mode->UsingIpv6); + ASSERT (OfferType == PxeOfferTypeProxyPxe10); + + VendorOpt = &Cache->Dhcp4.VendorOpt; + if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) { + return EFI_SUCCESS; + } + + // + // Display the boot menu on the screen. + // + SetMem (Blank, sizeof(Blank), ' '); + + MenuSize = VendorOpt->BootMenuLen; + MenuItem = VendorOpt->BootMenu; + + if (MenuSize == 0) { + return EFI_DEVICE_ERROR; + } + + while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) { + ASSERT (MenuItem != NULL); + MenuArray[Index] = MenuItem; + MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3)); + MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3); + Index++; + } + + if (UseDefaultItem) { + ASSERT (MenuArray[0] != NULL); + CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16)); + *Type = NTOHS (*Type); + return EFI_SUCCESS; + } + + MenuNum = Index; + + for (Index = 0; Index < MenuNum; Index++) { + ASSERT (MenuArray[Index] != NULL); + PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen); + } + + TopRow = gST->ConOut->Mode->CursorRow - MenuNum; + + // + // Select the boot item by user in the boot menu. + // + do { + // + // Highlight selected row. + // + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select); + ASSERT (Select < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[Select] != NULL); + Blank[MenuArray[Select]->DescLen] = 0; + AsciiPrint ("%a\r", Blank); + PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum); + LastSelect = Select; + + while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) { + gBS->Stall (10 * TICKS_PER_MS); + } + + if (InputKey.ScanCode != 0) { + switch (InputKey.UnicodeChar) { + case CTRL ('c'): + InputKey.ScanCode = SCAN_ESC; + break; + + case CTRL ('j'): /* linefeed */ + case CTRL ('m'): /* return */ + Finish = TRUE; + break; + + case CTRL ('i'): /* tab */ + case ' ': + case 'd': + case 'D': + InputKey.ScanCode = SCAN_DOWN; + break; + + case CTRL ('h'): /* backspace */ + case 'u': + case 'U': + InputKey.ScanCode = SCAN_UP; + break; + + default: + InputKey.ScanCode = 0; + } + } + + switch (InputKey.ScanCode) { + case SCAN_LEFT: + case SCAN_UP: + if (Select != 0) { + Select--; + } + break; + + case SCAN_DOWN: + case SCAN_RIGHT: + if (++Select == MenuNum) { + Select--; + } + break; + + case SCAN_PAGE_UP: + case SCAN_HOME: + Select = 0; + break; + + case SCAN_PAGE_DOWN: + case SCAN_END: + Select = (UINT16) (MenuNum - 1); + break; + + case SCAN_ESC: + return EFI_ABORTED; + } + + // + // Unhighlight the last selected row. + // + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect); + ASSERT (LastSelect < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[LastSelect] != NULL); + Blank[MenuArray[LastSelect]->DescLen] = 0; + AsciiPrint ("%a\r", Blank); + PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum); + } while (!Finish); + + // + // Swap the byte order. + // + ASSERT (Select < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[Select] != NULL); + CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16)); + *Type = NTOHS (*Type); + + return EFI_SUCCESS; +} + + +/** + Parse out the boot information from the last Dhcp4 reply packet. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDhcp4BootInfo ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + UINT16 Value; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Status = EFI_SUCCESS; + *BufferSize = 0; + + // + // Get the last received Dhcp4 reply packet. + // + if (Mode->PxeReplyReceived) { + Cache4 = &Private->PxeReply.Dhcp4; + } else if (Mode->ProxyOfferReceived) { + Cache4 = &Private->ProxyOffer.Dhcp4; + } else { + Cache4 = &Private->DhcpAck.Dhcp4; + } + + // + // Parse the boot server Ipv4 address by next server address. + // If this field isn't available, use option 54 instead. + // + CopyMem ( + &Private->ServerIp, + &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr, + sizeof (EFI_IPv4_ADDRESS) + ); + + if (Private->ServerIp.Addr[0] == 0) { + CopyMem ( + &Private->ServerIp, + Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + // + // Parse the boot file name by option. + // + ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL); + Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data; + + if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) { + // + // Parse the boot file size by option. + // + CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value)); + Value = NTOHS (Value); + // + // The field of boot file size is 512 bytes in unit. + // + *BufferSize = 512 * Value; + } else { + // + // Get the bootfile size by tftp command if no option available. + // + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + + // + // Save the value of boot file size. + // + Private->BootFileSize = (UINTN) *BufferSize; + + // + // Display all the information: boot server address, boot file name and boot file size. + // + AsciiPrint ("\n Server IP address is "); + PxeBcShowIp4Addr (&Private->ServerIp.v4); + AsciiPrint ("\n NBP filename is %a", Private->BootFileName); + AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize); + + return Status; +} + + +/** + Parse out the boot information from the last Dhcp6 reply packet. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval EFI_BUFFER_TOO_SMALL + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDhcp6BootInfo ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + PXEBC_DHCP6_PACKET_CACHE *Cache6; + UINT16 Value; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Status = EFI_SUCCESS; + *BufferSize = 0; + + // + // Get the last received Dhcp6 reply packet. + // + if (Mode->PxeReplyReceived) { + Cache6 = &Private->PxeReply.Dhcp6; + } else if (Mode->ProxyOfferReceived) { + Cache6 = &Private->ProxyOffer.Dhcp6; + } else { + Cache6 = &Private->DhcpAck.Dhcp6; + } + + ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + + // + // Parse (m)tftp server ip address and bootfile name. + // + Status = PxeBcExtractBootFileUrl ( + &Private->BootFileName, + &Private->ServerIp.v6, + (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), + NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Parse the value of boot file size. + // + if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) { + // + // Parse it out if have the boot file parameter option. + // + Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + // + // The field of boot file size is 512 bytes in unit. + // + *BufferSize = 512 * Value; + } else { + // + // Send get file size command by tftp if option unavailable. + // + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + + // + // Save the value of boot file size. + // + Private->BootFileSize = (UINTN) *BufferSize; + + // + // Display all the information: boot server address, boot file name and boot file size. + // + AsciiPrint ("\n Server IP address is "); + PxeBcShowIp6Addr (&Private->ServerIp.v6); + AsciiPrint ("\n NBP filename is %a", Private->BootFileName); + AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize); + + return Status; +} + + +/** + Extract the discover information and boot server entry from the + cached packets if unspecified. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type The type of bootstrap to perform. + @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO. + @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY. + @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully extracted the information. + @retval EFI_DEVICE_ERROR Failed to extract the information. + +**/ +EFI_STATUS +PxeBcExtractDiscoverInfo ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info, + OUT PXEBC_BOOT_SVR_ENTRY **BootEntry, + OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + PXEBC_VENDOR_OPTION *VendorOpt; + PXEBC_BOOT_SVR_ENTRY *Entry; + BOOLEAN IsFound; + + Mode = Private->PxeBc.Mode; + + if (Mode->UsingIpv6) { + Info->IpCnt = 1; + Info->UseUCast = TRUE; + + Info->SrvList[0].Type = Type; + Info->SrvList[0].AcceptAnyResponse = FALSE; + + // + // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet. + // + CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + *SrvList = Info->SrvList; + } else { + Entry = NULL; + IsFound = FALSE; + Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4; + VendorOpt = &Cache4->VendorOpt; + + if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) { + // + // Address is not acquired or no discovery options. + // + return EFI_INVALID_PARAMETER; + } + + // + // Parse the boot server entry from the vendor option in the last cached packet. + // + Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl); + Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl); + Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl); + Info->UseUCast = Info->MustUseList; + + if (Info->UseMCast) { + // + // Get the multicast discover ip address from vendor option if has. + // + CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS)); + } + + Info->IpCnt = 0; + + if (Info->MustUseList) { + Entry = VendorOpt->BootSvr; + + while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) { + if (Entry->Type == HTONS (Type)) { + IsFound = TRUE; + break; + } + Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry); + } + + if (!IsFound) { + return EFI_DEVICE_ERROR; + } + + Info->IpCnt = Entry->IpCnt; + } + + *BootEntry = Entry; + } + + return EFI_SUCCESS; +} + + +/** + Build the discover packet and send out for boot server. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the destination address. + @param[in] IpCount The count of the server address. + @param[in] SrvList Pointer to the server address list. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDiscoverBootServer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcDhcp6Discover ( + Private, + Type, + Layer, + UseBis, + DestIp + ); + } else { + return PxeBcDhcp4Discover ( + Private, + Type, + Layer, + UseBis, + DestIp, + IpCount, + SrvList + ); + } +} + + +/** + Discover all the boot information for boot file. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully obtained all the boot information . + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancel current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDiscoverBootFile ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + UINT16 Type; + UINT16 Layer; + BOOLEAN UseBis; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP; + Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL; + + // + // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and + // other pxe boot information. + // + Status = PxeBc->Dhcp (PxeBc, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select a boot server from boot server list. + // + Status = PxeBcSelectBootPrompt (Private); + + if (Status == EFI_SUCCESS) { + // + // Choose by user's input. + // + Status = PxeBcSelectBootMenu (Private, &Type, TRUE); + } else if (Status == EFI_TIMEOUT) { + // + // Choose by default item. + // + Status = PxeBcSelectBootMenu (Private, &Type, FALSE); + } + + if (!EFI_ERROR (Status)) { + + if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) { + // + // Local boot(PXE bootstrap server) need abort + // + return EFI_ABORTED; + } + + // + // Start to discover the boot server to get (m)tftp server ip address, bootfile + // name and bootfile size. + // + UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected); + Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Parse the boot information. + // + if (Mode->UsingIpv6) { + Status = PxeBcDhcp6BootInfo (Private, BufferSize); + } else { + Status = PxeBcDhcp4BootInfo (Private, BufferSize); + } + + return Status; +} + + +/** + Install PxeBaseCodeCallbackProtocol if not installed before. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] NewMakeCallback If TRUE, it is a new callback. + Otherwise, it is not new callback. + @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully. + @retval Others Failed to install PxeBaseCodeCallbackProtocol. + +**/ +EFI_STATUS +PxeBcInstallCallback ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT BOOLEAN *NewMakeCallback + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_STATUS Status; + + // + // Check whether PxeBaseCodeCallbackProtocol already installed. + // + PxeBc = &Private->PxeBc; + Status = gBS->HandleProtocol ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID **) &Private->PxeBcCallback + ); + if (Status == EFI_UNSUPPORTED) { + + CopyMem ( + &Private->LoadFileCallback, + &gPxeBcCallBackTemplate, + sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL) + ); + + // + // Install a default callback if user didn't offer one. + // + Status = gBS->InstallProtocolInterface ( + &Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->LoadFileCallback + ); + + (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS); + + Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback); + if (EFI_ERROR (Status)) { + PxeBc->Stop (PxeBc); + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Uninstall PxeBaseCodeCallbackProtocol. + + @param[in] Private Pointer to PxeBc private data. + @param[in] NewMakeCallback If TRUE, it is a new callback. + Otherwise, it is not new callback. + +**/ +VOID +PxeBcUninstallCallback ( + IN PXEBC_PRIVATE_DATA *Private, + IN BOOLEAN NewMakeCallback + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + + PxeBc = &Private->PxeBc; + + if (NewMakeCallback) { + + NewMakeCallback = FALSE; + + PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback); + + gBS->UninstallProtocolInterface ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + &Private->LoadFileCallback + ); + } +} + + +/** + Download one of boot file in the list, and it's special for IPv6. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Read one of boot file in the list successfully. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_NOT_FOUND There is no proper boot file available. + @retval Others Failed to download boot file in the list. + +**/ +EFI_STATUS +PxeBcReadBootFileList ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINT64 *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + + PxeBc = &Private->PxeBc; + + // + // Try to download the boot file if everything is ready. + // + if (Buffer != NULL) { + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + Buffer, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + + + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + return Status; +} + + +/** + Load boot file into user buffer. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Get all the boot information successfully. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancelled the current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcLoadBootFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + BOOLEAN NewMakeCallback; + UINT64 RequiredSize; + UINT64 CurrentSize; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + + NewMakeCallback = FALSE; + PxeBc = &Private->PxeBc; + PxeBcMode = &Private->Mode; + CurrentSize = *BufferSize; + RequiredSize = 0; + + // + // Install pxebc callback protocol if hasn't been installed yet. + // + Status = PxeBcInstallCallback (Private, &NewMakeCallback); + if (EFI_ERROR(Status)) { + return Status; + } + + if (Private->BootFileSize == 0) { + // + // Discover the boot information about the bootfile if hasn't. + // + Status = PxeBcDiscoverBootFile (Private, &RequiredSize); + + if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) { + // + // It's error if the required buffer size is beyond the system scope. + // + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } else if (RequiredSize > 0) { + // + // Get the right buffer size of the bootfile required. + // + if (CurrentSize < RequiredSize || Buffer == NULL) { + // + // It's buffer too small if the size of user buffer is smaller than the required. + // + CurrentSize = RequiredSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + CurrentSize = RequiredSize; + } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) { + // + // Try to download another bootfile in list if failed to get the filesize of the last one. + // It's special for the case of IPv6. + // + Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer); + goto ON_EXIT; + } + } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) { + // + // It's buffer too small if the size of user buffer is smaller than the required. + // + CurrentSize = Private->BootFileSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + // + // Begin to download the bootfile if everything is ready. + // + AsciiPrint ("\n Downloading NBP file...\n"); + if (PxeBcMode->UsingIpv6) { + Status = PxeBcReadBootFileList ( + Private, + &CurrentSize, + Buffer + ); + } else { + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + Buffer, + FALSE, + &CurrentSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + +ON_EXIT: + *BufferSize = (UINTN) CurrentSize; + PxeBcUninstallCallback(Private, NewMakeCallback); + + if (Status == EFI_SUCCESS) { + AsciiPrint ("\n Succeed to download NBP file.\n"); + return EFI_SUCCESS; + } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) { + AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n"); + } else if (Status == EFI_DEVICE_ERROR) { + AsciiPrint ("\n PXE-E07: Network device error.\n"); + } else if (Status == EFI_OUT_OF_RESOURCES) { + AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n"); + } else if (Status == EFI_NO_MEDIA) { + AsciiPrint ("\n PXE-E12: Could not detect network connection.\n"); + } else if (Status == EFI_NO_RESPONSE) { + AsciiPrint ("\n PXE-E16: No offer received.\n"); + } else if (Status == EFI_TIMEOUT) { + AsciiPrint ("\n PXE-E18: Server response timeout.\n"); + } else if (Status == EFI_ABORTED) { + AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n"); + } else if (Status == EFI_ICMP_ERROR) { + AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n"); + } else if (Status == EFI_TFTP_ERROR) { + AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n"); + } else if (Status != EFI_BUFFER_TOO_SMALL) { + AsciiPrint ("\n PXE-E99: Unexpected network error.\n"); + } + + return Status; +} + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h new file mode 100644 index 0000000000..ef18907aa6 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h @@ -0,0 +1,100 @@ +/** @file + Boot functions declaration for UefiPxeBc Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_PXEBC_BOOT_H__ +#define __EFI_PXEBC_BOOT_H__ + +#define PXEBC_DISPLAY_MAX_LINE 70 +#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE 8 +#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE 4 + +#define PXEBC_IS_SIZE_OVERFLOWED(x) ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF)) + + +/** + Extract the discover information and boot server entry from the + cached packets if unspecified. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type The type of bootstrap to perform. + @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO. + @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY. + @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully extracted the information. + @retval EFI_DEVICE_ERROR Failed to extract the information. + +**/ +EFI_STATUS +PxeBcExtractDiscoverInfo ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info, + OUT PXEBC_BOOT_SVR_ENTRY **BootEntry, + OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList + ); + + +/** + Build the discover packet and send out for boot. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to the server address list. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDiscoverBootServer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ); + + +/** + Load boot file into user buffer. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Successfully obtained all the boot information. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancelled the current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcLoadBootFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ); + +#endif diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c new file mode 100644 index 0000000000..08415d97b4 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c @@ -0,0 +1,1599 @@ +/** @file + Functions implementation related with DHCPv4 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + +// +// This is a map from the interested DHCP4 option tags' index to the tag value. +// +UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = { + PXEBC_DHCP4_TAG_BOOTFILE_LEN, + PXEBC_DHCP4_TAG_VENDOR, + PXEBC_DHCP4_TAG_OVERLOAD, + PXEBC_DHCP4_TAG_MSG_TYPE, + PXEBC_DHCP4_TAG_SERVER_ID, + PXEBC_DHCP4_TAG_CLASS_ID, + PXEBC_DHCP4_TAG_BOOTFILE +}; + +// +// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec. +// +UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32}; + + +/** + Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer. + + @param[in] Buffer Pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag Tag of the required option. + + @retval NULL Failed to find the required option. + @retval Others The position of the required option. + +**/ +EFI_DHCP4_PACKET_OPTION * +PxeBcParseDhcp4Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT8 OptTag + ) +{ + EFI_DHCP4_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP4_PACKET_OPTION *) Buffer; + Offset = 0; + + while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) { + + if (Option->OpCode == OptTag) { + // + // Found the required option. + // + return Option; + } + + // + // Skip the current option to the next. + // + if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) { + Offset++; + } else { + Offset += Option->Length + 2; + } + + Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + + +/** + Parse the PXE vender options and extract the information from them. + + @param[in] Dhcp4Option Pointer to vendor options in buffer. + @param[in] VendorOption Pointer to structure to store information in vendor options. + +**/ +VOID +PxeBcParseVendorOptions ( + IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option, + IN PXEBC_VENDOR_OPTION *VendorOption + ) +{ + UINT32 *BitMap; + UINT8 VendorOptionLen; + EFI_DHCP4_PACKET_OPTION *PxeOption; + UINT8 Offset; + + BitMap = VendorOption->BitMap; + VendorOptionLen = Dhcp4Option->Length; + PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0]; + Offset = 0; + + ASSERT (PxeOption != NULL); + + while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) { + // + // Parse all the interesting PXE vendor options one by one. + // + switch (PxeOption->OpCode) { + + case PXEBC_VENDOR_TAG_MTFTP_IP: + + CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_CPORT: + + CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_SPORT: + + CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT: + + VendorOption->MtftpTimeout = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MTFTP_DELAY: + + VendorOption->MtftpDelay = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_DISCOVER_CTRL: + + VendorOption->DiscoverCtrl = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_DISCOVER_MCAST: + + CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + break; + + case PXEBC_VENDOR_TAG_BOOT_SERVERS: + + VendorOption->BootSvrLen = PxeOption->Length; + VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_BOOT_MENU: + + VendorOption->BootMenuLen = PxeOption->Length; + VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MENU_PROMPT: + + VendorOption->MenuPromptLen = PxeOption->Length; + VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MCAST_ALLOC: + + CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock)); + CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange)); + break; + + case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES: + + VendorOption->CredTypeLen = PxeOption->Length; + VendorOption->CredType = (UINT32 *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_BOOT_ITEM: + + CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType)); + CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer)); + break; + + default: + // + // Not interesting PXE vendor options. + // + break; + } + + // + // Set the bit map for the special PXE options. + // + SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode); + + // + // Continue to the next option. + // + if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) { + Offset++; + } else { + Offset = (UINT8) (Offset + PxeOption->Length + 2); + } + + PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset); + } +} + + +/** + Build the options buffer for the DHCPv4 request packet. + + @param[in] Private Pointer to PxeBc private data. + @param[out] OptList Pointer to the option pointer array. + @param[in] Buffer Pointer to the buffer to contain the option list. + @param[in] NeedMsgType If TRUE, it is necessary to include the Msg type option. + Otherwise, it is not necessary. + + @return Index The count of the built-in options. + +**/ +UINT32 +PxeBcBuildDhcp4Options ( + IN PXEBC_PRIVATE_DATA *Private, + OUT EFI_DHCP4_PACKET_OPTION **OptList, + IN UINT8 *Buffer, + IN BOOLEAN NeedMsgType + ) +{ + UINT32 Index; + PXEBC_DHCP4_OPTION_ENTRY OptEnt; + UINT16 Value; + + Index = 0; + OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer; + + if (NeedMsgType) { + // + // Append message type. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MSG_TYPE; + OptList[Index]->Length = 1; + OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data; + OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append max message size. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MAXMSG; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE); + OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data; + Value = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8); + CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + } + + // + // Append parameter request list option. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_PARA_LIST; + OptList[Index]->Length = 35; + OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data; + OptEnt.Para->ParaList[0] = PXEBC_DHCP4_TAG_NETMASK; + OptEnt.Para->ParaList[1] = PXEBC_DHCP4_TAG_TIME_OFFSET; + OptEnt.Para->ParaList[2] = PXEBC_DHCP4_TAG_ROUTER; + OptEnt.Para->ParaList[3] = PXEBC_DHCP4_TAG_TIME_SERVER; + OptEnt.Para->ParaList[4] = PXEBC_DHCP4_TAG_NAME_SERVER; + OptEnt.Para->ParaList[5] = PXEBC_DHCP4_TAG_DNS_SERVER; + OptEnt.Para->ParaList[6] = PXEBC_DHCP4_TAG_HOSTNAME; + OptEnt.Para->ParaList[7] = PXEBC_DHCP4_TAG_BOOTFILE_LEN; + OptEnt.Para->ParaList[8] = PXEBC_DHCP4_TAG_DOMAINNAME; + OptEnt.Para->ParaList[9] = PXEBC_DHCP4_TAG_ROOTPATH; + OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH; + OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU; + OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL; + OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST; + OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN; + OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER; + OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER; + OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR; + OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP; + OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE; + OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID; + OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1; + OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2; + OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID; + OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP; + OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE; + OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID; + OptEnt.Para->ParaList[27] = 0x80; + OptEnt.Para->ParaList[28] = 0x81; + OptEnt.Para->ParaList[29] = 0x82; + OptEnt.Para->ParaList[30] = 0x83; + OptEnt.Para->ParaList[31] = 0x84; + OptEnt.Para->ParaList[32] = 0x85; + OptEnt.Para->ParaList[33] = 0x86; + OptEnt.Para->ParaList[34] = 0x87; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append UUID/Guid-based client identifier option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UUID; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID); + OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data; + OptEnt.Uuid->Type = 0; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID)); + } + + // + // Append client network device interface option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UNDI; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI); + OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_ARCH; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH); + OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append vendor class identify option + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_CLASS_ID; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID); + OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data; + CopyMem ( + OptEnt.Clid, + DEFAULT_CLASS_ID_DATA, + sizeof (PXEBC_DHCP4_OPTION_CLID) + ); + PxeBcUintnToAscDecWithFormat ( + EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.Clid->ArchitectureType, + sizeof (OptEnt.Clid->ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName)); + PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor)); + PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor)); + } + + Index++; + + return Index; +} + + +/** + Create a template DHCPv4 packet as a seed. + + @param[out] Seed Pointer to the seed packet. + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + +**/ +VOID +PxeBcSeedDhcp4Packet ( + OUT EFI_DHCP4_PACKET *Seed, + IN EFI_UDP4_PROTOCOL *Udp4 + ) +{ + EFI_SIMPLE_NETWORK_MODE Mode; + EFI_DHCP4_HEADER *Header; + + // + // Get IfType and HwAddressSize from SNP mode data. + // + Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode); + + Seed->Size = sizeof (EFI_DHCP4_PACKET); + Seed->Length = sizeof (Seed->Dhcp4); + Header = &Seed->Dhcp4.Header; + ZeroMem (Header, sizeof (EFI_DHCP4_HEADER)); + Header->OpCode = PXEBC_DHCP4_OPCODE_REQUEST; + Header->HwType = Mode.IfType; + Header->HwAddrLen = (UINT8) Mode.HwAddressSize; + CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen); + + Seed->Dhcp4.Magik = PXEBC_DHCP4_MAGIC; + Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP; +} + + +/** + Cache the DHCPv4 packet. + + @param[in] Dst Pointer to the cache buffer for DHCPv4 packet. + @param[in] Src Pointer to the DHCPv4 packet to be cached. + +**/ +VOID +PxeBcCacheDhcp4Packet ( + IN EFI_DHCP4_PACKET *Dst, + IN EFI_DHCP4_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); + Dst->Length = Src->Length; +} + + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp4Packet ( + IN PXEBC_DHCP4_PACKET_CACHE *Cache4 + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_DHCP4_PACKET_OPTION **Options; + EFI_DHCP4_PACKET_OPTION *Option; + PXEBC_OFFER_TYPE OfferType; + UINTN Index; + BOOLEAN IsProxyOffer; + BOOLEAN IsPxeOffer; + UINT8 *Ptr8; + + IsProxyOffer = FALSE; + IsPxeOffer = FALSE; + + ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); + ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt)); + + Offer = &Cache4->Packet.Offer; + Options = Cache4->OptList; + + // + // Parse DHCPv4 options in this offer, and store the pointers. + // + for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { + Options[Index] = PxeBcParseDhcp4Options ( + Offer->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Offer), + mInterestedDhcp4Tags[Index] + ); + } + + // + // The offer with "yiaddr" is a proxy offer. + // + if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { + IsProxyOffer = TRUE; + } + + // + // The offer with "PXEClient" is a PXE offer. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID]; + if ((Option != NULL) && (Option->Length >= 9) && + (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) { + IsPxeOffer = TRUE; + } + + // + // Parse PXE vendor options in this offer, and store the contents/pointers. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR]; + if (IsPxeOffer && Option != NULL) { + PxeBcParseVendorOptions (Option, &Cache4->VendorOpt); + } + + // + // Check whether bootfilename and serverhostname overloaded, refers to rfc-2132 in details. + // If overloaded, parse the buffer as nested DHCPv4 options, or else just parse as bootfilename + // and serverhostname option. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD]; + if (Option != NULL && (Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) { + + Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = PxeBcParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.BootFileName, + sizeof (Offer->Dhcp4.Header.BootFileName), + PXEBC_DHCP4_TAG_BOOTFILE + ); + // + // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null + // terminated string. So force to append null terminated character at the end of string. + // + if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + Ptr8 = (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; + Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length; + *Ptr8 = '\0'; + } + + } else if ((Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) && + (Offer->Dhcp4.Header.BootFileName[0] != 0)) { + // + // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. + // Do not count dhcp option header here, or else will destory the serverhostname. + // + Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) + (&Offer->Dhcp4.Header.BootFileName[0] - + OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); + + } + + // + // Determine offer type of the DHCPv4 packet. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE]; + if (Option == NULL || Option->Data[0] == 0) { + // + // It's a Bootp offer. + // + OfferType = PxeOfferTypeBootp; + + Option = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]; + if (Option == NULL) { + // + // If the Bootp offer without bootfilename, discard it. + // + return EFI_DEVICE_ERROR; + } + } else { + + if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { + // + // It's a PXE10 offer with PXEClient and discover vendor option. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10; + } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { + // + // It's a WFM11a offer with PXEClient and mtftp vendor option. + // But multi-cast download is not supported currently, so discard it. + // + return EFI_DEVICE_ERROR; + } else if (IsPxeOffer) { + // + // It's a BINL offer only with PXEClient. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl; + } else { + // + // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet. + // + OfferType = PxeOfferTypeDhcpOnly; + } + } + + Cache4->OfferType = OfferType; + + return EFI_SUCCESS; +} + + +/** + Cache the DHCPv4 ack packet, and parse it on demand. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Ack Pointer to the DHCPv4 ack packet. + @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. + +**/ +VOID +PxeBcCopyDhcp4Ack ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *Ack, + IN BOOLEAN Verified + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + + Mode = Private->PxeBc.Mode; + + PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack); + + if (Verified) { + // + // Parse the ack packet and store it into mode data if needed. + // + PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4); + CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length); + Mode->DhcpAckReceived = TRUE; + } +} + + +/** + Cache the DHCPv4 proxy offer packet according to the received order. + + @param[in] Private Pointer to PxeBc private data. + @param[in] OfferIndex The received order of offer packets. + +**/ +VOID +PxeBcCopyProxyOffer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 OfferIndex + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PACKET *Offer; + + ASSERT (OfferIndex < Private->OfferNum); + ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); + + Mode = Private->PxeBc.Mode; + Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer; + + // + // Cache the proxy offer packet and parse it. + // + PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer); + PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4); + + // + // Store this packet into mode data. + // + CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length); + Mode->ProxyOfferReceived = TRUE; +} + + +/** + Retry to request bootfile name by the BINL offer. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Index The received order of offer packets. + + @retval EFI_SUCCESS Successfully retried to request bootfile name. + @retval EFI_DEVICE_ERROR Failed to retry bootfile name. + +**/ +EFI_STATUS +PxeBcRetryBinlOffer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS Status; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Reply; + + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl || + Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl); + + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + + // + // Prefer to siaddr in header as next server address. If it's zero, then use option 54. + // + if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) { + CopyMem ( + &ServerIp.Addr[0], + Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, + sizeof (EFI_IPv4_ADDRESS) + ); + } else { + CopyMem ( + &ServerIp.Addr[0], + &Offer->Dhcp4.Header.ServerAddr, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + Private->IsDoDiscover = FALSE; + Cache4 = &Private->ProxyOffer.Dhcp4; + Reply = &Cache4->Packet.Offer; + + // + // Send another request packet for bootfile name. + // + Status = PxeBcDhcp4Discover ( + Private, + 0, + NULL, + FALSE, + &ServerIp, + 0, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Parse the reply for the last request packet. + // + Status = PxeBcParseDhcp4Packet (Cache4); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Cache4->OfferType != PxeOfferTypeProxyPxe10 && + Cache4->OfferType != PxeOfferTypeProxyWfm11a && + Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + // + // This BINL ack doesn't have discovery option set or multicast option set + // or bootfile name specified. + // + return EFI_DEVICE_ERROR; + } + + // + // Store the reply into mode data. + // + Private->PxeBc.Mode->ProxyOfferReceived = TRUE; + CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length); + + return EFI_SUCCESS; +} + + +/** + Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. + + @param[in] Private Pointer to PxeBc private data. + @param[in] RcvdOffer Pointer to the received offer packet. + +**/ +VOID +PxeBcCacheDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *RcvdOffer + ) +{ + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + + ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; + Offer = &Cache4->Packet.Offer; + + // + // Cache the content of DHCPv4 packet firstly. + // + PxeBcCacheDhcp4Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv4 packet, and parse the options and offer type. + // + if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) { + return; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache4->OfferType; + ASSERT (OfferType < PxeOfferTypeMax); + + if (OfferType == PxeOfferTypeBootp) { + // + // It's a Bootp offer, only cache the first one, and discard the others. + // + if (Private->OfferCount[OfferType] == 0) { + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return; + } + } else { + ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM); + if (IS_PROXY_DHCP_OFFER (Offer)) { + // + // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer. + // + Private->IsProxyRecved = TRUE; + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Cache all proxy BINL offers. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } else if (Private->OfferCount[OfferType] > 0) { + // + // Only cache the first PXE10/WFM11a offer, and discard the others. + // + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return ; + } + } else { + // + // It's a DHCPv4 offer with yiaddr, and cache them all. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } + } + + Private->OfferNum++; +} + + +/** + Select an DHCPv4 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private Pointer to PxeBc private data. + +**/ +VOID +PxeBcSelectDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + UINT32 Index; + UINT32 OfferIndex; + EFI_DHCP4_PACKET *Offer; + + Private->SelectIndex = 0; + + if (Private->IsOfferSorted) { + // + // Select offer by default policy. + // + if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) { + // + // 1. DhcpPxe10 offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) { + // + // 2. DhcpWfm11a offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) { + // + // 3. DhcpOnly offer and ProxyPxe10 offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyPxe10; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) { + // + // 4. DhcpOnly offer and ProxyWfm11a offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyWfm11a; + + } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) { + // + // 5. DhcpBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyBinl] > 0) { + // + // 6. DhcpOnly offer and ProxyBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyBinl; + + } else { + // + // 7. DhcpOnly offer with bootfilename. + // + for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) { + OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index]; + if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + Private->SelectIndex = OfferIndex + 1; + break; + } + } + // + // 8. Bootp offer with bootfilename. + // + OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0]; + if (Private->SelectIndex == 0 && + Private->OfferCount[PxeOfferTypeBootp] > 0 && + Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + Private->SelectIndex = OfferIndex + 1; + } + } + } else { + // + // Select offer by received order. + // + for (Index = 0; Index < Private->OfferNum; Index++) { + + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + + if (IS_PROXY_DHCP_OFFER (Offer)) { + // + // Skip proxy offers + // + continue; + } + + if (!Private->IsProxyRecved && + Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly && + Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + // + // Skip if DhcpOnly offer without any other proxy offers or bootfilename. + // + continue; + } + + // + // Record the index of the select offer. + // + Private->SelectIndex = Index + 1; + break; + } + } +} + + +/** + Handle the DHCPv4 offer packet. + + @param[in] Private Pointer to PxeBc private data. + + @retval EFI_SUCCESS Handled the DHCPv4 offer packet successfully. + @retval EFI_NO_RESPONSE No response to the following request packet. + +**/ +EFI_STATUS +PxeBcHandleDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET_OPTION **Options; + UINT32 Index; + EFI_DHCP4_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + UINT32 ProxyIndex; + UINT32 SelectIndex; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PACKET *Ack; + + ASSERT (Private->SelectIndex > 0); + SelectIndex = (UINT32) (Private->SelectIndex - 1); + ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[SelectIndex].Dhcp4; + Options = Cache4->OptList; + Status = EFI_SUCCESS; + + if (Cache4->OfferType == PxeOfferTypeDhcpBinl) { + // + // DhcpBinl offer is selected, so need try to request bootfilename by this offer. + // + if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) { + Status = EFI_NO_RESPONSE; + } + } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) { + + if (Private->IsProxyRecved) { + // + // DhcpOnly offer is selected, so need try to request bootfile name. + // + ProxyIndex = 0; + if (Private->IsOfferSorted) { + // + // The proxy offer should be determined if select by default policy. + // IsOfferSorted means all offers are labeled by OfferIndex. + // + ASSERT (Private->SelectProxyType < PxeOfferTypeMax); + ASSERT (Private->OfferCount[Private->SelectProxyType] > 0); + + if (Private->SelectProxyType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfile name. + // + for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) { + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index]; + if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) { + break; + } + } + if (Index == Private->OfferCount[Private->SelectProxyType]) { + Status = EFI_NO_RESPONSE; + } + } else { + // + // For other proxy offers, only one is buffered. + // + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + } + } else { + // + // The proxy offer should not be determined if select by received order. + // + Status = EFI_NO_RESPONSE; + + for (Index = 0; Index < Private->OfferNum; Index++) { + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType; + if (!IS_PROXY_DHCP_OFFER (Offer)) { + // + // Skip non proxy DHCPv4 offers. + // + continue; + } + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfile name. + // + if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) { + continue; + } + } + + Private->SelectProxyType = OfferType; + ProxyIndex = Index; + Status = EFI_SUCCESS; + break; + } + } + + if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) { + // + // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. + // + PxeBcCopyProxyOffer (Private, ProxyIndex); + } + } else { + // + // Othewise, the bootfile name must be included in DhcpOnly offer. + // + ASSERT (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL); + } + } + + if (!EFI_ERROR (Status)) { + // + // All PXE boot information is ready by now. + // + Mode = Private->PxeBc.Mode; + Offer = &Cache4->Packet.Offer; + Ack = &Private->DhcpAck.Dhcp4.Packet.Ack; + if (Cache4->OfferType == PxeOfferTypeBootp) { + // + // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply + // should be taken as ack. + // + Ack = Offer; + } + + PxeBcCopyDhcp4Ack (Private, Ack, TRUE); + Mode->DhcpDiscoverValid = TRUE; + } + + return Status; +} + + +/** + EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This Pointer to the EFI DHCPv4 Protocol. + @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. + @param[in] Dhcp4Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv4 packet that is going to be sent or already received. + @param[out] NewPacket The packet that is used to replace the above Packet. + + @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol + driver will continue to wait for more DHCPOFFER packets until the + retry timeout expires. + @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process + and return to the Dhcp4Init or Dhcp4InitReboot state. + +**/ +EFI_STATUS +EFIAPI +PxeBcDhcp4CallBack ( + IN EFI_DHCP4_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP4_STATE CurrentState, + IN EFI_DHCP4_EVENT Dhcp4Event, + IN EFI_DHCP4_PACKET *Packet OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_DHCP4_PACKET_OPTION *MaxMsgSize; + UINT16 Value; + EFI_STATUS Status; + BOOLEAN Received; + + if ((Dhcp4Event != Dhcp4RcvdOffer) && + (Dhcp4Event != Dhcp4SelectOffer) && + (Dhcp4Event != Dhcp4SendDiscover) && + (Dhcp4Event != Dhcp4RcvdAck)) { + return EFI_SUCCESS; + } + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + Callback = Private->PxeBcCallback; + + // + // Override the Maximum DHCP Message Size. + // + MaxMsgSize = PxeBcParseDhcp4Options ( + Packet->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Packet), + PXEBC_DHCP4_TAG_MAXMSG + ); + if (MaxMsgSize != NULL) { + Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8); + CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); + } + + // + // Callback to user if any packets sent or received. + // + if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) { + Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck); + Status = Callback->Callback ( + Callback, + Private->Function, + Received, + Packet->Length, + (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4 + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + return EFI_ABORTED; + } + } + + Status = EFI_SUCCESS; + + switch (Dhcp4Event) { + + case Dhcp4SendDiscover: + // + // Cache the DHCPv4 discover packet to mode data directly. + // It need to check SendGuid as well as Dhcp4SendRequest. + // + CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length); + + case Dhcp4SendRequest: + if (Mode->SendGUID) { + // + // Send the system Guid instead of the MAC address as the hardware address if required. + // + if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); + } + Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); + } + break; + + case Dhcp4RcvdOffer: + Status = EFI_NOT_READY; + if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { + // + // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + PxeBcCacheDhcp4Offer (Private, Packet); + } + break; + + case Dhcp4SelectOffer: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + PxeBcSelectDhcp4Offer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; + } + break; + + case Dhcp4RcvdAck: + // + // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data + // without verification. + // + ASSERT (Private->SelectIndex != 0); + + PxeBcCopyDhcp4Ack (Private, Packet, FALSE); + break; + + default: + break; + } + + return Status; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp4Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT Sport; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token; + BOOLEAN IsBCast; + EFI_STATUS Status; + UINT16 RepIndex; + UINT16 SrvIndex; + UINT16 TryIndex; + EFI_DHCP4_LISTEN_POINT ListenPoint; + EFI_DHCP4_PACKET *Response; + UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; + UINT32 OptCount; + EFI_DHCP4_PACKET_OPTION *PxeOpt; + PXEBC_OPTION_BOOT_ITEM *PxeBootItem; + UINT8 VendorOptLen; + UINT32 Xid; + + Mode = Private->PxeBc.Mode; + Dhcp4 = Private->Dhcp4; + Status = EFI_SUCCESS; + + ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN)); + + // + // Use broadcast if destination address not specified. + // + if (DestIp == NULL) { + Sport = PXEBC_DHCP4_S_PORT; + IsBCast = TRUE; + } else { + Sport = PXEBC_BS_DISCOVER_PORT; + IsBCast = FALSE; + } + + if (!UseBis && Layer != NULL) { + *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; + } + + // + // Build all the options for the request packet. + // + OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE); + + if (Private->IsDoDiscover) { + // + // Add vendor option of PXE_BOOT_ITEM + // + VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1); + OptList[OptCount] = AllocateZeroPool (VendorOptLen); + if (OptList[OptCount] == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OptList[OptCount]->OpCode = PXEBC_DHCP4_TAG_VENDOR; + OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2); + PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data; + PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM; + PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM); + PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data; + PxeBootItem->Type = HTONS (Type); + PxeOpt->Data[PxeOpt->Length] = PXEBC_DHCP4_TAG_EOP; + + if (Layer != NULL) { + PxeBootItem->Layer = HTONS (*Layer); + } + + OptCount++; + } + + // + // Build the request packet with seed packet and option list. + // + Status = Dhcp4->Build ( + Dhcp4, + &Private->SeedPacket, + 0, + NULL, + OptCount, + OptList, + &Token.Packet + ); + // + // Free the vendor option of PXE_BOOT_ITEM. + // + if (Private->IsDoDiscover) { + FreePool (OptList[OptCount - 1]); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Mode->SendGUID) { + if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); + } + Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); + } + + // + // Set fields of the token for the request packet. + // + Xid = NET_RANDOM (NetRandomInitSeed ()); + Token.Packet->Dhcp4.Header.Xid = HTONL (Xid); + Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0)); + CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + + Token.RemotePort = Sport; + + if (IsBCast) { + SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff); + } else { + CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS)); + } + + CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS)); + + if (!IsBCast) { + Token.ListenPointCount = 1; + Token.ListenPoints = &ListenPoint; + Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT; + CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS)); + CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS)); + } + + // + // Send out the request packet to discover the bootfile. + // + for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) { + + Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex); + Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1)); + + Status = Dhcp4->TransmitReceive (Dhcp4, &Token); + if (Token.Status != EFI_TIMEOUT) { + break; + } + } + + if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) { + // + // No server response our PXE request + // + Status = EFI_TIMEOUT; + } + + if (!EFI_ERROR (Status)) { + + RepIndex = 0; + SrvIndex = 0; + Response = Token.ResponseList; + // + // Find the right PXE Reply according to server address. + // + while (RepIndex < Token.ResponseCount) { + + while (SrvIndex < IpCount) { + if (SrvList[SrvIndex].AcceptAnyResponse) { + break; + } + if ((SrvList[SrvIndex].Type == Type) && + EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &Private->ServerIp)) { + break; + } + SrvIndex++; + } + + if ((IpCount != SrvIndex) || (IpCount == 0)) { + break; + } + + SrvIndex = 0; + RepIndex++; + + Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size); + } + + if (RepIndex < Token.ResponseCount) { + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + if (Private->IsDoDiscover) { + PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response); + CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length); + } else { + PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response); + } + } else { + // + // Not found the right PXE reply packet. + // + Status = EFI_NOT_FOUND; + } + if (Token.ResponseList != NULL) { + FreePool (Token.ResponseList); + } + } + + FreePool (Token.Packet); + return Status; +} + + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +PxeBcDhcp4Dora ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PROTOCOL *Dhcp4 + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeMode; + EFI_DHCP4_CONFIG_DATA Config; + EFI_DHCP4_MODE_DATA Mode; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; + UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; + UINT32 OptCount; + EFI_STATUS Status; + + ASSERT (Dhcp4 != NULL); + + Status = EFI_SUCCESS; + PxeMode = Private->PxeBc.Mode; + + // + // Build option list for the request packet. + // + OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE); + ASSERT (OptCount> 0); + + ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp4Callback = PxeBcDhcp4CallBack; + Config.CallbackContext = Private; + Config.DiscoverTryCount = PXEBC_DHCP_RETRIES; + Config.DiscoverTimeout = mPxeDhcpTimeout; + + // + // Configure the DHCPv4 instance for PXE boot. + // + Status = Dhcp4->Configure (Dhcp4, &Config); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Initialize the record fields for DHCPv4 offer in private data. + // + Private->IsProxyRecved = FALSE; + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + // + // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. + // + Status = Dhcp4->Start (Dhcp4, NULL); + if (EFI_ERROR (Status)) { + if (Status == EFI_ICMP_ERROR) { + PxeMode->IcmpErrorReceived = TRUE; + } + goto ON_EXIT; + } + + // + // Get the acquired IPv4 address and store them. + // + Status = Dhcp4->GetModeData (Dhcp4, &Mode); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (Mode.State == Dhcp4Bound); + + CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + + Status = PxeBcFlushStaionIp (Private, &Private->StationIp, &Private->SubnetMask); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check the selected offer whether BINL retry is needed. + // + Status = PxeBcHandleDhcp4Offer (Private); + + AsciiPrint ("\n Station IP address is "); + + PxeBcShowIp4Addr (&Private->StationIp.v4); + +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + } else { + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + Dhcp4->Configure (Dhcp4, &Config); + Private->IsAddressOk = TRUE; + } + + return Status; +} diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h new file mode 100644 index 0000000000..bc21b212a2 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h @@ -0,0 +1,389 @@ +/** @file + Functions declaration related with DHCPv4 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_PXEBC_DHCP4_H__ +#define __EFI_PXEBC_DHCP4_H__ + +#define PXEBC_DHCP4_OPTION_MAX_NUM 16 +#define PXEBC_DHCP4_OPTION_MAX_SIZE 312 +#define PXEBC_DHCP4_PACKET_MAX_SIZE 1472 +#define PXEBC_DHCP4_S_PORT 67 +#define PXEBC_DHCP4_C_PORT 68 +#define PXEBC_BS_DOWNLOAD_PORT 69 +#define PXEBC_BS_DISCOVER_PORT 4011 +#define PXEBC_DHCP4_OPCODE_REQUEST 1 +#define PXEBC_DHCP4_OPCODE_REPLY 2 +#define PXEBC_DHCP4_MSG_TYPE_REQUEST 3 +#define PXEBC_DHCP4_MAGIC 0x63538263 // network byte order + +// +// Dhcp Options +// +#define PXEBC_DHCP4_TAG_PAD 0 // Pad Option +#define PXEBC_DHCP4_TAG_EOP 255 // End Option +#define PXEBC_DHCP4_TAG_NETMASK 1 // Subnet Mask +#define PXEBC_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC +#define PXEBC_DHCP4_TAG_ROUTER 3 // Router option, +#define PXEBC_DHCP4_TAG_TIME_SERVER 4 // Time Server +#define PXEBC_DHCP4_TAG_NAME_SERVER 5 // Name Server +#define PXEBC_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server +#define PXEBC_DHCP4_TAG_HOSTNAME 12 // Host Name +#define PXEBC_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size +#define PXEBC_DHCP4_TAG_DUMP 14 // Merit Dump File +#define PXEBC_DHCP4_TAG_DOMAINNAME 15 // Domain Name +#define PXEBC_DHCP4_TAG_ROOTPATH 17 // Root path +#define PXEBC_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path +#define PXEBC_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size +#define PXEBC_DHCP4_TAG_TTL 23 // Default IP Time-to-live +#define PXEBC_DHCP4_TAG_BROADCAST 28 // Broadcast Address +#define PXEBC_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain +#define PXEBC_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers +#define PXEBC_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers +#define PXEBC_DHCP4_TAG_VENDOR 43 // Vendor Specific Information +#define PXEBC_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address +#define PXEBC_DHCP4_TAG_LEASE 51 // IP Address Lease Time +#define PXEBC_DHCP4_TAG_OVERLOAD 52 // Option Overload +#define PXEBC_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type +#define PXEBC_DHCP4_TAG_SERVER_ID 54 // Server Identifier +#define PXEBC_DHCP4_TAG_PARA_LIST 55 // Parameter Request List +#define PXEBC_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size +#define PXEBC_DHCP4_TAG_T1 58 // Renewal (T1) Time Value +#define PXEBC_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value +#define PXEBC_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier +#define PXEBC_DHCP4_TAG_CLIENT_ID 61 // Client-identifier +#define PXEBC_DHCP4_TAG_TFTP 66 // TFTP server name +#define PXEBC_DHCP4_TAG_BOOTFILE 67 // Bootfile name +#define PXEBC_PXE_DHCP4_TAG_ARCH 93 +#define PXEBC_PXE_DHCP4_TAG_UNDI 94 +#define PXEBC_PXE_DHCP4_TAG_UUID 97 +// +// Sub-Options in Dhcp Vendor Option +// +#define PXEBC_VENDOR_TAG_MTFTP_IP 1 +#define PXEBC_VENDOR_TAG_MTFTP_CPORT 2 +#define PXEBC_VENDOR_TAG_MTFTP_SPORT 3 +#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT 4 +#define PXEBC_VENDOR_TAG_MTFTP_DELAY 5 +#define PXEBC_VENDOR_TAG_DISCOVER_CTRL 6 +#define PXEBC_VENDOR_TAG_DISCOVER_MCAST 7 +#define PXEBC_VENDOR_TAG_BOOT_SERVERS 8 +#define PXEBC_VENDOR_TAG_BOOT_MENU 9 +#define PXEBC_VENDOR_TAG_MENU_PROMPT 10 +#define PXEBC_VENDOR_TAG_MCAST_ALLOC 11 +#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES 12 +#define PXEBC_VENDOR_TAG_BOOT_ITEM 71 + +#define PXEBC_BOOT_REQUEST_TIMEOUT 1 +#define PXEBC_BOOT_REQUEST_RETRIES 4 + +#define PXEBC_DHCP4_OVERLOAD_FILE 1 +#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME 2 + + +// +// The array index of the DHCP4 option tag interested +// +#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0 +#define PXEBC_DHCP4_TAG_INDEX_VENDOR 1 +#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD 2 +#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE 3 +#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID 4 +#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID 5 +#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE 6 +#define PXEBC_DHCP4_TAG_INDEX_MAX 7 + +// +// Dhcp4 and Dhcp6 share this definition, and corresponding +// relatioinship is as follows: +// +// Dhcp4Discover <> Dhcp6Solicit +// Dhcp4Offer <> Dhcp6Advertise +// Dhcp4Request <> Dhcp6Request +// Dhcp4Ack <> DHcp6Reply +// +typedef enum { + PxeOfferTypeDhcpOnly, + PxeOfferTypeDhcpPxe10, + PxeOfferTypeDhcpWfm11a, + PxeOfferTypeDhcpBinl, + PxeOfferTypeProxyPxe10, + PxeOfferTypeProxyWfm11a, + PxeOfferTypeProxyBinl, + PxeOfferTypeBootp, + PxeOfferTypeMax +} PXEBC_OFFER_TYPE; + +#define BIT(x) (1 << x) +#define CTRL(x) (0x1F & (x)) +#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:?????:????:??????" +#define DEFAULT_UNDI_TYPE 1 +#define DEFAULT_UNDI_MAJOR 3 +#define DEFAULT_UNDI_MINOR 0 + +#define MTFTP_VENDOR_OPTION_BIT_MAP \ + (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY)) + +#define DISCOVER_VENDOR_OPTION_BIT_MAP \ + (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \ + BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \ + BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \ + BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \ + BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) + +#define IS_VALID_BOOT_PROMPT(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \ + == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) + +#define IS_VALID_BOOT_MENU(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) + +#define IS_VALID_MTFTP_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \ + == MTFTP_VENDOR_OPTION_BIT_MAP) + +#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0) + +#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \ + == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) + +#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \ + (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \ + BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) + +#define SET_VENDOR_OPTION_BIT_MAP(x, y) \ + (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32))) + +#define GET_NEXT_DHCP_OPTION(Opt) \ + (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1) + +#define GET_OPTION_BUFFER_LEN(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4) + +#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \ + (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \ + ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS)) + +#define IS_PROXY_DHCP_OFFER(Offer) \ + EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr) + +#define IS_DISABLE_BCAST_DISCOVER(x) \ + (((x) & BIT (0)) == BIT (0)) + +#define IS_DISABLE_MCAST_DISCOVER(x) \ + (((x) & BIT (1)) == BIT (1)) + +#define IS_ENABLE_USE_SERVER_LIST(x) \ + (((x) & BIT (2)) == BIT (2)) + +#define IS_ENABLE_BOOT_FILE_NAME(x) \ + (((x) & BIT (3)) == BIT (3)) + + +#pragma pack(1) +typedef struct { + UINT8 ParaList[135]; +} PXEBC_DHCP4_OPTION_PARA; + +typedef struct { + UINT16 Size; +} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} PXEBC_DHCP4_OPTION_UNDI; + +typedef struct { + UINT8 Type; +} PXEBC_DHCP4_OPTION_MESG; + +typedef struct { + UINT16 Type; +} PXEBC_DHCP4_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} PXEBC_DHCP4_OPTION_CLID; + +typedef struct { + UINT8 Type; + UINT8 Guid[16]; +} PXEBC_DHCP4_OPTION_UUID; + +typedef struct { + UINT16 Type; + UINT16 Layer; +} PXEBC_OPTION_BOOT_ITEM; + +#pragma pack() + +typedef union { + PXEBC_DHCP4_OPTION_PARA *Para; + PXEBC_DHCP4_OPTION_UNDI *Undi; + PXEBC_DHCP4_OPTION_ARCH *Arch; + PXEBC_DHCP4_OPTION_CLID *Clid; + PXEBC_DHCP4_OPTION_UUID *Uuid; + PXEBC_DHCP4_OPTION_MESG *Mesg; + PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize; +} PXEBC_DHCP4_OPTION_ENTRY; + +typedef struct { + UINT16 Type; + UINT8 IpCnt; + EFI_IPv4_ADDRESS IpAddr[1]; +} PXEBC_BOOT_SVR_ENTRY; + +typedef struct { + UINT16 Type; + UINT8 DescLen; + UINT8 DescStr[1]; +} PXEBC_BOOT_MENU_ENTRY; + +typedef struct { + UINT8 Timeout; + UINT8 Prompt[1]; +} PXEBC_MENU_PROMPT; + +typedef struct { + UINT32 BitMap[8]; + EFI_IPv4_ADDRESS MtftpIp; + UINT16 MtftpCPort; + UINT16 MtftpSPort; + UINT8 MtftpTimeout; + UINT8 MtftpDelay; + UINT8 DiscoverCtrl; + EFI_IPv4_ADDRESS DiscoverMcastIp; + EFI_IPv4_ADDRESS McastIpBase; + UINT16 McastIpBlock; + UINT16 McastIpRange; + UINT16 BootSrvType; + UINT16 BootSrvLayer; + PXEBC_BOOT_SVR_ENTRY *BootSvr; + UINT8 BootSvrLen; + PXEBC_BOOT_MENU_ENTRY *BootMenu; + UINT8 BootMenuLen; + PXEBC_MENU_PROMPT *MenuPrompt; + UINT8 MenuPromptLen; + UINT32 *CredType; + UINT8 CredTypeLen; +} PXEBC_VENDOR_OPTION; + +typedef union { + EFI_DHCP4_PACKET Offer; + EFI_DHCP4_PACKET Ack; + UINT8 Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE]; +} PXEBC_DHCP4_PACKET; + +typedef struct { + PXEBC_DHCP4_PACKET Packet; + PXEBC_OFFER_TYPE OfferType; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX]; + PXEBC_VENDOR_OPTION VendorOpt; +} PXEBC_DHCP4_PACKET_CACHE; + + +/** + Create a template DHCPv4 packet as a seed. + + @param[out] Seed Pointer to the seed packet. + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + +**/ +VOID +PxeBcSeedDhcp4Packet ( + OUT EFI_DHCP4_PACKET *Seed, + IN EFI_UDP4_PROTOCOL *Udp4 + ); + + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp4Packet ( + IN PXEBC_DHCP4_PACKET_CACHE *Cache4 + ); + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp4Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ); + + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +PxeBcDhcp4Dora ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PROTOCOL *Dhcp4 + ); + +#endif + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c new file mode 100644 index 0000000000..0b7cf1f947 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c @@ -0,0 +1,1531 @@ +/** @file + Functions implementation related with DHCPv6 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + + +/** + Parse out a DHCPv6 option by OptTag, and find the position in buffer. + + @param[in] Buffer The pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag The required option tag. + + @retval NULL Failed to parse the required option. + @retval Others The postion of the required option in buffer. + +**/ +EFI_DHCP6_PACKET_OPTION * +PxeBcParseDhcp6Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT16 OptTag + ) +{ + EFI_DHCP6_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP6_PACKET_OPTION *) Buffer; + Offset = 0; + + // + // OpLen and OpCode here are both stored in network order. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == OptTag) { + + return Option; + } + + Offset += (NTOHS(Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + + +/** + Build the options buffer for the DHCPv6 request packet. + + @param[in] Private The pointer to PxeBc private data. + @param[out] OptList The pointer to the option pointer array. + @param[in] Buffer The pointer to the buffer to contain the option list. + + @return Index The count of the built-in options. + +**/ +UINT32 +PxeBcBuildDhcp6Options ( + IN PXEBC_PRIVATE_DATA *Private, + OUT EFI_DHCP6_PACKET_OPTION **OptList, + IN UINT8 *Buffer + ) +{ + PXEBC_DHCP6_OPTION_ENTRY OptEnt; + UINT32 Index; + UINT16 Value; + + Index = 0; + OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer; + + // + // Append client option request option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO); + OptList[Index]->OpLen = HTONS (4); + OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data; + OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL); + OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_UNDI)); + OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + OptEnt.Undi->Reserved = 0; + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH)); + OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append vendor class option to store the PXE class identifier. + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS)); + OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data; + OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM); + OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID)); + CopyMem ( + &OptEnt.VendorClass->ClassId, + DEFAULT_CLASS_ID_DATA, + sizeof (PXEBC_CLASS_ID) + ); + PxeBcUintnToAscDecWithFormat ( + EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.VendorClass->ClassId.ArchitectureType, + sizeof (OptEnt.VendorClass->ClassId.ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem ( + OptEnt.VendorClass->ClassId.InterfaceName, + Private->Nii->StringId, + sizeof (OptEnt.VendorClass->ClassId.InterfaceName) + ); + PxeBcUintnToAscDecWithFormat ( + Private->Nii->MajorVer, + OptEnt.VendorClass->ClassId.UndiMajor, + sizeof (OptEnt.VendorClass->ClassId.UndiMajor) + ); + PxeBcUintnToAscDecWithFormat ( + Private->Nii->MinorVer, + OptEnt.VendorClass->ClassId.UndiMinor, + sizeof (OptEnt.VendorClass->ClassId.UndiMinor) + ); + } + + Index++; + + return Index; +} + + +/** + Cache the DHCPv6 packet. + + @param[in] Dst The pointer to the cache buffer for DHCPv6 packet. + @param[in] Src The pointer to the DHCPv6 packet to be cached. + +**/ +VOID +PxeBcCacheDhcp6Packet ( + IN EFI_DHCP6_PACKET *Dst, + IN EFI_DHCP6_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); + Dst->Length = Src->Length; +} + + +/** + Free all the nodes in the list for boot file. + + @param[in] Head The pointer to the head of list. + +**/ +VOID +PxeBcFreeBootFileOption ( + IN LIST_ENTRY *Head + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + PXEBC_DHCP6_OPTION_NODE *Node; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) { + Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link); + RemoveEntryList (Entry); + FreePool (Node); + } +} + + +/** + Parse the Boot File URL option. + + @param[out] FileName The pointer to the boot file name. + @param[in, out] SrvAddr The pointer to the boot server address. + @param[in] BootFile The pointer to the boot file URL option data. + @param[in] Length The length of the boot file URL option data. + + @retval EFI_ABORTED User cancel operation. + @retval EFI_SUCCESS Selected the boot menu successfully. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcExtractBootFileUrl ( + OUT UINT8 **FileName, + IN OUT EFI_IPv6_ADDRESS *SrvAddr, + IN CHAR8 *BootFile, + IN UINT16 Length + ) +{ + UINT16 PrefixLen; + UINT8 *BootFileNamePtr; + UINT8 *BootFileName; + UINT16 BootFileNameLen; + CHAR8 *TmpStr; + CHAR8 *ServerAddressOption; + CHAR8 *ServerAddress; + EFI_STATUS Status; + + // + // The format of the Boot File URL option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPT_BOOTFILE_URL | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . bootfile-url (variable length) . + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Based upon RFC 5970 and UEFI errata that will appear in chapter 21.3 of UEFI 2.3 + // specification after 2.3 errata B and future UEFI Specifications after 2.3. + // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME + // As an example where the BOOTFILE_NAME is the EFI loader and + // SERVER_ADDRESS is the ASCII encoding of an IPV6 address. + // + PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX); + + if (Length <= PrefixLen || + CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) { + return EFI_NOT_FOUND; + } + + BootFile = BootFile + PrefixLen; + Length = (UINT16) (Length - PrefixLen); + + TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1); + if (TmpStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TmpStr, BootFile, Length); + TmpStr[Length] = '\0'; + + // + // Get the part of SERVER_ADDRESS string. + // + ServerAddressOption = TmpStr; + if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + ServerAddressOption ++; + ServerAddress = ServerAddressOption; + while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) { + ServerAddress++; + } + + if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + *ServerAddress = '\0'; + + // + // Convert the string of server address to Ipv6 address format and store it. + // + Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr); + if (EFI_ERROR (Status)) { + FreePool (TmpStr); + return Status; + } + + // + // Get the part of BOOTFILE_NAME string. + // + BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1); + if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + ++BootFileNamePtr; + BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1); + if (BootFileNameLen != 0 || FileName != NULL) { + BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen); + if (BootFileName == NULL) { + FreePool (TmpStr); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (BootFileName, BootFileNamePtr, BootFileNameLen); + BootFileName[BootFileNameLen - 1] = '\0'; + *FileName = BootFileName; + } + + + FreePool (TmpStr); + + return EFI_SUCCESS; +} + + +/** + Parse the Boot File Parameter option. + + @param[in] BootFilePara The pointer to boot file parameter option data. + @param[out] BootFileSize The pointer to the parsed boot file size. + + @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option. + @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option. + +**/ +EFI_STATUS +PxeBcExtractBootFileParam ( + IN CHAR8 *BootFilePara, + OUT UINT16 *BootFileSize + ) +{ + UINT16 Length; + UINT8 Index; + UINT8 Digit; + UINT32 Size; + + CopyMem (&Length, BootFilePara, sizeof (UINT16)); + Length = NTOHS (Length); + + // + // The BootFile Size should be 1~5 byte ASCII strings + // + if (Length < 1 || Length > 5) { + return EFI_NOT_FOUND; + } + + // + // Extract the value of BootFile Size. + // + BootFilePara = BootFilePara + sizeof (UINT16); + Size = 0; + for (Index = 0; Index < Length; Index++) { + if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) { + return EFI_NOT_FOUND; + } + + Size = (Size + Digit) * 10; + } + + Size = Size / 10; + if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) { + return EFI_NOT_FOUND; + } + + *BootFileSize = (UINT16) Size; + return EFI_SUCCESS; +} + + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet. + +**/ +EFI_STATUS +PxeBcParseDhcp6Packet ( + IN PXEBC_DHCP6_PACKET_CACHE *Cache6 + ) +{ + EFI_DHCP6_PACKET *Offer; + EFI_DHCP6_PACKET_OPTION **Options; + EFI_DHCP6_PACKET_OPTION *Option; + PXEBC_OFFER_TYPE OfferType; + BOOLEAN IsProxyOffer; + BOOLEAN IsPxeOffer; + UINT32 Offset; + UINT32 Length; + UINT32 EnterpriseNum; + + IsProxyOffer = TRUE; + IsPxeOffer = FALSE; + Offer = &Cache6->Packet.Offer; + Options = Cache6->OptList; + + ZeroMem (Cache6->OptList, sizeof (Cache6->OptList)); + + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option); + Offset = 0; + Length = GET_DHCP6_OPTION_SIZE (Offer); + + // + // OpLen and OpCode here are both stored in network order, since they are from original packet. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) { + Options[PXEBC_DHCP6_IDX_IA_NA] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) { + // + // The server sends this option to inform the client about an URL to a boot file. + // + Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) { + Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) { + Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option; + } + + Offset += (NTOHS (Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset); + } + + // + // The offer with assigned client address is a proxy offer. + // An ia_na option, embeded with valid ia_addr option and a status_code of success. + // + Option = Options[PXEBC_DHCP6_IDX_IA_NA]; + if (Option != NULL && NTOHS(Option->OpLen) >= 12) { + Option = PxeBcParseDhcp6Options ( + Option->Data + 12, + NTOHS (Option->OpLen), + PXEBC_DHCP6_OPT_STATUS_CODE + ); + if (Option != NULL && Option->Data[0] == 0) { + IsProxyOffer = FALSE; + } + } + + // + // The offer with "PXEClient" is a pxe offer. + // + Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS]; + EnterpriseNum = PXEBC_DHCP6_ENTERPRISE_NUM; + if (Option != NULL && + NTOHS(Option->OpLen) >= 13 && + CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 && + CompareMem (&Option->Data[4], DEFAULT_CLASS_ID_DATA, 9) == 0) { + IsPxeOffer = TRUE; + } + + // + // Determine offer type of the dhcp6 packet. + // + if (IsPxeOffer) { + // + // It's a binl offer only with PXEClient. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl; + } else { + // + // It's a dhcp only offer, which is a pure dhcp6 offer packet. + // + OfferType = PxeOfferTypeDhcpOnly; + } + + Cache6->OfferType = OfferType; + + return EFI_SUCCESS; +} + + +/** + Cache the DHCPv6 ack packet, and parse it on demand. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Ack The pointer to the DHCPv6 ack packet. + @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. + +**/ +VOID +PxeBcCopyDhcp6Ack ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *Ack, + IN BOOLEAN Verified + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + + Mode = Private->PxeBc.Mode; + + PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack); + + if (Verified) { + // + // Parse the ack packet and store it into mode data if needed. + // + PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6); + CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length); + Mode->DhcpAckReceived = TRUE; + } +} + + +/** + Cache the DHCPv6 proxy offer packet according to the received order. + + @param[in] Private The pointer to PxeBc private data. + @param[in] OfferIndex The received order of offer packets. + +**/ +VOID +PxeBcCopyDhcp6Proxy ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 OfferIndex + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP6_PACKET *Offer; + + ASSERT (OfferIndex < Private->OfferNum); + ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); + + Mode = Private->PxeBc.Mode; + Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer; + + // + // Cache the proxy offer packet and parse it. + // + PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer); + PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6); + + // + // Store this packet into mode data. + // + CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length); + Mode->ProxyOfferReceived = TRUE; +} + + +/** + Retry to request bootfile name by the BINL offer. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Index The received order of offer packets. + + @retval EFI_SUCCESS Successfully retried a request for the bootfile name. + @retval EFI_DEVICE_ERROR Failed to retry the bootfile name. + +**/ +EFI_STATUS +PxeBcRetryDhcp6Binl ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP6_PACKET_CACHE *Offer; + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS Status; + + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl || + Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl); + + Mode = Private->PxeBc.Mode; + Private->IsDoDiscover = FALSE; + Offer = &Private->OfferBuffer[Index].Dhcp6; + + ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + // + // Parse out the next server address from the last offer, and store it + // + Status = PxeBcExtractBootFileUrl ( + NULL, + &ServerIp.v6, + (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), + NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer. + // + Status = PxeBcDhcp6Discover ( + Private, + 0, + NULL, + FALSE, + &ServerIp + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Cache6 = &Private->ProxyOffer.Dhcp6; + Status = PxeBcParseDhcp6Packet (Cache6); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Cache6->OfferType != PxeOfferTypeProxyPxe10 && + Cache6->OfferType != PxeOfferTypeProxyWfm11a && + Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + // + // This BINL ack doesn't have discovery option set or multicast option set + // or bootfile name specified. + // + return EFI_DEVICE_ERROR; + } + + Mode->ProxyOfferReceived = TRUE; + CopyMem ( + &Mode->ProxyOffer.Dhcpv6, + &Cache6->Packet.Offer.Dhcp6, + Cache6->Packet.Offer.Length + ); + + return EFI_SUCCESS; +} + + +/** + Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] RcvdOffer The pointer to the received offer packet. + +**/ +VOID +PxeBcCacheDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *RcvdOffer + ) +{ + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_DHCP6_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + + Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; + Offer = &Cache6->Packet.Offer; + + // + // Cache the content of DHCPv6 packet firstly. + // + PxeBcCacheDhcp6Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv6 packet, and parse the options and offer type. + // + if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) { + return ; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache6->OfferType; + ASSERT (OfferType < PxeOfferTypeMax); + ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM); + + if (IS_PROXY_OFFER (OfferType)) { + // + // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer. + // + Private->IsProxyRecved = TRUE; + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Cache all proxy BINL offers. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } else if (Private->OfferCount[OfferType] > 0) { + // + // Only cache the first PXE10/WFM11a offer, and discard the others. + // + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return; + } + } else { + // + // It's a DHCPv6 offer with yiaddr, and cache them all. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } + + Private->OfferNum++; +} + + +/** + Select an DHCPv6 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcSelectDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + UINT32 Index; + UINT32 OfferIndex; + PXEBC_OFFER_TYPE OfferType; + + Private->SelectIndex = 0; + + if (Private->IsOfferSorted) { + // + // Select offer by default policy. + // + if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) { + // + // 1. DhcpPxe10 offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) { + // + // 2. DhcpWfm11a offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) { + // + // 3. DhcpOnly offer and ProxyPxe10 offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyPxe10; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) { + // + // 4. DhcpOnly offer and ProxyWfm11a offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyWfm11a; + + } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) { + // + // 5. DhcpBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyBinl] > 0) { + // + // 6. DhcpOnly offer and ProxyBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyBinl; + + } else { + // + // 7. DhcpOnly offer with bootfilename. + // + for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) { + OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index]; + if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) { + Private->SelectIndex = OfferIndex + 1; + break; + } + } + } + } else { + // + // Select offer by received order. + // + for (Index = 0; Index < Private->OfferNum; Index++) { + + OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType; + + if (IS_PROXY_OFFER (OfferType)) { + // + // Skip proxy offers + // + continue; + } + + if (!Private->IsProxyRecved && + OfferType == PxeOfferTypeDhcpOnly && + Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + // + // Skip if DhcpOnly offer without any other proxy offers or bootfilename. + // + continue; + } + + Private->SelectIndex = Index + 1; + break; + } + } +} + + +/** + Handle the DHCPv6 offer packet. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully. + @retval EFI_NO_RESPONSE No response to the following request packet. + +**/ +EFI_STATUS +PxeBcHandleDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_STATUS Status; + PXEBC_OFFER_TYPE OfferType; + UINT32 ProxyIndex; + UINT32 SelectIndex; + UINT32 Index; + + ASSERT (Private->SelectIndex > 0); + SelectIndex = (UINT32) (Private->SelectIndex - 1); + ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM); + Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6; + Status = EFI_SUCCESS; + + if (Cache6->OfferType == PxeOfferTypeDhcpBinl) { + // + // DhcpBinl offer is selected, so need try to request bootfilename by this offer. + // + if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) { + Status = EFI_NO_RESPONSE; + } + } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) { + + if (Private->IsProxyRecved) { + // + // DhcpOnly offer is selected, so need try to request bootfilename. + // + ProxyIndex = 0; + if (Private->IsOfferSorted) { + // + // The proxy offer should be determined if select by default policy. + // IsOfferSorted means all offers are labeled by OfferIndex. + // + ASSERT (Private->OfferCount[Private->SelectProxyType] > 0); + + if (Private->SelectProxyType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfilename. + // + for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) { + + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index]; + if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) { + break; + } + } + if (Index == Private->OfferCount[Private->SelectProxyType]) { + Status = EFI_NO_RESPONSE; + } + } else { + // + // For other proxy offers (pxe10 or wfm11a), only one is buffered. + // + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + } + } else { + // + // The proxy offer should not be determined if select by received order. + // + Status = EFI_NO_RESPONSE; + + for (Index = 0; Index < Private->OfferNum; Index++) { + + OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType; + + if (!IS_PROXY_OFFER (OfferType)) { + // + // Skip non proxy dhcp offers. + // + continue; + } + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfilename. + // + if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) { + continue; + } + } + + Private->SelectProxyType = OfferType; + ProxyIndex = Index; + Status = EFI_SUCCESS; + break; + } + } + + if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) { + // + // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. + // + PxeBcCopyDhcp6Proxy (Private, ProxyIndex); + } + } else { + // + // Othewise, the bootfilename must be included in DhcpOnly offer. + // + ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + } + } + + if (!EFI_ERROR (Status)) { + // + // All PXE boot information is ready by now. + // + PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE); + Private->PxeBc.Mode->DhcpDiscoverValid = TRUE; + } + + return Status; +} + + +/** + Unregister the address by Ip6Config protocol. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcUnregisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) { + // + // PXE driver change the policy of IP6 driver, it's a chance to recover. + // Keep the point and there is no enough requirements to do recovery. + // + } +} + + +/** + Register the ready address by Ip6Config protocol. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] Address The pointer to the ready address. + + @retval EFI_SUCCESS Registered the address succesfully. + @retval Others Failed to register the address. + +**/ +EFI_STATUS +PxeBcRegisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_IPv6_ADDRESS *Address + ) +{ + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr; + UINTN DataSize; + EFI_EVENT TimeOutEvt; + EFI_EVENT MappedEvt; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + TimeOutEvt = NULL; + MappedEvt = NULL; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Ip6Cfg = Private->Ip6Cfg; + Ip6 = Private->Ip6; + + ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS)); + + // + // Get and store the current policy of IP6 driver. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + &DataSize, + &Private->Ip6Policy + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // There is no channel between IP6 and PXE driver about address setting, + // so it has to set the new address by Ip6ConfigProtocol manually. + // + Policy = Ip6ConfigPolicyManual; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + // + // There is no need to recover later. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + goto ON_EXIT; + } + + // + // Create a timer as setting address timeout event since DAD in IP6 driver. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Create a notify event to set address flag when DAD if IP6 driver succeeded. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &Private->IsAddressOk, + &MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Ip6Cfg->RegisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS), + &CfgAddr + ); + if (EFI_ERROR(Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } + + // + // Start the 5 secondes timer to wait for setting address. + // + Status = EFI_NO_MAPPING; + gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT); + + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + Ip6->Poll (Ip6); + if (Private->IsAddressOk) { + Status = EFI_SUCCESS; + break; + } + } + +ON_EXIT: + if (MappedEvt != NULL) { + Ip6Cfg->UnregisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + gBS->CloseEvent (MappedEvt); + } + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + return Status; +} + + +/** + EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This The pointer to the EFI DHCPv6 Protocol. + @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver. + @param[in] Dhcp6Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv6 packet that is going to be sent or was already received. + @param[out] NewPacket The packet that is used to replace the Packet above. + + @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol + driver will continue to wait for more packets. + @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process. + +**/ +EFI_STATUS +EFIAPI +PxeBcDhcp6CallBack ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_STATE CurrentState, + IN EFI_DHCP6_EVENT Dhcp6Event, + IN EFI_DHCP6_PACKET *Packet, + OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_DHCP6_PACKET *SelectAd; + EFI_STATUS Status; + BOOLEAN Received; + + if ((Dhcp6Event != Dhcp6RcvdAdvertise) && + (Dhcp6Event != Dhcp6SelectAdvertise) && + (Dhcp6Event != Dhcp6SendSolicit) && + (Dhcp6Event != Dhcp6SendRequest) && + (Dhcp6Event != Dhcp6RcvdReply)) { + return EFI_SUCCESS; + } + + ASSERT (Packet != NULL); + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + Callback = Private->PxeBcCallback; + + // + // Callback to user when any traffic ocurred if has. + // + if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) { + Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply); + Status = Callback->Callback ( + Callback, + Private->Function, + Received, + Packet->Length, + (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6 + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + return EFI_ABORTED; + } + } + + Status = EFI_SUCCESS; + + switch (Dhcp6Event) { + + case Dhcp6SendSolicit: + // + // Cache the dhcp discover packet to mode data directly. + // + CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length); + break; + + case Dhcp6RcvdAdvertise: + Status = EFI_NOT_READY; + if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { + // + // Cache the dhcp offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + PxeBcCacheDhcp6Offer (Private, Packet); + } + break; + + case Dhcp6SendRequest: + // + // Store the request packet as seed packet for discover. + // + if (Private->Dhcp6Request != NULL) { + FreePool (Private->Dhcp6Request); + } + Private->Dhcp6Request = AllocateZeroPool (Packet->Size); + if (Private->Dhcp6Request != NULL) { + CopyMem (Private->Dhcp6Request, Packet, Packet->Size); + } + break; + + case Dhcp6SelectAdvertise: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + PxeBcSelectDhcp6Offer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + ASSERT (NewPacket != NULL); + SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer; + *NewPacket = AllocateZeroPool (SelectAd->Size); + ASSERT (*NewPacket != NULL); + CopyMem (*NewPacket, SelectAd, SelectAd->Size); + } + break; + + case Dhcp6RcvdReply: + // + // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data + // without verification. + // + ASSERT (Private->SelectIndex != 0); + PxeBcCopyDhcp6Ack (Private, Packet, FALSE); + break; + + default: + ASSERT (0); + } + + return Status; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer The pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp The pointer to the server address. + + @retval EFI_SUCCESS Successfully discovered the boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover the boot file. + +**/ +EFI_STATUS +PxeBcDhcp6Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT SrcPort; + EFI_PXE_BASE_CODE_UDP_PORT DestPort; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover; + UINTN DiscoverLen; + EFI_DHCP6_PACKET *Request; + UINTN RequestLen; + EFI_DHCP6_PACKET *Reply; + UINT8 *RequestOpt; + UINT8 *DiscoverOpt; + UINTN ReadSize; + UINT16 OpFlags; + UINT16 OpCode; + UINT16 OpLen; + UINT32 Xid; + EFI_STATUS Status; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Request = Private->Dhcp6Request; + SrcPort = PXEBC_BS_DISCOVER_PORT; + DestPort = PXEBC_BS_DISCOVER_PORT; + OpFlags = 0; + + if (!UseBis && Layer != NULL) { + *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; + } + + if (Request == NULL) { + return EFI_DEVICE_ERROR; + } + + Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET)); + if (Discover == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Build the discover packet by the cached request packet before. + // + Xid = NET_RANDOM (NetRandomInitSeed ()); + Discover->TransactionId = HTONL (Xid); + Discover->MessageType = Request->Dhcp6.Header.MessageType; + RequestOpt = Request->Dhcp6.Option; + DiscoverOpt = Discover->DhcpOptions; + DiscoverLen = sizeof (EFI_DHCP6_HEADER); + RequestLen = DiscoverLen; + + while (RequestLen < Request->Length) { + OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode); + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen); + if (OpCode != EFI_DHCP6_IA_TYPE_NA && + OpCode != EFI_DHCP6_IA_TYPE_TA) { + // + // Copy all the options except IA option. + // + CopyMem (DiscoverOpt, RequestOpt, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + } + RequestOpt += (OpLen + 4); + RequestLen += (OpLen + 4); + } + + Status = PxeBc->UdpWrite ( + PxeBc, + OpFlags, + &Private->ServerIp, + &DestPort, + NULL, + &Private->StationIp, + &SrcPort, + NULL, + NULL, + &DiscoverLen, + (VOID *) Discover + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + if (Private->IsDoDiscover) { + CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen); + Reply = &Private->PxeReply.Dhcp6.Packet.Ack; + } else { + Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer; + } + ReadSize = (UINTN) Reply->Size; + + Status = PxeBc->UdpRead ( + PxeBc, + OpFlags, + &Private->StationIp, + &SrcPort, + &Private->ServerIp, + &DestPort, + NULL, + NULL, + &ReadSize, + (VOID *) &Reply->Dhcp6 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL + + @retval EFI_SUCCESS The S.A.R.R. process successfully finished. + @retval Others Failed to finish the S.A.R.R. process. + +**/ +EFI_STATUS +PxeBcDhcp6Sarr ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PROTOCOL *Dhcp6 + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeMode; + EFI_DHCP6_CONFIG_DATA Config; + EFI_DHCP6_MODE_DATA Mode; + EFI_DHCP6_RETRANSMISSION *Retransmit; + EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_OPTION_MAX_NUM]; + UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE]; + UINT32 OptCount; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + PxeMode = Private->PxeBc.Mode; + + // + // Build option list for the request packet. + // + OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer); + ASSERT (OptCount> 0); + + Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION)); + if (Retransmit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp6Callback = PxeBcDhcp6CallBack; + Config.CallbackContext = Private; + Config.IaInfoEvent = NULL; + Config.RapidCommit = FALSE; + Config.ReconfigureAccept = FALSE; + Config.IaDescriptor.IaId = 1; + Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Config.SolicitRetransmission = Retransmit; + Retransmit->Irt = 4; + Retransmit->Mrc = 4; + Retransmit->Mrt = 32; + Retransmit->Mrd = 60; + + // + // Configure the DHCPv6 instance for PXE boot. + // + Status = Dhcp6->Configure (Dhcp6, &Config); + if (EFI_ERROR (Status)) { + FreePool (Retransmit); + return Status; + } + + // + // Initialize the record fields for DHCPv6 offer in private data. + // + Private->IsProxyRecved = FALSE; + Private->OfferNum = 0; + Private->SelectIndex = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + + // + // Start DHCPv6 S.A.R.R. process to acquire IPv6 address. + // + Status = Dhcp6->Start (Dhcp6); + if (EFI_ERROR (Status)) { + if (Status == EFI_ICMP_ERROR) { + PxeMode->IcmpErrorReceived = TRUE; + } + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + // + // Get the acquired IPv6 address and store them. + // + Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + ASSERT (Mode.Ia->State == Dhcp6Bound); + CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + + Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL); + if (EFI_ERROR (Status)) { + PxeBcUnregisterIp6Address (Private); + Dhcp6->Stop (Dhcp6); + return Status; + } + + // + // Check the selected offer whether BINL retry is needed. + // + Status = PxeBcHandleDhcp6Offer (Private); + if (EFI_ERROR (Status)) { + PxeBcUnregisterIp6Address (Private); + Dhcp6->Stop (Dhcp6); + return Status; + } + + AsciiPrint ("\n Station IP address is "); + + PxeBcShowIp6Addr (&Private->StationIp.v6); + + return EFI_SUCCESS; +} + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h new file mode 100644 index 0000000000..0eccacb962 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h @@ -0,0 +1,277 @@ +/** @file + Functions declaration related with DHCPv6 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_PXEBC_DHCP6_H__ +#define __EFI_PXEBC_DHCP6_H__ + +#define PXEBC_DHCP6_OPTION_MAX_NUM 16 +#define PXEBC_DHCP6_OPTION_MAX_SIZE 312 +#define PXEBC_DHCP6_PACKET_MAX_SIZE 1472 +#define PXEBC_DHCP6_MAPPING_TIMEOUT 50000000 // 5 seconds, unit is 10nanosecond. +#define PXEBC_IP6_POLICY_MAX 0xff + +#define PXEBC_DHCP6_S_PORT 547 +#define PXEBC_DHCP6_C_PORT 546 + +#define PXEBC_DHCP6_OPT_CLIENT_ID 1 +#define PXEBC_DHCP6_OPT_SERVER_ID 2 +#define PXEBC_DHCP6_OPT_IA_NA 3 +#define PXEBC_DHCP6_OPT_IA_TA 4 +#define PXEBC_DHCP6_OPT_IAADDR 5 +#define PXEBC_DHCP6_OPT_ORO 6 +#define PXEBC_DHCP6_OPT_PREFERENCE 7 +#define PXEBC_DHCP6_OPT_ELAPSED_TIME 8 +#define PXEBC_DHCP6_OPT_REPLAY_MSG 9 +#define PXEBC_DHCP6_OPT_AUTH 11 +#define PXEBC_DHCP6_OPT_UNICAST 12 +#define PXEBC_DHCP6_OPT_STATUS_CODE 13 +#define PXEBC_DHCP6_OPT_RAPID_COMMIT 14 +#define PXEBC_DHCP6_OPT_USER_CLASS 15 +#define PXEBC_DHCP6_OPT_VENDOR_CLASS 16 +#define PXEBC_DHCP6_OPT_VENDOR_OPTS 17 +#define PXEBC_DHCP6_OPT_INTERFACE_ID 18 +#define PXEBC_DHCP6_OPT_RECONFIG_MSG 19 +#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT 20 +#define PXEBC_DHCP6_OPT_BOOT_FILE_URL 59 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM 60 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_ARCH 61 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_UNDI 62 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's +#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes. + + +#define PXEBC_DHCP6_IDX_IA_NA 0 +#define PXEBC_DHCP6_IDX_BOOT_FILE_URL 1 +#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM 2 +#define PXEBC_DHCP6_IDX_VENDOR_CLASS 3 +#define PXEBC_DHCP6_IDX_MAX 4 + +#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX "tftp://" +#define PXEBC_TFTP_URL_SEPARATOR '/' +#define PXEBC_ADDR_START_DELIMITER '[' +#define PXEBC_ADDR_END_DELIMITER ']' + +#define GET_NEXT_DHCP6_OPTION(Opt) \ + (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1) + +#define GET_DHCP6_OPTION_SIZE(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER)) + +#define IS_PROXY_OFFER(Type) \ + ((Type) == PxeOfferTypeProxyBinl || \ + (Type) == PxeOfferTypeProxyPxe10 || \ + (Type) == PxeOfferTypeProxyWfm11a) + + +#pragma pack(1) +typedef struct { + UINT16 OpCode[256]; +} PXEBC_DHCP6_OPTION_ORO; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; + UINT8 Reserved; +} PXEBC_DHCP6_OPTION_UNDI; + +typedef struct { + UINT16 Type; +} PXEBC_DHCP6_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} PXEBC_CLASS_ID; + +typedef struct { + UINT32 Vendor; + UINT16 ClassLen; + PXEBC_CLASS_ID ClassId; +} PXEBC_DHCP6_OPTION_VENDOR_CLASS; + +#pragma pack() + +typedef union { + PXEBC_DHCP6_OPTION_ORO *Oro; + PXEBC_DHCP6_OPTION_UNDI *Undi; + PXEBC_DHCP6_OPTION_ARCH *Arch; + PXEBC_DHCP6_OPTION_VENDOR_CLASS *VendorClass; +} PXEBC_DHCP6_OPTION_ENTRY; + +typedef struct { + LIST_ENTRY Link; + EFI_DHCP6_PACKET_OPTION *Option; + UINT8 Precedence; +} PXEBC_DHCP6_OPTION_NODE; + +typedef union { + EFI_DHCP6_PACKET Offer; + EFI_DHCP6_PACKET Ack; + UINT8 Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE]; +} PXEBC_DHCP6_PACKET; + +typedef struct { + PXEBC_DHCP6_PACKET Packet; + PXEBC_OFFER_TYPE OfferType; + EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX]; +} PXEBC_DHCP6_PACKET_CACHE; + + +/** + Free all the nodes in the boot file list. + + @param[in] Head The pointer to the head of the list. + +**/ +VOID +PxeBcFreeBootFileOption ( + IN LIST_ENTRY *Head + ); + + +/** + Parse the Boot File URL option. + + @param[out] FileName The pointer to the boot file name. + @param[in, out] SrvAddr The pointer to the boot server address. + @param[in] BootFile The pointer to the boot file URL option data. + @param[in] Length Length of the boot file URL option data. + + @retval EFI_ABORTED User canceled the operation. + @retval EFI_SUCCESS Selected the boot menu successfully. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcExtractBootFileUrl ( + OUT UINT8 **FileName, + IN OUT EFI_IPv6_ADDRESS *SrvAddr, + IN CHAR8 *BootFile, + IN UINT16 Length + ); + + +/** + Parse the Boot File Parameter option. + + @param[in] BootFilePara The pointer to the boot file parameter option data. + @param[out] BootFileSize The pointer to the parsed boot file size. + + @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option. + @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option. + +**/ +EFI_STATUS +PxeBcExtractBootFileParam ( + IN CHAR8 *BootFilePara, + OUT UINT16 *BootFileSize + ); + + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp6Packet ( + IN PXEBC_DHCP6_PACKET_CACHE *Cache6 + ); + + +/** + Register the ready address by Ip6Config protocol. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Address The pointer to the ready address. + + @retval EFI_SUCCESS Registered the address succesfully. + @retval Others Failed to register the address. + +**/ +EFI_STATUS +PxeBcRegisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_IPv6_ADDRESS *Address + ); + + +/** + Unregister the address by Ip6Config protocol. + + @param[in] Private The pointer to the PxeBc private data. + +**/ +VOID +PxeBcUnregisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ); + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer The pointer to the option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp The pointer to the server address. + + @retval EFI_SUCCESS Successfully discovered theboot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp6Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp + ); + + +/** + Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Dhcp6 The pointer to EFI_DHCP6_PROTOCOL. + + @retval EFI_SUCCESS The S.A.R.R. process successfully finished. + @retval Others Failed to finish the S.A.R.R. process. + +**/ +EFI_STATUS +PxeBcDhcp6Sarr ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PROTOCOL *Dhcp6 + ); + +#endif + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c new file mode 100644 index 0000000000..f78525546a --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c @@ -0,0 +1,1339 @@ +/** @file + Driver Binding functions implementationfor for UefiPxeBc Driver. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gPxeBcDriverBinding = { + PxeBcDriverBindingSupported, + PxeBcDriverBindingStart, + PxeBcDriverBindingStop, + 0xa, + NULL, + NULL +}; + + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + } + } + } + + return NicHandle; +} + + +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + } + } + + return NicHandle; +} + + +/** + Destroy the opened instances based on IPv4. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcDestroyIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + ASSERT(Private != NULL); + + if (Private->ArpChild != NULL) { + // + // Close Arp for PxeBc->Arp and destroy the instance. + // + gBS->CloseProtocol ( + Private->ArpChild, + &gEfiArpProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiArpServiceBindingProtocolGuid, + Private->ArpChild + ); + } + + if (Private->Ip4Child != NULL) { + // + // Close Ip4 for background ICMP error message and destroy the instance. + // + gBS->CloseProtocol ( + Private->Ip4Child, + &gEfiIp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp4ServiceBindingProtocolGuid, + Private->Ip4Child + ); + } + + if (Private->Udp4WriteChild != NULL) { + // + // Close Udp4 for PxeBc->UdpWrite and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp4WriteChild, + &gEfiUdp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + Private->Udp4WriteChild + ); + } + + if (Private->Udp4ReadChild != NULL) { + // + // Close Udp4 for PxeBc->UdpRead and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp4ReadChild, + &gEfiUdp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + Private->Udp4ReadChild + ); + } + + if (Private->Mtftp4Child != NULL) { + // + // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance. + // + gBS->CloseProtocol ( + Private->Mtftp4Child, + &gEfiMtftp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + Private->Mtftp4Child + ); + } + + if (Private->Dhcp4Child != NULL) { + // + // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance. + // + gBS->CloseProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + Private->Dhcp4Child + ); + } + + if (Private->Ip4Nic != NULL) { + // + // Close PxeBc from the parent Nic handle and destroy the virtual handle. + // + gBS->CloseProtocol ( + Private->Controller, + &gEfiPxeBaseCodeProtocolGuid, + This->DriverBindingHandle, + Private->Ip4Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + NULL + ); + FreePool (Private->Ip4Nic); + } + + Private->ArpChild = NULL; + Private->Ip4Child = NULL; + Private->Udp4WriteChild = NULL; + Private->Udp4ReadChild = NULL; + Private->Mtftp4Child = NULL; + Private->Dhcp4Child = NULL; + Private->Ip4Nic = NULL; +} + + +/** + Destroy the opened instances based on IPv6. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcDestroyIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + ASSERT(Private != NULL); + + if (Private->Ip6Child != NULL) { + // + // Close Ip6 for Ip6->Ip6Config and destroy the instance. + // + gBS->CloseProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + Private->Ip6Child + ); + } + + if (Private->Udp6WriteChild != NULL) { + // + // Close Udp6 for PxeBc->UdpWrite and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp6WriteChild, + &gEfiUdp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + Private->Udp6WriteChild + ); + } + + if (Private->Udp6ReadChild != NULL) { + // + // Close Udp6 for PxeBc->UdpRead and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp6ReadChild, + &gEfiUdp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + Private->Udp6ReadChild + ); + } + + if (Private->Mtftp6Child != NULL) { + // + // Close Mtftp6 for PxeBc->Mtftp and destroy the instance. + // + gBS->CloseProtocol ( + Private->Mtftp6Child, + &gEfiMtftp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + Private->Mtftp6Child + ); + } + + if (Private->Dhcp6Child != NULL) { + // + // Close Dhcp6 for PxeBc->Dhcp and destroy the instance. + // + gBS->CloseProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + Private->Dhcp6Child + ); + } + + if (Private->Ip6Nic != NULL) { + // + // Close PxeBc from the parent Nic handle and destroy the virtual handle. + // + gBS->CloseProtocol ( + Private->Controller, + &gEfiPxeBaseCodeProtocolGuid, + This->DriverBindingHandle, + Private->Ip6Nic->Controller + ); + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + NULL + ); + FreePool (Private->Ip6Nic); + } + + Private->Ip6Child = NULL; + Private->Udp6WriteChild = NULL; + Private->Udp6ReadChild = NULL; + Private->Mtftp6Child = NULL; + Private->Dhcp6Child = NULL; + Private->Ip6Nic = NULL; + Private->Mode.Ipv6Available = FALSE; +} + + +/** + Create the opened instances based on IPv4. + + @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL. + @param[in] ControllerHandle Handle of the child to destroy. + @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The instances based on IPv4 were all created successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +PxeBcCreateIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + IPv4_DEVICE_PATH Ip4Node; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_CONFIG_DATA *Udp4CfgData; + EFI_IP4_CONFIG_DATA *Ip4CfgData; + EFI_IP4_MODE_DATA Ip4ModeData; + + if (Private->Ip4Nic != NULL) { + // + // Already created before. + // + return EFI_SUCCESS; + } + + // + // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Private->Dhcp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Private->Dhcp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + &Private->Mtftp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Mtftp4Child, + &gEfiMtftp4ProtocolGuid, + (VOID **) &Private->Mtftp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Private->Udp4ReadChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp4ReadChild, + &gEfiUdp4ProtocolGuid, + (VOID **) &Private->Udp4Read, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Private->Udp4WriteChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp4WriteChild, + &gEfiUdp4ProtocolGuid, + (VOID **) &Private->Udp4Write, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Arp child and open Arp protocol for PxeBc->Arp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiArpServiceBindingProtocolGuid, + &Private->ArpChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->ArpChild, + &gEfiArpProtocolGuid, + (VOID **) &Private->Arp, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip4 child and open Ip4 protocol for background ICMP packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp4ServiceBindingProtocolGuid, + &Private->Ip4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip4Child, + &gEfiIp4ProtocolGuid, + (VOID **) &Private->Ip4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get max packet size from Ip4 to calculate block size for Tftp later. + // + Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize; + + Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC)); + if (Private->Ip4Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Ip4Nic->Private = Private; + Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE; + + // + // Create a device path node for Ipv4 virtual nic, and append it. + // + ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH)); + Ip4Node.Header.Type = MESSAGING_DEVICE_PATH; + Ip4Node.Header.SubType = MSG_IPv4_DP; + Ip4Node.StaticIpAddress = FALSE; + + SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node)); + + Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header); + + if (Private->Ip4Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + CopyMem ( + &Private->Ip4Nic->LoadFile, + &gLoadFileProtocolTemplate, + sizeof (EFI_LOAD_FILE_PROTOCOL) + ); + + // + // Create a new handle for IPv4 virtual nic, + // and install PxeBaseCode, LoadFile and DevicePath protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open PxeBaseCode protocol by child to setup a parent-child relationship between + // real NIC handle and the virtual IPv4 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + (VOID **) &PxeBc, + This->DriverBindingHandle, + Private->Ip4Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set default configure data for Udp4Read and Ip4 instance. + // + Mode = PxeBc->Mode; + Udp4CfgData = &Private->Udp4CfgData; + Ip4CfgData = &Private->Ip4CfgData; + + Udp4CfgData->AcceptBroadcast = TRUE; + Udp4CfgData->AcceptAnyPort = TRUE; + Udp4CfgData->AllowDuplicatePort = TRUE; + Udp4CfgData->TypeOfService = Mode->ToS; + Udp4CfgData->TimeToLive = Mode->TTL; + Udp4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + Ip4CfgData->AcceptIcmpErrors = TRUE; + Ip4CfgData->DefaultProtocol = EFI_IP_PROTO_ICMP; + Ip4CfgData->TypeOfService = Mode->ToS; + Ip4CfgData->TimeToLive = Mode->TTL; + Ip4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Ip4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + return EFI_SUCCESS; + +ON_ERROR: + PxeBcDestroyIp4Children (This, Private); + return Status; +} + + +/** + Create the opened instances based on IPv6. + + @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL. + @param[in] ControllerHandle Handle of the child to destroy. + @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The instances based on IPv6 were all created successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +PxeBcCreateIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + IPv6_DEVICE_PATH Ip6Node; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_UDP6_CONFIG_DATA *Udp6CfgData; + EFI_IP6_CONFIG_DATA *Ip6CfgData; + EFI_IP6_MODE_DATA Ip6ModeData; + + if (Private->Ip6Nic != NULL) { + // + // Already created before. + // + return EFI_SUCCESS; + } + + Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC)); + + if (Private->Ip6Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Ip6Nic->Private = Private; + Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE; + + // + // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Private->Dhcp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Private->Dhcp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + &Private->Mtftp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Mtftp6Child, + &gEfiMtftp6ProtocolGuid, + (VOID **) &Private->Mtftp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Private->Udp6ReadChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp6ReadChild, + &gEfiUdp6ProtocolGuid, + (VOID **) &Private->Udp6Read, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Private->Udp6WriteChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp6WriteChild, + &gEfiUdp6ProtocolGuid, + (VOID **) &Private->Udp6Write, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip6 child and open Ip6 protocol for background ICMP6 packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &Private->Ip6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + (VOID **) &Private->Ip6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get max packet size from Ip6 to calculate block size for Tftp later. + // + Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize; + + // + // Locate Ip6->Ip6Config and store it for set IPv6 address. + // + Status = gBS->HandleProtocol ( + ControllerHandle, + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Private->Ip6Cfg + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create a device path node for Ipv6 virtual nic, and append it. + // + ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH)); + Ip6Node.Header.Type = MESSAGING_DEVICE_PATH; + Ip6Node.Header.SubType = MSG_IPv6_DP; + Ip6Node.StaticIpAddress = FALSE; + + SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node)); + + Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header); + + if (Private->Ip6Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + CopyMem ( + &Private->Ip6Nic->LoadFile, + &gLoadFileProtocolTemplate, + sizeof (EFI_LOAD_FILE_PROTOCOL) + ); + + // + // Create a new handle for IPv6 virtual nic, + // and install PxeBaseCode, LoadFile and DevicePath protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open PxeBaseCode protocol by child to setup a parent-child relationship between + // real NIC handle and the virtual IPv6 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + (VOID **) &PxeBc, + This->DriverBindingHandle, + Private->Ip6Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set IPv6 avaiable flag and set default configure data for + // Udp6Read and Ip6 instance. + // + Private->Mode.Ipv6Available = TRUE; + Udp6CfgData = &Private->Udp6CfgData; + Ip6CfgData = &Private->Ip6CfgData; + + Udp6CfgData->AcceptAnyPort = TRUE; + Udp6CfgData->AllowDuplicatePort = TRUE; + Udp6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT; + Udp6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + Ip6CfgData->AcceptIcmpErrors = TRUE; + Ip6CfgData->DefaultProtocol = IP6_ICMP; + Ip6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT; + Ip6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Ip6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + return EFI_SUCCESS; + +ON_ERROR: + PxeBcDestroyIp6Children (This, Private); + return Status; +} + + +/** + The entry point for UefiPxeBc driver that installs the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The Image handle of the driver. + @param[in] SystemTable The system table. + + @return EFI_SUCCESS + @return Others + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPxeBcDriverBinding, + ImageHandle, + &gPxeBcComponentName, + &gPxeBcComponentName2 + ); +} + + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Ip4Status; + EFI_STATUS Ip6Status; + + // + // Try to open the Mtftp4 and Dhcp4 protocol to test whether IPv4 stack is ready. + // + Ip4Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Ip4Status)) { + Ip4Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + } + + // + // Try to open the Mtftp6 and Dhcp6 protocol to test whether IPv4 stack is ready. + // + Ip6Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Ip6Status)) { + Ip6Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + } + + // + // It's unsupported case if both stack are not ready. + // + if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_STATUS Status; + EFI_STATUS Ip4Status; + EFI_STATUS Ip6Status; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + (VOID **) &PxeBc, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Skip the initialization if the driver has been started already. + // + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc); + } else { + // + // If the driver has not been started yet, it should do initialization. + // + Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + &Private->PxeBc, + &gPxeBcProtocolTemplate, + sizeof (EFI_PXE_BASE_CODE_PROTOCOL) + ); + + Private->Signature = PXEBC_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + Private->PxeBc.Mode = &Private->Mode; + Private->Mode.Ipv6Supported = TRUE; + Private->Mode.AutoArp = TRUE; + Private->Mode.TTL = DEFAULT_TTL; + Private->Mode.ToS = DEFAULT_ToS; + + // + // Open device path to prepare for appending virtual NIC node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } + + // + // Install PxeBaseCode protocol onto the real NIC handler. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->PxeBc + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Try to create virtual NIC handle for IPv4. + // + Ip4Status = PxeBcCreateIp4Children (This, ControllerHandle, Private); + + // + // Try to create virtual NIC handle for IPv6. + // + Ip6Status = PxeBcCreateIp6Children (This, ControllerHandle, Private); + + if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) { + // + // Failed to start PXE driver if IPv4 and IPv6 stack are both not available. + // + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc + ); + PxeBcDestroyIp4Children (This, Private); + PxeBcDestroyIp6Children (This, Private); + FreePool (Private); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver was removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + PXEBC_PRIVATE_DATA *Private; + PXEBC_VIRTUAL_NIC *VirtualNic; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + EFI_STATUS Status; + EFI_HANDLE NicHandle; + BOOLEAN IsIpv6; + + Private = NULL; + NicHandle = NULL; + VirtualNic = NULL; + LoadFile = NULL; + PxeBc = NULL; + IsIpv6 = FALSE; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Get the Nic handle by any pass-over service child handle. + // + NicHandle = PxeBcGetNicByIp4Children (ControllerHandle); + if (NicHandle == NULL) { + NicHandle = PxeBcGetNicByIp6Children (ControllerHandle); + if (NicHandle == NULL) { + return EFI_DEVICE_ERROR; + } else { + IsIpv6 = TRUE; + } + } + + // + // Try to retrieve the private data by PxeBc protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiPxeBaseCodeProtocolGuid, + (VOID **) &PxeBc, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc); + + } else { + // + // It's a virtual handle with LoadFileProtocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile); + Private = VirtualNic->Private; + NicHandle = Private->Controller; + + if (Private->Ip6Nic == VirtualNic) { + IsIpv6 = TRUE; + } + } + + if (Private->Ip4Nic != NULL && !IsIpv6) { + PxeBcDestroyIp4Children (This, Private); + } + + if (Private->Ip6Nic != NULL && IsIpv6) { + PxeBcDestroyIp6Children (This, Private); + } + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc + ); + FreePool (Private); + } + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h new file mode 100644 index 0000000000..8df4bae27b --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h @@ -0,0 +1,104 @@ +/** @file + Driver Binding functions declaration for UefiPxeBc Driver. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_PXEBC_DRIVER_H__ +#define __EFI_PXEBC_DRIVER_H__ + +extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2; + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +#endif + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c new file mode 100644 index 0000000000..7e5b4d63fd --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c @@ -0,0 +1,2200 @@ +/** @file + This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + + +/** + Enables the use of the PXE Base Code Protocol functions. + + This function enables the use of the PXE Base Code Protocol functions. If the + Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then + EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted + addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted + addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported + field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will + be returned. If there is not enough memory or other resources to start the PXE + Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the + PXE Base Code Protocol will be started. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] UseIpv6 Specifies the type of IP addresses that are to be + used during the session that is being started. + Set to TRUE for IPv6, and FALSE for IPv4. + + @retval EFI_SUCCESS The PXE Base Code Protocol was started. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_UNSUPPORTED UseIpv6 is TRUE, but the Ipv6Supported field of the + EFI_PXE_BASE_CODE_MODE structure is FALSE. + @retval EFI_ALREADY_STARTED The PXE Base Code Protocol is already in the started state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory or other resources to start the + PXE Base Code Protocol. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcStart ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIpv6 + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + UINTN Index; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (Mode->Started) { + return EFI_ALREADY_STARTED; + } + + // + // Detect whether using IPv6 or not, and set it into mode data. + // + if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) { + Mode->UsingIpv6 = TRUE; + } else if (!UseIpv6 && Private->Ip4Nic != NULL) { + Mode->UsingIpv6 = FALSE; + } else { + return EFI_UNSUPPORTED; + } + + if (Mode->UsingIpv6) { + AsciiPrint ("\n>>Start PXE over IPv6"); + // + // Configure block size for TFTP as a default value to handle all link layers. + // + Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize - + PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE); + + // + // PXE over IPv6 starts here, initialize the fields and list header. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + Private->DhcpAck.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + Private->PxeReply.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + + for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + } + + // + // Create event and set status for token to capture ICMP6 error message. + // + Private->Icmp6Token.Status = EFI_NOT_READY; + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcIcmp6ErrorUpdate, + Private, + &Private->Icmp6Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } else { + AsciiPrint ("\n>>Start PXE over IPv4"); + // + // Configure block size for TFTP as a default value to handle all link layers. + // + Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize - + PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE); + + // + // PXE over IPv4 starts here, initialize the fields. + // + Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + Private->DhcpAck.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + Private->PxeReply.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + + for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + } + + PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read); + + // + // Create the event for Arp cache update. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + PxeBcArpCacheUpdate, + Private, + &Private->ArpUpdateEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Start a periodic timer by second to update Arp cache. + // + Status = gBS->SetTimer ( + Private->ArpUpdateEvent, + TimerPeriodic, + TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create event and set status for token to capture ICMP error message. + // + Private->Icmp6Token.Status = EFI_NOT_READY; + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcIcmpErrorUpdate, + Private, + &Private->IcmpToken.Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // If PcdTftpBlockSize is set to non-zero, override the default value. + // + if (PcdGet64 (PcdTftpBlockSize) != 0) { + Private->BlockSize = (UINTN) PcdGet64 (PcdTftpBlockSize); + } + + // + // Create event for UdpRead/UdpWrite timeout since they are both blocking API. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->UdpTimeOutEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->IsAddressOk = FALSE; + Mode->Started = TRUE; + + return EFI_SUCCESS; + +ON_ERROR: + if (Mode->UsingIpv6) { + if (Private->Icmp6Token.Event != NULL) { + gBS->CloseEvent (Private->Icmp6Token.Event); + Private->Icmp6Token.Event = NULL; + } + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + Private->Ip6->Configure (Private->Ip6, NULL); + } else { + if (Private->ArpUpdateEvent != NULL) { + gBS->CloseEvent (Private->ArpUpdateEvent); + Private->ArpUpdateEvent = NULL; + } + if (Private->IcmpToken.Event != NULL) { + gBS->CloseEvent (Private->IcmpToken.Event); + Private->IcmpToken.Event = NULL; + } + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + Private->Ip4->Configure (Private->Ip4, NULL); + } + return Status; +} + + +/** + Disable the use of the PXE Base Code Protocol functions. + + This function stops all activity on the network device. All the resources allocated + in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is + set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE + structure is already FALSE, then EFI_NOT_STARTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + + @retval EFI_SUCCESS The PXE Base Code Protocol was stopped. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is already in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval Others + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcStop ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + BOOLEAN Ipv6Supported; + BOOLEAN Ipv6Available; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Ipv6Supported = Mode->Ipv6Supported; + Ipv6Available = Mode->Ipv6Available; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + // + // Configure all the instances for IPv6 as NULL. + // + ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS)); + Private->Dhcp6->Stop (Private->Dhcp6); + Private->Dhcp6->Configure (Private->Dhcp6, NULL); + Private->Udp6Write->Configure (Private->Udp6Write, NULL); + Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL); + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token); + Private->Ip6->Configure (Private->Ip6, NULL); + PxeBcUnregisterIp6Address (Private); + if (Private->Icmp6Token.Event != NULL) { + gBS->CloseEvent (Private->Icmp6Token.Event); + Private->Icmp6Token.Event = NULL; + } + if (Private->Dhcp6Request != NULL) { + FreePool (Private->Dhcp6Request); + Private->Dhcp6Request = NULL; + } + if (Private->BootFileName != NULL) { + FreePool (Private->BootFileName); + Private->BootFileName = NULL; + } + } else { + // + // Configure all the instances for IPv4 as NULL. + // + ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + Private->Udp4Write->Configure (Private->Udp4Write, NULL); + Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL); + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken); + Private->Ip4->Configure (Private->Ip4, NULL); + if (Private->ArpUpdateEvent != NULL) { + gBS->CloseEvent (Private->ArpUpdateEvent); + Private->ArpUpdateEvent = NULL; + } + if (Private->IcmpToken.Event != NULL) { + gBS->CloseEvent (Private->IcmpToken.Event); + Private->IcmpToken.Event = NULL; + } + } + + gBS->CloseEvent (Private->UdpTimeOutEvent); + Private->CurSrcPort = 0; + Private->BootFileSize = 0; + + // + // Reset the mode data. + // + ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE)); + Mode->Ipv6Available = Ipv6Available; + Mode->Ipv6Supported = Ipv6Supported; + Mode->AutoArp = TRUE; + Mode->TTL = DEFAULT_TTL; + Mode->ToS = DEFAULT_ToS; + + return EFI_SUCCESS; +} + + +/** + Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6 + S.A.R.R (solicit / advertise / request / reply) sequence. + + If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before + they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will + be tried in the order in which they are received. Please see the Preboot Execution + Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI) + Specification for additional details on the implementation of DHCP. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the DHCP sequence will be stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] SortOffers TRUE if the offers received should be sorted. Set to FALSE to + try the offers in the order that they are received. + + @retval EFI_SUCCESS Valid DHCP has completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete the DHCP Protocol. + @retval EFI_ABORTED The callback function aborted the DHCP Protocol. + @retval EFI_TIMEOUT The DHCP Protocol timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the DHCP session. + @retval EFI_NO_RESPONSE Valid PXE offer was not received. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcDhcp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Mode->IcmpErrorReceived = FALSE; + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP; + Private->IsOfferSorted = SortOffers; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + // + // Start S.A.R.R. process to get a IPv6 address and other boot information. + // + Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6); + } else { + + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + + // + // Start D.O.R.A. process to get a IPv4 address and other boot information. + // + Status = PxeBcDhcp4Dora (Private, Private->Dhcp4); + } + + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Attempts to complete the PXE Boot Server and/or boot image discovery sequence. + + This function attempts to complete the PXE Boot Server and/or boot image discovery + sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the + PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the + EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the + PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure + will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE. + In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[], + has two uses: It is the Boot Server IP address list used for unicast discovery + (if the UseUCast field is TRUE), and it is the list used for Boot Server verification + (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure + is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot + Server reply of that type will be accepted. If the AcceptAnyResponse field is + FALSE, only responses from Boot Servers with matching IP addresses will be accepted. + This function can take at least 10 seconds to timeout and return control to the + caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be + returned. Please see the Preboot Execution Environment (PXE) Specification for + additional details on the implementation of the Discovery sequence. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the Discovery sequence is stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] Type The type of bootstrap to perform. + @param[in] Layer Pointer to the boot server layer number to discover, which must be + PXE_BOOT_LAYER_INITIAL when a new server type is being + discovered. + @param[in] UseBis TRUE if Boot Integrity Services are to be used. FALSE otherwise. + @param[in] Info Pointer to a data structure that contains additional information + on the type of discovery operation that is to be performed. + It is optional. + + @retval EFI_SUCCESS The Discovery sequence has been completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete Discovery. + @retval EFI_ABORTED The callback function aborted the Discovery sequence. + @retval EFI_TIMEOUT The Discovery sequence timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the PXE discovery + session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcDiscover ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo; + EFI_PXE_BASE_CODE_SRVLIST *SrvList; + PXEBC_BOOT_SVR_ENTRY *BootSvrEntry; + UINT16 Index; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Mode->IcmpErrorReceived = FALSE; + BootSvrEntry = NULL; + SrvList = NULL; + Status = EFI_DEVICE_ERROR; + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + // + // Station address should be ready before do discover. + // + if (!Private->IsAddressOk) { + return EFI_INVALID_PARAMETER; + } + + if (Mode->UsingIpv6) { + + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + } else { + + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + } + + // + // There are 3 methods to get the information for discover. + // + if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) { + // + // 1. Take the previous setting as the discover info. + // + if (!Mode->PxeDiscoverValid || + !Mode->PxeReplyReceived || + (!Mode->PxeBisReplyReceived && UseBis)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Info = &DefaultInfo; + Info->IpCnt = 1; + Info->UseUCast = TRUE; + SrvList = Info->SrvList; + SrvList[0].Type = Type; + SrvList[0].AcceptAnyResponse = FALSE; + + CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + } else if (Info == NULL) { + // + // 2. Extract the discover information from the cached packets if unspecified. + // + Info = &DefaultInfo; + Status = PxeBcExtractDiscoverInfo (Private, Type, Info, &BootSvrEntry, &SrvList); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else { + // + // 3. Take the pass-in information as the discover info, and validate the server list. + // + SrvList = Info->SrvList; + + if (!SrvList[0].AcceptAnyResponse) { + for (Index = 1; Index < Info->IpCnt; Index++) { + if (SrvList[Index].AcceptAnyResponse) { + break; + } + } + if (Index != Info->IpCnt) { + // + // It's invalid if the first server doesn't accecpt any response + // and meanwhile any of the rest servers accept any reponse. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + } + + // + // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast. + // + if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) || + (Info->MustUseList && Info->IpCnt == 0)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Private->IsDoDiscover = TRUE; + + if (Info->UseUCast) { + // + // Do discover by unicast. + // + for (Index = 0; Index < Info->IpCnt; Index++) { + if (BootSvrEntry == NULL) { + CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS)); + } else { + ASSERT (!Mode->UsingIpv6); + ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS)); + } + + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + &SrvList[Index].IpAddr, + 0, + NULL + ); + } + } else if (Info->UseMCast) { + // + // Do discover by multicast. + // + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + &Info->ServerMCastIp, + 0, + NULL + ); + + } else if (Info->UseBCast) { + // + // Do discover by broadcast, but only valid for IPv4. + // + ASSERT (!Mode->UsingIpv6); + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + NULL, + Info->IpCnt, + SrvList + ); + } + + if (!EFI_ERROR (Status)) { + // + // Parse the cached PXE reply packet, and store it into mode data if valid. + // + if (Mode->UsingIpv6) { + Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6); + if (!EFI_ERROR (Status)) { + CopyMem ( + &Mode->PxeReply.Dhcpv6, + &Private->PxeReply.Dhcp6.Packet.Offer, + Private->PxeReply.Dhcp6.Packet.Offer.Length + ); + Mode->PxeReplyReceived = TRUE; + Mode->PxeDiscoverValid = TRUE; + } + } else { + Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4); + if (!EFI_ERROR (Status)) { + CopyMem ( + &Mode->PxeReply.Dhcpv4, + &Private->PxeReply.Dhcp4.Packet.Offer, + Private->PxeReply.Dhcp4.Packet.Offer.Length + ); + Mode->PxeReplyReceived = TRUE; + Mode->PxeDiscoverValid = TRUE; + } + } + } + +ON_EXIT: + + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Used to perform TFTP and MTFTP services. + + This function is used to perform TFTP and MTFTP services. This includes the + TFTP operations to get the size of a file, read a directory, read a file, and + write a file. It also includes the MTFTP operations to get the size of a file, + read a directory, and read a file. The type of operation is specified by Operation. + If the callback function that is invoked during the TFTP/MTFTP operation does + not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will + be returned. + For read operations, the return data will be placed in the buffer specified by + BufferPtr. If BufferSize is too small to contain the entire downloaded file, + then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero, + or the size of the requested file. (NOTE: the size of the requested file is only returned + if the TFTP server supports TFTP options). If BufferSize is large enough for the + read operation, then BufferSize will be set to the size of the downloaded file, + and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services + should use the get-file-size operations to determine the size of the downloaded + file prior to using the read-file operations-especially when downloading large + (greater than 64 MB) files-instead of making two calls to the read-file operation. + Following this recommendation will save time if the file is larger than expected + and the TFTP server does not support TFTP option extensions. Without TFTP option + extension support, the client must download the entire file, counting and discarding + the received packets, to determine the file size. + For write operations, the data to be sent is in the buffer specified by BufferPtr. + BufferSize specifies the number of bytes to send. If the write operation completes + successfully, then EFI_SUCCESS will be returned. + For TFTP "get file size" operations, the size of the requested file or directory + is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server + does not support options, the file will be downloaded into a bit bucket and the + length of the downloaded file will be returned. For MTFTP "get file size" operations, + if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED + will be returned. + This function can take up to 10 seconds to timeout and return control to the caller. + If the TFTP sequence does not complete, EFI_TIMEOUT will be returned. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the TFTP sequence is stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] Operation The type of operation to perform. + @param[in, out] BufferPtr A pointer to the data buffer. + @param[in] Overwrite Only used on write file operations. TRUE if a file on a remote + server can be overwritten. + @param[in, out] BufferSize For get-file-size operations, *BufferSize returns the size of the + requested file. + @param[in] BlockSize The requested block size to be used during a TFTP transfer. + @param[in] ServerIp The TFTP / MTFTP server IP address. + @param[in] Filename A Null-terminated ASCII string that specifies a directory name + or a file name. + @param[in] Info Pointer to the MTFTP information. + @param[in] DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation. + + @retval EFI_SUCCESS The TFTP/MTFTP operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The buffer is not large enough to complete the read operation. + @retval EFI_ABORTED The callback function aborted the TFTP/MTFTP operation. + @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the MTFTP session. + @retval EFI_TFTP_ERROR A TFTP error packet was received during the MTFTP session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcMtftp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr OPTIONAL, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSize, + IN UINTN *BlockSize OPTIONAL, + IN EFI_IP_ADDRESS *ServerIp, + IN UINT8 *Filename, + IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL, + IN BOOLEAN DontUseBuffer + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_MTFTP4_CONFIG_DATA Mtftp4Config; + EFI_MTFTP6_CONFIG_DATA Mtftp6Config; + VOID *Config; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + + + if ((This == NULL) || + (Filename == NULL) || + (BufferSize == NULL) || + (ServerIp == NULL) || + ((BufferPtr == NULL) && DontUseBuffer) || + ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) || + (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) { + return EFI_INVALID_PARAMETER; + } + + Config = NULL; + Status = EFI_DEVICE_ERROR; + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (Mode->UsingIpv6) { + // + // Set configuration data for Mtftp6 instance. + // + ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA)); + Config = &Mtftp6Config; + Mtftp6Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT; + Mtftp6Config.TryCount = PXEBC_MTFTP_RETRIES; + CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS)); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + } else { + // + // Set configuration data for Mtftp4 instance. + // + ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA)); + Config = &Mtftp4Config; + Mtftp4Config.UseDefaultSetting = FALSE; + Mtftp4Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT; + Mtftp4Config.TryCount = PXEBC_MTFTP_RETRIES; + CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS)); + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + } + + Mode->TftpErrorReceived = FALSE; + Mode->IcmpErrorReceived = FALSE; + + switch (Operation) { + + case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE: + // + // Send TFTP request to get file size. + // + Status = PxeBcTftpGetFileSize ( + Private, + Config, + Filename, + BlockSize, + BufferSize + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_READ_FILE: + // + // Send TFTP request to read file. + // + Status = PxeBcTftpReadFile ( + Private, + Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE: + // + // Send TFTP request to write file. + // + Status = PxeBcTftpWriteFile ( + Private, + Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY: + // + // Send TFTP request to read directory. + // + Status = PxeBcTftpReadDirectory ( + Private, + Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + + break; + + case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE: + case EFI_PXE_BASE_CODE_MTFTP_READ_FILE: + case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY: + Status = EFI_UNSUPPORTED; + + break; + + default: + Status = EFI_INVALID_PARAMETER; + + break; + } + + if (Status == EFI_ICMP_ERROR) { + Mode->IcmpErrorReceived = TRUE; + } + + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Writes a UDP packet to the network interface. + + This function writes a UDP packet specified by the (optional HeaderPtr and) + BufferPtr parameters to the network interface. The UDP header is automatically + built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp, + SrcIp, and SrcPort to build this header. If the packet is successfully built and + transmitted through the network interface, then EFI_SUCCESS will be returned. + If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will + be returned. If an ICMP error occurs during the transmission of the packet, then + the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and + EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return + EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] OpFlags The UDP operation flags. + @param[in] DestIp The destination IP address. + @param[in] DestPort The destination UDP port number. + @param[in] GatewayIp The gateway IP address. + @param[in] SrcIp The source IP address. + @param[in, out] SrcPort The source UDP port number. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS The UDP Write operation completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_BAD_BUFFER_SIZE The buffer is too long to be transmitted. + @retval EFI_ABORTED The callback function aborted the UDP Write operation. + @retval EFI_TIMEOUT The UDP Write operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the UDP write session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcUdpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp OPTIONAL, + IN EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_SESSION_DATA Udp4Session; + EFI_UDP6_SESSION_DATA Udp6Session; + EFI_STATUS Status; + BOOLEAN DoNotFragment; + + if (This == NULL || DestIp == NULL || DestPort == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) { + DoNotFragment = FALSE; + } else { + DoNotFragment = TRUE; + } + + if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) { + // + // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6. + // + return EFI_INVALID_PARAMETER; + } + + if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (!Private->IsAddressOk && SrcIp == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->CurSrcPort == 0 || + (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) { + // + // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed. + // + if (SrcPort != NULL) { + Private->CurSrcPort = *SrcPort; + } + } + + if (Mode->UsingIpv6) { + Status = PxeBcConfigUdp6Write ( + Private->Udp6Write, + &Private->StationIp.v6, + &Private->CurSrcPort + ); + } else { + // + // Configure the UDPv4 instance with gateway information from DHCP server as default. + // + Status = PxeBcConfigUdp4Write ( + Private->Udp4Write, + &Private->StationIp.v4, + &Private->SubnetMask.v4, + &Private->GatewayIp.v4, + &Private->CurSrcPort, + DoNotFragment + ); + } + + if (EFI_ERROR (Status)) { + Private->CurSrcPort = 0; + return EFI_INVALID_PARAMETER; + } else if (SrcPort != NULL) { + *SrcPort = Private->CurSrcPort; + } + + // + // Start a timer as timeout event for this blocking API. + // + gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT); + + if (Mode->UsingIpv6) { + // + // Construct UDPv6 session data. + // + ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA)); + CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS)); + Udp6Session.DestinationPort = *DestPort; + if (SrcIp != NULL) { + CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS)); + } + if (SrcPort != NULL) { + Udp6Session.SourcePort = *SrcPort; + } + + Status = PxeBcUdp6Write ( + Private->Udp6Write, + &Udp6Session, + Private->UdpTimeOutEvent, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr + ); + } else { + // + // Construct UDPv4 session data. + // + ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA)); + CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS)); + Udp4Session.DestinationPort = *DestPort; + if (SrcIp != NULL) { + CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS)); + } + if (SrcPort != NULL) { + Udp4Session.SourcePort = *SrcPort; + } + // + // Override the gateway information if user specified. + // + Status = PxeBcUdp4Write ( + Private->Udp4Write, + &Udp4Session, + Private->UdpTimeOutEvent, + (EFI_IPv4_ADDRESS *) GatewayIp, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr + ); + } + + gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0); + + + // + // Reset the UdpWrite instance. + // + if (Mode->UsingIpv6) { + Private->Udp6Write->Configure (Private->Udp6Write, NULL); + } else { + Private->Udp4Write->Configure (Private->Udp4Write, NULL); + } + + return Status; +} + + +/** + Reads a UDP packet from the network interface. ++ + This function reads a UDP packet from a network interface. The data contents + are returned in (the optional HeaderPtr and) BufferPtr, and the size of the + buffer received is returned in BufferSize . If the input BufferSize is smaller + than the UDP packet received (less optional HeaderSize), it will be set to the + required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the + contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is + successfully received, then EFI_SUCCESS will be returned, and the information + from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if + they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort, + SrcIp, and SrcPort input values, different types of UDP packet receive filtering + will be performed. The following tables summarize these receive filter operations. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] OpFlags The UDP operation flags. + @param[in, out] DestIp The destination IP address. + @param[in, out] DestPort The destination UDP port number. + @param[in, out] SrcIp The source IP address. + @param[in, out] SrcPort The source UDP port number. + @param[in] HeaderSize An optional field which may be set to the length of a + header at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in, out] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be read. + + @retval EFI_SUCCESS The UDP Read operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The packet is larger than Buffer can hold. + @retval EFI_ABORTED The callback function aborted the UDP Read operation. + @retval EFI_TIMEOUT The UDP Read operation timed out. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcUdpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_COMPLETION_TOKEN Udp4Token; + EFI_UDP6_COMPLETION_TOKEN Udp6Token; + EFI_UDP4_RECEIVE_DATA *Udp4Rx; + EFI_UDP6_RECEIVE_DATA *Udp6Rx; + EFI_STATUS Status; + BOOLEAN IsDone; + BOOLEAN IsMatched; + UINTN CopiedLen; + + if (This == NULL || DestIp == NULL || DestPort == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + IsDone = FALSE; + IsMatched = FALSE; + Udp4Rx = NULL; + Udp6Rx = NULL; + + if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0 && DestPort == NULL) || + ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0 && SrcIp == NULL) || + ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0 && SrcPort == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((BufferSize == NULL) || (BufferPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN)); + ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN)); + + if (Mode->UsingIpv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Udp6Token.Event + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Udp4Token.Event + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Start a timer as timeout event for this blocking API. + // + gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT); + Mode->IcmpErrorReceived = FALSE; + + // + // Read packet by Udp4Read/Udp6Read until matched or timeout. + // + while (!IsMatched && !EFI_ERROR (Status)) { + if (Mode->UsingIpv6) { + Status = PxeBcUdp6Read ( + Private->Udp6Read, + &Udp6Token, + Mode, + Private->UdpTimeOutEvent, + OpFlags, + &IsDone, + &IsMatched, + DestIp, + DestPort, + SrcIp, + SrcPort + ); + } else { + Status = PxeBcUdp4Read ( + Private->Udp4Read, + &Udp4Token, + Mode, + Private->UdpTimeOutEvent, + OpFlags, + &IsDone, + &IsMatched, + DestIp, + DestPort, + SrcIp, + SrcPort + ); + } + } + + if (Status == EFI_ICMP_ERROR || + Status == EFI_NETWORK_UNREACHABLE || + Status == EFI_HOST_UNREACHABLE || + Status == EFI_PROTOCOL_UNREACHABLE || + Status == EFI_PORT_UNREACHABLE) { + // + // Get different return status for icmp error from Udp, refers to UEFI spec. + // + Mode->IcmpErrorReceived = TRUE; + } + gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0); + + if (IsMatched) { + // + // Copy the rececived packet to user if matched by filter. + // + CopiedLen = 0; + if (Mode->UsingIpv6) { + Udp6Rx = Udp6Token.Packet.RxData; + ASSERT (Udp6Rx != NULL); + // + // Copy the header part of received data. + // + if (HeaderSize != NULL) { + CopiedLen = MIN (*HeaderSize, Udp6Rx->DataLength); + *HeaderSize = CopiedLen; + CopyMem (HeaderPtr, Udp6Rx->FragmentTable[0].FragmentBuffer, *HeaderSize); + } + // + // Copy the other part of received data. + // + if (Udp6Rx->DataLength - CopiedLen > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + *BufferSize = Udp6Rx->DataLength - CopiedLen; + CopyMem (BufferPtr, (UINT8 *) Udp6Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize); + } + // + // Recycle the receiving buffer after copy to user. + // + gBS->SignalEvent (Udp6Rx->RecycleSignal); + } else { + Udp4Rx = Udp4Token.Packet.RxData; + ASSERT (Udp4Rx != NULL); + // + // Copy the header part of received data. + // + if (HeaderSize != NULL) { + CopiedLen = MIN (*HeaderSize, Udp4Rx->DataLength); + *HeaderSize = CopiedLen; + CopyMem (HeaderPtr, Udp4Rx->FragmentTable[0].FragmentBuffer, *HeaderSize); + } + // + // Copy the other part of received data. + // + if (Udp4Rx->DataLength - CopiedLen > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + *BufferSize = Udp4Rx->DataLength - CopiedLen; + CopyMem (BufferPtr, (UINT8 *) Udp4Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize); + } + // + // Recycle the receiving buffer after copy to user. + // + gBS->SignalEvent (Udp4Rx->RecycleSignal); + } + } + + if (Mode->UsingIpv6) { + Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token); + gBS->CloseEvent (Udp6Token.Event); + } else { + Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token); + gBS->CloseEvent (Udp4Token.Event); + } + + return Status; +} + + +/** + Updates the IP receive filters of a network device and enables software filtering. + + The NewFilter field is used to modify the network device's current IP receive + filter settings and to enable a software filter. This function updates the IpFilter + field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter. + The software filter is used when the USE_FILTER in OpFlags is set to UdpRead(). + The current hardware filter remains in effect no matter what the settings of OpFlags. + This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those + packets whose reception is enabled in hardware-physical NIC address (unicast), + broadcast address, logical address or addresses (multicast), or all (promiscuous). + UdpRead() does not modify the IP filter settings. + Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive + filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + If an application or driver wishes to preserve the IP receive filter settings, + it will have to preserve the IP receive filter settings before these calls, and + use SetIpFilter() to restore them after the calls. If incompatible filtering is + requested (for example, PROMISCUOUS with anything else), or if the device does not + support a requested filter setting and it cannot be accommodated in software + (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned. + The IPlist field is used to enable IPs other than the StationIP. They may be + multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP, + then both the StationIP and the IPs from the IPlist will be used. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewFilter Pointer to the new set of IP receive filters. + + @retval EFI_SUCCESS The IP receive filter settings were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetIpFilter ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter + ) +{ + EFI_STATUS Status; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_CONFIG_DATA Udp4Cfg; + EFI_UDP6_CONFIG_DATA Udp6Cfg; + UINTN Index; + BOOLEAN NeedPromiscuous; + + if (This == NULL || NewFilter == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + NeedPromiscuous = FALSE; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + for (Index = 0; Index < NewFilter->IpCnt; Index++) { + ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT); + if (!Mode->UsingIpv6 && + IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) { + // + // IPv4 broadcast address should not be in IP filter. + // + return EFI_INVALID_PARAMETER; + } + if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 && + (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) || + NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) { + // + // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address + // is in IpList, promiscuous mode is needed. + // + NeedPromiscuous = TRUE; + } + } + + // + // Clear configuration for UdpRead and leave the original group joined before. + // + if (Mode->UsingIpv6) { + CopyMem(&Udp6Cfg, &Private->Udp6CfgData, sizeof (EFI_UDP6_CONFIG_DATA)); + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + Udp6Cfg.AcceptPromiscuous = FALSE; + } else { + CopyMem(&Udp4Cfg, &Private->Udp4CfgData, sizeof (EFI_UDP4_CONFIG_DATA)); + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + Udp4Cfg.AcceptPromiscuous = FALSE; + Udp4Cfg.AcceptBroadcast = FALSE; + } + + if (NeedPromiscuous || + (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 || + (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) { + // + // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets. + // + Udp4Cfg.AcceptPromiscuous = TRUE; + Udp6Cfg.AcceptPromiscuous = TRUE; + + } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) { + // + // Configure UDPv4 to receive all broadcast packets. + // + Udp4Cfg.AcceptBroadcast = TRUE; + } + + // + // Configure UDPv4/UDPv6 instance with the new configuration. + // + if (Mode->UsingIpv6) { + Status = Private->Udp6Read->Configure (Private->Udp6Read, &Udp6Cfg); + } else { + Status = Private->Udp4Read->Configure (Private->Udp4Read, &Udp4Cfg); + } + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) { + + for (Index = 0; Index < NewFilter->IpCnt; Index++) { + // + // Join the multicast group if needed. + // + if (Mode->UsingIpv6) { + if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) { + Status = Private->Udp6Read->Groups ( + Private->Udp6Read, + TRUE, + &NewFilter->IpList[Index].v6 + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + } else { + if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) { + Status = Private->Udp4Read->Groups ( + Private->Udp4Read, + TRUE, + &NewFilter->IpList[Index].v4 + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + } + } + } + + // + // Save the new IP filter into mode data. + // + CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter)); + +ON_EXIT: + return Status; +} + + +/** + Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6. + + This function uses the ARP protocol to resolve a MAC address. The IP address specified + by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving + the specified address, then the ArpCacheEntries and ArpCache fields of the mode data + are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved + MAC address is placed there as well. If the PXE Base Code protocol is in the + stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters + a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is + returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then EFI_ABORTED is returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] IpAddr Pointer to the IP address that is used to resolve a MAC address. + @param[in] MacAddr If not NULL, a pointer to the MAC address that was resolved with the + ARP protocol. + + @retval EFI_SUCCESS The IP or MAC address was resolved. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_ICMP_ERROR An error occur with the ICMP packet message. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcArp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_MAC_ADDRESS *MacAddr OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_EVENT ResolvedEvent; + EFI_STATUS Status; + EFI_MAC_ADDRESS TempMac; + EFI_MAC_ADDRESS ZeroMac; + BOOLEAN IsResolved; + + if (This == NULL || IpAddr == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + ResolvedEvent = NULL; + Status = EFI_SUCCESS; + IsResolved = FALSE; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + return EFI_UNSUPPORTED; + } + + // + // Station address should be ready before do arp. + // + if (!Private->IsAddressOk) { + return EFI_INVALID_PARAMETER; + } + + Mode->IcmpErrorReceived = FALSE; + ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS)); + ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS)); + + if (!Mode->AutoArp) { + // + // If AutoArp is FALSE, only search in the current Arp cache. + // + PxeBcArpCacheUpdate (NULL, Private); + if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsResolved, + &ResolvedEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // If AutoArp is TRUE, try to send Arp request on initiative. + // + Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac); + if (EFI_ERROR (Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } + + while (!IsResolved) { + if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) { + break; + } + } + if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) { + Status = EFI_SUCCESS; + } else { + Status = EFI_TIMEOUT; + } + } + + // + // Copy the Mac address to user if needed. + // + if (MacAddr != NULL && !EFI_ERROR (Status)) { + CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS)); + } + +ON_EXIT: + if (ResolvedEvent != NULL) { + gBS->CloseEvent (ResolvedEvent); + } + return Status; +} + + +/** + Updates the parameters that affect the operation of the PXE Base Code Protocol. + + This function sets parameters that affect the operation of the PXE Base Code Protocol. + The parameter specified by NewAutoArp is used to control the generation of ARP + protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated + as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP + Protocol packets will be generated. In this case, the only mappings that are + available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure. + If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol + service, then the service will fail. This function updates the AutoArp field of + the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp. + The SetParameters() call must be invoked after a Callback Protocol is installed + to enable the use of callbacks. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewAutoArp If not NULL, a pointer to a value that specifies whether to replace the + current value of AutoARP. + @param[in] NewSendGUID If not NULL, a pointer to a value that specifies whether to replace the + current value of SendGUID. + @param[in] NewTTL If not NULL, a pointer to be used in place of the current value of TTL, + the "time to live" field of the IP header. + @param[in] NewToS If not NULL, a pointer to be used in place of the current value of ToS, + the "type of service" field of the IP header. + @param[in] NewMakeCallback If not NULL, a pointer to a value that specifies whether to replace the + current value of the MakeCallback field of the Mode structure. + + @retval EFI_SUCCESS The new parameters values were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetParameters ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN *NewAutoArp OPTIONAL, + IN BOOLEAN *NewSendGUID OPTIONAL, + IN UINT8 *NewTTL OPTIONAL, + IN UINT8 *NewToS OPTIONAL, + IN BOOLEAN *NewMakeCallback OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_GUID SystemGuid; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (NewMakeCallback != NULL) { + if (*NewMakeCallback) { + // + // Update the previous PxeBcCallback protocol. + // + Status = gBS->HandleProtocol ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID **) &Private->PxeBcCallback + ); + + if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) { + return EFI_INVALID_PARAMETER; + } + } else { + Private->PxeBcCallback = NULL; + } + Mode->MakeCallbacks = *NewMakeCallback; + } + + if (NewSendGUID != NULL) { + if (*NewSendGUID && EFI_ERROR (PxeBcGetSystemGuid (&SystemGuid))) { + return EFI_INVALID_PARAMETER; + } + Mode->SendGUID = *NewSendGUID; + } + + if (NewAutoArp != NULL) { + Mode->AutoArp = *NewAutoArp; + } + + if (NewTTL != NULL) { + Mode->TTL = *NewTTL; + } + + if (NewToS != NULL) { + Mode->ToS = *NewToS; + } + + return EFI_SUCCESS; +} + + +/** + Updates the station IP address and/or subnet mask values of a network device. + + This function updates the station IP address and/or subnet mask values of a network + device. The NewStationIp field is used to modify the network device's current IP address. + If NewStationIP is NULL, then the current IP address will not be modified. Otherwise, + this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure + with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet + mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified. + Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE + structure with NewSubnetMask. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewStationIp Pointer to the new IP address to be used by the network device. + @param[in] NewSubnetMask Pointer to the new subnet mask to be used by the network device. + + @retval EFI_SUCCESS The new station IP address and/or subnet mask were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetStationIP ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *NewStationIp OPTIONAL, + IN EFI_IP_ADDRESS *NewSubnetMask OPTIONAL + ) +{ + EFI_STATUS Status; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_ARP_CONFIG_DATA ArpConfigData; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (NewStationIp != NULL && + (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) && + !NetIp6IsValidUnicast (&NewStationIp->v6))) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + + if (!Mode->UsingIpv6 && + NewSubnetMask != NULL && + !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6 && NewStationIp != NULL) { + // + // Set the IPv6 address by Ip6Config protocol. + // + Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else if (!Mode->UsingIpv6 && NewStationIp != NULL) { + // + // Configure the corresponding ARP with the IPv4 address. + // + ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA)); + + ArpConfigData.SwAddressType = 0x0800; + ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS); + ArpConfigData.StationAddress = &NewStationIp->v4; + + Private->Arp->Configure (Private->Arp, NULL); + Private->Arp->Configure (Private->Arp, &ArpConfigData); + + if (NewSubnetMask != NULL) { + Mode->RouteTableEntries = 1; + Mode->RouteTable[0].IpAddr.Addr[0] = NewStationIp->Addr[0] & NewSubnetMask->Addr[0]; + Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0]; + Mode->RouteTable[0].GwAddr.Addr[0] = 0; + } + + Private->IsAddressOk = TRUE; + } + + if (NewStationIp != NULL) { + CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS)); + } + + if (!Mode->UsingIpv6 && NewSubnetMask != NULL) { + CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS)); + } + + Status = PxeBcFlushStaionIp (Private, NewStationIp, NewSubnetMask); +ON_EXIT: + return Status; +} + + +/** + Updates the contents of the cached DHCP and Discover packets. + + The pointers to the new packets are used to update the contents of the cached + packets in the EFI_PXE_BASE_CODE_MODE structure. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewDhcpDiscoverValid Pointer to a value that will replace the current + DhcpDiscoverValid field. + @param[in] NewDhcpAckReceived Pointer to a value that will replace the current + DhcpAckReceived field. + @param[in] NewProxyOfferReceived Pointer to a value that will replace the current + ProxyOfferReceived field. + @param[in] NewPxeDiscoverValid Pointer to a value that will replace the current + ProxyOfferReceived field. + @param[in] NewPxeReplyReceived Pointer to a value that will replace the current + PxeReplyReceived field. + @param[in] NewPxeBisReplyReceived Pointer to a value that will replace the current + PxeBisReplyReceived field. + @param[in] NewDhcpDiscover Pointer to the new cached DHCP Discover packet contents. + @param[in] NewDhcpAck Pointer to the new cached DHCP Ack packet contents. + @param[in] NewProxyOffer Pointer to the new cached Proxy Offer packet contents. + @param[in] NewPxeDiscover Pointer to the new cached PXE Discover packet contents. + @param[in] NewPxeReply Pointer to the new cached PXE Reply packet contents. + @param[in] NewPxeBisReply Pointer to the new cached PXE BIS Reply packet contents. + + @retval EFI_SUCCESS The cached packet contents were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER This is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetPackets ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN *NewDhcpDiscoverValid OPTIONAL, + IN BOOLEAN *NewDhcpAckReceived OPTIONAL, + IN BOOLEAN *NewProxyOfferReceived OPTIONAL, + IN BOOLEAN *NewPxeDiscoverValid OPTIONAL, + IN BOOLEAN *NewPxeReplyReceived OPTIONAL, + IN BOOLEAN *NewPxeBisReplyReceived OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpDiscover OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpAck OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewProxyOffer OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeDiscover OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeReply OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeBisReply OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (NewDhcpDiscoverValid != NULL) { + Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid; + } + + if (NewDhcpAckReceived != NULL) { + Mode->DhcpAckReceived = *NewDhcpAckReceived; + } + + if (NewProxyOfferReceived != NULL) { + Mode->ProxyOfferReceived = *NewProxyOfferReceived; + } + + if (NewPxeDiscoverValid != NULL) { + Mode->PxeDiscoverValid = *NewPxeDiscoverValid; + } + + if (NewPxeReplyReceived != NULL) { + Mode->PxeReplyReceived = *NewPxeReplyReceived; + } + + if (NewPxeBisReplyReceived != NULL) { + Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived; + } + + if (NewDhcpDiscover != NULL) { + CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewDhcpAck != NULL) { + CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewProxyOffer != NULL) { + CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeDiscover != NULL) { + CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeReply != NULL) { + CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeBisReply != NULL) { + CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + return EFI_SUCCESS; +} + +EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate = { + EFI_PXE_BASE_CODE_PROTOCOL_REVISION, + EfiPxeBcStart, + EfiPxeBcStop, + EfiPxeBcDhcp, + EfiPxeBcDiscover, + EfiPxeBcMtftp, + EfiPxeBcUdpWrite, + EfiPxeBcUdpRead, + EfiPxeBcSetIpFilter, + EfiPxeBcArp, + EfiPxeBcSetParameters, + EfiPxeBcSetStationIP, + EfiPxeBcSetPackets, + NULL +}; + + +/** + Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has + received, or is waiting to receive a packet. + + This function is invoked when the PXE Base Code Protocol is about to transmit, has received, + or is waiting to receive a packet. Parameters Function and Received specify the type of event. + Parameters PacketLen and Packet specify the packet that generated the event. If these fields + are zero and NULL respectively, then this is a status update callback. If the operation specified + by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation + specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to + the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms. + The SetParameters() function must be called after a Callback Protocol is installed to enable the + use of callbacks. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance. + @param[in] Function The PXE Base Code Protocol function that is waiting for an event. + @param[in] Received TRUE if the callback is being invoked due to a receive event. FALSE if + the callback is being invoked due to a transmit event. + @param[in] PacketLength The length, in bytes, of Packet. This field will have a value of zero if + this is a wait for receive event. + @param[in] PacketPtr If Received is TRUE, a pointer to the packet that was just received; + otherwise a pointer to the packet that is about to be transmitted. + + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation. + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT If Function specifies an abort operation. + +**/ +EFI_PXE_BASE_CODE_CALLBACK_STATUS +EFIAPI +EfiPxeLoadFileCallback ( + IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN BOOLEAN Received, + IN UINT32 PacketLength, + IN EFI_PXE_BASE_CODE_PACKET *PacketPtr OPTIONAL + ) +{ + EFI_INPUT_KEY Key; + EFI_STATUS Status; + + // + // Catch Ctrl-C or ESC to abort. + // + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + if (!EFI_ERROR (Status)) { + + if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) { + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT; + } + } + // + // No print if receive packet + // + if (Received) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + // + // Print only for three functions + // + switch (Function) { + + case EFI_PXE_BASE_CODE_FUNCTION_MTFTP: + // + // Print only for open MTFTP packets, not every MTFTP packets + // + if (PacketLength != 0 && PacketPtr != NULL) { + if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + } + break; + + case EFI_PXE_BASE_CODE_FUNCTION_DHCP: + case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER: + break; + + default: + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + + if (PacketLength != 0 && PacketPtr != NULL) { + // + // Print '.' when transmit a packet + // + AsciiPrint ("."); + } + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; +} + +EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = { + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION, + EfiPxeLoadFileCallback +}; + + +/** + Causes the driver to load a specified file. + + @param[in] This Protocol instance pointer. + @param[in] FilePath The device specific path of the file to load. + @param[in] BootPolicy If TRUE, indicates that the request originates from the + boot manager is attempting to load FilePath as a boot + selection. If FALSE, then FilePath must match an exact file + to be loaded. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[in] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then no the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NO_MEDIA No medium was present to load the file. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NO_RESPONSE The remote system did not respond. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_ABORTED The file load process was manually cancelled. + +**/ +EFI_STATUS +EFIAPI +EfiPxeLoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + PXEBC_VIRTUAL_NIC *VirtualNic; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + BOOLEAN UsingIpv6; + EFI_STATUS Status; + BOOLEAN MediaPresent; + + VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This); + Private = VirtualNic->Private; + PxeBc = &Private->PxeBc; + UsingIpv6 = FALSE; + Status = EFI_DEVICE_ERROR; + + if (This == NULL || BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support BootPolicy + // + if (!BootPolicy) { + return EFI_UNSUPPORTED; + } + + // + // Check media status before PXE start + // + MediaPresent = TRUE; + NetLibDetectMedia (Private->Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Check whether the virtual nic is using IPv6 or not. + // + if (VirtualNic == Private->Ip6Nic) { + UsingIpv6 = TRUE; + } + + // + // Start Pxe Base Code to initialize PXE boot. + // + Status = PxeBc->Start (PxeBc, UsingIpv6); + if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) { + Status = PxeBcLoadBootFile (Private, BufferSize, Buffer); + } + + if (Status != EFI_SUCCESS && + Status != EFI_UNSUPPORTED && + Status != EFI_BUFFER_TOO_SMALL) { + // + // There are three cases, which needn't stop pxebc here. + // 1. success to download file. + // 2. success to get file size. + // 3. unsupported. + // + PxeBc->Stop (PxeBc); + } + + return Status; +} + +EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate = { EfiPxeLoadFile }; + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h new file mode 100644 index 0000000000..7491cf8285 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h @@ -0,0 +1,207 @@ +/** @file + This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL. + interfaces declaration. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_PXEBC_IMPL_H__ +#define __EFI_PXEBC_IMPL_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PXEBC_PRIVATE_DATA PXEBC_PRIVATE_DATA; +typedef struct _PXEBC_VIRTUAL_NIC PXEBC_VIRTUAL_NIC; + +#include "PxeBcDriver.h" +#include "PxeBcDhcp4.h" +#include "PxeBcDhcp6.h" +#include "PxeBcMtftp.h" +#include "PxeBcBoot.h" +#include "PxeBcSupport.h" + +#define PXEBC_DEFAULT_HOPLIMIT 64 +#define PXEBC_DEFAULT_LIFETIME 50000 // 50 ms, unit is microsecond +#define PXEBC_UDP_TIMEOUT 30000000 // 3 seconds, unit is 100nanosecond +#define PXEBC_MTFTP_TIMEOUT 4 +#define PXEBC_MTFTP_RETRIES 6 +#define PXEBC_DHCP_RETRIES 4 // refers to mPxeDhcpTimeout, also by PXE2.1 spec. +#define PXEBC_MENU_MAX_NUM 24 +#define PXEBC_OFFER_MAX_NUM 16 + +#define PXEBC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'P') +#define PXEBC_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'V') +#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a) CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE) +#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE) + +typedef union { + PXEBC_DHCP4_PACKET_CACHE Dhcp4; + PXEBC_DHCP6_PACKET_CACHE Dhcp6; +} PXEBC_DHCP_PACKET_CACHE; + +struct _PXEBC_VIRTUAL_NIC { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + PXEBC_PRIVATE_DATA *Private; +}; + +struct _PXEBC_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + PXEBC_VIRTUAL_NIC *Ip4Nic; + PXEBC_VIRTUAL_NIC *Ip6Nic; + + EFI_HANDLE ArpChild; + EFI_HANDLE Ip4Child; + EFI_HANDLE Dhcp4Child; + EFI_HANDLE Mtftp4Child; + EFI_HANDLE Udp4ReadChild; + EFI_HANDLE Udp4WriteChild; + + EFI_ARP_PROTOCOL *Arp; + EFI_IP4_PROTOCOL *Ip4; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_UDP4_PROTOCOL *Udp4Read; + EFI_UDP4_PROTOCOL *Udp4Write; + + EFI_HANDLE Ip6Child; + EFI_HANDLE Dhcp6Child; + EFI_HANDLE Mtftp6Child; + EFI_HANDLE Udp6ReadChild; + EFI_HANDLE Udp6WriteChild; + + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_UDP6_PROTOCOL *Udp6Read; + EFI_UDP6_PROTOCOL *Udp6Write; + + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + EFI_PXE_BASE_CODE_PROTOCOL PxeBc; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL LoadFileCallback; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *PxeBcCallback; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + EFI_PXE_BASE_CODE_MODE Mode; + EFI_PXE_BASE_CODE_FUNCTION Function; + UINT32 Ip6Policy; + + EFI_UDP4_CONFIG_DATA Udp4CfgData; + EFI_UDP6_CONFIG_DATA Udp6CfgData; + EFI_IP4_CONFIG_DATA Ip4CfgData; + EFI_IP6_CONFIG_DATA Ip6CfgData; + + EFI_EVENT UdpTimeOutEvent; + EFI_EVENT ArpUpdateEvent; + EFI_IP4_COMPLETION_TOKEN IcmpToken; + EFI_IP6_COMPLETION_TOKEN Icmp6Token; + + BOOLEAN IsAddressOk; + BOOLEAN IsOfferSorted; + BOOLEAN IsProxyRecved; + BOOLEAN IsDoDiscover; + + EFI_IP_ADDRESS StationIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; + EFI_IP_ADDRESS ServerIp; + UINT16 CurSrcPort; + + UINT32 Ip4MaxPacketSize; + UINT32 Ip6MaxPacketSize; + UINT8 *BootFileName; + UINTN BootFileSize; + UINTN BlockSize; + + PXEBC_DHCP_PACKET_CACHE ProxyOffer; + PXEBC_DHCP_PACKET_CACHE DhcpAck; + PXEBC_DHCP_PACKET_CACHE PxeReply; + EFI_DHCP6_PACKET *Dhcp6Request; + EFI_DHCP4_PACKET SeedPacket; + + // + // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer. + // + // It supposed that + // + // OfferNum: 8 + // OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl] + // (OfferBuffer is 0-based.) + // + // And assume that (DhcpPxe10 is the first priority actually.) + // + // SelectIndex: 2 + // SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL + // (SelectIndex is 1-based, and 0 means no one is selected.) + // + // So it should be + // + // DhcpOnly DhcpPxe10 DhcpWfm11a DhcpBinl ProxyPxe10 ProxyWfm11a ProxyBinl Bootp + // OfferCount: [ 2(n), 1(n), 0(n), 1(n), 1(1), 0(1), 3(n), 1(1)] + // + // OfferIndex: {[ 2, 5, 0, 6, 3, 0, *0, 0] + // [ 4, 0, 0, 0, 0, 0, 1, 0] + // [ 0, 0, 0, 0, 0, 0, 7, 0] + // ... ]} + // (OfferIndex is 0-based.) + // + // + UINT32 SelectIndex; + UINT32 SelectProxyType; + PXEBC_DHCP_PACKET_CACHE OfferBuffer[PXEBC_OFFER_MAX_NUM]; + UINT32 OfferNum; + UINT32 OfferCount[PxeOfferTypeMax]; + UINT32 OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM]; +}; + +extern EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate; +extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate; +extern EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate; + +#endif diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c new file mode 100644 index 0000000000..8adba6620f --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c @@ -0,0 +1,1105 @@ +/** @file + Functions implementation related with Mtftp for UefiPxeBc Driver. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + +CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = { + "blksize", + "timeout", + "tsize", + "multicast" +}; + + +/** + This is a callback function when packets are received or transmitted in Mtftp driver. + + A callback function that is provided by the caller to intercept + the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the + EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept + EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to + EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). + + @param[in] This Pointer to EFI_MTFTP6_PROTOCOL. + @param[in] Token Pointer to EFI_MTFTP6_TOKEN. + @param[in] PacketLen Length of EFI_MTFTP6_PACKET. + @param[in] Packet Pointer to EFI_MTFTP6_PACKET to be checked. + + @retval EFI_SUCCESS The current operation succeeded. + @retval EFI_ABORTED Abort the current transfer process. + +**/ +EFI_STATUS +EFIAPI +PxeBcMtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Token->Context; + Callback = Private->PxeBcCallback; + Status = EFI_SUCCESS; + + if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpy ( + Private->Mode.TftpError.ErrorString, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH + ); + } + + if (Callback != NULL) { + // + // Callback to user if has when received any tftp packet. + // + Status = Callback->Callback ( + Callback, + Private->Function, + TRUE, + PacketLen, + (EFI_PXE_BASE_CODE_PACKET *) Packet + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + // + // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_ABORTED; + } else { + // + // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + This function is to get the size of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Sucessfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp ptions failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Has not obtained the size of the file. + +**/ +EFI_STATUS +PxeBcMtftp6GetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_OPTION ReqOpt[2]; + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_OPTION *Option; + UINT32 PktLen; + UINT8 OptBuf[128]; + UINT32 OptCnt; + EFI_STATUS Status; + + *BufferSize = 0; + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + Packet = NULL; + Option = NULL; + PktLen = 0; + OptCnt = 1; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build the required options for get info. + // + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX]; + PxeBcUintnToAscDec (0, OptBuf); + ReqOpt[0].ValueStr = OptBuf; + + if (BlockSize != NULL) { + ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1); + PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr); + OptCnt++; + } + + Status = Mtftp6->GetInfo ( + Mtftp6, + FALSE, + Filename, + NULL, + (UINT8) OptCnt, + ReqOpt, + &PktLen, + &Packet + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_TFTP_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpy ( + Private->Mode.TftpError.ErrorString, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH + ); + } + goto ON_ERROR; + } + + // + // Parse the options in the reply packet. + // + OptCnt = 0; + Status = Mtftp6->ParseOptions ( + Mtftp6, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &Option + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Parse out the value of "tsize" option. + // + Status = EFI_NOT_FOUND; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) { + *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr)); + Status = EFI_SUCCESS; + } + OptCnt--; + } + FreePool (Option); + +ON_ERROR: + if (Packet != NULL) { + FreePool (Packet); + } + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is to get data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether with a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcMtftp6ReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->ReadFile (Mtftp6, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is used to write the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicate whether with overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into a special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcMtftp6WriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->WriteFile (Mtftp6, &Token); + // + // Get the real size of transmitted buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is to read the data (file) from a directory using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully obtained the data from the file included in directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcMtftp6ReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->ReadDirectory (Mtftp6, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This is a callback function when packets are received or transmitted in Mtftp driver. + + A callback function that is provided by the caller to intercept + the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the + EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept + EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to + EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). + + @param[in] This Pointer to EFI_MTFTP4_PROTOCOL. + @param[in] Token Pointer to EFI_MTFTP4_TOKEN. + @param[in] PacketLen Length of EFI_MTFTP4_PACKET. + @param[in] Packet Pointer to EFI_MTFTP4_PACKET to be checked. + + @retval EFI_SUCCESS The current operation succeeeded. + @retval EFI_ABORTED Abort the current transfer process. + +**/ +EFI_STATUS +EFIAPI +PxeBcMtftp4CheckPacket ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Packet + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Token->Context; + Callback = Private->PxeBcCallback; + Status = EFI_SUCCESS; + + if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpy ( + Private->Mode.TftpError.ErrorString, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH + ); + } + + if (Callback != NULL) { + // + // Callback to user if has when received any tftp packet. + // + Status = Callback->Callback ( + Callback, + Private->Function, + TRUE, + PacketLen, + (EFI_PXE_BASE_CODE_PACKET *) Packet + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + // + // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_ABORTED; + } else { + // + // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + This function is to get size of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp options failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcMtftp4GetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_OPTION ReqOpt[2]; + EFI_MTFTP4_PACKET *Packet; + EFI_MTFTP4_OPTION *Option; + UINT32 PktLen; + UINT8 OptBuf[128]; + UINT32 OptCnt; + EFI_STATUS Status; + + *BufferSize = 0; + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + Packet = NULL; + Option = NULL; + PktLen = 0; + OptCnt = 1; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build the required options for get info. + // + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX]; + PxeBcUintnToAscDec (0, OptBuf); + ReqOpt[0].ValueStr = OptBuf; + + if (BlockSize != NULL) { + ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1); + PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr); + OptCnt++; + } + + Status = Mtftp4->GetInfo ( + Mtftp4, + FALSE, + Filename, + NULL, + (UINT8) OptCnt, + ReqOpt, + &PktLen, + &Packet + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_TFTP_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpy ( + Private->Mode.TftpError.ErrorString, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH + ); + } + goto ON_ERROR; + } + + // + // Parse the options in the reply packet. + // + OptCnt = 0; + Status = Mtftp4->ParseOptions ( + Mtftp4, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &Option + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Parse out the value of "tsize" option. + // + Status = EFI_NOT_FOUND; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) { + *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr)); + Status = EFI_SUCCESS; + } + OptCnt--; + } + FreePool (Option); + +ON_ERROR: + if (Packet != NULL) { + FreePool (Packet); + } + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to read the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcMtftp4ReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->ReadFile (Mtftp4, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to write the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicates whether to use the overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully write the data into the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcMtftp4WriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->WriteFile (Mtftp4, &Token); + // + // Get the real size of transmitted buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to get data (file) from a directory using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in the directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcMtftp4ReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->ReadDirectory (Mtftp4, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is wrapper to get the file size using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to configure data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp options failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcTftpGetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6GetFileSize ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferSize + ); + } else { + return PxeBcMtftp4GetFileSize ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferSize + ); + } +} + + +/** + This function is a wrapper to get file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Sucessfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcTftpReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6ReadFile ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } else { + return PxeBcMtftp4ReadFile ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } +} + + +/** + This function is a wrapper to write file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicate whether with overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into a special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcTftpWriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6WriteFile ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + } else { + return PxeBcMtftp4WriteFile ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + } +} + + +/** + This function is a wrapper to get the data (file) from a directory using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicatse whether to use a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in the directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcTftpReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6ReadDirectory ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } else { + return PxeBcMtftp4ReadDirectory ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } +} + diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h new file mode 100644 index 0000000000..1064195a82 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h @@ -0,0 +1,136 @@ +/** @file + Functions declaration related with Mtftp for UefiPxeBc Driver. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_PXEBC_MTFTP_H__ +#define __EFI_PXEBC_MTFTP_H__ + +#define PXE_MTFTP_OPTION_BLKSIZE_INDEX 0 +#define PXE_MTFTP_OPTION_TIMEOUT_INDEX 1 +#define PXE_MTFTP_OPTION_TSIZE_INDEX 2 +#define PXE_MTFTP_OPTION_MULTICAST_INDEX 3 +#define PXE_MTFTP_OPTION_MAXIMUM_INDEX 4 + +#define PXE_MTFTP_ERROR_STRING_LENGTH 127 // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR. +#define PXE_MTFTP_DEFAULT_BLOCK_SIZE 512 // refer to rfc-1350. + + +/** + This function is wrapper to get the file size using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to configure data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp ptions failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcTftpGetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ); + + +/** + This function is a wrapper to get a file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcTftpReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ); + + +/** + This function is a wrapper to put file with TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicates whether to use an overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcTftpWriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ); + + +/** + This function is a wrapper to get the data (file) from a directory using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether with a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcTftpReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ); +#endif diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c new file mode 100644 index 0000000000..7ad070c5e3 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c @@ -0,0 +1,1588 @@ +/** @file + Support functions implementation for UefiPxeBc Driver. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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 "PxeBcImpl.h" + + + +/** + This function returns SMBIOS string given the string number. + + @param[in] Smbios The pointer to the SMBIOS structure + @param[in] StringNumber String number to return. 0 is used to skip all + strings and point to the next SMBIOS structure. + + @return String The pointer to the next SMBIOS structure if + StringNumber == 0. + +**/ +CHAR8 * +PxeBcGetSmbiosString ( + IN SMBIOS_STRUCTURE_POINTER *Smbios, + IN UINT16 StringNumber + ) +{ + UINT16 Index; + CHAR8 *String; + + // + // Skip over formatted section. + // + String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length); + + // + // Look through unformated section. + // + for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) { + if (StringNumber == Index) { + return String; + } + + // + // Skip zero string. + // + while (*String != 0) { + String++; + } + String++; + + if (*String == 0) { + // + // If double NULL then we are done. + // Return pointer to next structure in Smbios. + // if you pass in a 0 you will always get here + // + Smbios->Raw = (UINT8 *)++String; + return NULL; + } + } + + return NULL; +} + + +/** + This function obtains the system guid and the serial number from the smbios table. + + @param[out] SystemGuid The pointer of the returned system guid. + + @retval EFI_SUCCESS Successfully obtained the system guid. + @retval EFI_NOT_FOUND Did not find the SMBIOS table. + +**/ +EFI_STATUS +PxeBcGetSystemGuid ( + OUT EFI_GUID *SystemGuid + ) +{ + EFI_STATUS Status; + SMBIOS_TABLE_ENTRY_POINT *SmbiosTable; + SMBIOS_STRUCTURE_POINTER Smbios; + SMBIOS_STRUCTURE_POINTER SmbiosEnd; + UINT16 Index; + + SmbiosTable = NULL; + Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable); + + if (EFI_ERROR (Status) || SmbiosTable == NULL) { + return EFI_NOT_FOUND; + } + + Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress; + SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength); + + for (Index = 0; Index < SmbiosTable->TableLength; Index++) { + if (Smbios.Hdr->Type == 1) { + if (Smbios.Hdr->Length < 0x19) { + // + // Older version did not support Guid and Serial number + // + continue; + } + // + // SMBIOS tables are byte packed so we need to do a byte copy to + // prevend alignment faults on Itanium-based platform. + // + CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID)); + PxeBcGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber); + + return EFI_SUCCESS; + } + // + // Make Smbios point to the next record + // + PxeBcGetSmbiosString (&Smbios, 0); + + if (Smbios.Raw >= SmbiosEnd.Raw) { + // + // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e. + // given this we must double check against the length of the structure. + // + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Flush the previous configration using the new station Ip address. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] StationIp The pointer to the station Ip address. + @param[in] SubnetMask The pointer to the subnet mask address for v4. + + @retval EFI_SUCCESS Successfully flushed the previous configuration. + @retval Others Failed to flush using the new station Ip. + +**/ +EFI_STATUS +PxeBcFlushStaionIp ( + PXEBC_PRIVATE_DATA *Private, + EFI_IP_ADDRESS *StationIp, + EFI_IP_ADDRESS *SubnetMask OPTIONAL + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + + ASSERT (StationIp != NULL); + + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + + if (Mode->UsingIpv6) { + + CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + + // + // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address. + // + Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token); + Private->Ip6->Configure (Private->Ip6, NULL); + + Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else { + ASSERT (SubnetMask != NULL); + CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + + // + // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address. + // + Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken); + Private->Ip4->Configure (Private->Ip4, NULL); + + Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } + +ON_EXIT: + return Status; +} + + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +PxeBcCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + + +/** + Do arp resolution from arp cache in PxeBcMode. + + @param Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param Ip4Addr The Ip4 address for resolution. + @param MacAddress The resoluted MAC address if the resolution is successful. + The value is undefined if the resolution fails. + + @retval TRUE Found an matched entry. + @retval FALSE Did not find a matched entry. + +**/ +BOOLEAN +PxeBcCheckArpCache ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_IPv4_ADDRESS *Ip4Addr, + OUT EFI_MAC_ADDRESS *MacAddress + ) +{ + UINT32 Index; + + ASSERT (!Mode->UsingIpv6); + + // + // Check whether the current Arp cache in mode data contains this information or not. + // + for (Index = 0; Index < Mode->ArpCacheEntries; Index++) { + if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) { + CopyMem ( + MacAddress, + &Mode->ArpCache[Index].MacAddr, + sizeof (EFI_MAC_ADDRESS) + ); + return TRUE; + } + } + + return FALSE; +} + + +/** + Update the arp cache periodically. + + @param Event The pointer to EFI_PXE_BC_PROTOCOL. + @param Context Context of the timer event. + +**/ +VOID +EFIAPI +PxeBcArpCacheUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_ARP_FIND_DATA *ArpEntry; + UINT32 EntryLength; + UINT32 EntryCount; + UINT32 Index; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + + ASSERT (!Mode->UsingIpv6); + + // + // Get the current Arp cache from Arp driver. + // + Status = Private->Arp->Find ( + Private->Arp, + TRUE, + NULL, + &EntryLength, + &EntryCount, + &ArpEntry, + TRUE + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // Update the Arp cache in mode data. + // + Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES); + + for (Index = 0; Index < Mode->ArpCacheEntries; Index++) { + CopyMem ( + &Mode->ArpCache[Index].IpAddr, + ArpEntry + 1, + ArpEntry->SwAddressLength + ); + CopyMem ( + &Mode->ArpCache[Index].MacAddr, + (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength, + ArpEntry->HwAddressLength + ); + ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength); + } +} + + +/** + Notify function to handle the received ICMP message in DPC. + + @param Context The PXEBC private data. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorDpcHandle ( + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_IP4_RECEIVE_DATA *RxData; + EFI_IP4_PROTOCOL *Ip4; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + UINT8 Type; + UINTN Index; + UINT32 CopiedLen; + UINT8 *IcmpError; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = &Private->Mode; + Status = Private->IcmpToken.Status; + RxData = Private->IcmpToken.Packet.RxData; + Ip4 = Private->Ip4; + + ASSERT (!Mode->UsingIpv6); + + if (Status == EFI_ABORTED) { + // + // It's triggered by user cancellation. + // + return; + } + + if (RxData == NULL) { + goto ON_EXIT; + } + + if (Status != EFI_ICMP_ERROR) { + // + // The return status should be recognized as EFI_ICMP_ERROR. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (EFI_IP4 (RxData->Header->SourceAddress) != 0 && + !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) { + // + // The source address of the received packet should be a valid unicast address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) { + // + // The destination address of the received packet should be equal to the host address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) { + // + // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer); + + if (Type != ICMP_DEST_UNREACHABLE && + Type != ICMP_SOURCE_QUENCH && + Type != ICMP_REDIRECT && + Type != ICMP_TIME_EXCEEDED && + Type != ICMP_PARAMETER_PROBLEM) { + // + // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + // + // Copy the right ICMP error message into mode data. + // + CopiedLen = 0; + IcmpError = (UINT8 *) &Mode->IcmpError; + + for (Index = 0; Index < RxData->FragmentCount; Index++) { + CopiedLen += RxData->FragmentTable[Index].FragmentLength; + if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) { + CopyMem ( + IcmpError, + RxData->FragmentTable[Index].FragmentBuffer, + RxData->FragmentTable[Index].FragmentLength + ); + } else { + CopyMem ( + IcmpError, + RxData->FragmentTable[Index].FragmentBuffer, + CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + } + IcmpError += CopiedLen; + } + +ON_EXIT: + Private->IcmpToken.Status = EFI_NOT_READY; + Ip4->Receive (Ip4, &Private->IcmpToken); +} + + +/** + Callback function to update the latest ICMP6 error message. + + @param Event The event signalled. + @param Context The context passed in using the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context); +} + + +/** + Notify function to handle the received ICMP6 message in DPC. + + @param Context The PXEBC private data. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorDpcHandle ( + IN VOID *Context + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_IP6_RECEIVE_DATA *RxData; + EFI_IP6_PROTOCOL *Ip6; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + UINTN Index; + UINT8 Type; + UINT32 CopiedLen; + UINT8 *Icmp6Error; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = &Private->Mode; + Status = Private->Icmp6Token.Status; + RxData = Private->Icmp6Token.Packet.RxData; + Ip6 = Private->Ip6; + + ASSERT (Mode->UsingIpv6); + + if (Status == EFI_ABORTED) { + // + // It's triggered by user cancellation. + // + return; + } + + if (RxData == NULL) { + goto ON_EXIT; + } + + if (Status != EFI_ICMP_ERROR) { + // + // The return status should be recognized as EFI_ICMP_ERROR. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) { + // + // The source address of the received packet should be a valid unicast address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) && + !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) { + // + // The destination address of the received packet should be equal to the host address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (RxData->Header->NextHeader != IP6_ICMP) { + // + // The nextheader in the header of the receveid packet should be IP6_ICMP. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer); + + if (Type != ICMP_V6_DEST_UNREACHABLE && + Type != ICMP_V6_PACKET_TOO_BIG && + Type != ICMP_V6_PACKET_TOO_BIG && + Type != ICMP_V6_PARAMETER_PROBLEM) { + // + // The type of the receveid packet should be an ICMP6 error message. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + // + // Copy the right ICMP6 error message into mode data. + // + CopiedLen = 0; + Icmp6Error = (UINT8 *) &Mode->IcmpError; + + for (Index = 0; Index < RxData->FragmentCount; Index++) { + CopiedLen += RxData->FragmentTable[Index].FragmentLength; + if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) { + CopyMem ( + Icmp6Error, + RxData->FragmentTable[Index].FragmentBuffer, + RxData->FragmentTable[Index].FragmentLength + ); + } else { + CopyMem ( + Icmp6Error, + RxData->FragmentTable[Index].FragmentBuffer, + CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + } + Icmp6Error += CopiedLen; + } + +ON_EXIT: + Private->Icmp6Token.Status = EFI_NOT_READY; + Ip6->Receive (Ip6, &Private->Icmp6Token); +} + + +/** + Callback function to update the latest ICMP6 error message. + + @param Event The event signalled. + @param Context The context passed in using the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context); +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] StationIp The pointer to the station address. + @param[in] SubnetMask The pointer to the subnet mask. + @param[in] Gateway The pointer to the gateway address. + @param[in, out] SrcPort The pointer to the source port. + @param[in] DoNotFragment If TRUE, fragment is not enabled. + Otherwise, fragment is enabled. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_IPv4_ADDRESS *StationIp, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *Gateway, + IN OUT UINT16 *SrcPort, + IN BOOLEAN DoNotFragment + ) +{ + EFI_UDP4_CONFIG_DATA Udp4CfgData; + EFI_STATUS Status; + + ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData)); + + Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData.TypeOfService = DEFAULT_ToS; + Udp4CfgData.TimeToLive = DEFAULT_TTL; + Udp4CfgData.AllowDuplicatePort = TRUE; + Udp4CfgData.DoNotFragment = DoNotFragment; + + CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp)); + CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask)); + + Udp4CfgData.StationPort = *SrcPort; + + // + // Reset the UDPv4 instance. + // + Udp4->Configure (Udp4, NULL); + + Status = Udp4->Configure (Udp4, &Udp4CfgData); + if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) { + // + // The basic configuration is OK, need to add the default route entry + // + Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway); + if (EFI_ERROR (Status)) { + Udp4->Configure (Udp4, NULL); + } + } + + if (!EFI_ERROR (Status) && *SrcPort == 0) { + Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL); + *SrcPort = Udp4CfgData.StationPort; + } + + return Status; +} + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] StationIp The pointer to the station address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_IPv6_ADDRESS *StationIp, + IN OUT UINT16 *SrcPort + ) +{ + EFI_UDP6_CONFIG_DATA CfgData; + EFI_STATUS Status; + + ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA)); + + CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + CfgData.HopLimit = PXEBC_DEFAULT_HOPLIMIT; + CfgData.AllowDuplicatePort = TRUE; + CfgData.StationPort = *SrcPort; + + CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + + // + // Reset the UDPv6 instance. + // + Udp6->Configure (Udp6, NULL); + + Status = Udp6->Configure (Udp6, &CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!EFI_ERROR (Status) && *SrcPort == 0) { + Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL); + *SrcPort = CfgData.StationPort; + } + + return Status; +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] Session The pointer to the UDP4 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] Gateway The pointer to the gateway address. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully send out data using Udp4Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN EFI_IPv4_ADDRESS *Gateway OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_UDP4_COMPLETION_TOKEN Token; + EFI_UDP4_TRANSMIT_DATA *TxData; + UINT32 TxLength; + UINT32 FragCount; + UINT32 DataLength; + BOOLEAN IsDone; + EFI_STATUS Status; + + // + // Arrange one fragment buffer for data, and another fragment buffer for header if has. + // + FragCount = (HeaderSize != NULL) ? 2 : 1; + TxLength = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA); + TxData = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength); + if (TxData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxData->FragmentCount = FragCount; + TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize; + TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr; + DataLength = (UINT32) *BufferSize; + + if (HeaderSize != NULL) { + TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize; + TxData->FragmentTable[0].FragmentBuffer = HeaderPtr; + DataLength += (UINT32) *HeaderSize; + } + + if (Gateway != NULL) { + TxData->GatewayAddress = Gateway; + } + + TxData->UdpSessionData = Session; + TxData->DataLength = DataLength; + Token.Packet.TxData = TxData; + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Udp4->Transmit (Udp4, &Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!IsDone && + Token.Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Udp4->Poll (Udp4); + } + + Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status; + +ON_EXIT: + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + FreePool (TxData); + + return Status; +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] Session The pointer to the UDP6 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully sent out data using Udp6Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_UDP6_COMPLETION_TOKEN Token; + EFI_UDP6_TRANSMIT_DATA *TxData; + UINT32 TxLength; + UINT32 FragCount; + UINT32 DataLength; + BOOLEAN IsDone; + EFI_STATUS Status; + + // + // Arrange one fragment buffer for data, and another fragment buffer for header if has. + // + FragCount = (HeaderSize != NULL) ? 2 : 1; + TxLength = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA); + TxData = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength); + if (TxData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxData->FragmentCount = FragCount; + TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize; + TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr; + DataLength = (UINT32) *BufferSize; + + if (HeaderSize != NULL) { + TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize; + TxData->FragmentTable[0].FragmentBuffer = HeaderPtr; + DataLength += (UINT32) *HeaderSize; + } + + TxData->UdpSessionData = Session; + TxData->DataLength = DataLength; + Token.Packet.TxData = TxData; + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Udp6->Transmit (Udp6, &Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!IsDone && + Token.Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Udp6->Poll (Udp6); + } + + Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status; + +ON_EXIT: + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + FreePool (TxData); + + return Status; +} + + +/** + Check the received packet using the Ip filter. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the Ip filter successfully. + @retval FALSE Failed to pass the Ip filter. + +**/ +BOOLEAN +PxeBcCheckByIpFilter ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN UINT16 OpFlags + ) +{ + EFI_IP_ADDRESS DestinationIp; + UINTN Index; + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) { + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) { + return TRUE; + } + + // + // Convert the destination address in session data to host order. + // + if (Mode->UsingIpv6) { + CopyMem ( + &DestinationIp, + &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + NTOHLLL (&DestinationIp.v6); + } else { + ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + &DestinationIp, + &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + EFI_NTOHL (DestinationIp); + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 && + (IP4_IS_MULTICAST (DestinationIp.Addr[0]) || + IP6_IS_MULTICAST (&DestinationIp))) { + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 && + IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) { + ASSERT (!Mode->UsingIpv6); + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 && + (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) || + EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) { + // + // Matched if the dest address is equal to the station address. + // + return TRUE; + } + + for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) { + ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT); + if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) || + EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) { + // + // Matched if the dest address is equal to any of address in the filter list. + // + return TRUE; + } + } + + return FALSE; +} + + +/** + Filter the received packet using the destination Ip. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] DestIp The pointer to the destination Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *DestIp, + IN UINT16 OpFlags + ) +{ + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) { + // + // Copy the destination address from the received packet if accept any. + // + if (DestIp != NULL) { + if (Mode->UsingIpv6) { + CopyMem ( + DestIp, + &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + DestIp, + &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + } + return TRUE; + } else if (DestIp != NULL && + (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) || + EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) { + // + // The destination address in the received packet is matched if present. + // + return TRUE; + } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) || + EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) { + // + // The destination address in the received packet is equal to the host address. + // + return TRUE; + } + + return FALSE; +} + + +/** + Check the received packet using the destination port. + + @param[in] PxeBcMode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] DestPort The pointer to the destination port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *DestPort, + IN UINT16 OpFlags + ) +{ + UINT16 Port; + + if (Mode->UsingIpv6) { + Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort; + } else { + Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort; + } + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) { + // + // Return the destination port in the received packet if accept any. + // + if (DestPort != NULL) { + *DestPort = Port; + } + return TRUE; + } else if (DestPort != NULL && *DestPort == Port) { + // + // The destination port in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + Filter the received packet using the source Ip. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] SrcIp The pointer to the source Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *SrcIp, + IN UINT16 OpFlags + ) +{ + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) { + // + // Copy the source address from the received packet if accept any. + // + if (SrcIp != NULL) { + if (Mode->UsingIpv6) { + CopyMem ( + SrcIp, + &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + SrcIp, + &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + } + return TRUE; + } else if (SrcIp != NULL && + (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) || + EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) { + // + // The source address in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + Filter the received packet using the source port. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] SrcPort The pointer to the source port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *SrcPort, + IN UINT16 OpFlags + ) +{ + UINT16 Port; + + if (Mode->UsingIpv6) { + Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort; + } else { + Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort; + } + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) { + // + // Return the source port in the received packet if accept any. + // + if (SrcPort != NULL) { + *SrcPort = Port; + } + return TRUE; + } else if (SrcPort != NULL && *SrcPort == Port) { + // + // The source port in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + This function is to receive packet using Udp4Read. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] Token The pointer to EFI_UDP4_COMPLETION_TOKEN. + @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone The pointer to the IsDone flag. + @param[out] IsMatched The pointer to the IsMatched flag. + @param[in, out] DestIp The pointer to the destination address. + @param[in, out] DestPort The pointer to the destination port. + @param[in, out] SrcIp The pointer to the source address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully read the data using Udp4. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Read ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ) +{ + EFI_UDP4_RECEIVE_DATA *RxData; + EFI_UDP4_SESSION_DATA *Session; + EFI_STATUS Status; + + Token->Status = EFI_NOT_READY; + *IsDone = FALSE; + + Status = Udp4->Receive (Udp4, Token); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!(*IsDone) && + Token->Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + // + // Poll the token utill reply/ICMPv6 error message received or timeout. + // + Udp4->Poll (Udp4); + if (Token->Status == EFI_ICMP_ERROR || + Token->Status == EFI_NETWORK_UNREACHABLE || + Token->Status == EFI_HOST_UNREACHABLE || + Token->Status == EFI_PROTOCOL_UNREACHABLE || + Token->Status == EFI_PORT_UNREACHABLE) { + break; + } + } + + Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status; + + if (!EFI_ERROR (Status)) { + // + // check whether this packet matches the filters + // + RxData = Token->Packet.RxData; + Session = &RxData->UdpSession; + + *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags); + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags); + } + + if (!(*IsMatched)) { + // + // Recycle the receiving buffer if not matched. + // + gBS->SignalEvent (RxData->RecycleSignal); + } + } + + return Status; +} + + +/** + This function is to receive packets using Udp6Read. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] Token The pointer to EFI_UDP6_COMPLETION_TOKEN. + @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone The pointer to the IsDone flag. + @param[out] IsMatched The pointer to the IsMatched flag. + @param[in, out] DestIp The pointer to the destination address. + @param[in, out] DestPort The pointer to the destination port. + @param[in, out] SrcIp The pointer to the source address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully read data using Udp6. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Read ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ) +{ + EFI_UDP6_RECEIVE_DATA *RxData; + EFI_UDP6_SESSION_DATA *Session; + EFI_STATUS Status; + + Token->Status = EFI_NOT_READY; + *IsDone = FALSE; + + Status = Udp6->Receive (Udp6, Token); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!(*IsDone) && + Token->Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + // + // Poll the token utill reply/ICMPv6 error message received or timeout. + // + Udp6->Poll (Udp6); + if (Token->Status == EFI_ICMP_ERROR || + Token->Status == EFI_NETWORK_UNREACHABLE || + Token->Status == EFI_HOST_UNREACHABLE || + Token->Status == EFI_PROTOCOL_UNREACHABLE || + Token->Status == EFI_PORT_UNREACHABLE) { + break; + } + } + + Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status; + + if (!EFI_ERROR (Status)) { + // + // check whether this packet matches the filters + // + RxData = Token->Packet.RxData; + Session = &RxData->UdpSession; + + *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags); + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags); + } + + if (!(*IsMatched)) { + // + // Recycle the receiving buffer if not matched. + // + gBS->SignalEvent (RxData->RecycleSignal); + } + } + + return Status; +} + + +/** + This function is to display the IPv4 address. + + @param[in] Ip The pointer to the IPv4 address. + +**/ +VOID +PxeBcShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 4; Index++) { + AsciiPrint ("%d", Ip->Addr[Index]); + if (Index < 3) { + AsciiPrint ("."); + } + } +} + + +/** + This function is to display the IPv6 address. + + @param[in] Ip The pointer to the IPv6 address. + +**/ +VOID +PxeBcShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 16; Index++) { + + if (Ip->Addr[Index] != 0) { + AsciiPrint ("%x", Ip->Addr[Index]); + } + Index++; + if (Index > 15) { + return; + } + if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) { + AsciiPrint ("0"); + } + AsciiPrint ("%x", Ip->Addr[Index]); + if (Index < 15) { + AsciiPrint (":"); + } + } +} + + +/** + This function is to convert UINTN to ASCII string with the required formatting. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] Length The length of the required format. + +**/ +VOID +PxeBcUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ) +{ + UINTN Remainder; + + while (Length > 0) { + Length--; + Remainder = Number % 10; + Number /= 10; + Buffer[Length] = (UINT8) ('0' + Remainder); + } +} + + +/** + This function is to convert a UINTN to a ASCII string, and return the + actual length of the buffer. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + + @return Length The actual length of the ASCII string. + +**/ +UINTN +PxeBcUintnToAscDec ( + IN UINTN Number, + IN UINT8 *Buffer + ) +{ + UINTN Index; + UINTN Length; + CHAR8 TempStr[64]; + + Index = 63; + TempStr[Index] = 0; + + do { + Index--; + TempStr[Index] = (CHAR8) ('0' + (Number % 10)); + Number = (UINTN) (Number / 10); + } while (Number != 0); + + AsciiStrCpy ((CHAR8 *) Buffer, &TempStr[Index]); + + Length = AsciiStrLen ((CHAR8 *) Buffer); + + return Length; +} + + +/** + This function is to convert unicode hex number to a UINT8. + + @param[out] Digit The converted UINT8 for output. + @param[in] Char The unicode hex number to be converted. + + @retval EFI_SUCCESS Successfully converted the unicode hex. + @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex. + +**/ +EFI_STATUS +PxeBcUniHexToUint8 ( + OUT UINT8 *Digit, + IN CHAR16 Char + ) +{ + if ((Char >= L'0') && (Char <= L'9')) { + *Digit = (UINT8) (Char - L'0'); + return EFI_SUCCESS; + } + + if ((Char >= L'A') && (Char <= L'F')) { + *Digit = (UINT8) (Char - L'A' + 0x0A); + return EFI_SUCCESS; + } + + if ((Char >= L'a') && (Char <= L'f')) { + *Digit = (UINT8) (Char - L'a' + 0x0A); + return EFI_SUCCESS; + } + + return EFI_INVALID_PARAMETER; +} diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h new file mode 100644 index 0000000000..0d782050f9 --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h @@ -0,0 +1,491 @@ +/** @file + Support functions declaration for UefiPxeBc Driver. + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_PXEBC_SUPPORT_H__ +#define __EFI_PXEBC_SUPPORT_H__ + + +#define ICMP_DEST_UNREACHABLE 3 +#define ICMP_SOURCE_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO_REQUEST 8 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 + + +/** + This function obtain the system guid and serial number from the smbios table. + + @param[out] SystemGuid The pointer of returned system guid. + + @retval EFI_SUCCESS Successfully obtained the system guid. + @retval EFI_NOT_FOUND Did not find the SMBIOS table. + +**/ +EFI_STATUS +PxeBcGetSystemGuid ( + OUT EFI_GUID *SystemGuid + ); + + +/** + Flush the previous configration using the new station Ip address. + + @param[in] Private Pointer to PxeBc private data. + @param[in] StationIp Pointer to the station Ip address. + @param[in] SubnetMask Pointer to the subnet mask address for v4. + + @retval EFI_SUCCESS Successfully flushed the previous config. + @retval Others Failed to flush using the new station Ip. + +**/ +EFI_STATUS +PxeBcFlushStaionIp ( + PXEBC_PRIVATE_DATA *Private, + EFI_IP_ADDRESS *StationIp, + EFI_IP_ADDRESS *SubnetMask OPTIONAL + ); + + +/** + Notify callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +PxeBcCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + Perform arp resolution from the arp cache in PxeBcMode. + + @param Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param Ip4Addr The Ip4 address for resolution. + @param MacAddress The resoluted MAC address if the resolution is successful. + The value is undefined if resolution fails. + + @retval TRUE Found a matched entry. + @retval FALSE Did not find a matched entry. + +**/ +BOOLEAN +PxeBcCheckArpCache ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_IPv4_ADDRESS *Ip4Addr, + OUT EFI_MAC_ADDRESS *MacAddress + ); + + +/** + Update arp cache periodically. + + @param Event Pointer to EFI_PXE_BC_PROTOCOL. + @param Context Context of the timer event. + +**/ +VOID +EFIAPI +PxeBcArpCacheUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + xxx + + @param Event The event signaled. + @param Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + xxx + + @param Event The event signaled. + @param Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] StationIp Pointer to the station address. + @param[in] SubnetMask Pointer to the subnet mask. + @param[in] Gateway Pointer to the gateway address. + @param[in, out] SrcPort Pointer to the source port. + @param[in] DoNotFragment The flag of DoNotFragment bit in the IPv4 + packet. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_IPv4_ADDRESS *StationIp, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *Gateway, + IN OUT UINT16 *SrcPort, + IN BOOLEAN DoNotFragment + ); + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] StationIp Pointer to the station address. + @param[in, out] SrcPort Pointer to the source port. + + @retval EFI_SUCCESS Successfuly configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_IPv6_ADDRESS *StationIp, + IN OUT UINT16 *SrcPort + ); + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] Session Pointer to the UDP4 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] Gateway Pointer to the gateway address. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully sent out data with Udp4Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN EFI_IPv4_ADDRESS *Gateway OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] Session Pointer to the UDP6 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully to send out data with Udp6Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + + +/** + Check the received packet with the Ip filter. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the Ip filter. + @retval FALSE Failed to pass the Ip filter. + +**/ +BOOLEAN +PxeBcCheckByIpFilter ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the destination Ip. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] DestIp Pointer to the dest Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *DestIp, + IN UINT16 OpFlags + ); + + +/** + Check the received packet with the destination port. + + @param[in] PxeBcMode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] DestPort Pointer to the destination port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *DestPort, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the source Ip. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] SrcIp Pointer to the source Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *SrcIp, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the source port. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] SrcPort Pointer to the source port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *SrcPort, + IN UINT16 OpFlags + ); + + +/** + This function is to receive packet with Udp4Read. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] Token Pointer to EFI_UDP4_COMPLETION_TOKEN. + @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone Pointer to IsDone flag. + @param[out] IsMatched Pointer to IsMatched flag. + @param[in, out] DestIp Pointer to destination address. + @param[in, out] DestPort Pointer to destination port. + @param[in, out] SrcIp Pointer to source address. + @param[in, out] SrcPort Pointer to source port. + + @retval EFI_SUCCESS Successfully read data with Udp4. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Read ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ); + + +/** + This function is to receive packet with Udp6Read. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] Token Pointer to EFI_UDP6_COMPLETION_TOKEN. + @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone Pointer to IsDone flag. + @param[out] IsMatched Pointer to IsMatched flag. + @param[in, out] DestIp Pointer to destination address. + @param[in, out] DestPort Pointer to destination port. + @param[in, out] SrcIp Pointer to source address. + @param[in, out] SrcPort Pointer to source port. + + @retval EFI_SUCCESS Successfully read data with Udp6. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Read ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ); + + +/** + This function is to display the IPv4 address. + + @param[in] Ip Pointer to the IPv4 address. + +**/ +VOID +PxeBcShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ); + + +/** + This function is to display the IPv6 address. + + @param[in] Ip Pointer to the IPv6 address. + +**/ +VOID +PxeBcShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ); + + +/** + This function is to convert UINTN to ASCII string with required format. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer Pointer to the buffer for ASCII string. + @param[in] Length Length of the required format. + +**/ +VOID +PxeBcUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ); + + +/** + This function is to convert a UINTN to a ASCII string, and return the + actual length of the buffer. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer Pointer to the buffer for ASCII string. + + @return Length The actual length of the ASCII string. + +**/ +UINTN +PxeBcUintnToAscDec ( + IN UINTN Number, + IN UINT8 *Buffer + ); + +/** + This function is to convert unicode hex number to a UINT8. + + @param[out] Digit The converted UINT8 for output. + @param[in] Char The unicode hex number to be converted. + + @retval EFI_SUCCESS Successfully converted the unicode hex. + @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex. + +**/ +EFI_STATUS +PxeBcUniHexToUint8 ( + OUT UINT8 *Digit, + IN CHAR16 Char + ); + +#endif diff --git a/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf b/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf new file mode 100644 index 0000000000..1e690b882f --- /dev/null +++ b/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf @@ -0,0 +1,98 @@ +## @file +# Component name for module PxeBc +# +# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UefiPxeBcDxe + FILE_GUID = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = PxeBcDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + ComponentName.c + PxeBcDriver.c + PxeBcDriver.h + PxeBcImpl.c + PxeBcImpl.h + PxeBcBoot.c + PxeBcBoot.h + PxeBcDhcp6.c + PxeBcDhcp6.h + PxeBcDhcp4.c + PxeBcDhcp4.h + PxeBcMtftp.c + PxeBcMtftp.h + PxeBcSupport.c + PxeBcSupport.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + MemoryAllocationLib + DebugLib + NetLib + DpcLib + DevicePathLib + PcdLib + + +[Guids] + gEfiSmbiosTableGuid + + +[Protocols] + gEfiDevicePathProtocolGuid + gEfiNetworkInterfaceIdentifierProtocolGuid_31 + gEfiArpServiceBindingProtocolGuid + gEfiArpProtocolGuid + gEfiIp4ServiceBindingProtocolGuid + gEfiIp4ProtocolGuid + gEfiIp6ServiceBindingProtocolGuid + gEfiIp6ProtocolGuid + gEfiIp6ConfigProtocolGuid + gEfiUdp4ServiceBindingProtocolGuid + gEfiUdp4ProtocolGuid + gEfiMtftp4ServiceBindingProtocolGuid + gEfiMtftp4ProtocolGuid + gEfiDhcp4ServiceBindingProtocolGuid + gEfiDhcp4ProtocolGuid + gEfiUdp6ServiceBindingProtocolGuid + gEfiUdp6ProtocolGuid + gEfiMtftp6ServiceBindingProtocolGuid + gEfiMtftp6ProtocolGuid + gEfiDhcp6ServiceBindingProtocolGuid + gEfiDhcp6ProtocolGuid + gEfiPxeBaseCodeCallbackProtocolGuid + gEfiPxeBaseCodeProtocolGuid + gEfiLoadFileProtocolGuid + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize ## CONSUMES