--- /dev/null
+/** @file\r
+ The implementation for Shell application IfConfig6.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/ShellLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+\r
+#include "IfConfig6.h"\r
+\r
+EFI_HII_HANDLE mHiiHandle;\r
+\r
+EFI_GUID mEfiIfConfig6Guid = EFI_IFCONFIG6_GUID;\r
+SHELL_PARAM_ITEM mIfConfig6CheckList[] = {\r
+ {\r
+ L"-b",\r
+ TypeFlag\r
+ },\r
+ {\r
+ L"-s",\r
+ TypeMaxValue\r
+ },\r
+ {\r
+ L"-l",\r
+ TypeValue\r
+ },\r
+ {\r
+ L"-r",\r
+ TypeValue\r
+ },\r
+ {\r
+ L"-?",\r
+ TypeFlag\r
+ },\r
+ {\r
+ NULL,\r
+ TypeMax\r
+ },\r
+};\r
+\r
+VAR_CHECK_ITEM mSetCheckList[] = {\r
+ {\r
+ L"auto",\r
+ 0x00000001,\r
+ 0x00000001,\r
+ FlagTypeSingle\r
+ },\r
+ {\r
+ L"man",\r
+ 0x00000002,\r
+ 0x00000001,\r
+ FlagTypeSingle\r
+ },\r
+ {\r
+ L"host",\r
+ 0x00000004,\r
+ 0x00000002,\r
+ FlagTypeSingle\r
+ },\r
+ {\r
+ L"dad",\r
+ 0x00000008,\r
+ 0x00000004,\r
+ FlagTypeSingle\r
+ },\r
+ {\r
+ L"gw",\r
+ 0x00000010,\r
+ 0x00000008,\r
+ FlagTypeSingle\r
+ },\r
+ {\r
+ L"dns",\r
+ 0x00000020,\r
+ 0x00000010,\r
+ FlagTypeSingle\r
+ },\r
+ {\r
+ L"id",\r
+ 0x00000040,\r
+ 0x00000020,\r
+ FlagTypeSingle\r
+ },\r
+ {\r
+ NULL,\r
+ 0x0,\r
+ 0x0,\r
+ FlagTypeSkipUnknown\r
+ },\r
+};\r
+\r
+/**\r
+ Split a string with specified separator and save the substring to a list.\r
+\r
+ @param[in] String The pointer of the input string.\r
+ @param[in] Separator The specified separator.\r
+\r
+ @return The pointer of headnode of ARG_LIST.\r
+\r
+**/\r
+ARG_LIST *\r
+SplitStrToList (\r
+ IN CONST CHAR16 *String,\r
+ IN CHAR16 Separator\r
+ )\r
+{\r
+ CHAR16 *Str;\r
+ CHAR16 *ArgStr;\r
+ ARG_LIST *ArgList;\r
+ ARG_LIST *ArgNode;\r
+\r
+ if (*String == L'\0') {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Copy the CONST string to a local copy.\r
+ //\r
+ Str = (CHAR16 *) AllocateZeroPool (StrSize (String));\r
+ ASSERT (Str != NULL);\r
+ Str = StrCpy (Str, String);\r
+ ArgStr = Str;\r
+\r
+ //\r
+ // init a node for the list head.\r
+ //\r
+ ArgNode = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));\r
+ ASSERT (ArgNode != NULL);\r
+ ArgList = ArgNode;\r
+\r
+ //\r
+ // Split the local copy and save in the list node.\r
+ //\r
+ while (*Str != L'\0') {\r
+ if (*Str == Separator) {\r
+ *Str = L'\0';\r
+ ArgNode->Arg = ArgStr;\r
+ ArgStr = Str + 1;\r
+ ArgNode->Next = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));\r
+ ASSERT (ArgNode->Next != NULL);\r
+ ArgNode = ArgNode->Next;\r
+ }\r
+\r
+ Str++;\r
+ }\r
+\r
+ ArgNode->Arg = ArgStr;\r
+ ArgNode->Next = NULL;\r
+\r
+ return ArgList;\r
+}\r
+\r
+/**\r
+ Check the correctness of input Args with '-s' option.\r
+\r
+ @param[in] CheckList The pointer of VAR_CHECK_ITEM array.\r
+ @param[in] Name The pointer of input arg.\r
+ @param[in] Init The switch to execute the check.\r
+\r
+ @return The value of VAR_CHECK_CODE.\r
+\r
+**/\r
+VAR_CHECK_CODE\r
+IfConfig6RetriveCheckListByName(\r
+ IN VAR_CHECK_ITEM *CheckList,\r
+ IN CHAR16 *Name,\r
+ IN BOOLEAN Init\r
+)\r
+{\r
+ STATIC UINT32 CheckDuplicate;\r
+ STATIC UINT32 CheckConflict;\r
+ VAR_CHECK_CODE RtCode;\r
+ UINT32 Index;\r
+ VAR_CHECK_ITEM Arg;\r
+\r
+ if (Init) {\r
+ CheckDuplicate = 0;\r
+ CheckConflict = 0;\r
+ return VarCheckOk;\r
+ }\r
+\r
+ RtCode = VarCheckOk;\r
+ Index = 0;\r
+ Arg = CheckList[Index];\r
+\r
+ //\r
+ // Check the Duplicated/Conflicted/Unknown input Args.\r
+ //\r
+ while (Arg.FlagStr != NULL) {\r
+ if (StrCmp (Arg.FlagStr, Name) == 0) {\r
+\r
+ if (CheckDuplicate & Arg.FlagID) {\r
+ RtCode = VarCheckDuplicate;\r
+ break;\r
+ }\r
+\r
+ if (CheckConflict & Arg.ConflictMask) {\r
+ RtCode = VarCheckConflict;\r
+ break;\r
+ }\r
+\r
+ CheckDuplicate |= Arg.FlagID;\r
+ CheckConflict |= Arg.ConflictMask;\r
+ break;\r
+ }\r
+\r
+ Arg = CheckList[++Index];\r
+ }\r
+\r
+ if (Arg.FlagStr == NULL) {\r
+ RtCode = VarCheckUnknown;\r
+ }\r
+\r
+ return RtCode;\r
+}\r
+\r
+/**\r
+ The notify function of create event when performing a manual config.\r
+\r
+ @param[in] CheckList The pointer of Event.\r
+ @param[in] Context The pointer of Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IfConfig6ManualAddressNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+/**\r
+ Print MAC address.\r
+\r
+ @param[in] Node The pointer of MAC address buffer.\r
+ @param[in] Size The size of MAC address buffer.\r
+\r
+**/\r
+VOID\r
+IfConfig6PrintMacAddr (\r
+ IN UINT8 *Node,\r
+ IN UINT32 Size\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ ASSERT (Size <= MACADDRMAXSIZE);\r
+\r
+ for (Index = 0; Index < Size; Index++) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_BODY), mHiiHandle, Node[Index]);\r
+ if (Index + 1 < Size) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+ }\r
+ }\r
+\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+}\r
+\r
+/**\r
+ Print IPv6 address.\r
+\r
+ @param[in] Ip The pointer of Ip bufffer in EFI_IPv6_ADDRESS format.\r
+ @param[in] PrefixLen The pointer of PrefixLen that describes the size Prefix.\r
+\r
+**/\r
+VOID\r
+IfConfig6PrintIpAddr (\r
+ IN EFI_IPv6_ADDRESS *Ip,\r
+ IN UINT8 *PrefixLen\r
+ )\r
+{\r
+ UINTN Index;\r
+ BOOLEAN Short;\r
+\r
+ Short = FALSE;\r
+\r
+ for (Index = 0; Index < PREFIXMAXLEN; Index = Index + 2) {\r
+\r
+ if (!Short && (Index + 1 < PREFIXMAXLEN) && (Index % 2 == 0) && (Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0)) {\r
+ //\r
+ // Deal with the case of ::.\r
+ //\r
+ if (Index == 0) {\r
+ //\r
+ // :: is at the beginning of the address.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+ }\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+\r
+ while ((Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0) && (Index < PREFIXMAXLEN)) {\r
+ Index = Index + 2;\r
+ if (Index > PREFIXMAXLEN - 2) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ Short = TRUE;\r
+\r
+ if (Index == PREFIXMAXLEN) {\r
+ //\r
+ // :: is at the end of the address.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Index < PREFIXMAXLEN - 1) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index]);\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index + 1]);\r
+ }\r
+\r
+ if (Index + 2 < PREFIXMAXLEN) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);\r
+ }\r
+ }\r
+\r
+ if (PrefixLen != NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_PREFIX_LEN), mHiiHandle, *PrefixLen);\r
+ }\r
+}\r
+\r
+/**\r
+ Pick up host IPv6 address in string format from Args with "-s" option and convert it to EFI_IP6_CONFIG_MANUAL_ADDRESS format.\r
+\r
+ @param[in, out] Arg The pointer of the address of ARG_LIST which save Args with the "-s" option.\r
+ @param[out] Buf The pointer of the address of EFI_IP6_CONFIG_MANUAL_ADDRESS.\r
+ @param[out] Address The pointer of BufSize that describes the size of Buf in bytes.\r
+\r
+ @retval EFI_SUCCESS The convertion is successful.\r
+ @retval Others Does't find the host address, or it is an invalid IPv6 address in string format.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseManualAddressList (\r
+ IN OUT ARG_LIST **Arg,\r
+ OUT EFI_IP6_CONFIG_MANUAL_ADDRESS **Buf,\r
+ OUT UINTN *BufSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *AddrBuf;\r
+ ARG_LIST *VarArg;\r
+ EFI_IPv6_ADDRESS Address;\r
+ UINT8 Prefix;\r
+ UINT8 AddrCnt;\r
+\r
+ AddrCnt = 0;\r
+ *BufSize = 0;\r
+ *Buf = NULL;\r
+ VarArg = *Arg;\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Go through the list to check the correctness of input host ip6 address.\r
+ //\r
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // host ip ip ... gw\r
+ //\r
+ break;\r
+ }\r
+\r
+ VarArg = VarArg->Next;\r
+ AddrCnt++;\r
+ }\r
+\r
+ if (AddrCnt == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+ ASSERT (AddrBuf != NULL);\r
+\r
+ AddrCnt = 0;\r
+ VarArg = *Arg;\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Go through the list to fill in the EFI_IP6_CONFIG_MANUAL_ADDRESS structure.\r
+ //\r
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // If prefix length is not set, set it as Zero here. In the IfConfigSetInterfaceInfo()\r
+ // Zero prefix, length will be transfered to default prefix length.\r
+ //\r
+ if (Prefix == 0xFF) {\r
+ Prefix = 0;\r
+ }\r
+ AddrBuf[AddrCnt].IsAnycast = FALSE;\r
+ AddrBuf[AddrCnt].PrefixLength = Prefix;\r
+ IP6_COPY_ADDRESS (&AddrBuf[AddrCnt].Address, &Address);\r
+ VarArg = VarArg->Next;\r
+ AddrCnt++;\r
+ }\r
+\r
+ *Arg = VarArg;\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ *Buf = AddrBuf;\r
+ *BufSize = AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ FreePool (AddrBuf);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Pick up gw/dns IPv6 address in string format from Args with "-s" option and convert it to EFI_IPv6_ADDRESS format.\r
+\r
+ @param[in, out] Arg The pointer of the address of ARG_LIST that save Args with the "-s" option.\r
+ @param[out] Buf The pointer of the address of EFI_IPv6_ADDRESS.\r
+ @param[out] Address The pointer of BufSize that describes the size of Buf in bytes.\r
+\r
+ @retval EFI_SUCCESS The conversion is successful.\r
+ @retval Others Doesn't find the host address, or it is an invalid IPv6 address in string format.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseGwDnsAddressList (\r
+ IN OUT ARG_LIST **Arg,\r
+ OUT EFI_IPv6_ADDRESS **Buf,\r
+ OUT UINTN *BufSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IPv6_ADDRESS *AddrBuf;\r
+ ARG_LIST *VarArg;\r
+ EFI_IPv6_ADDRESS Address;\r
+ UINT8 Prefix;\r
+ UINT8 AddrCnt;\r
+\r
+ AddrCnt = 0;\r
+ *BufSize = 0;\r
+ *Buf = NULL;\r
+ VarArg = *Arg;\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Go through the list to check the correctness of input gw/dns address.\r
+ //\r
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // gw ip ip ... host\r
+ //\r
+ break;\r
+ }\r
+\r
+ VarArg = VarArg->Next;\r
+ AddrCnt++;\r
+ }\r
+\r
+ if (AddrCnt == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IPv6_ADDRESS));\r
+ ASSERT (AddrBuf != NULL);\r
+\r
+ AddrCnt = 0;\r
+ VarArg = *Arg;\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Go through the list to fill in the EFI_IPv6_ADDRESS structure.\r
+ //\r
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {\r
+\r
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&AddrBuf[AddrCnt], &Address);\r
+\r
+ VarArg = VarArg->Next;\r
+ AddrCnt++;\r
+ }\r
+\r
+ *Arg = VarArg;\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ *Buf = AddrBuf;\r
+ *BufSize = AddrCnt * sizeof (EFI_IPv6_ADDRESS);\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ FreePool (AddrBuf);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Parse InterfaceId in string format from Args with the "-s" option and convert it to EFI_IP6_CONFIG_INTERFACE_ID format.\r
+\r
+ @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option.\r
+ @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+ @retval EFI_SUCCESS The get status processed successfullly.\r
+ @retval EFI_INVALID_PARAMETER The get status process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseInterfaceId (\r
+ IN OUT ARG_LIST **Arg,\r
+ OUT EFI_IP6_CONFIG_INTERFACE_ID **IfId\r
+ )\r
+{\r
+ UINT8 Index;\r
+ UINT8 NodeVal;\r
+ CHAR16 *IdStr;\r
+\r
+ if (*Arg == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Index = 0;\r
+ IdStr = (*Arg)->Arg;\r
+ ASSERT (IfId != NULL);\r
+ *IfId = AllocateZeroPool (sizeof (EFI_IP6_CONFIG_INTERFACE_ID));\r
+ ASSERT (*IfId != NULL);\r
+\r
+ while ((*IdStr != L'\0') && (Index < 8)) {\r
+\r
+ NodeVal = 0;\r
+ while ((*IdStr != L':') && (*IdStr != L'\0')) {\r
+\r
+ if ((*IdStr <= L'F') && (*IdStr >= L'A')) {\r
+ NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'A' + 10);\r
+ } else if ((*IdStr <= L'f') && (*IdStr >= L'a')) {\r
+ NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'a' + 10);\r
+ } else if ((*IdStr <= L'9') && (*IdStr >= L'0')) {\r
+ NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'0');\r
+ } else {\r
+ FreePool (*IfId);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IdStr++;\r
+ }\r
+\r
+ (*IfId)->Id[Index++] = NodeVal;\r
+\r
+ if (*IdStr == L':') {\r
+ IdStr++;\r
+ }\r
+ }\r
+\r
+ *Arg = (*Arg)->Next;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Parse dad in string format from Args with the "-s" option and convert it to UINT32 format.\r
+\r
+ @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option.\r
+ @param[out] Xmits The pointer of Xmits.\r
+\r
+ @retval EFI_SUCCESS The get status processed successfully.\r
+ @retval others The get status process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ParseDadXmits (\r
+ IN OUT ARG_LIST **Arg,\r
+ OUT UINT32 *Xmits\r
+ )\r
+{\r
+ CHAR16 *ValStr;\r
+\r
+ if (*Arg == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ValStr = (*Arg)->Arg;\r
+ *Xmits = 0;\r
+\r
+ while (*ValStr != L'\0') {\r
+\r
+ if ((*ValStr <= L'9') && (*ValStr >= L'0')) {\r
+\r
+ *Xmits = (*Xmits * 10) + (*ValStr - L'0');\r
+\r
+ } else {\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ValStr++;\r
+ }\r
+\r
+ *Arg = (*Arg)->Next;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The get current status of all handles.\r
+\r
+ @param[in] ImageHandle The handle of ImageHandle.\r
+ @param[in] IfName The pointer of IfName(interface name).\r
+ @param[in] IfList The pointer of IfList(interface list).\r
+\r
+ @retval EFI_SUCCESS The get status processed successfully.\r
+ @retval others The get status process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6GetInterfaceInfo (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN CHAR16 *IfName,\r
+ IN LIST_ENTRY *IfList\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN HandleIndex;\r
+ UINTN HandleNum;\r
+ EFI_HANDLE *HandleBuffer;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;\r
+ IFCONFIG6_INTERFACE_CB *IfCb;\r
+ UINTN DataSize;\r
+\r
+ HandleBuffer = NULL;\r
+ HandleNum = 0;\r
+\r
+ IfInfo = NULL;\r
+ IfCb = NULL;\r
+\r
+ //\r
+ // Locate all the handles with ip6 service binding protocol.\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ &HandleNum,\r
+ &HandleBuffer\r
+ );\r
+ if (EFI_ERROR (Status) || (HandleNum == 0)) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ //\r
+ // Enumerate all handles that installed with ip6 service binding protocol.\r
+ //\r
+ for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {\r
+ IfCb = NULL;\r
+ IfInfo = NULL;\r
+ DataSize = 0;\r
+\r
+ //\r
+ // Ip6config protocol and ip6 service binding protocol are installed\r
+ // on the same handle.\r
+ //\r
+ ASSERT (HandleBuffer != NULL);\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[HandleIndex],\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ (VOID **) &Ip6Cfg\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // Get the interface information size.\r
+ //\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &DataSize,\r
+ NULL\r
+ );\r
+\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ IfInfo = AllocateZeroPool (DataSize);\r
+\r
+ if (IfInfo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // Get the interface info.\r
+ //\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &DataSize,\r
+ IfInfo\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // Check the interface name if required.\r
+ //\r
+ if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) != 0)) {\r
+ FreePool (IfInfo);\r
+ continue;\r
+ }\r
+\r
+ DataSize = 0;\r
+ //\r
+ // Get the size of dns server list.\r
+ //\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeDnsServer,\r
+ &DataSize,\r
+ NULL\r
+ );\r
+\r
+ if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ IfCb = AllocateZeroPool (sizeof (IFCONFIG6_INTERFACE_CB) + DataSize);\r
+\r
+ if (IfCb == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ IfCb->NicHandle = HandleBuffer[HandleIndex];\r
+ IfCb->IfInfo = IfInfo;\r
+ IfCb->IfCfg = Ip6Cfg;\r
+ IfCb->DnsCnt = (UINT32) (DataSize / sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ //\r
+ // Get the dns server list if has.\r
+ //\r
+ if (DataSize > 0) {\r
+\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeDnsServer,\r
+ &DataSize,\r
+ IfCb->DnsAddr\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+ }\r
+ //\r
+ // Get the interface id if has.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);\r
+ IfCb->IfId = AllocateZeroPool (DataSize);\r
+\r
+ if (IfCb->IfId == NULL) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeAltInterfaceId,\r
+ &DataSize,\r
+ IfCb->IfId\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ if (Status == EFI_NOT_FOUND) {\r
+ FreePool (IfCb->IfId);\r
+ IfCb->IfId = NULL;\r
+ }\r
+ //\r
+ // Get the config policy.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypePolicy,\r
+ &DataSize,\r
+ &IfCb->Policy\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // Get the dad transmits.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+ &DataSize,\r
+ &IfCb->Xmits\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ InsertTailList (IfList, &IfCb->Link);\r
+\r
+ if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) == 0)) {\r
+ //\r
+ // Only need the appointed interface, keep the allocated buffer.\r
+ //\r
+ IfCb = NULL;\r
+ IfInfo = NULL;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (HandleBuffer != NULL) {\r
+ FreePool (HandleBuffer);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ if (IfInfo != NULL) {\r
+ FreePool (IfInfo);\r
+ }\r
+\r
+ if (IfCb != NULL) {\r
+ if (IfCb->IfId != NULL) {\r
+ FreePool (IfCb->IfId);\r
+ }\r
+\r
+ FreePool (IfCb);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The list process of the IfConfig6 application.\r
+\r
+ @param[in] IfList The pointer of IfList(interface list).\r
+\r
+ @retval EFI_SUCCESS The IfConfig6 list processed successfully.\r
+ @retval others The IfConfig6 list process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ShowInterfaceInfo (\r
+ IN LIST_ENTRY *IfList\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Entry;\r
+ IFCONFIG6_INTERFACE_CB *IfCb;\r
+ UINTN Index;\r
+\r
+ Entry = IfList->ForwardLink;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (IsListEmpty (IfList)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);\r
+ }\r
+\r
+ //\r
+ // Go through the interface list.\r
+ //\r
+ while (Entry != IfList) {\r
+\r
+ IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);\r
+\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);\r
+\r
+ //\r
+ // Print interface name.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IF_NAME), mHiiHandle, IfCb->IfInfo->Name);\r
+\r
+ //\r
+ // Print interface config policy.\r
+ //\r
+ if (IfCb->Policy == Ip6ConfigPolicyAutomatic) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_AUTO), mHiiHandle);\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_MAN), mHiiHandle);\r
+ }\r
+\r
+ //\r
+ // Print dad transmit.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DAD_TRANSMITS), mHiiHandle, IfCb->Xmits);\r
+\r
+ //\r
+ // Print interface id if has.\r
+ //\r
+ if (IfCb->IfId != NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_INTERFACE_ID_HEAD), mHiiHandle);\r
+\r
+ IfConfig6PrintMacAddr (\r
+ IfCb->IfId->Id,\r
+ 8\r
+ );\r
+ }\r
+ //\r
+ // Print mac address of the interface.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_HEAD), mHiiHandle);\r
+\r
+ IfConfig6PrintMacAddr (\r
+ IfCb->IfInfo->HwAddress.Addr,\r
+ IfCb->IfInfo->HwAddressSize\r
+ );\r
+\r
+ //\r
+ // Print ip addresses list of the interface.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_HEAD), mHiiHandle);\r
+\r
+ for (Index = 0; Index < IfCb->IfInfo->AddressInfoCount; Index++) {\r
+ IfConfig6PrintIpAddr (\r
+ &IfCb->IfInfo->AddressInfo[Index].Address,\r
+ &IfCb->IfInfo->AddressInfo[Index].PrefixLength\r
+ );\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+ }\r
+\r
+ //\r
+ // Print dns server addresses list of the interface if has.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DNS_ADDR_HEAD), mHiiHandle);\r
+\r
+ for (Index = 0; Index < IfCb->DnsCnt; Index++) {\r
+ IfConfig6PrintIpAddr (\r
+ &IfCb->DnsAddr[Index],\r
+ NULL\r
+ );\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+ }\r
+\r
+ //\r
+ // Print route table of the interface if has.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_ROUTE_HEAD), mHiiHandle);\r
+\r
+ for (Index = 0; Index < IfCb->IfInfo->RouteCount; Index++) {\r
+ IfConfig6PrintIpAddr (\r
+ &IfCb->IfInfo->RouteTable[Index].Destination,\r
+ &IfCb->IfInfo->RouteTable[Index].PrefixLength\r
+ );\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_JOINT), mHiiHandle);\r
+\r
+ IfConfig6PrintIpAddr (\r
+ &IfCb->IfInfo->RouteTable[Index].Gateway,\r
+ NULL\r
+ );\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);\r
+ }\r
+\r
+ Entry = Entry->ForwardLink;\r
+ }\r
+\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The clean process of the IfConfig6 application.\r
+\r
+ @param[in] IfList The pointer of IfList(interface list).\r
+\r
+ @retval EFI_SUCCESS The IfConfig6 clean processed successfully.\r
+ @retval others The IfConfig6 clean process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6ClearInterfaceInfo (\r
+ IN LIST_ENTRY *IfList\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Entry;\r
+ IFCONFIG6_INTERFACE_CB *IfCb;\r
+ EFI_IP6_CONFIG_POLICY Policy;\r
+\r
+ Policy = Ip6ConfigPolicyAutomatic;\r
+ Entry = IfList->ForwardLink;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (IsListEmpty (IfList)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);\r
+ }\r
+\r
+ //\r
+ // Go through the interface list.\r
+ //\r
+ while (Entry != IfList) {\r
+\r
+ IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);\r
+\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypePolicy,\r
+ sizeof (EFI_IP6_CONFIG_POLICY),\r
+ &Policy\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ Entry = Entry->ForwardLink;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The set process of the IfConfig6 application.\r
+\r
+ @param[in] IfList The pointer of IfList(interface list).\r
+ @param[in] VarArg The pointer of ARG_LIST(Args with "-s" option).\r
+\r
+ @retval EFI_SUCCESS The IfConfig6 set processed successfully.\r
+ @retval others The IfConfig6 set process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6SetInterfaceInfo (\r
+ IN LIST_ENTRY *IfList,\r
+ IN ARG_LIST *VarArg\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IFCONFIG6_INTERFACE_CB *IfCb;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *CfgManAddr;\r
+ EFI_IPv6_ADDRESS *CfgAddr;\r
+ UINTN AddrSize;\r
+ EFI_IP6_CONFIG_INTERFACE_ID *InterfaceId;\r
+ UINT32 DadXmits;\r
+ UINT32 CurDadXmits;\r
+ UINTN CurDadXmitsLen;\r
+ EFI_IP6_CONFIG_POLICY Policy;\r
+\r
+ VAR_CHECK_CODE CheckCode;\r
+ EFI_EVENT TimeOutEvt;\r
+ EFI_EVENT MappedEvt;\r
+ BOOLEAN IsAddressOk;\r
+\r
+ UINTN DataSize;\r
+ UINT32 Index;\r
+ UINT32 Index2;\r
+ BOOLEAN IsAddressSet;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;\r
+\r
+ CfgManAddr = NULL;\r
+ CfgAddr = NULL;\r
+ TimeOutEvt = NULL;\r
+ MappedEvt = NULL;\r
+ IfInfo = NULL;\r
+ InterfaceId = NULL;\r
+ CurDadXmits = 0;\r
+\r
+ if (IsListEmpty (IfList)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ //\r
+ // Make sure to set only one interface each time.\r
+ //\r
+ IfCb = BASE_CR (IfList->ForwardLink, IFCONFIG6_INTERFACE_CB, Link);\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Initialize check list mechanism.\r
+ //\r
+ CheckCode = IfConfig6RetriveCheckListByName(\r
+ NULL,\r
+ NULL,\r
+ TRUE\r
+ );\r
+\r
+ //\r
+ // Create events & timers for asynchronous settings.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &TimeOutEvt\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ IfConfig6ManualAddressNotify,\r
+ &IsAddressOk,\r
+ &MappedEvt\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Parse the setting variables.\r
+ //\r
+ while (VarArg != NULL) {\r
+ //\r
+ // Check invalid parameters (duplication & unknown & conflict).\r
+ //\r
+ CheckCode = IfConfig6RetriveCheckListByName(\r
+ mSetCheckList,\r
+ VarArg->Arg,\r
+ FALSE\r
+ );\r
+\r
+ if (VarCheckOk != CheckCode) {\r
+ switch (CheckCode) {\r
+ case VarCheckDuplicate:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_DUPLICATE_COMMAND), mHiiHandle, VarArg->Arg);\r
+ break;\r
+\r
+ case VarCheckConflict:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_CONFLICT_COMMAND), mHiiHandle, VarArg->Arg);\r
+ break;\r
+\r
+ case VarCheckUnknown:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_UNKNOWN_COMMAND), mHiiHandle, VarArg->Arg);\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ VarArg = VarArg->Next;\r
+ continue;\r
+ }\r
+ //\r
+ // Process valid variables.\r
+ //\r
+ if (StrCmp(VarArg->Arg, L"auto") == 0) {\r
+ //\r
+ // Set automaic config policy\r
+ //\r
+ Policy = Ip6ConfigPolicyAutomatic;\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypePolicy,\r
+ sizeof (EFI_IP6_CONFIG_POLICY),\r
+ &Policy\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ VarArg= VarArg->Next;\r
+\r
+ } else if (StrCmp (VarArg->Arg, L"man") == 0) {\r
+ //\r
+ // Set manual config policy.\r
+ //\r
+ Policy = Ip6ConfigPolicyManual;\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypePolicy,\r
+ sizeof (EFI_IP6_CONFIG_POLICY),\r
+ &Policy\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ VarArg= VarArg->Next;\r
+\r
+ } else if (StrCmp (VarArg->Arg, L"host") == 0) {\r
+ //\r
+ // Parse till the next tag or the end of command line.\r
+ //\r
+ VarArg = VarArg->Next;\r
+ Status = IfConfig6ParseManualAddressList (\r
+ &VarArg,\r
+ &CfgManAddr,\r
+ &AddrSize\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"host");\r
+ continue;\r
+ } else {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Set static host ip6 address list.\r
+ // This is a asynchronous process.\r
+ //\r
+ IsAddressOk = FALSE;\r
+\r
+ Status = IfCb->IfCfg->RegisterDataNotify (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ MappedEvt\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ AddrSize,\r
+ CfgManAddr\r
+ );\r
+\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // Get current dad transmits count.\r
+ //\r
+ CurDadXmitsLen = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+ IfCb->IfCfg->GetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+ &CurDadXmitsLen,\r
+ &CurDadXmits\r
+ );\r
+\r
+ gBS->SetTimer (TimeOutEvt, TimerRelative, 50000000 + 10000000 * CurDadXmits);\r
+\r
+ while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
+ if (IsAddressOk) {\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ IfCb->IfCfg->UnregisterDataNotify (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ MappedEvt\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_MAN_HOST), mHiiHandle, Status);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Check whether the address is set successfully.\r
+ //\r
+ DataSize = 0;\r
+\r
+ Status = IfCb->IfCfg->GetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &DataSize,\r
+ NULL\r
+ );\r
+\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ IfInfo = AllocateZeroPool (DataSize);\r
+\r
+ if (IfInfo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = IfCb->IfCfg->GetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &DataSize,\r
+ IfInfo\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ for ( Index = 0; Index < (UINTN) (AddrSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); Index++) {\r
+ IsAddressSet = FALSE;\r
+ //\r
+ // By default, the prefix length 0 is regarded as 64.\r
+ //\r
+ if (CfgManAddr[Index].PrefixLength == 0) {\r
+ CfgManAddr[Index].PrefixLength = 64;\r
+ }\r
+\r
+ for (Index2 = 0; Index2 < IfInfo->AddressInfoCount; Index2++) {\r
+ if (EFI_IP6_EQUAL (&IfInfo->AddressInfo[Index2].Address, &CfgManAddr[Index].Address) &&\r
+ (IfInfo->AddressInfo[Index2].PrefixLength == CfgManAddr[Index].PrefixLength)) {\r
+ IsAddressSet = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!IsAddressSet) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_ADDRESS_FAILED), mHiiHandle);\r
+ IfConfig6PrintIpAddr (\r
+ &CfgManAddr[Index].Address,\r
+ &CfgManAddr[Index].PrefixLength\r
+ );\r
+ }\r
+ }\r
+\r
+ } else if (StrCmp (VarArg->Arg, L"gw") == 0) {\r
+ //\r
+ // Parse till the next tag or the end of command line.\r
+ //\r
+ VarArg = VarArg->Next;\r
+ Status = IfConfig6ParseGwDnsAddressList (\r
+ &VarArg,\r
+ &CfgAddr,\r
+ &AddrSize\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"gw");\r
+ continue;\r
+ } else {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Set static gateway ip6 address list.\r
+ //\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeGateway,\r
+ AddrSize,\r
+ CfgAddr\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ } else if (StrCmp (VarArg->Arg, L"dns") == 0) {\r
+ //\r
+ // Parse till the next tag or the end of command line.\r
+ //\r
+ VarArg = VarArg->Next;\r
+ Status = IfConfig6ParseGwDnsAddressList (\r
+ &VarArg,\r
+ &CfgAddr,\r
+ &AddrSize\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"dns");\r
+ continue;\r
+ } else {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Set static dhs server ip6 address list.\r
+ //\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeDnsServer,\r
+ AddrSize,\r
+ CfgAddr\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ } else if (StrCmp (VarArg->Arg, L"id") == 0) {\r
+ //\r
+ // Parse till the next tag or the end of command line.\r
+ //\r
+ VarArg = VarArg->Next;\r
+ Status = IfConfig6ParseInterfaceId (&VarArg, &InterfaceId);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Set alternative interface id.\r
+ //\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeAltInterfaceId,\r
+ sizeof (EFI_IP6_CONFIG_INTERFACE_ID),\r
+ InterfaceId\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ } else if (StrCmp (VarArg->Arg, L"dad") == 0) {\r
+ //\r
+ // Parse till the next tag or the end of command line.\r
+ //\r
+ VarArg = VarArg->Next;\r
+ Status = IfConfig6ParseDadXmits (&VarArg, &DadXmits);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Set dad transmits count.\r
+ //\r
+ Status = IfCb->IfCfg->SetData (\r
+ IfCb->IfCfg,\r
+ Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+ sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),\r
+ &DadXmits\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ if (CfgManAddr != NULL) {\r
+ FreePool (CfgManAddr);\r
+ }\r
+\r
+ if (CfgAddr != NULL) {\r
+ FreePool (CfgAddr);\r
+ }\r
+\r
+ if (MappedEvt != NULL) {\r
+ gBS->CloseEvent (MappedEvt);\r
+ }\r
+\r
+ if (TimeOutEvt != NULL) {\r
+ gBS->CloseEvent (TimeOutEvt);\r
+ }\r
+\r
+ if (IfInfo != NULL) {\r
+ FreePool (IfInfo);\r
+ }\r
+\r
+ return Status;\r
+\r
+}\r
+\r
+/**\r
+ The IfConfig6 main process.\r
+\r
+ @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA.\r
+\r
+ @retval EFI_SUCCESS IfConfig6 processed successfully.\r
+ @retval others The IfConfig6 process failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IfConfig6 (\r
+ IN IFCONFIG6_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Get configure information of all interfaces.\r
+ //\r
+ Status = IfConfig6GetInterfaceInfo (\r
+ Private->ImageHandle,\r
+ Private->IfName,\r
+ &Private->IfList\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ switch (Private->OpCode) {\r
+ case IfConfig6OpList:\r
+ Status = IfConfig6ShowInterfaceInfo (&Private->IfList);\r
+ break;\r
+\r
+ case IfConfig6OpClear:\r
+ Status = IfConfig6ClearInterfaceInfo (&Private->IfList);\r
+ break;\r
+\r
+ case IfConfig6OpSet:\r
+ Status = IfConfig6SetInterfaceInfo (&Private->IfList, Private->VarArg);\r
+ break;\r
+\r
+ default:\r
+ Status = EFI_ABORTED;\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The IfConfig6 cleanup process, free the allocated memory.\r
+\r
+ @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+IfConfig6Cleanup (\r
+ IN IFCONFIG6_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ IFCONFIG6_INTERFACE_CB *IfCb;\r
+ ARG_LIST *ArgNode;\r
+ ARG_LIST *ArgHead;\r
+\r
+ ASSERT (Private != NULL);\r
+\r
+ //\r
+ // Clean the list which save the set config Args.\r
+ //\r
+ if (Private->VarArg != NULL) {\r
+ ArgHead = Private->VarArg;\r
+\r
+ while (ArgHead->Next != NULL) {\r
+ ArgNode = ArgHead->Next;\r
+ FreePool (ArgHead);\r
+ ArgHead = ArgNode;\r
+ }\r
+\r
+ FreePool (ArgHead);\r
+ }\r
+\r
+ if (Private->IfName != NULL)\r
+ FreePool (Private->IfName);\r
+\r
+\r
+ //\r
+ // Clean the IFCONFIG6_INTERFACE_CB list.\r
+ //\r
+ Entry = Private->IfList.ForwardLink;\r
+ NextEntry = Entry->ForwardLink;\r
+\r
+ while (Entry != &Private->IfList) {\r
+\r
+ IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);\r
+\r
+ RemoveEntryList (&IfCb->Link);\r
+\r
+ if (IfCb->IfId != NULL) {\r
+\r
+ FreePool (IfCb->IfId);\r
+ }\r
+\r
+ if (IfCb->IfInfo != NULL) {\r
+\r
+ FreePool (IfCb->IfInfo);\r
+ }\r
+\r
+ FreePool (IfCb);\r
+\r
+ Entry = NextEntry;\r
+ NextEntry = Entry->ForwardLink;\r
+ }\r
+\r
+ FreePool (Private);\r
+}\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+ both device drivers and bus drivers.\r
+\r
+ The entry point for the IfConfig6 application which parses the command line input and calls the IfConfig6 process.\r
+\r
+ @param[in] ImageHandle The image handle of this application.\r
+ @param[in] SystemTable The pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval Others Some errors occur.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IfConfig6Initialize (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IFCONFIG6_PRIVATE_DATA *Private;\r
+ LIST_ENTRY *ParamPackage;\r
+ CONST CHAR16 *ValueStr;\r
+ ARG_LIST *ArgList;\r
+ CHAR16 *ProblemParam;\r
+ CHAR16 *Str;\r
+\r
+ Private = NULL;\r
+\r
+ //\r
+ // Register our string package with HII and return the handle to it.\r
+ //\r
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IfConfig6Strings, NULL);\r
+ ASSERT (mHiiHandle != NULL);\r
+\r
+ Status = ShellCommandLineParseEx (mIfConfig6CheckList, &ParamPackage, &ProblemParam, TRUE, FALSE);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_COMMAND), mHiiHandle, ProblemParam);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // To handle no option.\r
+ //\r
+ if (!ShellCommandLineGetFlag (ParamPackage, L"-r") && !ShellCommandLineGetFlag (ParamPackage, L"-s") &&\r
+ !ShellCommandLineGetFlag (ParamPackage, L"-?") && !ShellCommandLineGetFlag (ParamPackage, L"-l")) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_LACK_OPTION), mHiiHandle);\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // To handle conflict options.\r
+ //\r
+ if (((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-s"))) ||\r
+ ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||\r
+ ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||\r
+ ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||\r
+ ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||\r
+ ((ShellCommandLineGetFlag (ParamPackage, L"-l")) && (ShellCommandLineGetFlag (ParamPackage, L"-?")))) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_CONFLICT_OPTIONS), mHiiHandle);\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // To show the help information of ifconfig6 command.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_HELP), mHiiHandle);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ Private = AllocateZeroPool (sizeof (IFCONFIG6_PRIVATE_DATA));\r
+\r
+ if (Private == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ InitializeListHead (&Private->IfList);\r
+\r
+ //\r
+ // To get interface name for the list option.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {\r
+ Private->OpCode = IfConfig6OpList;\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");\r
+ if (ValueStr != NULL) {\r
+ Str = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));\r
+ ASSERT (Str != NULL);\r
+\r
+ Str = StrCpy (Str, ValueStr);\r
+ Private->IfName = Str;\r
+ }\r
+ }\r
+ //\r
+ // To get interface name for the clear option.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-r")) {\r
+ Private->OpCode = IfConfig6OpClear;\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-r");\r
+ if (ValueStr != NULL) {\r
+ Str = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));\r
+ ASSERT (Str != NULL);\r
+\r
+ Str = StrCpy (Str, ValueStr);\r
+ Private->IfName = Str;\r
+ }\r
+ }\r
+ //\r
+ // To get interface name and corresponding Args for the set option.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-s")) {\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");\r
+ if (ValueStr == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_INTERFACE), mHiiHandle);\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // To split the configuration into multi-section.\r
+ //\r
+ ArgList = SplitStrToList (ValueStr, L' ');\r
+ ASSERT (ArgList != NULL);\r
+\r
+ Private->OpCode = IfConfig6OpSet;\r
+ Private->IfName = ArgList->Arg;\r
+\r
+ Private->VarArg = ArgList->Next;\r
+\r
+ if (Private->IfName == NULL || Private->VarArg == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_COMMAND), mHiiHandle);\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Main process of ifconfig6.\r
+ //\r
+ Status = IfConfig6 (Private);\r
+\r
+ON_EXIT:\r
+\r
+ ShellCommandLineFreeVarList (ParamPackage);\r
+ HiiRemovePackages (mHiiHandle);\r
+ if (Private != NULL)\r
+ IfConfig6Cleanup (Private);\r
+\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ The interface function declaration of shell application IfConfig6.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _IFCONFIG6_H_\r
+#define _IFCONFIG6_H_\r
+\r
+#define EFI_IFCONFIG6_GUID \\r
+ { \\r
+ 0xbab7296b, 0x222c, 0x4408, {0x9e, 0x6c, 0xc2, 0x5c, 0x18, 0x7e, 0xff, 0x33} \\r
+ }\r
+\r
+enum {\r
+ IfConfig6OpList = 1,\r
+ IfConfig6OpSet = 2,\r
+ IfConfig6OpClear = 3\r
+};\r
+\r
+typedef enum {\r
+ VarCheckReserved = -1,\r
+ VarCheckOk = 0,\r
+ VarCheckDuplicate,\r
+ VarCheckConflict,\r
+ VarCheckUnknown,\r
+ VarCheckLackValue,\r
+ VarCheckOutOfMem\r
+} VAR_CHECK_CODE;\r
+\r
+typedef enum {\r
+ FlagTypeSingle = 0,\r
+ FlagTypeNeedVar,\r
+ FlagTypeNeedSet,\r
+ FlagTypeSkipUnknown\r
+} VAR_CHECK_FLAG_TYPE;\r
+\r
+#define MACADDRMAXSIZE 32\r
+#define PREFIXMAXLEN 16 \r
+\r
+typedef struct _IFCONFIG6_INTERFACE_CB {\r
+ EFI_HANDLE NicHandle;\r
+ LIST_ENTRY Link;\r
+ EFI_IP6_CONFIG_PROTOCOL *IfCfg;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; \r
+ EFI_IP6_CONFIG_INTERFACE_ID *IfId;\r
+ EFI_IP6_CONFIG_POLICY Policy;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS Xmits;\r
+ UINT32 DnsCnt;\r
+ EFI_IPv6_ADDRESS DnsAddr[1];\r
+} IFCONFIG6_INTERFACE_CB;\r
+\r
+typedef struct _ARG_LIST ARG_LIST;\r
+\r
+struct _ARG_LIST {\r
+ ARG_LIST *Next;\r
+ CHAR16 *Arg;\r
+};\r
+\r
+typedef struct _IFCONFIG6_PRIVATE_DATA {\r
+ EFI_HANDLE ImageHandle;\r
+ LIST_ENTRY IfList;\r
+\r
+ UINT32 OpCode;\r
+ CHAR16 *IfName;\r
+ ARG_LIST *VarArg;\r
+} IFCONFIG6_PRIVATE_DATA;\r
+\r
+typedef struct _VAR_CHECK_ITEM{\r
+ CHAR16 *FlagStr;\r
+ UINT32 FlagID;\r
+ UINT32 ConflictMask;\r
+ VAR_CHECK_FLAG_TYPE FlagType;\r
+} VAR_CHECK_ITEM;\r
+#endif\r
--- /dev/null
+## @file\r
+# Component description file for Shell application IfConfig6.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010006\r
+ BASE_NAME = IfConfig6\r
+ FILE_GUID = 6F71926E-60CE-428d-AA58-A3D9FB879429\r
+ MODULE_TYPE = UEFI_APPLICATION\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = IfConfig6Initialize\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF\r
+#\r
+[Sources]\r
+ IfConfig6Strings.uni\r
+ IfConfig6.c\r
+ IfConfig6.h\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+ ShellPkg/ShellPkg.dec\r
+ \r
+[LibraryClasses]\r
+ BaseLib\r
+ UefiBootServicesTableLib\r
+ UefiApplicationEntryPoint\r
+ BaseMemoryLib\r
+ ShellLib\r
+ MemoryAllocationLib\r
+ DebugLib\r
+ HiiLib\r
+ NetLib\r
+\r
+[Protocols]\r
+ gEfiIp6ServiceBindingProtocolGuid ## CONSUMS\r
+ gEfiIp6ConfigProtocolGuid ## CONSUMS\r
--- /dev/null
+/** @file\r
+ The implementation of delete policy entry function in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Delete.h"\r
+#include "Match.h"\r
+#include "ForEach.h"\r
+\r
+/**\r
+ Private function to delete entry information in database.\r
+\r
+ @param[in] Selector The pointer to EFI_IPSEC_CONFIG_SELECTOR structure.\r
+ @param[in] Data The pointer to Data.\r
+ @param[in] Context The pointer to DELETE_POLICY_ENTRY_CONTEXT.\r
+\r
+ @retval EFI_ABORTED Abort the iteration.\r
+ @retval EFI_SUCCESS Continue the iteration.\r
+**/\r
+EFI_STATUS\r
+DeletePolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN DELETE_POLICY_ENTRY_CONTEXT *Context\r
+ )\r
+{\r
+ if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {\r
+ Context->Status = mIpSecConfig->SetData (\r
+ mIpSecConfig,\r
+ Context->DataType,\r
+ Selector,\r
+ NULL,\r
+ NULL\r
+ );\r
+ //\r
+ // Abort the iteration after the insertion.\r
+ //\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Flush or delete entry information in the database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Delete entry information successfully.\r
+ @retval EFI_NOT_FOUND Can't find the specified entry.\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+FlushOrDeletePolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ DELETE_POLICY_ENTRY_CONTEXT Context;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ //\r
+ // If user wants to remove all.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {\r
+ Status = mIpSecConfig->SetData (\r
+ mIpSecConfig,\r
+ DataType,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+ );\r
+ } else {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+ if (ValueStr == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);\r
+ if (!EFI_ERROR (Status)) {\r
+ Context.DataType = DataType;\r
+ Context.Status = EFI_NOT_FOUND;\r
+ ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) DeletePolicyEntry, &Context);\r
+ Status = Context.Status;\r
+\r
+ if (Status == EFI_NOT_FOUND) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);\r
+ } else if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DELETE_FAILED), mHiiHandle, mAppName);\r
+ }\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+ The internal structure and function declaration of delete policy entry function\r
+ in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __DELETE_H_\r
+#define __DELETE_H_\r
+\r
+typedef struct {\r
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;\r
+ POLICY_ENTRY_INDEXER Indexer;\r
+ EFI_STATUS Status; //Indicate whether deletion succeeds.\r
+} DELETE_POLICY_ENTRY_CONTEXT;\r
+\r
+/**\r
+ Flush or delete entry information in the database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Delete entry information successfully.\r
+ @retval EFI_NOT_FOUND Can't find the specified entry.\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+FlushOrDeletePolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The implementation of dump policy entry function in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Dump.h"\r
+#include "ForEach.h"\r
+#include "Helper.h"\r
+\r
+/**\r
+ Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure.\r
+\r
+ @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure.\r
+\r
+ @return the value of version.\r
+**/\r
+UINTN\r
+GetVerFromAddrInfo (\r
+ IN EFI_IP_ADDRESS_INFO *AddressInfo\r
+)\r
+{\r
+ if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) &&\r
+ (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) {\r
+ return IP_VERSION_4;\r
+ } else {\r
+ return IP_VERSION_6;\r
+ }\r
+}\r
+\r
+/**\r
+ Private function called to get the version information from a EFI_IP_ADDRESS structure.\r
+\r
+ @param[in] Address The pointer to the EFI_IP_ADDRESS structure.\r
+\r
+ @return The value of the version.\r
+**/\r
+UINTN\r
+GetVerFromIpAddr (\r
+ IN EFI_IP_ADDRESS *Address\r
+)\r
+{\r
+ if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) {\r
+ return IP_VERSION_4;\r
+ } else {\r
+ return IP_VERSION_6;\r
+ }\r
+}\r
+\r
+/**\r
+ Private function called to print an ASCII string in unicode char format.\r
+\r
+ @param[in] Str The pointer to the ASCII string.\r
+ @param[in] Length The value of the ASCII string length.\r
+**/\r
+VOID\r
+DumpAsciiString (\r
+ IN CHAR8 *Str,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Index < Length; Index++) {\r
+ Print (L"%c", (CHAR16) Str[Index]);\r
+ }\r
+}\r
+\r
+/**\r
+ Private function called to print EFI_IP_ADDRESS_INFO content.\r
+\r
+ @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure.\r
+**/\r
+VOID\r
+DumpAddressInfo (\r
+ IN EFI_IP_ADDRESS_INFO *AddressInfo\r
+ )\r
+{\r
+ if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) {\r
+ Print (\r
+ L"%d.%d.%d.%d",\r
+ (UINTN) AddressInfo->Address.v4.Addr[0],\r
+ (UINTN) AddressInfo->Address.v4.Addr[1],\r
+ (UINTN) AddressInfo->Address.v4.Addr[2],\r
+ (UINTN) AddressInfo->Address.v4.Addr[3]\r
+ );\r
+ if (AddressInfo->PrefixLength != 32) {\r
+ Print (L"/%d", (UINTN) AddressInfo->PrefixLength);\r
+ }\r
+ }\r
+\r
+ if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) {\r
+ Print (\r
+ L"%x:%x:%x:%x:%x:%x:%x:%x",\r
+ (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]),\r
+ (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]),\r
+ (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]),\r
+ (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]),\r
+ (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]),\r
+ (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]),\r
+ (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]),\r
+ (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15])\r
+ );\r
+ if (AddressInfo->PrefixLength != 128) {\r
+ Print (L"/%d", AddressInfo->PrefixLength);\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Private function called to print EFI_IP_ADDRESS content.\r
+\r
+ @param[in] IpAddress The pointer to the EFI_IP_ADDRESS structure.\r
+**/\r
+VOID\r
+DumpIpAddress (\r
+ IN EFI_IP_ADDRESS *IpAddress\r
+ )\r
+{\r
+ if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) {\r
+ Print (\r
+ L"%d.%d.%d.%d",\r
+ (UINTN) IpAddress->v4.Addr[0],\r
+ (UINTN) IpAddress->v4.Addr[1],\r
+ (UINTN) IpAddress->v4.Addr[2],\r
+ (UINTN) IpAddress->v4.Addr[3]\r
+ );\r
+ }\r
+\r
+ if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) {\r
+ Print (\r
+ L"%x:%x:%x:%x:%x:%x:%x:%x",\r
+ (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]),\r
+ (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]),\r
+ (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]),\r
+ (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]),\r
+ (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]),\r
+ (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]),\r
+ (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]),\r
+ (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15])\r
+ );\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Private function called to print EFI_IPSEC_SPD_SELECTOR content.\r
+\r
+ @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+**/\r
+VOID\r
+DumpSpdSelector (\r
+ IN EFI_IPSEC_SPD_SELECTOR *Selector\r
+ )\r
+{\r
+ UINT32 Index;\r
+ CHAR16 *Str;\r
+\r
+ for (Index = 0; Index < Selector->LocalAddressCount; Index++) {\r
+ if (Index > 0) {\r
+ Print (L",");\r
+ }\r
+\r
+ DumpAddressInfo (&Selector->LocalAddress[Index]);\r
+ }\r
+\r
+ if (Index == 0) {\r
+ Print (L"localhost");\r
+ }\r
+\r
+ Print (L" -> ");\r
+\r
+ for (Index = 0; Index < Selector->RemoteAddressCount; Index++) {\r
+ if (Index > 0) {\r
+ Print (L",");\r
+ }\r
+\r
+ DumpAddressInfo (&Selector->RemoteAddress[Index]);\r
+ }\r
+\r
+ Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol);\r
+ if (Str != NULL) {\r
+ Print (L" %s", Str);\r
+ } else {\r
+ Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol);\r
+ }\r
+\r
+ if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) {\r
+ Print (L" port:");\r
+ if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) {\r
+ Print (L"%d", Selector->LocalPort);\r
+ if (Selector->LocalPortRange != 0) {\r
+ Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange);\r
+ }\r
+ } else {\r
+ Print (L"any");\r
+ }\r
+\r
+ Print (L" -> ");\r
+ if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) {\r
+ Print (L"%d", Selector->RemotePort);\r
+ if (Selector->RemotePortRange != 0) {\r
+ Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange);\r
+ }\r
+ } else {\r
+ Print (L"any");\r
+ }\r
+ } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) {\r
+ Print (L" class/code:");\r
+ if (Selector->LocalPort != 0) {\r
+ Print (L"%d", (UINTN) (UINT8) Selector->LocalPort);\r
+ } else {\r
+ Print (L"any");\r
+ }\r
+\r
+ Print (L"/");\r
+ if (Selector->RemotePort != 0) {\r
+ Print (L"%d", (UINTN) (UINT8) Selector->RemotePort);\r
+ } else {\r
+ Print (L"any");\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content.\r
+\r
+ @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+ @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+ @param[in] EntryIndex The pointer to the Index in SPD Database.\r
+\r
+ @retval EFI_SUCCESS Dump SPD information successfully.\r
+**/\r
+EFI_STATUS\r
+DumpSpdEntry (\r
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,\r
+ IN EFI_IPSEC_SPD_DATA *Data,\r
+ IN UINTN *EntryIndex\r
+ )\r
+{\r
+ BOOLEAN HasPre;\r
+ CHAR16 DataName[128];\r
+ CHAR16 *String1;\r
+ CHAR16 *String2;\r
+ CHAR16 *String3;\r
+ UINT8 Index;\r
+\r
+ Print (L"%d.", (*EntryIndex)++);\r
+\r
+ //\r
+ // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400\r
+ // Protect PF:0x34323423 Name:First Entry\r
+ // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600]\r
+ // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set]\r
+ //\r
+\r
+ DumpSpdSelector (Selector);\r
+ Print (L"\n ");\r
+\r
+ Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction));\r
+ Print (L"PF:%08x ", Data->PackageFlag);\r
+\r
+ Index = 0;\r
+ while (Data->Name[Index] != 0) {\r
+ DataName[Index] = (CHAR16) Data->Name[Index];\r
+ Index++;\r
+ ASSERT (Index < 128);\r
+ }\r
+ DataName[Index] = L'\0';\r
+\r
+ Print (L"Name:%s", DataName);\r
+\r
+ if (Data->Action == EfiIPsecActionProtect) {\r
+ Print (L"\n ");\r
+ if (Data->ProcessingPolicy->ExtSeqNum) {\r
+ Print (L"ext-sequence ");\r
+ }\r
+\r
+ if (Data->ProcessingPolicy->SeqOverflow) {\r
+ Print (L"sequence-overflow ");\r
+ }\r
+\r
+ if (Data->ProcessingPolicy->FragCheck) {\r
+ Print (L"fragment-check ");\r
+ }\r
+\r
+ HasPre = FALSE;\r
+ if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) {\r
+ Print (HasPre ? L"," : L"life:[");\r
+ Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount);\r
+ HasPre = TRUE;\r
+ }\r
+\r
+ if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) {\r
+ Print (HasPre ? L"," : L"life:[");\r
+ Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime);\r
+ HasPre = TRUE;\r
+ }\r
+\r
+ if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) {\r
+ Print (HasPre ? L"," : L"life:[");\r
+ Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime);\r
+ HasPre = TRUE;\r
+ }\r
+\r
+ if (HasPre) {\r
+ Print (L"]");\r
+ }\r
+\r
+ if (HasPre || Data->ProcessingPolicy->ExtSeqNum ||\r
+ Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) {\r
+ Print (L"\n ");\r
+ }\r
+\r
+ String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol);\r
+ String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo);\r
+ String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo);\r
+ Print (\r
+ L"%s Auth:%s Encrypt:%s ",\r
+ String1,\r
+ String2,\r
+ String3\r
+ );\r
+\r
+ Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode));\r
+ if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
+ Print (L"[");\r
+ DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress);\r
+ Print (L" -> ");\r
+ DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);\r
+ Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption));\r
+ }\r
+ }\r
+\r
+ Print (L"\n");\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA content.\r
+\r
+ @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure.\r
+ @param[in] Data The pointer to the EFI_IPSEC_SA_DATA structure.\r
+ @param[in] EntryIndex The pointer to the Index in the SAD Database.\r
+\r
+ @retval EFI_SUCCESS Dump SAD information successfully.\r
+**/\r
+EFI_STATUS\r
+DumpSadEntry (\r
+ IN EFI_IPSEC_SA_ID *SaId,\r
+ IN EFI_IPSEC_SA_DATA *Data,\r
+ IN UINTN *EntryIndex\r
+ )\r
+{\r
+ BOOLEAN HasPre;\r
+ CHAR16 *String1;\r
+ CHAR16 *String2;\r
+\r
+ //\r
+ // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx\r
+ // Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34\r
+ // Auth:xxxx/password Encrypt:yyyy/password\r
+ // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400\r
+ //\r
+\r
+ Print (L"%d.", (*EntryIndex)++);\r
+ Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol));\r
+ Print (L"Destination:");\r
+ DumpIpAddress (&SaId->DestAddress);\r
+ Print (L"\n");\r
+\r
+ Print (\r
+ L" Mode:%s SeqNum:%lx AntiReplayWin:%d ",\r
+ MapIntegerToString (Data->Mode, mMapIpSecMode),\r
+ Data->SNCount,\r
+ (UINTN) Data->AntiReplayWindows\r
+ );\r
+\r
+ HasPre = FALSE;\r
+ if (Data->SaLifetime.ByteCount != 0) {\r
+ Print (HasPre ? L"," : L"life:[");\r
+ Print (L"%lxB", Data->SaLifetime.ByteCount);\r
+ HasPre = TRUE;\r
+ }\r
+\r
+ if (Data->SaLifetime.SoftLifetime != 0) {\r
+ Print (HasPre ? L"," : L"life:[");\r
+ Print (L"%lxs", Data->SaLifetime.SoftLifetime);\r
+ HasPre = TRUE;\r
+ }\r
+\r
+ if (Data->SaLifetime.HardLifetime != 0) {\r
+ Print (HasPre ? L"," : L"life:[");\r
+ Print (L"%lxS", Data->SaLifetime.HardLifetime);\r
+ HasPre = TRUE;\r
+ }\r
+\r
+ if (HasPre) {\r
+ Print (L"] ");\r
+ }\r
+\r
+ Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU);\r
+\r
+ if (SaId->Proto == EfiIPsecAH) {\r
+ Print (\r
+ L" Auth:%s/%s\n",\r
+ MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo),\r
+ Data->AlgoInfo.AhAlgoInfo.AuthKey\r
+ );\r
+ } else {\r
+ String1 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo);\r
+ String2 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo);\r
+ Print (\r
+ L" Auth:%s/%s Encrypt:%s/%s\n",\r
+ String1,\r
+ Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+ String2,\r
+ Data->AlgoInfo.EspAlgoInfo.EncKey\r
+ );\r
+ }\r
+\r
+ if (Data->SpdSelector != NULL) {\r
+ Print (L" ");\r
+ DumpSpdSelector (Data->SpdSelector);\r
+ Print (L"\n");\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content.\r
+\r
+ @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure.\r
+ @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+ @param[in] EntryIndex The pointer to the Index in the PAD Database.\r
+\r
+ @retval EFI_SUCCESS Dump PAD information successfully.\r
+**/\r
+EFI_STATUS\r
+DumpPadEntry (\r
+ IN EFI_IPSEC_PAD_ID *PadId,\r
+ IN EFI_IPSEC_PAD_DATA *Data,\r
+ IN UINTN *EntryIndex\r
+ )\r
+{\r
+ CHAR16 *String1;\r
+ CHAR16 *String2;\r
+\r
+ //\r
+ // ADDR:10.23.17.34/15\r
+ // IDEv1 PreSharedSecret IKE-ID\r
+ // password\r
+ //\r
+\r
+ Print (L"%d.", (*EntryIndex)++);\r
+\r
+ if (PadId->PeerIdValid) {\r
+ Print (L"ID:%s", PadId->Id.PeerId);\r
+ } else {\r
+ Print (L"ADDR:");\r
+ DumpAddressInfo (&PadId->Id.IpAddress);\r
+ }\r
+\r
+ Print (L"\n");\r
+\r
+ String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto);\r
+ String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod);\r
+ Print (\r
+ L" %s %s",\r
+ String1,\r
+ String2\r
+ );\r
+\r
+ if (Data->IkeIdFlag) {\r
+ Print (L"IKE-ID");\r
+ }\r
+\r
+ Print (L"\n");\r
+\r
+ if (Data->AuthData != NULL) {\r
+ DumpAsciiString (Data->AuthData, Data->AuthDataSize);\r
+ Print (L"\n");\r
+ }\r
+\r
+ if (Data->RevocationData != NULL) {\r
+ Print (L" %s\n", Data->RevocationData);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+}\r
+\r
+VISIT_POLICY_ENTRY mDumpPolicyEntry[] = {\r
+ (VISIT_POLICY_ENTRY) DumpSpdEntry,\r
+ (VISIT_POLICY_ENTRY) DumpSadEntry,\r
+ (VISIT_POLICY_ENTRY) DumpPadEntry\r
+};\r
+\r
+/**\r
+ Print all entry information in the database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Dump all information successfully.\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+ListPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ )\r
+{\r
+ UINTN EntryIndex;\r
+\r
+ EntryIndex = 0;\r
+ return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex);\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ The function declaration of dump policy entry function in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _DUMP_H_\r
+#define _DUMP_H_\r
+\r
+/**\r
+ Print all entry information in the database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Dump all information successfully.\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+ListPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The implementation to go through each entry in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "ForEach.h"\r
+\r
+\r
+/**\r
+ Enumerate all entries in the database to execute specified operations according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] Routine The pointer to the function of a specified operation.\r
+ @param[in] Context The pointer to the context of a function.\r
+\r
+ @retval EFI_SUCCESS Execute specified operation successfully.\r
+**/\r
+EFI_STATUS\r
+ForeachPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN VISIT_POLICY_ENTRY Routine,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS GetNextStatus;\r
+ EFI_STATUS GetDataStatus;\r
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;\r
+ VOID *Data;\r
+ UINTN SelectorSize;\r
+ UINTN DataSize;\r
+ BOOLEAN FirstGetNext;\r
+\r
+ FirstGetNext = TRUE;\r
+ SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR);\r
+ Selector = AllocateZeroPool (SelectorSize);\r
+\r
+ DataSize = 0;\r
+ Data = NULL;\r
+\r
+ while (TRUE) {\r
+ GetNextStatus = mIpSecConfig->GetNextSelector (\r
+ mIpSecConfig,\r
+ DataType,\r
+ &SelectorSize,\r
+ Selector\r
+ );\r
+ if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {\r
+ gBS->FreePool (Selector);\r
+ Selector = FirstGetNext ? AllocateZeroPool (SelectorSize) : AllocatePool (SelectorSize);\r
+\r
+ GetNextStatus = mIpSecConfig->GetNextSelector (\r
+ mIpSecConfig,\r
+ DataType,\r
+ &SelectorSize,\r
+ Selector\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (GetNextStatus)) {\r
+ break;\r
+ }\r
+\r
+ FirstGetNext = FALSE;\r
+\r
+ GetDataStatus = mIpSecConfig->GetData (\r
+ mIpSecConfig,\r
+ DataType,\r
+ Selector,\r
+ &DataSize,\r
+ Data\r
+ );\r
+ if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {\r
+ if (Data != NULL) {\r
+ gBS->FreePool (Data);\r
+ }\r
+\r
+ Data = AllocateZeroPool (DataSize);\r
+ GetDataStatus = mIpSecConfig->GetData (\r
+ mIpSecConfig,\r
+ DataType,\r
+ Selector,\r
+ &DataSize,\r
+ Data\r
+ );\r
+ }\r
+\r
+ ASSERT_EFI_ERROR (GetDataStatus);\r
+\r
+ if (EFI_ERROR (Routine (Selector, Data, Context))) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Data != NULL) {\r
+ gBS->FreePool (Data);\r
+ }\r
+\r
+ if (Selector != NULL) {\r
+ gBS->FreePool (Selector);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ The internal structure and function declaration of the implementation\r
+ to go through each entry in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _FOREACH_H_\r
+#define _FOREACH_H_\r
+\r
+/**\r
+ The prototype for the DumpSpdEntry()/DumpSadEntry()/DumpPadEntry().\r
+ Print EFI_IPSEC_CONFIG_SELECTOR and corresponding content.\r
+\r
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.\r
+ @param[in] Data The pointer to the corresponding data.\r
+ @param[in] Context The pointer to the Index in SPD/SAD/PAD Database.\r
+\r
+ @retval EFI_SUCCESS Dump SPD/SAD/PAD information successfully.\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*VISIT_POLICY_ENTRY) (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Enumerate all entry in the database to execute a specified operation according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] Routine The pointer to function of a specified operation.\r
+ @param[in] Context The pointer to the context of a function.\r
+\r
+ @retval EFI_SUCCESS Execute specified operation successfully.\r
+**/\r
+EFI_STATUS\r
+ForeachPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN VISIT_POLICY_ENTRY Routine,\r
+ IN VOID *Context\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The assistant function implementation for IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Helper.h"\r
+\r
+/**\r
+ Helper function called to change an input parameter in the string format to a number.\r
+\r
+ @param[in] FlagStr The pointer to the flag string.\r
+ @param[in] Maximum Greatest value number.\r
+ @param[in, out] ValuePtr The pointer to the input parameter in string format.\r
+ @param[in] ByteCount The valid byte count\r
+ @param[in] Map The pointer to the STR2INT table.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+ @param[in] FormatMask The bit mask.\r
+ BIT 0 set indicates the value of a flag might be a number.\r
+ BIT 1 set indicates the value of a flag might be a string that needs to be looked up.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_FOUND The input parameter can't be found.\r
+ @retval EFI_INVALID_PARAMETER The input parameter is an invalid input.\r
+**/\r
+EFI_STATUS\r
+GetNumber (\r
+ IN CHAR16 *FlagStr,\r
+ IN UINT64 Maximum,\r
+ IN OUT VOID *ValuePtr,\r
+ IN UINTN ByteCount,\r
+ IN STR2INT *Map,\r
+ IN LIST_ENTRY *ParamPackage,\r
+ IN UINT32 FormatMask\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 Value64;\r
+ BOOLEAN Converted;\r
+ UINTN Index;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ ASSERT (FormatMask & (FORMAT_NUMBER | FORMAT_STRING));\r
+\r
+ Converted = FALSE;\r
+ Value64 = 0;\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, FlagStr);\r
+\r
+ if (ValueStr == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ } else {\r
+ //\r
+ // Try to convert to integer directly if MaybeNumber is TRUE.\r
+ //\r
+ if ((FormatMask & FORMAT_NUMBER) != 0) {\r
+ Value64 = StrToUInteger (ValueStr, &Status);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Convert successfully.\r
+ //\r
+ if (Value64 > Maximum) {\r
+ //\r
+ // But the result is invalid\r
+ //\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ FlagStr,\r
+ ValueStr\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Converted = TRUE;\r
+ }\r
+ }\r
+\r
+ if (!Converted && ((FormatMask & FORMAT_STRING) != 0)) {\r
+ //\r
+ // Convert falied, so use String->Integer map.\r
+ //\r
+ Value64 = MapStringToInteger (ValueStr, Map);\r
+ if (Value64 == (UINT32) -1) {\r
+ //\r
+ // Cannot find the string in the map.\r
+ //\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ FlagStr,\r
+ ValueStr\r
+ );\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ACCEPT_PARAMETERS), mHiiHandle);\r
+ for (Index = 0; Map[Index].String != NULL; Index++) {\r
+ Print (L" %s", Map[Index].String);\r
+ }\r
+\r
+ Print (L"\n");\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ CopyMem (ValuePtr, &Value64, ByteCount);\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ Helper function called to convert a string containing an Ipv4 or Ipv6 Internet Protocol address\r
+ into a proper address for the EFI_IP_ADDRESS structure.\r
+\r
+ @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.\r
+ @param[out] Ip The pointer to the EFI_IP_ADDRESS structure to contain the result.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddr2 (\r
+ IN CHAR16 *Ptr,\r
+ OUT EFI_IP_ADDRESS *Ip\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if ((Ptr == NULL) || (Ip == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Parse the input address as Ipv4 Address first.\r
+ //\r
+ Status = NetLibStrToIp4 (Ptr, &Ip->v4);\r
+ if (!EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = NetLibStrToIp6 (Ptr, &Ip->v6);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Helper function called to calculate the prefix length associated with the string\r
+ containing an Ipv4 or Ipv6 Internet Protocol address.\r
+\r
+ @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.\r
+ @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval Others Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddrRange (\r
+ IN CHAR16 *Ptr,\r
+ OUT EFI_IP_ADDRESS_INFO *Addr\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if ((Ptr == NULL) || (Addr == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = NetLibStrToIp4 (Ptr, &Addr->Address.v4);\r
+ if (!EFI_ERROR (Status)) {\r
+ if ((UINT32)(*Addr->Address.v4.Addr) == 0) {\r
+ Addr->PrefixLength = 0;\r
+ } else {\r
+ Addr->PrefixLength = 32;\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ Status = NetLibStrToIp6andPrefix (Ptr, &Addr->Address.v6, &Addr->PrefixLength);\r
+ if (!EFI_ERROR (Status) && (Addr->PrefixLength == 0xFF)) {\r
+ Addr->PrefixLength = 128;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Helper function called to calculate the port range associated with the string.\r
+\r
+ @param[in] Ptr The pointer to the string containing a port and range.\r
+ @param[out] Port The pointer to the Port to contain the result.\r
+ @param[out] PortRange The pointer to the PortRange to contain the result.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval Others Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetPortRange (\r
+ IN CHAR16 *Ptr,\r
+ OUT UINT16 *Port,\r
+ OUT UINT16 *PortRange\r
+ )\r
+{\r
+ CHAR16 *BreakPtr;\r
+ CHAR16 Ch;\r
+ EFI_STATUS Status;\r
+\r
+ for (BreakPtr = Ptr; (*BreakPtr != L'\0') && (*BreakPtr != L':'); BreakPtr++) {\r
+ ;\r
+ }\r
+\r
+ Ch = *BreakPtr;\r
+ *BreakPtr = L'\0';\r
+ *Port = (UINT16) StrToUInteger (Ptr, &Status);\r
+ *BreakPtr = Ch;\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ *PortRange = 0;\r
+ if (*BreakPtr == L':') {\r
+ BreakPtr++;\r
+ *PortRange = (UINT16) StrToUInteger (BreakPtr, &Status);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (*PortRange < *Port) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *PortRange = (UINT16) (*PortRange - *Port);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Helper function called to transfer a string to an unsigned integer.\r
+\r
+ @param[in] Str The pointer to the string.\r
+ @param[out] Status The operation status.\r
+\r
+ @return The integer value of converted Str.\r
+**/\r
+UINT64\r
+StrToUInteger (\r
+ IN CONST CHAR16 *Str,\r
+ OUT EFI_STATUS *Status\r
+ )\r
+{\r
+ UINT64 Value;\r
+ UINT64 NewValue;\r
+ CHAR16 *StrTail;\r
+ CHAR16 Char;\r
+ UINTN Base;\r
+ UINTN Len;\r
+\r
+ Base = 10;\r
+ Value = 0;\r
+ *Status = EFI_ABORTED;\r
+\r
+ //\r
+ // Skip leading white space.\r
+ //\r
+ while ((*Str != 0) && (*Str == ' ')) {\r
+ Str++;\r
+ }\r
+ //\r
+ // For NULL Str, just return.\r
+ //\r
+ if (*Str == 0) {\r
+ return 0;\r
+ }\r
+ //\r
+ // Skip white space in tail.\r
+ //\r
+ Len = StrLen (Str);\r
+ StrTail = (CHAR16 *) (Str + Len - 1);\r
+ while (*StrTail == ' ') {\r
+ *StrTail = 0;\r
+ StrTail--;\r
+ }\r
+\r
+ Len = StrTail - Str + 1;\r
+\r
+ //\r
+ // Check hex prefix '0x'.\r
+ //\r
+ if ((Len >= 2) && (*Str == '0') && ((*(Str + 1) == 'x') || (*(Str + 1) == 'X'))) {\r
+ Str += 2;\r
+ Len -= 2;\r
+ Base = 16;\r
+ }\r
+\r
+ if (Len == 0) {\r
+ return 0;\r
+ }\r
+ //\r
+ // Convert the string to value.\r
+ //\r
+ for (; Str <= StrTail; Str++) {\r
+\r
+ Char = *Str;\r
+\r
+ if (Base == 16) {\r
+ if (RShiftU64 (Value, 60) != 0) {\r
+ //\r
+ // Overflow here x16.\r
+ //\r
+ return 0;\r
+ }\r
+\r
+ NewValue = LShiftU64 (Value, 4);\r
+ } else {\r
+ if (RShiftU64 (Value, 61) != 0) {\r
+ //\r
+ // Overflow here x8.\r
+ //\r
+ return 0;\r
+ }\r
+\r
+ NewValue = LShiftU64 (Value, 3);\r
+ Value = LShiftU64 (Value, 1);\r
+ NewValue += Value;\r
+ if (NewValue < Value) {\r
+ //\r
+ // Overflow here.\r
+ //\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ Value = NewValue;\r
+\r
+ if ((Base == 16) && (Char >= 'a') && (Char <= 'f')) {\r
+ Char = (CHAR16) (Char - 'a' + 'A');\r
+ }\r
+\r
+ if ((Base == 16) && (Char >= 'A') && (Char <= 'F')) {\r
+ Value += (Char - 'A') + 10;\r
+ } else if ((Char >= '0') && (Char <= '9')) {\r
+ Value += (Char - '0');\r
+ } else {\r
+ //\r
+ // Unexpected Char encountered.\r
+ //\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ *Status = EFI_SUCCESS;\r
+ return Value;\r
+}\r
+\r
+/**\r
+ Helper function called to transfer a string to an unsigned integer according to the map table.\r
+\r
+ @param[in] Str The pointer to the string.\r
+ @param[in] Map The pointer to the map table.\r
+\r
+ @return The integer value of converted Str. If not found, then return -1.\r
+**/\r
+UINT32\r
+MapStringToInteger (\r
+ IN CONST CHAR16 *Str,\r
+ IN STR2INT *Map\r
+ )\r
+{\r
+ STR2INT *Item;\r
+\r
+ for (Item = Map; Item->String != NULL; Item++) {\r
+ if (StrCmp (Item->String, Str) == 0) {\r
+ return Item->Integer;\r
+ }\r
+ }\r
+\r
+ return (UINT32) -1;\r
+}\r
+\r
+/**\r
+ Helper function called to transfer an unsigned integer to a string according to the map table.\r
+\r
+ @param[in] Integer The pointer to the string.\r
+ @param[in] Map The pointer to the map table.\r
+\r
+ @return The converted Str. If not found, then return NULL.\r
+**/\r
+CHAR16 *\r
+MapIntegerToString (\r
+ IN UINT32 Integer,\r
+ IN STR2INT *Map\r
+ )\r
+{\r
+ STR2INT *Item;\r
+\r
+ for (Item = Map; Item->String != NULL; Item++) {\r
+ if (Integer == Item->Integer) {\r
+ return Item->String;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
--- /dev/null
+/** @file\r
+ The assistant function declaration for IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _HELPER_H_\r
+#define _HELPER_H_\r
+\r
+#define FORMAT_NUMBER 0x1\r
+#define FORMAT_STRING 0x2\r
+\r
+/**\r
+ Helper function called to change input parameter in string format to number.\r
+\r
+ @param[in] FlagStr The pointer to the flag string.\r
+ @param[in] Maximum most value number.\r
+ @param[in, out] ValuePtr The pointer to the input parameter in string format.\r
+ @param[in] ByteCount The valid byte count\r
+ @param[in] Map The pointer to the STR2INT table.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+ @param[in] FormatMask The bit mask.\r
+ BIT 0 set indicates the value of flag might be number.\r
+ BIT 1 set indicates the value of flag might be a string that needs to be looked up.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_FOUND The input parameter can't be found.\r
+ @retval EFI_INVALID_PARAMETER The input parameter is an invalid input.\r
+**/\r
+EFI_STATUS\r
+GetNumber (\r
+ IN CHAR16 *FlagStr,\r
+ IN UINT64 Maximum,\r
+ IN OUT VOID *ValuePtr,\r
+ IN UINTN ByteCount,\r
+ IN STR2INT *Map,\r
+ IN LIST_ENTRY *ParamPackage,\r
+ IN UINT32 FormatMask\r
+ );\r
+\r
+/**\r
+ Helper function called to convert a string containing an (Ipv4) Internet Protocol dotted address\r
+ into a proper address for the EFI_IP_ADDRESS structure.\r
+\r
+ @param[in] Ptr The pointer to the string containing an (Ipv4) Internet Protocol dotted address.\r
+ @param[out] Ip The pointer to the Ip address structure to contain the result.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddr2 (\r
+ IN CHAR16 *Ptr,\r
+ OUT EFI_IP_ADDRESS *Ip\r
+ );\r
+\r
+/**\r
+ Helper function called to calculate the prefix length associated with the string\r
+ containing an Ipv4 or Ipv6 Internet Protocol address.\r
+\r
+ @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.\r
+ @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval Others Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetAddrRange (\r
+ IN CHAR16 *Ptr,\r
+ OUT EFI_IP_ADDRESS_INFO *Addr\r
+ );\r
+\r
+/**\r
+ Helper function called to calculate the port range associated with the string.\r
+\r
+ @param[in] Ptr The pointer to the string containing a port and range.\r
+ @param[out] Port The pointer to the Port to contain the result.\r
+ @param[out] PortRange The pointer to the PortRange to contain the result.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval Others Other mistake case.\r
+**/\r
+EFI_STATUS\r
+EfiInetPortRange (\r
+ IN CHAR16 *Ptr,\r
+ OUT UINT16 *Port,\r
+ OUT UINT16 *PortRange\r
+ );\r
+\r
+/**\r
+ Helper function called to transfer a string to an unsigned integer.\r
+\r
+ @param[in] Str The pointer to the string.\r
+ @param[out] Status The operation status.\r
+\r
+ @return The integer value of a converted str.\r
+**/\r
+UINT64\r
+StrToUInteger (\r
+ IN CONST CHAR16 *Str,\r
+ OUT EFI_STATUS *Status\r
+ );\r
+\r
+/**\r
+ Helper function called to transfer a string to an unsigned integer according to the map table.\r
+\r
+ @param[in] Str The pointer to the string.\r
+ @param[in] Map The pointer to the map table.\r
+\r
+ @return The integer value of converted str. If not found, then return -1.\r
+**/\r
+UINT32\r
+MapStringToInteger (\r
+ IN CONST CHAR16 *Str,\r
+ IN STR2INT *Map\r
+ );\r
+\r
+/**\r
+ Helper function called to transfer an unsigned integer to a string according to the map table.\r
+\r
+ @param[in] Integer The pointer to the string.\r
+ @param[in] Map The pointer to the map table.\r
+\r
+ @return The converted str. If not found, then return NULL.\r
+**/\r
+CHAR16 *\r
+MapIntegerToString (\r
+ IN UINT32 Integer,\r
+ IN STR2INT *Map\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The implementation of construct ENTRY_INDEXER in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Helper.h"\r
+\r
+/**\r
+ Fill in SPD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+ @param[in, out] Indexer The pointer to the SPD_ENTRY_INDEXER structure.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully.\r
+**/\r
+EFI_STATUS\r
+ConstructSpdIndexer (\r
+ IN OUT SPD_ENTRY_INDEXER *Indexer,\r
+ IN LIST_ENTRY *ParamPackage\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 Value64;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ ValueStr = NULL;\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+ } else {\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ ASSERT (ValueStr != NULL);\r
+\r
+ Value64 = StrToUInteger (ValueStr, &Status);\r
+ if (!EFI_ERROR (Status)) {\r
+ Indexer->Index = (UINTN) Value64;\r
+ Indexer->Name = NULL;\r
+ } else {\r
+ UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) Indexer->Name);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Fill in SAD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+ @param[in, out] Indexer The pointer to the SAD_ENTRY_INDEXER structure.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully.\r
+ @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list.\r
+**/\r
+EFI_STATUS\r
+ConstructSadIndexer (\r
+ IN OUT SAD_ENTRY_INDEXER *Indexer,\r
+ IN LIST_ENTRY *ParamPackage\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS Status1;\r
+ UINT64 Value64;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ ValueStr = NULL;\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+ } else {\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ ASSERT (ValueStr != NULL);\r
+\r
+ Value64 = StrToUInteger (ValueStr, &Status);\r
+ if (!EFI_ERROR (Status)) {\r
+ Indexer->Index = (UINTN) Value64;\r
+ ZeroMem (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID));\r
+ } else {\r
+ if ((!ShellCommandLineGetFlag (ParamPackage, L"--lookup-spi")) ||\r
+ (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-ipsec-proto")) ||\r
+ (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-dest"))) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--lookup-spi --lookup-ipsec-proto --lookup-dest"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--lookup-spi",\r
+ (UINT32) -1,\r
+ &Indexer->SaId.Spi,\r
+ sizeof (UINT32),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ Status1 = GetNumber (\r
+ L"--lookup-ipsec-proto",\r
+ 0,\r
+ &Indexer->SaId.Proto,\r
+ sizeof (EFI_IPSEC_PROTOCOL_TYPE),\r
+ mMapIpSecProtocol,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+\r
+ if (EFI_ERROR (Status) || EFI_ERROR (Status1)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-dest");\r
+ ASSERT (ValueStr != NULL);\r
+\r
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &Indexer->SaId.DestAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--lookup-dest",\r
+ ValueStr\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Fill in PAD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+ @param[in, out] Indexer The pointer to the PAD_ENTRY_INDEXER structure.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Filled in PAD_ENTRY_INDEXER successfully.\r
+ @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list.\r
+**/\r
+EFI_STATUS\r
+ConstructPadIndexer (\r
+ IN OUT PAD_ENTRY_INDEXER *Indexer,\r
+ IN LIST_ENTRY *ParamPackage\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 Value64;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ ValueStr = NULL;\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+ } else {\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ ASSERT (ValueStr != NULL);\r
+\r
+ Value64 = StrToUInteger (ValueStr, &Status);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Indexer->Index = (UINTN) Value64;\r
+ ZeroMem (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID));\r
+ } else {\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"--lookup-peer-address")) {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-address");\r
+ ASSERT (ValueStr != NULL);\r
+\r
+ Indexer->PadId.PeerIdValid = FALSE;\r
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &Indexer->PadId.Id.IpAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--lookup-peer-address",\r
+ ValueStr\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } else {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-id");\r
+ if (ValueStr == NULL) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--lookup-peer-address --lookup-peer-id"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Indexer->PadId.PeerIdValid = TRUE;\r
+ StrnCpy ((CHAR16 *) Indexer->PadId.Id.PeerId, ValueStr, ARRAY_SIZE (Indexer->PadId.Id.PeerId) - 1);\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[] = {\r
+ (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSpdIndexer,\r
+ (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSadIndexer,\r
+ (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructPadIndexer\r
+};\r
--- /dev/null
+/** @file\r
+ The internal structure and function declaration to construct ENTRY_INDEXER in\r
+ IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _INDEXER_H_\r
+#define _INDEXER_H_\r
+\r
+typedef struct {\r
+ UINT8 *Name;\r
+ UINTN Index; // Used only if Name is NULL.\r
+} SPD_ENTRY_INDEXER;\r
+\r
+typedef struct {\r
+ EFI_IPSEC_SA_ID SaId;\r
+ UINTN Index;\r
+} SAD_ENTRY_INDEXER;\r
+\r
+typedef struct {\r
+ EFI_IPSEC_PAD_ID PadId;\r
+ UINTN Index;\r
+} PAD_ENTRY_INDEXER;\r
+\r
+typedef union {\r
+ SPD_ENTRY_INDEXER Spd;\r
+ SAD_ENTRY_INDEXER Sad;\r
+ PAD_ENTRY_INDEXER Pad;\r
+} POLICY_ENTRY_INDEXER;\r
+\r
+/**\r
+ The prototype for the ConstructSpdIndexer()/ConstructSadIndexer()/ConstructPadIndexer().\r
+ Fill in SPD_ENTRY_INDEXER/SAD_ENTRY_INDEXER/PAD_ENTRY_INDEXER through ParamPackage list.\r
+\r
+ @param[in, out] Indexer The pointer to the POLICY_ENTRY_INDEXER union.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Filled in POLICY_ENTRY_INDEXER successfully.\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(* CONSTRUCT_POLICY_ENTRY_INDEXER) (\r
+ IN POLICY_ENTRY_INDEXER *Indexer,\r
+ IN LIST_ENTRY *ParamPackage\r
+);\r
+\r
+extern CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[];\r
+#endif\r
--- /dev/null
+/** @file\r
+ The main process for IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/HiiLib.h>\r
+\r
+#include <Protocol/IpSec.h>\r
+\r
+#include "IpSecConfig.h"\r
+#include "Dump.h"\r
+#include "Indexer.h"\r
+#include "PolicyEntryOperation.h"\r
+#include "Delete.h"\r
+#include "Helper.h"\r
+\r
+//\r
+// Used for ShellCommandLineParseEx only\r
+// and to ensure user inputs are in valid format\r
+//\r
+SHELL_PARAM_ITEM mIpSecConfigParamList[] = {\r
+ { L"-p", TypeValue },\r
+ { L"-a", TypeValue },\r
+ { L"-i", TypeValue },\r
+ { L"-e", TypeValue },\r
+ { L"-d", TypeValue },\r
+ { L"-f", TypeFlag },\r
+ { L"-l", TypeFlag },\r
+ { L"-enable", TypeFlag },\r
+ { L"-disable", TypeFlag },\r
+ { L"-status", TypeFlag },\r
+ { L"-?", TypeFlag },\r
+\r
+ //\r
+ // SPD Selector\r
+ //\r
+ { L"--local", TypeValue },\r
+ { L"--remote", TypeValue },\r
+ { L"--proto", TypeValue },\r
+ { L"--local-port", TypeValue },\r
+ { L"--remote-port", TypeValue },\r
+ { L"--icmp-type", TypeValue },\r
+ { L"--icmp-code", TypeValue },\r
+\r
+ //\r
+ // SPD Data\r
+ //\r
+ { L"--name", TypeValue },\r
+ { L"--packet-flag", TypeValue },\r
+ { L"--action", TypeValue },\r
+ { L"--lifebyte", TypeValue },\r
+ { L"--lifetime-soft", TypeValue },\r
+ { L"--lifetime", TypeValue },\r
+ { L"--mode", TypeValue },\r
+ { L"--tunnel-local", TypeValue },\r
+ { L"--tunnel-remote", TypeValue },\r
+ { L"--dont-fragment", TypeValue },\r
+ { L"--ipsec-proto", TypeValue },\r
+ { L"--auth-algo", TypeValue },\r
+ { L"--encrypt-algo", TypeValue },\r
+\r
+ { L"--ext-sequence", TypeFlag },\r
+ { L"--sequence-overflow", TypeFlag },\r
+ { L"--fragment-check", TypeFlag },\r
+ { L"--ext-sequence-", TypeFlag },\r
+ { L"--sequence-overflow-", TypeFlag },\r
+ { L"--fragment-check-", TypeFlag },\r
+\r
+ //\r
+ // SA ID\r
+ // --ipsec-proto\r
+ //\r
+ { L"--spi", TypeValue },\r
+ { L"--dest", TypeValue },\r
+ { L"--lookup-spi", TypeValue },\r
+ { L"--lookup-ipsec-proto", TypeValue },\r
+ { L"--lookup-dest", TypeValue },\r
+\r
+ //\r
+ // SA DATA\r
+ // --mode\r
+ // --auth-algo\r
+ // --encrypt-algo\r
+ //\r
+ { L"--sequence-number", TypeValue },\r
+ { L"--antireplay-window", TypeValue },\r
+ { L"--auth-key", TypeValue },\r
+ { L"--encrypt-key", TypeValue },\r
+ { L"--path-mtu", TypeValue },\r
+\r
+ //\r
+ // PAD ID\r
+ //\r
+ { L"--peer-id", TypeValue },\r
+ { L"--peer-address", TypeValue },\r
+ { L"--auth-proto", TypeValue },\r
+ { L"--auth-method", TypeValue },\r
+ { L"--ike-id", TypeValue },\r
+ { L"--ike-id-", TypeValue },\r
+ { L"--auth-data", TypeValue },\r
+ { L"--revocation-data", TypeValue },\r
+ { L"--lookup-peer-id", TypeValue },\r
+ { L"--lookup-peer-address", TypeValue },\r
+\r
+ { NULL, TypeMax },\r
+};\r
+\r
+//\r
+// -P\r
+//\r
+STR2INT mMapPolicy[] = {\r
+ { L"SPD", IPsecConfigDataTypeSpd },\r
+ { L"SAD", IPsecConfigDataTypeSad },\r
+ { L"PAD", IPsecConfigDataTypePad },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --proto\r
+//\r
+STR2INT mMapIpProtocol[] = {\r
+ { L"TCP", EFI_IP4_PROTO_TCP },\r
+ { L"UDP", EFI_IP4_PROTO_UDP },\r
+ { L"ICMP", EFI_IP4_PROTO_ICMP },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --action\r
+//\r
+STR2INT mMapIpSecAction[] = {\r
+ { L"Bypass", EfiIPsecActionBypass },\r
+ { L"Discard", EfiIPsecActionDiscard },\r
+ { L"Protect", EfiIPsecActionProtect },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --mode\r
+//\r
+STR2INT mMapIpSecMode[] = {\r
+ { L"Transport", EfiIPsecTransport },\r
+ { L"Tunnel", EfiIPsecTunnel },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --dont-fragment\r
+//\r
+STR2INT mMapDfOption[] = {\r
+ { L"clear", EfiIPsecTunnelClearDf },\r
+ { L"set", EfiIPsecTunnelSetDf },\r
+ { L"copy", EfiIPsecTunnelCopyDf },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --ipsec-proto\r
+//\r
+STR2INT mMapIpSecProtocol[] = {\r
+ { L"AH", EfiIPsecAH },\r
+ { L"ESP", EfiIPsecESP },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --auth-algo\r
+//\r
+STR2INT mMapAuthAlgo[] = {\r
+ { L"NONE", EFI_IPSEC_AALG_NONE },\r
+ { L"MD5HMAC", EFI_IPSEC_AALG_MD5HMAC },\r
+ { L"SHA1HMAC", EFI_IPSEC_AALG_SHA1HMAC },\r
+ { L"SHA2-256HMAC", EFI_IPSEC_AALG_SHA2_256HMAC },\r
+ { L"SHA2-384HMAC", EFI_IPSEC_AALG_SHA2_384HMAC },\r
+ { L"SHA2-512HMAC", EFI_IPSEC_AALG_SHA2_512HMAC },\r
+ { L"AES-XCBC-MAC", EFI_IPSEC_AALG_AES_XCBC_MAC },\r
+ { L"NULL", EFI_IPSEC_AALG_NULL },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --encrypt-algo\r
+//\r
+STR2INT mMapEncAlgo[] = {\r
+ { L"NONE", EFI_IPSEC_EALG_NONE },\r
+ { L"DESCBC", EFI_IPSEC_EALG_DESCBC },\r
+ { L"3DESCBC", EFI_IPSEC_EALG_3DESCBC },\r
+ { L"CASTCBC", EFI_IPSEC_EALG_CASTCBC },\r
+ { L"BLOWFISHCBC", EFI_IPSEC_EALG_BLOWFISHCBC },\r
+ { L"NULL", EFI_IPSEC_EALG_NULL },\r
+ { L"AESCBC", EFI_IPSEC_EALG_AESCBC },\r
+ { L"AESCTR", EFI_IPSEC_EALG_AESCTR },\r
+ { L"AES-CCM-ICV8", EFI_IPSEC_EALG_AES_CCM_ICV8 },\r
+ { L"AES-CCM-ICV12",EFI_IPSEC_EALG_AES_CCM_ICV12 },\r
+ { L"AES-CCM-ICV16",EFI_IPSEC_EALG_AES_CCM_ICV16 },\r
+ { L"AES-GCM-ICV8", EFI_IPSEC_EALG_AES_GCM_ICV8 },\r
+ { L"AES-GCM-ICV12",EFI_IPSEC_EALG_AES_GCM_ICV12 },\r
+ { L"AES-GCM-ICV16",EFI_IPSEC_EALG_AES_GCM_ICV16 },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --auth-proto\r
+//\r
+STR2INT mMapAuthProto[] = {\r
+ { L"IKEv1", EfiIPsecAuthProtocolIKEv1 },\r
+ { L"IKEv2", EfiIPsecAuthProtocolIKEv2 },\r
+ { NULL, 0 },\r
+};\r
+\r
+//\r
+// --auth-method\r
+//\r
+STR2INT mMapAuthMethod[] = {\r
+ { L"PreSharedSecret", EfiIPsecAuthMethodPreSharedSecret },\r
+ { L"Certificates", EfiIPsecAuthMethodCertificates },\r
+ { NULL, 0 },\r
+};\r
+\r
+EFI_IPSEC_PROTOCOL *mIpSec;\r
+EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig;\r
+EFI_HII_HANDLE mHiiHandle;\r
+EFI_GUID mEfiIpSecConfigGuid = EFI_IPSEC_CONFIG_GUID;\r
+CHAR16 mAppName[] = L"IpSecConfig";\r
+\r
+//\r
+// Used for IpSecConfigRetriveCheckListByName only to check the validation of user input\r
+//\r
+VAR_CHECK_ITEM mIpSecConfigVarCheckList[] = {\r
+ { L"-enable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-disable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-status", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-p", BIT(1), 0, BIT(2)|BIT(1)|BIT(0), 0 },\r
+\r
+ { L"-a", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-i", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-d", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-e", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-l", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },\r
+ { L"-f", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },\r
+\r
+ { L"-?", BIT(0), BIT(0), BIT(2)|BIT(1)|BIT(0), 0 },\r
+\r
+ //\r
+ // SPD Selector\r
+ //\r
+ { L"--local", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--remote", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--proto", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--local-port", 0, 0, BIT(2)|BIT(1), BIT(0) },\r
+ { L"--remote-port", 0, 0, BIT(2)|BIT(1), BIT(0) },\r
+ { L"--icmp-type", 0, 0, BIT(2)|BIT(1), BIT(1) },\r
+ { L"--icmp-code", 0, 0, BIT(2)|BIT(1), BIT(1) },\r
+\r
+ //\r
+ // SPD Data\r
+ //\r
+ { L"--name", 0, 0, BIT(2), 0 },\r
+ { L"--packet-flag", 0, 0, BIT(2), 0 },\r
+ { L"--action", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--lifebyte", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--lifetime-soft", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--lifetime", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--mode", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--tunnel-local", 0, 0, BIT(2), 0 },\r
+ { L"--tunnel-remote", 0, 0, BIT(2), 0 },\r
+ { L"--dont-fragment", 0, 0, BIT(2), 0 },\r
+ { L"--ipsec-proto", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--auth-algo", 0, 0, BIT(2)|BIT(1), 0 },\r
+ { L"--encrypt-algo", 0, 0, BIT(2)|BIT(1), 0 },\r
+\r
+ { L"--ext-sequence", 0, 0, BIT(2), BIT(2) },\r
+ { L"--sequence-overflow", 0, 0, BIT(2), BIT(2) },\r
+ { L"--fragment-check", 0, 0, BIT(2), BIT(2) },\r
+ { L"--ext-sequence-", 0, 0, BIT(2), BIT(3) },\r
+ { L"--sequence-overflow-", 0, 0, BIT(2), BIT(3) },\r
+ { L"--fragment-check-", 0, 0, BIT(2), BIT(3) },\r
+\r
+ //\r
+ // SA ID\r
+ // --ipsec-proto\r
+ //\r
+ { L"--spi", 0, 0, BIT(1), 0 },\r
+ { L"--dest", 0, 0, BIT(1), 0 },\r
+ { L"--lookup-spi", 0, 0, BIT(1), 0 },\r
+ { L"--lookup-ipsec-proto", 0, 0, BIT(1), 0 },\r
+ { L"--lookup-dest", 0, 0, BIT(1), 0 },\r
+\r
+ //\r
+ // SA DATA\r
+ // --mode\r
+ // --auth-algo\r
+ // --encrypt-algo\r
+ //\r
+ { L"--sequence-number", 0, 0, BIT(1), 0 },\r
+ { L"--antireplay-window", 0, 0, BIT(1), 0 },\r
+ { L"--auth-key", 0, 0, BIT(1), 0 },\r
+ { L"--encrypt-key", 0, 0, BIT(1), 0 },\r
+ { L"--path-mtu", 0, 0, BIT(1), 0 },\r
+\r
+ //\r
+ // The example to add a PAD:\r
+ // "-A --peer-id Mike [--peer-address 10.23.2.2] --auth-proto IKE1/IKE2\r
+ // --auth-method PreSharedSeceret/Certificate --ike-id\r
+ // --auth-data 343343 --revocation-data 2342432"\r
+ // The example to delete a PAD:\r
+ // "-D * --lookup-peer-id Mike [--lookup-peer-address 10.23.2.2]"\r
+ // "-D 1"\r
+ // The example to edit a PAD:\r
+ // "-E * --lookup-peer-id Mike --auth-method Certificate"\r
+\r
+ //\r
+ // PAD ID\r
+ //\r
+ { L"--peer-id", 0, 0, BIT(0), BIT(4) },\r
+ { L"--peer-address", 0, 0, BIT(0), BIT(5) },\r
+ { L"--auth-proto", 0, 0, BIT(0), 0 },\r
+ { L"--auth-method", 0, 0, BIT(0), 0 },\r
+ { L"--IKE-ID", 0, 0, BIT(0), BIT(6) },\r
+ { L"--IKE-ID-", 0, 0, BIT(0), BIT(7) },\r
+ { L"--auth-data", 0, 0, BIT(0), 0 },\r
+ { L"--revocation-data", 0, 0, BIT(0), 0 },\r
+ { L"--lookup-peer-id", 0, 0, BIT(0), BIT(4) },\r
+ { L"--lookup-peer-address",0, 0, BIT(0), BIT(5) },\r
+\r
+ { NULL, 0, 0, 0, 0 },\r
+};\r
+\r
+/**\r
+ The function to allocate the proper sized buffer for various\r
+ EFI interfaces.\r
+\r
+ @param[in, out] Status Current status.\r
+ @param[in, out] Buffer Current allocated buffer, or NULL.\r
+ @param[in] BufferSize Current buffer size needed\r
+\r
+ @retval TRUE If the buffer was reallocated and the caller should try the API again.\r
+ @retval FALSE If the buffer was not reallocated successfully.\r
+**/\r
+BOOLEAN\r
+GrowBuffer (\r
+ IN OUT EFI_STATUS *Status,\r
+ IN OUT VOID **Buffer,\r
+ IN UINTN BufferSize\r
+ )\r
+{\r
+ BOOLEAN TryAgain;\r
+\r
+ ASSERT (Status != NULL);\r
+ ASSERT (Buffer != NULL);\r
+\r
+ //\r
+ // If this is an initial request, buffer will be null with a new buffer size.\r
+ //\r
+ if ((NULL == *Buffer) && (BufferSize != 0)) {\r
+ *Status = EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ //\r
+ // If the status code is "buffer too small", resize the buffer.\r
+ //\r
+ TryAgain = FALSE;\r
+ if (*Status == EFI_BUFFER_TOO_SMALL) {\r
+\r
+ if (*Buffer != NULL) {\r
+ FreePool (*Buffer);\r
+ }\r
+\r
+ *Buffer = AllocateZeroPool (BufferSize);\r
+\r
+ if (*Buffer != NULL) {\r
+ TryAgain = TRUE;\r
+ } else {\r
+ *Status = EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If there's an error, free the buffer.\r
+ //\r
+ if (!TryAgain && EFI_ERROR (*Status) && (*Buffer != NULL)) {\r
+ FreePool (*Buffer);\r
+ *Buffer = NULL;\r
+ }\r
+\r
+ return TryAgain;\r
+}\r
+\r
+/**\r
+ Function returns an array of handles that support the requested protocol\r
+ in a buffer allocated from a pool.\r
+\r
+ @param[in] SearchType Specifies which handle(s) are to be returned.\r
+ @param[in] Protocol Provides the protocol to search by.\r
+ This parameter is only valid for SearchType ByProtocol.\r
+\r
+ @param[in] SearchKey Supplies the search key depending on the SearchType.\r
+ @param[in, out] NoHandles The number of handles returned in Buffer.\r
+ @param[out] Buffer A pointer to the buffer to return the requested array of\r
+ handles that support Protocol.\r
+\r
+ @retval EFI_SUCCESS The resulting array of handles was returned.\r
+ @retval Others Other mistake case.\r
+**/\r
+EFI_STATUS\r
+LocateHandle (\r
+ IN EFI_LOCATE_SEARCH_TYPE SearchType,\r
+ IN EFI_GUID *Protocol OPTIONAL,\r
+ IN VOID *SearchKey OPTIONAL,\r
+ IN OUT UINTN *NoHandles,\r
+ OUT EFI_HANDLE **Buffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN BufferSize;\r
+\r
+ ASSERT (NoHandles != NULL);\r
+ ASSERT (Buffer != NULL);\r
+\r
+ //\r
+ // Initialize for GrowBuffer loop.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ *Buffer = NULL;\r
+ BufferSize = 50 * sizeof (EFI_HANDLE);\r
+\r
+ //\r
+ // Call the real function.\r
+ //\r
+ while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {\r
+ Status = gBS->LocateHandle (\r
+ SearchType,\r
+ Protocol,\r
+ SearchKey,\r
+ &BufferSize,\r
+ *Buffer\r
+ );\r
+ }\r
+\r
+ *NoHandles = BufferSize / sizeof (EFI_HANDLE);\r
+ if (EFI_ERROR (Status)) {\r
+ *NoHandles = 0;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Find the first instance of this protocol in the system and return its interface.\r
+\r
+ @param[in] ProtocolGuid The guid of the protocol.\r
+ @param[out] Interface The pointer to the first instance of the protocol.\r
+\r
+ @retval EFI_SUCCESS A protocol instance matching ProtocolGuid was found.\r
+ @retval Others A protocol instance matching ProtocolGuid was not found.\r
+**/\r
+EFI_STATUS\r
+LocateProtocol (\r
+ IN EFI_GUID *ProtocolGuid,\r
+ OUT VOID **Interface\r
+ )\r
+\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN NumberHandles;\r
+ UINTN Index;\r
+ EFI_HANDLE *Handles;\r
+\r
+ *Interface = NULL;\r
+ Handles = NULL;\r
+ NumberHandles = 0;\r
+\r
+ Status = LocateHandle (ByProtocol, ProtocolGuid, NULL, &NumberHandles, &Handles);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_INFO, "LibLocateProtocol: Handle not found\n"));\r
+ return Status;\r
+ }\r
+\r
+ for (Index = 0; Index < NumberHandles; Index++) {\r
+ ASSERT (Handles != NULL);\r
+ Status = gBS->HandleProtocol (\r
+ Handles[Index],\r
+ ProtocolGuid,\r
+ Interface\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Handles != NULL) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Helper function called to check the conflicted flags.\r
+\r
+ @param[in] CheckList The pointer to the VAR_CHECK_ITEM table.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS No conflicted flags.\r
+ @retval EFI_INVALID_PARAMETER The input parameter is erroroneous or there are some conflicted flags.\r
+**/\r
+EFI_STATUS\r
+IpSecConfigRetriveCheckListByName (\r
+ IN VAR_CHECK_ITEM *CheckList,\r
+ IN LIST_ENTRY *ParamPackage\r
+)\r
+{\r
+\r
+ LIST_ENTRY *Node;\r
+ VAR_CHECK_ITEM *Item;\r
+ UINT32 Attribute1;\r
+ UINT32 Attribute2;\r
+ UINT32 Attribute3;\r
+ UINT32 Attribute4;\r
+ UINT32 Index;\r
+\r
+ Attribute1 = 0;\r
+ Attribute2 = 0;\r
+ Attribute3 = 0;\r
+ Attribute4 = 0;\r
+ Index = 0;\r
+ Item = mIpSecConfigVarCheckList;\r
+\r
+ if ((ParamPackage == NULL) || (CheckList == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Enumerate through the list of parameters that are input by user.\r
+ //\r
+ for (Node = GetFirstNode (ParamPackage); !IsNull (ParamPackage, Node); Node = GetNextNode (ParamPackage, Node)) {\r
+ if (((SHELL_PARAM_PACKAGE *) Node)->Name != NULL) {\r
+ //\r
+ // Enumerate the check list that defines the conflicted attributes of each flag.\r
+ //\r
+ for (; Item->VarName != NULL; Item++) {\r
+ if (StrCmp (((SHELL_PARAM_PACKAGE *) Node)->Name, Item->VarName) == 0) {\r
+ Index++;\r
+ if (Index == 1) {\r
+ Attribute1 = Item->Attribute1;\r
+ Attribute2 = Item->Attribute2;\r
+ Attribute3 = Item->Attribute3;\r
+ Attribute4 = Item->Attribute4;\r
+ } else {\r
+ Attribute1 &= Item->Attribute1;\r
+ Attribute2 |= Item->Attribute2;\r
+ Attribute3 &= Item->Attribute3;\r
+ Attribute4 |= Item->Attribute4;\r
+ if (Attribute1 != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Attribute2 != 0) {\r
+ if ((Index == 2) && (StrCmp (Item->VarName, L"-p") == 0)) {\r
+ continue;\r
+ }\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Attribute3 == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (((Attribute4 & 0xFF) == 0x03) || ((Attribute4 & 0xFF) == 0x0C) ||\r
+ ((Attribute4 & 0xFF) == 0x30) || ((Attribute4 & 0xFF) == 0xC0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ Item = mIpSecConfigVarCheckList;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+ both device drivers and bus drivers.\r
+\r
+ The entry point for IpSecConfig application that parse the command line input and call an IpSecConfig process.\r
+\r
+ @param[in] ImageHandle The image handle of this application.\r
+ @param[in] SystemTable The pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeIpSecConfig (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;\r
+ UINT8 Value;\r
+ LIST_ENTRY *ParamPackage;\r
+ CONST CHAR16 *ValueStr;\r
+ CHAR16 *ProblemParam;\r
+ UINTN NonOptionCount;\r
+\r
+ //\r
+ // Register our string package with HII and return the handle to it.\r
+ //\r
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IpSecConfigStrings, NULL);\r
+ ASSERT (mHiiHandle != NULL);\r
+\r
+ Status = ShellCommandLineParseEx (mIpSecConfigParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, ProblemParam);\r
+ goto Done;\r
+ }\r
+\r
+ Status = IpSecConfigRetriveCheckListByName (mIpSecConfigVarCheckList, ParamPackage);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_MISTAKEN_OPTIONS), mHiiHandle);\r
+ goto Done;\r
+ }\r
+\r
+ Status = LocateProtocol (&gEfiIpSecConfigProtocolGuid, (VOID **) &mIpSecConfig);\r
+ if (EFI_ERROR (Status) || mIpSecConfig == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);\r
+ goto Done;\r
+ }\r
+\r
+ Status = LocateProtocol (&gEfiIpSecProtocolGuid, (VOID **) &mIpSec);\r
+ if (EFI_ERROR (Status) || mIpSec == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Enable IPsec.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-enable")) {\r
+ if (!(mIpSec->DisabledFlag)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_ENABLE), mHiiHandle, mAppName);\r
+ } else {\r
+ //\r
+ // Set enable flag.\r
+ //\r
+ Value = IPSEC_STATUS_ENABLED;\r
+ Status = gRT->SetVariable (\r
+ IPSECCONFIG_STATUS_NAME,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+ sizeof (Value),\r
+ &Value\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ mIpSec->DisabledFlag = FALSE;\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_SUCCESS), mHiiHandle, mAppName);\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_FAILED), mHiiHandle, mAppName);\r
+ }\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Disable IPsec.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-disable")) {\r
+ if (mIpSec->DisabledFlag) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_DISABLE), mHiiHandle, mAppName);\r
+ } else {\r
+ //\r
+ // Set disable flag; however, leave it to be disabled in the callback function of DisabledEvent.\r
+ //\r
+ gBS->SignalEvent (mIpSec->DisabledEvent);\r
+ if (mIpSec->DisabledFlag) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_SUCCESS), mHiiHandle, mAppName);\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_FAILED), mHiiHandle, mAppName);\r
+ }\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ //IPsec Status.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-status")) {\r
+ if (mIpSec->DisabledFlag) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_DISABLE), mHiiHandle, mAppName);\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_ENABLE), mHiiHandle, mAppName);\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Try to get policy database type.\r
+ //\r
+ DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) -1;\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-p");\r
+ if (ValueStr != NULL) {\r
+ DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) MapStringToInteger (ValueStr, mMapPolicy);\r
+ if (DataType == -1) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle, mAppName, ValueStr);\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {\r
+ switch (DataType) {\r
+ case (EFI_IPSEC_CONFIG_DATA_TYPE) -1:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_HELP), mHiiHandle);\r
+ break;\r
+\r
+ case IPsecConfigDataTypeSpd:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SPD_HELP), mHiiHandle);\r
+ break;\r
+\r
+ case IPsecConfigDataTypeSad:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SAD_HELP), mHiiHandle);\r
+ break;\r
+\r
+ case IPsecConfigDataTypePad:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PAD_HELP), mHiiHandle);\r
+ break;\r
+\r
+ default:\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle);\r
+ break;\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+\r
+ NonOptionCount = ShellCommandLineGetCount ();\r
+ if ((NonOptionCount - 1) > 0) {\r
+ ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32) (NonOptionCount - 1));\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_REDUNDANCY_MANY), mHiiHandle, mAppName, ValueStr);\r
+ goto Done;\r
+ }\r
+\r
+ if (DataType == -1) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_DB), mHiiHandle, mAppName);\r
+ goto Done;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {\r
+ Status = AddOrInsertPolicyEntry (DataType, ParamPackage);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {\r
+ Status = AddOrInsertPolicyEntry (DataType, ParamPackage);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {\r
+ Status = EditPolicyEntry (DataType, ParamPackage);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {\r
+ Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {\r
+ Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {\r
+ Status = ListPolicyEntry (DataType, ParamPackage);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, mAppName);\r
+ goto Done;\r
+ }\r
+\r
+Done:\r
+ ShellCommandLineFreeVarList (ParamPackage);\r
+ HiiRemovePackages (mHiiHandle);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ The internal structure and function declaration in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _IPSEC_CONFIG_H_\r
+#define _IPSEC_CONFIG_H_\r
+\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/ShellLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/IpSecConfig.h>\r
+\r
+#define EFI_IPSEC_CONFIG_GUID \\r
+ { \\r
+ 0x9db0c3ac, 0xd9d2, 0x4f96, {0x9e, 0xd7, 0x6d, 0xa6, 0x12, 0xa4, 0xf3, 0x27} \\r
+ }\r
+\r
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))\r
+\r
+#define IPSECCONFIG_STATUS_NAME L"IpSecStatus"\r
+\r
+#define BIT(x) (UINT32) (1 << (x))\r
+\r
+#define IPSEC_STATUS_DISABLED 0x0\r
+#define IPSEC_STATUS_ENABLED 0x1\r
+\r
+#define EFI_IP4_PROTO_ICMP 0x1\r
+#define EFI_IP4_PROTO_TCP 0x6\r
+#define EFI_IP4_PROTO_UDP 0x11\r
+\r
+#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF\r
+#define EFI_IPSEC_ANY_PORT 0\r
+\r
+typedef struct _VAR_CHECK_ITEM {\r
+ CHAR16 *VarName;\r
+ UINT32 Attribute1;\r
+ UINT32 Attribute2;\r
+ UINT32 Attribute3;\r
+ UINT32 Attribute4;\r
+} VAR_CHECK_ITEM;\r
+\r
+typedef struct _SHELL_PARAM_PACKAGE{\r
+ LIST_ENTRY Link;\r
+ CHAR16 *Name;\r
+ ParamType Type;\r
+ CHAR16 *Value;\r
+ UINTN OriginalPosition;\r
+} SHELL_PARAM_PACKAGE;\r
+\r
+typedef struct _STR2INT {\r
+ CHAR16 *String;\r
+ UINT32 Integer;\r
+} STR2INT;\r
+\r
+extern EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig;\r
+extern EFI_HII_HANDLE mHiiHandle;\r
+extern CHAR16 mAppName[];\r
+\r
+//\r
+// -P\r
+//\r
+extern STR2INT mMapPolicy[];\r
+\r
+//\r
+// --proto\r
+//\r
+extern STR2INT mMapIpProtocol[];\r
+\r
+//\r
+// --action\r
+//\r
+extern STR2INT mMapIpSecAction[];\r
+\r
+//\r
+// --mode\r
+//\r
+extern STR2INT mMapIpSecMode[];\r
+\r
+//\r
+// --dont-fragment\r
+//\r
+extern STR2INT mMapDfOption[];\r
+\r
+//\r
+// --ipsec-proto\r
+//\r
+extern STR2INT mMapIpSecProtocol[];\r
+//\r
+// --auth-algo\r
+//\r
+extern STR2INT mMapAuthAlgo[];\r
+\r
+//\r
+// --encrypt-algo\r
+//\r
+extern STR2INT mMapEncAlgo[];\r
+//\r
+// --auth-proto\r
+//\r
+extern STR2INT mMapAuthProto[];\r
+\r
+//\r
+// --auth-method\r
+//\r
+extern STR2INT mMapAuthMethod[];\r
+\r
+#endif\r
--- /dev/null
+## @file\r
+# Component description file for IpSecConfig6 application.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010006\r
+ BASE_NAME = IpSecConfig\r
+ FILE_GUID = 0922E604-F5EC-42ef-980D-A35E9A2B1844\r
+ MODULE_TYPE = UEFI_APPLICATION\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = InitializeIpSecConfig\r
+\r
+[Sources]\r
+ IpSecConfigStrings.uni\r
+ IpSecConfig.c\r
+ IpSecConfig.h\r
+ Dump.c\r
+ Dump.h\r
+ Indexer.c\r
+ Indexer.h\r
+ Match.c\r
+ Match.h\r
+ Delete.h\r
+ Delete.c\r
+ Helper.c\r
+ Helper.h\r
+ ForEach.c\r
+ ForEach.h\r
+ PolicyEntryOperation.c\r
+ PolicyEntryOperation.h\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+ ShellPkg/ShellPkg.dec\r
+\r
+[LibraryClasses]\r
+ UefiBootServicesTableLib\r
+ UefiApplicationEntryPoint\r
+ BaseMemoryLib\r
+ ShellLib\r
+ MemoryAllocationLib\r
+ DebugLib\r
+ HiiLib\r
+ NetLib\r
+ UefiLib\r
+\r
+[Protocols]\r
+ gEfiIpSecProtocolGuid ##CONSUMS\r
+ gEfiIpSecConfigProtocolGuid ##CONSUMS\r
--- /dev/null
+/** @file\r
+ The implementation of match policy entry function in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Match.h"\r
+\r
+/**\r
+ Private function to validate a buffer that should be filled with zero.\r
+\r
+ @param[in] Memory The pointer to the buffer.\r
+ @param[in] Size The size of the buffer.\r
+\r
+ @retval TRUE The memory is filled with zero.\r
+ @retval FALSE The memory isn't filled with zero.\r
+**/\r
+BOOLEAN\r
+IsMemoryZero (\r
+ IN VOID *Memory,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < Size; Index++) {\r
+ if (*((UINT8 *) Memory + Index) != 0) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Find the matching SPD with Indexer.\r
+\r
+ @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+ @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+ @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure.\r
+\r
+ @retval TRUE The matched SPD is found.\r
+ @retval FALSE The matched SPD is not found.\r
+**/\r
+BOOLEAN\r
+MatchSpdEntry (\r
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,\r
+ IN EFI_IPSEC_SPD_DATA *Data,\r
+ IN SPD_ENTRY_INDEXER *Indexer\r
+ )\r
+{\r
+ BOOLEAN Match;\r
+\r
+ Match = FALSE;\r
+ if (Indexer->Name != NULL) {\r
+ if ((Data->Name != NULL) && (AsciiStrCmp ((CHAR8 *) Indexer->Name, (CHAR8 *) Data->Name) == 0)) {\r
+ Match = TRUE;\r
+ }\r
+ } else {\r
+ if (Indexer->Index == 0) {\r
+ Match = TRUE;\r
+ }\r
+\r
+ Indexer->Index--;\r
+ }\r
+\r
+ return Match;\r
+}\r
+\r
+/**\r
+ Find the matching SAD with Indexer.\r
+\r
+ @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure.\r
+ @param[in] Data The pointer to the EFI_IPSEC_SA_DATA structure.\r
+ @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure.\r
+\r
+ @retval TRUE The matched SAD is found.\r
+ @retval FALSE The matched SAD is not found.\r
+**/\r
+BOOLEAN\r
+MatchSadEntry (\r
+ IN EFI_IPSEC_SA_ID *SaId,\r
+ IN EFI_IPSEC_SA_DATA *Data,\r
+ IN SAD_ENTRY_INDEXER *Indexer\r
+ )\r
+{\r
+ BOOLEAN Match;\r
+\r
+ Match = FALSE;\r
+ if (!IsMemoryZero (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID))) {\r
+ Match = (BOOLEAN) (CompareMem (&Indexer->SaId, SaId, sizeof (EFI_IPSEC_SA_ID)) == 0);\r
+ } else {\r
+ if (Indexer->Index == 0) {\r
+ Match = TRUE;\r
+ }\r
+ Indexer->Index--;\r
+ }\r
+\r
+ return Match;\r
+}\r
+\r
+/**\r
+ Find the matching PAD with Indexer.\r
+\r
+ @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure.\r
+ @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+ @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure.\r
+\r
+ @retval TRUE The matched PAD is found.\r
+ @retval FALSE The matched PAD is not found.\r
+**/\r
+BOOLEAN\r
+MatchPadEntry (\r
+ IN EFI_IPSEC_PAD_ID *PadId,\r
+ IN EFI_IPSEC_PAD_DATA *Data,\r
+ IN PAD_ENTRY_INDEXER *Indexer\r
+ )\r
+{\r
+ BOOLEAN Match;\r
+\r
+ Match = FALSE;\r
+ if (!IsMemoryZero (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID))) {\r
+ Match = (BOOLEAN) ((Indexer->PadId.PeerIdValid == PadId->PeerIdValid) &&\r
+ ((PadId->PeerIdValid &&\r
+ (StrCmp (\r
+ (CONST CHAR16 *) Indexer->PadId.Id.PeerId,\r
+ (CONST CHAR16 *) PadId->Id.PeerId\r
+ ) == 0)) ||\r
+ ((!PadId->PeerIdValid) &&\r
+ (Indexer->PadId.Id.IpAddress.PrefixLength == PadId->Id.IpAddress.PrefixLength) &&\r
+ (CompareMem (\r
+ &Indexer->PadId.Id.IpAddress.Address,\r
+ &PadId->Id.IpAddress.Address,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ ) == 0))));\r
+ } else {\r
+ if (Indexer->Index == 0) {\r
+ Match = TRUE;\r
+ }\r
+\r
+ Indexer->Index--;\r
+ }\r
+\r
+ return Match;\r
+}\r
+\r
+MATCH_POLICY_ENTRY mMatchPolicyEntry[] = {\r
+ (MATCH_POLICY_ENTRY) MatchSpdEntry,\r
+ (MATCH_POLICY_ENTRY) MatchSadEntry,\r
+ (MATCH_POLICY_ENTRY) MatchPadEntry\r
+};\r
+\r
--- /dev/null
+/** @file\r
+ The internal structure and function declaration of \r
+ match policy entry function in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _MATCH_H_\r
+#define _MATCH_H_\r
+\r
+/**\r
+ The prototype for the MatchSpdEntry()/MatchSadEntry()/MatchPadEntry().\r
+ The functionality is to find the matching SPD/SAD/PAD with Indexer.\r
+\r
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.\r
+ @param[in] Data The pointer to corresponding Data.\r
+ @param[in] Indexer The pointer to the POLICY_ENTRY_INDEXER union.\r
+\r
+ @retval TRUE The matched SPD/SAD/PAD is found.\r
+ @retval FALSE The matched SPD/SAD/PAD is not found.\r
+**/\r
+typedef\r
+BOOLEAN\r
+(* MATCH_POLICY_ENTRY) (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN POLICY_ENTRY_INDEXER *Indexer\r
+ );\r
+\r
+extern MATCH_POLICY_ENTRY mMatchPolicyEntry[];\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The implementation of policy entry operation function in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfig.h"\r
+#include "Indexer.h"\r
+#include "Match.h"\r
+#include "Helper.h"\r
+#include "ForEach.h"\r
+#include "PolicyEntryOperation.h"\r
+\r
+/**\r
+ Fill in EFI_IPSEC_SPD_SELECTOR through ParamPackage list.\r
+\r
+ @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+ @param[in, out] ParamPackage The pointer to the Mask.\r
+\r
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateSpdSelector (\r
+ OUT EFI_IPSEC_SPD_SELECTOR *Selector,\r
+ IN LIST_ENTRY *ParamPackage,\r
+ IN OUT UINT32 *Mask\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ Status = EFI_SUCCESS;\r
+ ReturnStatus = EFI_SUCCESS;\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local");\r
+ if (ValueStr != NULL) {\r
+ Selector->LocalAddressCount = 1;\r
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->LocalAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--local",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= LOCAL;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote");\r
+ if (ValueStr != NULL) {\r
+ Selector->RemoteAddressCount = 1;\r
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->RemoteAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--remote",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= REMOTE;\r
+ }\r
+ }\r
+\r
+ Selector->NextLayerProtocol = EFI_IPSEC_ANY_PROTOCOL;\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+ //\r
+ Status = GetNumber (\r
+ L"--proto",\r
+ (UINT16) -1,\r
+ &Selector->NextLayerProtocol,\r
+ sizeof (UINT16),\r
+ mMapIpProtocol,\r
+ ParamPackage,\r
+ FORMAT_NUMBER | FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= PROTO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Selector->LocalPort = EFI_IPSEC_ANY_PORT;\r
+ Selector->RemotePort = EFI_IPSEC_ANY_PORT;\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local-port");\r
+ if (ValueStr != NULL) {\r
+ Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->LocalPort, &Selector->LocalPortRange);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--local-port",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= LOCAL_PORT;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote-port");\r
+ if (ValueStr != NULL) {\r
+ Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->RemotePort, &Selector->RemotePortRange);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--remote-port",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= REMOTE_PORT;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+ //\r
+ Status = GetNumber (\r
+ L"--icmp-type",\r
+ (UINT8) -1,\r
+ &Selector->LocalPort,\r
+ sizeof (UINT16),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= ICMP_TYPE;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.\r
+ //\r
+ Status = GetNumber (\r
+ L"--icmp-code",\r
+ (UINT8) -1,\r
+ &Selector->RemotePort,\r
+ sizeof (UINT16),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= ICMP_CODE;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+/**\r
+ Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA through ParamPackage list.\r
+\r
+ @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+ @param[out] Data The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+ @param[out] Mask The pointer to the Mask.\r
+ @param[in] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateSpdEntry (\r
+ OUT EFI_IPSEC_SPD_SELECTOR **Selector,\r
+ OUT EFI_IPSEC_SPD_DATA **Data,\r
+ IN LIST_ENTRY *ParamPackage,\r
+ OUT UINT32 *Mask,\r
+ IN BOOLEAN CreateNew\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ CONST CHAR16 *ValueStr;\r
+ UINTN DataSize;\r
+\r
+ Status = EFI_SUCCESS;\r
+ *Mask = 0;\r
+\r
+ *Selector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR) + 2 * sizeof (EFI_IP_ADDRESS_INFO));\r
+ ASSERT (*Selector != NULL);\r
+\r
+ (*Selector)->LocalAddress = (EFI_IP_ADDRESS_INFO *) (*Selector + 1);\r
+ (*Selector)->RemoteAddress = (*Selector)->LocalAddress + 1;\r
+\r
+ ReturnStatus = CreateSpdSelector (*Selector, ParamPackage, Mask);\r
+\r
+ //\r
+ // SPD DATA\r
+ // NOTE: Allocate enough memory and add padding for different arch.\r
+ //\r
+ DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SPD_DATA));\r
+ DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_PROCESS_POLICY));\r
+ DataSize += sizeof (EFI_IPSEC_TUNNEL_OPTION);\r
+\r
+ *Data = AllocateZeroPool (DataSize);\r
+ ASSERT (*Data != NULL);\r
+\r
+ (*Data)->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (\r
+ (*Data + 1),\r
+ sizeof (UINTN)\r
+ );\r
+ (*Data)->ProcessingPolicy->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER (\r
+ ((*Data)->ProcessingPolicy + 1),\r
+ sizeof (UINTN)\r
+ );\r
+\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the Name in EFI_IPSEC_SPD_DATA.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--name");\r
+ if (ValueStr != NULL) {\r
+ UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) (*Data)->Name);\r
+ *Mask |= NAME;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the PackageFlag in EFI_IPSEC_SPD_DATA.\r
+ //\r
+ Status = GetNumber (\r
+ L"--packet-flag",\r
+ (UINT8) -1,\r
+ &(*Data)->PackageFlag,\r
+ sizeof (UINT32),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= PACKET_FLAG;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the Action in EFI_IPSEC_SPD_DATA.\r
+ //\r
+ Status = GetNumber (\r
+ L"--action",\r
+ (UINT8) -1,\r
+ &(*Data)->Action,\r
+ sizeof (UINT32),\r
+ mMapIpSecAction,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= ACTION;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the ExtSeqNum in EFI_IPSEC_SPD_DATA.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence")) {\r
+ (*Data)->ProcessingPolicy->ExtSeqNum = TRUE;\r
+ *Mask |= EXT_SEQUENCE;\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence-")) {\r
+ (*Data)->ProcessingPolicy->ExtSeqNum = FALSE;\r
+ *Mask |= EXT_SEQUENCE;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the SeqOverflow in EFI_IPSEC_SPD_DATA.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow")) {\r
+ (*Data)->ProcessingPolicy->SeqOverflow = TRUE;\r
+ *Mask |= SEQUENCE_OVERFLOW;\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow-")) {\r
+ (*Data)->ProcessingPolicy->SeqOverflow = FALSE;\r
+ *Mask |= SEQUENCE_OVERFLOW;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the FragCheck in EFI_IPSEC_SPD_DATA.\r
+ //\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check")) {\r
+ (*Data)->ProcessingPolicy->FragCheck = TRUE;\r
+ *Mask |= FRAGMENT_CHECK;\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check-")) {\r
+ (*Data)->ProcessingPolicy->FragCheck = FALSE;\r
+ *Mask |= FRAGMENT_CHECK;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the ProcessingPolicy in EFI_IPSEC_SPD_DATA.\r
+ //\r
+ Status = GetNumber (\r
+ L"--lifebyte",\r
+ (UINT64) -1,\r
+ &(*Data)->ProcessingPolicy->SaLifetime.ByteCount,\r
+ sizeof (UINT64),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= LIFEBYTE;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--lifetime",\r
+ (UINT64) -1,\r
+ &(*Data)->ProcessingPolicy->SaLifetime.HardLifetime,\r
+ sizeof (UINT64),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= LIFETIME;\r
+ }\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--lifetime-soft",\r
+ (UINT64) -1,\r
+ &(*Data)->ProcessingPolicy->SaLifetime.SoftLifetime,\r
+ sizeof (UINT64),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= LIFETIME_SOFT;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ (*Data)->ProcessingPolicy->Mode = EfiIPsecTransport;\r
+ Status = GetNumber (\r
+ L"--mode",\r
+ 0,\r
+ &(*Data)->ProcessingPolicy->Mode,\r
+ sizeof (UINT32),\r
+ mMapIpSecMode,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= MODE;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-local");\r
+ if (ValueStr != NULL) {\r
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->LocalTunnelAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--tunnel-local",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= TUNNEL_LOCAL;\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-remote");\r
+ if (ValueStr != NULL) {\r
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--tunnel-remote",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= TUNNEL_REMOTE;\r
+ }\r
+ }\r
+\r
+ (*Data)->ProcessingPolicy->TunnelOption->DF = EfiIPsecTunnelCopyDf;\r
+ Status = GetNumber (\r
+ L"--dont-fragment",\r
+ 0,\r
+ &(*Data)->ProcessingPolicy->TunnelOption->DF,\r
+ sizeof (UINT32),\r
+ mMapDfOption,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= DONT_FRAGMENT;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ (*Data)->ProcessingPolicy->Proto = EfiIPsecESP;\r
+ Status = GetNumber (\r
+ L"--ipsec-proto",\r
+ 0,\r
+ &(*Data)->ProcessingPolicy->Proto,\r
+ sizeof (UINT32),\r
+ mMapIpSecProtocol,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= IPSEC_PROTO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--encrypt-algo",\r
+ 0,\r
+ &(*Data)->ProcessingPolicy->EncAlgoId,\r
+ sizeof (UINT8),\r
+ mMapEncAlgo,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= ENCRYPT_ALGO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--auth-algo",\r
+ 0,\r
+ &(*Data)->ProcessingPolicy->AuthAlgoId,\r
+ sizeof (UINT8),\r
+ mMapAuthAlgo,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= AUTH_ALGO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Cannot check Mode against EfiIPsecTunnel, because user may want to change tunnel_remote only so the Mode is not set.\r
+ //\r
+ if ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE | DONT_FRAGMENT)) == 0) {\r
+ (*Data)->ProcessingPolicy->TunnelOption = NULL;\r
+ }\r
+\r
+ if ((*Mask & (EXT_SEQUENCE | SEQUENCE_OVERFLOW | FRAGMENT_CHECK | LIFEBYTE |\r
+ LIFETIME_SOFT | LIFETIME | MODE | TUNNEL_LOCAL | TUNNEL_REMOTE |\r
+ DONT_FRAGMENT | IPSEC_PROTO | AUTH_ALGO | ENCRYPT_ALGO)) == 0) {\r
+ if ((*Data)->Action != EfiIPsecActionProtect) {\r
+ //\r
+ // User may not provide additional parameter for Protect action, so we cannot simply set ProcessingPolicy to NULL.\r
+ //\r
+ (*Data)->ProcessingPolicy = NULL;\r
+ }\r
+ }\r
+\r
+ if (CreateNew) {\r
+ if ((*Mask & (LOCAL | REMOTE | PROTO | ACTION)) != (LOCAL | REMOTE | PROTO | ACTION)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--local --remote --proto --action"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else if (((*Data)->Action == EfiIPsecActionProtect) &&\r
+ ((*Data)->ProcessingPolicy->Mode == EfiIPsecTunnel) &&\r
+ ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE))) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--tunnel-local --tunnel-remote"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+/**\r
+ Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA through ParamPackage list.\r
+\r
+ @param[out] SaId The pointer to the EFI_IPSEC_SA_ID structure.\r
+ @param[out] Data The pointer to the EFI_IPSEC_SA_DATA structure.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+ @param[out] Mask The pointer to the Mask.\r
+ @param[in] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateSadEntry (\r
+ OUT EFI_IPSEC_SA_ID **SaId,\r
+ OUT EFI_IPSEC_SA_DATA **Data,\r
+ IN LIST_ENTRY *ParamPackage,\r
+ OUT UINT32 *Mask,\r
+ IN BOOLEAN CreateNew\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ UINTN AuthKeyLength;\r
+ UINTN EncKeyLength;\r
+ CONST CHAR16 *ValueStr;\r
+ UINTN DataSize;\r
+\r
+ Status = EFI_SUCCESS;\r
+ ReturnStatus = EFI_SUCCESS;\r
+ *Mask = 0;\r
+ AuthKeyLength = 0;\r
+ EncKeyLength = 0;\r
+\r
+ *SaId = AllocateZeroPool (sizeof (EFI_IPSEC_SA_ID));\r
+ ASSERT (*SaId != NULL);\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the Spi in EFI_IPSEC_SA_ID.\r
+ //\r
+ Status = GetNumber (L"--spi", (UINT32) -1, &(*SaId)->Spi, sizeof (UINT32), NULL, ParamPackage, FORMAT_NUMBER);\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= SPI;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the Proto in EFI_IPSEC_SA_ID.\r
+ //\r
+ Status = GetNumber (\r
+ L"--ipsec-proto",\r
+ 0,\r
+ &(*SaId)->Proto,\r
+ sizeof (EFI_IPSEC_PROTOCOL_TYPE),\r
+ mMapIpSecProtocol,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= IPSEC_PROTO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--dest");\r
+ if (ValueStr != NULL) {\r
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*SaId)->DestAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--dest",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= DEST;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in EFI_IPSEC_SA_DATA.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");\r
+ if (ValueStr != NULL) {\r
+ AuthKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");\r
+ if (ValueStr != NULL) {\r
+ EncKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);\r
+ }\r
+\r
+ //\r
+ // EFI_IPSEC_SA_DATA:\r
+ // +------------\r
+ // | EFI_IPSEC_SA_DATA\r
+ // +-----------------------\r
+ // | AuthKey\r
+ // +-------------------------\r
+ // | EncKey\r
+ // +-------------------------\r
+ // | SpdSelector\r
+ //\r
+ // Notes: To make sure the address alignment add padding after each data if needed.\r
+ //\r
+ DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));\r
+ DataSize = ALIGN_VARIABLE (DataSize + AuthKeyLength);\r
+ DataSize = ALIGN_VARIABLE (DataSize + EncKeyLength);\r
+ DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_SPD_SELECTOR));\r
+ DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IP_ADDRESS_INFO));\r
+ DataSize += sizeof (EFI_IP_ADDRESS_INFO);\r
+\r
+\r
+\r
+ *Data = AllocateZeroPool (DataSize);\r
+ ASSERT (*Data != NULL);\r
+\r
+ (*Data)->ManualSet = TRUE;\r
+ (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER (((*Data) + 1), sizeof (UINTN));\r
+ (*Data)->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (\r
+ ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.AuthKey + AuthKeyLength),\r
+ sizeof (UINTN)\r
+ );\r
+ (*Data)->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER (\r
+ ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.EncKey + EncKeyLength),\r
+ sizeof (UINTN)\r
+ );\r
+ (*Data)->SpdSelector->LocalAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (\r
+ ((UINT8 *) (*Data)->SpdSelector + sizeof (EFI_IPSEC_SPD_SELECTOR)),\r
+ sizeof (UINTN));\r
+ (*Data)->SpdSelector->RemoteAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (\r
+ (*Data)->SpdSelector->LocalAddress + 1,\r
+ sizeof (UINTN)\r
+ );\r
+\r
+ (*Data)->Mode = EfiIPsecTransport;\r
+ Status = GetNumber (\r
+ L"--mode",\r
+ 0,\r
+ &(*Data)->Mode,\r
+ sizeof (EFI_IPSEC_MODE),\r
+ mMapIpSecMode,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= MODE;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // According to RFC 4303-3.3.3. The first packet sent using a given SA\r
+ // will contain a sequence number of 1.\r
+ //\r
+ (*Data)->SNCount = 1;\r
+ Status = GetNumber (\r
+ L"--sequence-number",\r
+ (UINT64) -1,\r
+ &(*Data)->SNCount,\r
+ sizeof (UINT64),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= SEQUENCE_NUMBER;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ (*Data)->AntiReplayWindows = 0;\r
+ Status = GetNumber (\r
+ L"--antireplay-window",\r
+ (UINT8) -1,\r
+ &(*Data)->AntiReplayWindows,\r
+ sizeof (UINT8),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= SEQUENCE_NUMBER;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--encrypt-algo",\r
+ 0,\r
+ &(*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId,\r
+ sizeof (UINT8),\r
+ mMapEncAlgo,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= ENCRYPT_ALGO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");\r
+ if (ValueStr != NULL ) {\r
+ (*Data)->AlgoInfo.EspAlgoInfo.EncKeyLength = EncKeyLength;\r
+ CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.EncKey, ValueStr, EncKeyLength);\r
+ *Mask |= ENCRYPT_KEY;\r
+ } else {\r
+ (*Data)->AlgoInfo.EspAlgoInfo.EncKey = NULL;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--auth-algo",\r
+ 0,\r
+ &(*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId,\r
+ sizeof (UINT8),\r
+ mMapAuthAlgo,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= AUTH_ALGO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");\r
+ if (ValueStr != NULL) {\r
+ (*Data)->AlgoInfo.EspAlgoInfo.AuthKeyLength = AuthKeyLength;\r
+ CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.AuthKey, ValueStr, AuthKeyLength);\r
+ *Mask |= AUTH_KEY;\r
+ } else {\r
+ (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = NULL;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--lifebyte",\r
+ (UINT64) -1,\r
+ &(*Data)->SaLifetime.ByteCount,\r
+ sizeof (UINT64),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= LIFEBYTE;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--lifetime",\r
+ (UINT64) -1,\r
+ &(*Data)->SaLifetime.HardLifetime,\r
+ sizeof (UINT64),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= LIFETIME;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--lifetime-soft",\r
+ (UINT64) -1,\r
+ &(*Data)->SaLifetime.SoftLifetime,\r
+ sizeof (UINT64),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= LIFETIME_SOFT;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--path-mtu",\r
+ (UINT32) -1,\r
+ &(*Data)->PathMTU,\r
+ sizeof (UINT32),\r
+ NULL,\r
+ ParamPackage,\r
+ FORMAT_NUMBER\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= PATH_MTU;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ReturnStatus = CreateSpdSelector ((*Data)->SpdSelector, ParamPackage, Mask);\r
+\r
+ if (CreateNew) {\r
+ if ((*Mask & (SPI | IPSEC_PROTO | DEST)) != (SPI | IPSEC_PROTO | DEST)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--spi --ipsec-proto --dest"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ if ((*SaId)->Proto == EfiIPsecAH) {\r
+ if ((*Mask & AUTH_ALGO) == 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--auth-algo"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != EFI_IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--auth-key"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+ } else {\r
+ if ((*Mask & ENCRYPT_ALGO) == 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--encrypt-algo"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else if ((*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (*Mask & ENCRYPT_KEY) == 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--encrypt-key"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+/**\r
+ Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA through ParamPackage list.\r
+\r
+ @param[out] PadId The pointer to the EFI_IPSEC_PAD_ID structure.\r
+ @param[out] Data The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+ @param[out] Mask The pointer to the Mask.\r
+ @param[in] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CreatePadEntry (\r
+ OUT EFI_IPSEC_PAD_ID **PadId,\r
+ OUT EFI_IPSEC_PAD_DATA **Data,\r
+ IN LIST_ENTRY *ParamPackage,\r
+ OUT UINT32 *Mask,\r
+ IN BOOLEAN CreateNew\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ EFI_FILE_HANDLE FileHandle;\r
+ UINT64 FileSize;\r
+ UINTN AuthDataLength;\r
+ UINTN RevocationDataLength;\r
+ UINTN DataLength;\r
+ UINTN Index;\r
+ CONST CHAR16 *ValueStr;\r
+ UINTN DataSize;\r
+\r
+ Status = EFI_SUCCESS;\r
+ ReturnStatus = EFI_SUCCESS;\r
+ *Mask = 0;\r
+ AuthDataLength = 0;\r
+ RevocationDataLength = 0;\r
+\r
+ *PadId = AllocateZeroPool (sizeof (EFI_IPSEC_PAD_ID));\r
+ ASSERT (*PadId != NULL);\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_ID.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-address");\r
+ if (ValueStr != NULL) {\r
+ (*PadId)->PeerIdValid = FALSE;\r
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &(*PadId)->Id.IpAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--peer-address",\r
+ ValueStr\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ *Mask |= PEER_ADDRESS;\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-id");\r
+ if (ValueStr != NULL) {\r
+ (*PadId)->PeerIdValid = TRUE;\r
+ StrnCpy ((CHAR16 *) (*PadId)->Id.PeerId, ValueStr, ARRAY_SIZE ((*PadId)->Id.PeerId) - 1);\r
+ *Mask |= PEER_ID;\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");\r
+ if (ValueStr != NULL) {\r
+ if (ValueStr[0] == L'@') {\r
+ //\r
+ // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"\r
+ //\r
+ Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+ mHiiHandle,\r
+ mAppName,\r
+ &ValueStr[1]\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ Status = ShellGetFileSize (FileHandle, &FileSize);\r
+ ShellCloseFile (&FileHandle);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+ mHiiHandle,\r
+ mAppName,\r
+ &ValueStr[1]\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else {\r
+ AuthDataLength = (UINTN) FileSize;\r
+ }\r
+ }\r
+ } else {\r
+ AuthDataLength = StrLen (ValueStr);\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");\r
+ if (ValueStr != NULL) {\r
+ RevocationDataLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);\r
+ }\r
+\r
+ //\r
+ // Allocate Buffer for Data. Add padding after each struct to make sure the alignment\r
+ // in different Arch.\r
+ //\r
+ DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));\r
+ DataSize = ALIGN_VARIABLE (DataSize + AuthDataLength);\r
+ DataSize += RevocationDataLength;\r
+\r
+ *Data = AllocateZeroPool (DataSize);\r
+ ASSERT (*Data != NULL);\r
+\r
+ (*Data)->AuthData = (VOID *) ALIGN_POINTER ((*Data + 1), sizeof (UINTN));\r
+ (*Data)->RevocationData = (VOID *) ALIGN_POINTER (((UINT8 *) (*Data + 1) + AuthDataLength), sizeof (UINTN));\r
+ (*Data)->AuthProtocol = EfiIPsecAuthProtocolIKEv1;\r
+\r
+ //\r
+ // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_DATA.\r
+ //\r
+ Status = GetNumber (\r
+ L"--auth-proto",\r
+ 0,\r
+ &(*Data)->AuthProtocol,\r
+ sizeof (EFI_IPSEC_AUTH_PROTOCOL_TYPE),\r
+ mMapAuthProto,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= AUTH_PROTO;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetNumber (\r
+ L"--auth-method",\r
+ 0,\r
+ &(*Data)->AuthMethod,\r
+ sizeof (EFI_IPSEC_AUTH_METHOD),\r
+ mMapAuthMethod,\r
+ ParamPackage,\r
+ FORMAT_STRING\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *Mask |= AUTH_METHOD;\r
+ }\r
+\r
+ if (Status == EFI_INVALID_PARAMETER) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id")) {\r
+ (*Data)->IkeIdFlag = TRUE;\r
+ *Mask |= IKE_ID;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id-")) {\r
+ (*Data)->IkeIdFlag = FALSE;\r
+ *Mask |= IKE_ID;\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");\r
+ if (ValueStr != NULL) {\r
+ if (ValueStr[0] == L'@') {\r
+ //\r
+ // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"\r
+ //\r
+\r
+ Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+ mHiiHandle,\r
+ mAppName,\r
+ &ValueStr[1]\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ (*Data)->AuthData = NULL;\r
+ } else {\r
+ DataLength = AuthDataLength;\r
+ Status = ShellReadFile (FileHandle, &DataLength, (*Data)->AuthData);\r
+ ShellCloseFile (&FileHandle);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),\r
+ mHiiHandle,\r
+ mAppName,\r
+ &ValueStr[1]\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ (*Data)->AuthData = NULL;\r
+ } else {\r
+ ASSERT (DataLength == AuthDataLength);\r
+ *Mask |= AUTH_DATA;\r
+ }\r
+ }\r
+ } else {\r
+ for (Index = 0; Index < AuthDataLength; Index++) {\r
+ ((CHAR8 *) (*Data)->AuthData)[Index] = (CHAR8) ValueStr[Index];\r
+ }\r
+ (*Data)->AuthDataSize = AuthDataLength;\r
+ *Mask |= AUTH_DATA;\r
+ }\r
+ }\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");\r
+ if (ValueStr != NULL) {\r
+ CopyMem ((*Data)->RevocationData, ValueStr, RevocationDataLength);\r
+ (*Data)->RevocationDataSize = RevocationDataLength;\r
+ *Mask |= REVOCATION_DATA;\r
+ } else {\r
+ (*Data)->RevocationData = NULL;\r
+ }\r
+\r
+ if (CreateNew) {\r
+ if ((*Mask & (PEER_ID | PEER_ADDRESS)) == 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--peer-id --peer-address"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ } else if ((*Mask & (AUTH_METHOD | AUTH_DATA)) != (AUTH_METHOD | AUTH_DATA)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--auth-method --auth-data"\r
+ );\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+CREATE_POLICY_ENTRY mCreatePolicyEntry[] = {\r
+ (CREATE_POLICY_ENTRY) CreateSpdEntry,\r
+ (CREATE_POLICY_ENTRY) CreateSadEntry,\r
+ (CREATE_POLICY_ENTRY) CreatePadEntry\r
+};\r
+\r
+/**\r
+ Combine old SPD entry with new SPD entry.\r
+\r
+ @param[in, out] OldSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+ @param[in, out] OldData The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+ @param[in] NewSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.\r
+ @param[in] NewData The pointer to the EFI_IPSEC_SPD_DATA structure.\r
+ @param[in] Mask The pointer to the Mask.\r
+ @param[out] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Combined successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CombineSpdEntry (\r
+ IN OUT EFI_IPSEC_SPD_SELECTOR *OldSelector,\r
+ IN OUT EFI_IPSEC_SPD_DATA *OldData,\r
+ IN EFI_IPSEC_SPD_SELECTOR *NewSelector,\r
+ IN EFI_IPSEC_SPD_DATA *NewData,\r
+ IN UINT32 Mask,\r
+ OUT BOOLEAN *CreateNew\r
+ )\r
+{\r
+\r
+ //\r
+ // Process Selector\r
+ //\r
+ *CreateNew = FALSE;\r
+ if ((Mask & LOCAL) == 0) {\r
+ NewSelector->LocalAddressCount = OldSelector->LocalAddressCount;\r
+ NewSelector->LocalAddress = OldSelector->LocalAddress;\r
+ } else if ((NewSelector->LocalAddressCount != OldSelector->LocalAddressCount) ||\r
+ (CompareMem (NewSelector->LocalAddress, OldSelector->LocalAddress, NewSelector->LocalAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ if ((Mask & REMOTE) == 0) {\r
+ NewSelector->RemoteAddressCount = OldSelector->RemoteAddressCount;\r
+ NewSelector->RemoteAddress = OldSelector->RemoteAddress;\r
+ } else if ((NewSelector->RemoteAddressCount != OldSelector->RemoteAddressCount) ||\r
+ (CompareMem (NewSelector->RemoteAddress, OldSelector->RemoteAddress, NewSelector->RemoteAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ if ((Mask & PROTO) == 0) {\r
+ NewSelector->NextLayerProtocol = OldSelector->NextLayerProtocol;\r
+ } else if (NewSelector->NextLayerProtocol != OldSelector->NextLayerProtocol) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ switch (NewSelector->NextLayerProtocol) {\r
+ case EFI_IP4_PROTO_TCP:\r
+ case EFI_IP4_PROTO_UDP:\r
+ if ((Mask & LOCAL_PORT) == 0) {\r
+ NewSelector->LocalPort = OldSelector->LocalPort;\r
+ NewSelector->LocalPortRange = OldSelector->LocalPortRange;\r
+ } else if ((NewSelector->LocalPort != OldSelector->LocalPort) ||\r
+ (NewSelector->LocalPortRange != OldSelector->LocalPortRange)) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ if ((Mask & REMOTE_PORT) == 0) {\r
+ NewSelector->RemotePort = OldSelector->RemotePort;\r
+ NewSelector->RemotePortRange = OldSelector->RemotePortRange;\r
+ } else if ((NewSelector->RemotePort != OldSelector->RemotePort) ||\r
+ (NewSelector->RemotePortRange != OldSelector->RemotePortRange)) {\r
+ *CreateNew = TRUE;\r
+ }\r
+ break;\r
+\r
+ case EFI_IP4_PROTO_ICMP:\r
+ if ((Mask & ICMP_TYPE) == 0) {\r
+ NewSelector->LocalPort = OldSelector->LocalPort;\r
+ } else if (NewSelector->LocalPort != OldSelector->LocalPort) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ if ((Mask & ICMP_CODE) == 0) {\r
+ NewSelector->RemotePort = OldSelector->RemotePort;\r
+ } else if (NewSelector->RemotePort != OldSelector->RemotePort) {\r
+ *CreateNew = TRUE;\r
+ }\r
+ break;\r
+ }\r
+ //\r
+ // Process Data\r
+ //\r
+ if ((Mask & NAME) != 0) {\r
+ AsciiStrCpy ((CHAR8 *) OldData->Name, (CHAR8 *) NewData->Name);\r
+ }\r
+\r
+ if ((Mask & PACKET_FLAG) != 0) {\r
+ OldData->PackageFlag = NewData->PackageFlag;\r
+ }\r
+\r
+ if ((Mask & ACTION) != 0) {\r
+ OldData->Action = NewData->Action;\r
+ }\r
+\r
+ if (OldData->Action != EfiIPsecActionProtect) {\r
+ OldData->ProcessingPolicy = NULL;\r
+ } else {\r
+ //\r
+ // Protect\r
+ //\r
+ if (OldData->ProcessingPolicy == NULL) {\r
+ //\r
+ // Just point to new data if originally NULL.\r
+ //\r
+ OldData->ProcessingPolicy = NewData->ProcessingPolicy;\r
+ if (OldData->ProcessingPolicy->Mode == EfiIPsecTunnel &&\r
+ (Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)\r
+ ) {\r
+ //\r
+ // Change to Protect action and Tunnel mode, but without providing local/remote tunnel address.\r
+ //\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--tunnel-local --tunnel-remote"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } else {\r
+ //\r
+ // Modify some of the data.\r
+ //\r
+ if ((Mask & EXT_SEQUENCE) != 0) {\r
+ OldData->ProcessingPolicy->ExtSeqNum = NewData->ProcessingPolicy->ExtSeqNum;\r
+ }\r
+\r
+ if ((Mask & SEQUENCE_OVERFLOW) != 0) {\r
+ OldData->ProcessingPolicy->SeqOverflow = NewData->ProcessingPolicy->SeqOverflow;\r
+ }\r
+\r
+ if ((Mask & FRAGMENT_CHECK) != 0) {\r
+ OldData->ProcessingPolicy->FragCheck = NewData->ProcessingPolicy->FragCheck;\r
+ }\r
+\r
+ if ((Mask & LIFEBYTE) != 0) {\r
+ OldData->ProcessingPolicy->SaLifetime.ByteCount = NewData->ProcessingPolicy->SaLifetime.ByteCount;\r
+ }\r
+\r
+ if ((Mask & LIFETIME_SOFT) != 0) {\r
+ OldData->ProcessingPolicy->SaLifetime.SoftLifetime = NewData->ProcessingPolicy->SaLifetime.SoftLifetime;\r
+ }\r
+\r
+ if ((Mask & LIFETIME) != 0) {\r
+ OldData->ProcessingPolicy->SaLifetime.HardLifetime = NewData->ProcessingPolicy->SaLifetime.HardLifetime;\r
+ }\r
+\r
+ if ((Mask & MODE) != 0) {\r
+ OldData->ProcessingPolicy->Mode = NewData->ProcessingPolicy->Mode;\r
+ }\r
+\r
+ if ((Mask & IPSEC_PROTO) != 0) {\r
+ OldData->ProcessingPolicy->Proto = NewData->ProcessingPolicy->Proto;\r
+ }\r
+\r
+ if ((Mask & AUTH_ALGO) != 0) {\r
+ OldData->ProcessingPolicy->AuthAlgoId = NewData->ProcessingPolicy->AuthAlgoId;\r
+ }\r
+\r
+ if ((Mask & ENCRYPT_ALGO) != 0) {\r
+ OldData->ProcessingPolicy->EncAlgoId = NewData->ProcessingPolicy->EncAlgoId;\r
+ }\r
+\r
+ if (OldData->ProcessingPolicy->Mode != EfiIPsecTunnel) {\r
+ OldData->ProcessingPolicy->TunnelOption = NULL;\r
+ } else {\r
+ if (OldData->ProcessingPolicy->TunnelOption == NULL) {\r
+ //\r
+ // Set from Transport mode to Tunnel mode, should ensure TUNNEL_LOCAL & TUNNEL_REMOTE both exists.\r
+ //\r
+ if ((Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--tunnel-local --tunnel-remote"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldData->ProcessingPolicy->TunnelOption = NewData->ProcessingPolicy->TunnelOption;\r
+ } else {\r
+ if ((Mask & TUNNEL_LOCAL) != 0) {\r
+ CopyMem (\r
+ &OldData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,\r
+ &NewData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ );\r
+ }\r
+\r
+ if ((Mask & TUNNEL_REMOTE) != 0) {\r
+ CopyMem (\r
+ &OldData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,\r
+ &NewData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ );\r
+ }\r
+\r
+ if ((Mask & DONT_FRAGMENT) != 0) {\r
+ OldData->ProcessingPolicy->TunnelOption->DF = NewData->ProcessingPolicy->TunnelOption->DF;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Combine old SAD entry with new SAD entry.\r
+\r
+ @param[in, out] OldSaId The pointer to the EFI_IPSEC_SA_ID structure.\r
+ @param[in, out] OldData The pointer to the EFI_IPSEC_SA_DATA structure.\r
+ @param[in] NewSaId The pointer to the EFI_IPSEC_SA_ID structure.\r
+ @param[in] NewData The pointer to the EFI_IPSEC_SA_DATA structure.\r
+ @param[in] Mask The pointer to the Mask.\r
+ @param[out] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Combined successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CombineSadEntry (\r
+ IN OUT EFI_IPSEC_SA_ID *OldSaId,\r
+ IN OUT EFI_IPSEC_SA_DATA *OldData,\r
+ IN EFI_IPSEC_SA_ID *NewSaId,\r
+ IN EFI_IPSEC_SA_DATA *NewData,\r
+ IN UINT32 Mask,\r
+ OUT BOOLEAN *CreateNew\r
+ )\r
+{\r
+\r
+ *CreateNew = FALSE;\r
+\r
+ if ((Mask & SPI) == 0) {\r
+ NewSaId->Spi = OldSaId->Spi;\r
+ } else if (NewSaId->Spi != OldSaId->Spi) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ if ((Mask & IPSEC_PROTO) == 0) {\r
+ NewSaId->Proto = OldSaId->Proto;\r
+ } else if (NewSaId->Proto != OldSaId->Proto) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ if ((Mask & DEST) == 0) {\r
+ CopyMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS));\r
+ } else if (CompareMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS)) != 0) {\r
+ *CreateNew = TRUE;\r
+ }\r
+\r
+ //\r
+ // Process SA_DATA.\r
+ //\r
+ if ((Mask & MODE) != 0) {\r
+ OldData->Mode = NewData->Mode;\r
+ }\r
+\r
+ if ((Mask & SEQUENCE_NUMBER) != 0) {\r
+ OldData->SNCount = NewData->SNCount;\r
+ }\r
+\r
+ if ((Mask & ANTIREPLAY_WINDOW) != 0) {\r
+ OldData->AntiReplayWindows = NewData->AntiReplayWindows;\r
+ }\r
+\r
+ if ((Mask & AUTH_ALGO) != 0) {\r
+ OldData->AlgoInfo.EspAlgoInfo.AuthAlgoId = NewData->AlgoInfo.EspAlgoInfo.AuthAlgoId;\r
+ }\r
+\r
+ if ((Mask & AUTH_KEY) != 0) {\r
+ OldData->AlgoInfo.EspAlgoInfo.AuthKey = NewData->AlgoInfo.EspAlgoInfo.AuthKey;\r
+ OldData->AlgoInfo.EspAlgoInfo.AuthKeyLength = NewData->AlgoInfo.EspAlgoInfo.AuthKeyLength;\r
+ }\r
+\r
+ if ((Mask & ENCRYPT_ALGO) != 0) {\r
+ OldData->AlgoInfo.EspAlgoInfo.EncAlgoId = NewData->AlgoInfo.EspAlgoInfo.EncAlgoId;\r
+ }\r
+\r
+ if ((Mask & ENCRYPT_KEY) != 0) {\r
+ OldData->AlgoInfo.EspAlgoInfo.EncKey = NewData->AlgoInfo.EspAlgoInfo.EncKey;\r
+ OldData->AlgoInfo.EspAlgoInfo.EncKeyLength = NewData->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+ }\r
+\r
+ if (NewSaId->Proto == EfiIPsecAH) {\r
+ if ((Mask & (ENCRYPT_ALGO | ENCRYPT_KEY)) != 0) {\r
+ //\r
+ // Should not provide encrypt_* if AH.\r
+ //\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_UNWANTED_PARAMETER),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--encrypt-algo --encrypt-key"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if (NewSaId->Proto == EfiIPsecESP && OldSaId->Proto == EfiIPsecAH) {\r
+ //\r
+ // AH -> ESP\r
+ // Should provide encrypt_algo at least.\r
+ //\r
+ if ((Mask & ENCRYPT_ALGO) == 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--encrypt-algo"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Encrypt_key should be provided if algorithm is not NONE.\r
+ //\r
+ if (NewData->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (Mask & ENCRYPT_KEY) == 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--encrypt-algo"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if ((Mask & LIFEBYTE) != 0) {\r
+ OldData->SaLifetime.ByteCount = NewData->SaLifetime.ByteCount;\r
+ }\r
+\r
+ if ((Mask & LIFETIME_SOFT) != 0) {\r
+ OldData->SaLifetime.SoftLifetime = NewData->SaLifetime.SoftLifetime;\r
+ }\r
+\r
+ if ((Mask & LIFETIME) != 0) {\r
+ OldData->SaLifetime.HardLifetime = NewData->SaLifetime.HardLifetime;\r
+ }\r
+\r
+ if ((Mask & PATH_MTU) != 0) {\r
+ OldData->PathMTU = NewData->PathMTU;\r
+ }\r
+ //\r
+ // Process SpdSelector.\r
+ //\r
+ if (OldData->SpdSelector == NULL) {\r
+ if ((Mask & (LOCAL | REMOTE | PROTO | LOCAL_PORT | REMOTE_PORT | ICMP_TYPE | ICMP_CODE)) != 0) {\r
+ if ((Mask & (LOCAL | REMOTE | PROTO)) != (LOCAL | REMOTE | PROTO)) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),\r
+ mHiiHandle,\r
+ mAppName,\r
+ L"--local --remote --proto"\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldData->SpdSelector = NewData->SpdSelector;\r
+ }\r
+ } else {\r
+ if ((Mask & LOCAL) != 0) {\r
+ OldData->SpdSelector->LocalAddressCount = NewData->SpdSelector->LocalAddressCount;\r
+ OldData->SpdSelector->LocalAddress = NewData->SpdSelector->LocalAddress;\r
+ }\r
+\r
+ if ((Mask & REMOTE) != 0) {\r
+ OldData->SpdSelector->RemoteAddressCount = NewData->SpdSelector->RemoteAddressCount;\r
+ OldData->SpdSelector->RemoteAddress = NewData->SpdSelector->RemoteAddress;\r
+ }\r
+\r
+ if ((Mask & PROTO) != 0) {\r
+ OldData->SpdSelector->NextLayerProtocol = NewData->SpdSelector->NextLayerProtocol;\r
+ }\r
+\r
+ if (OldData->SpdSelector != NULL) {\r
+ switch (OldData->SpdSelector->NextLayerProtocol) {\r
+ case EFI_IP4_PROTO_TCP:\r
+ case EFI_IP4_PROTO_UDP:\r
+ if ((Mask & LOCAL_PORT) != 0) {\r
+ OldData->SpdSelector->LocalPort = NewData->SpdSelector->LocalPort;\r
+ }\r
+\r
+ if ((Mask & REMOTE_PORT) != 0) {\r
+ OldData->SpdSelector->RemotePort = NewData->SpdSelector->RemotePort;\r
+ }\r
+ break;\r
+\r
+ case EFI_IP4_PROTO_ICMP:\r
+ if ((Mask & ICMP_TYPE) != 0) {\r
+ OldData->SpdSelector->LocalPort = (UINT8) NewData->SpdSelector->LocalPort;\r
+ }\r
+\r
+ if ((Mask & ICMP_CODE) != 0) {\r
+ OldData->SpdSelector->RemotePort = (UINT8) NewData->SpdSelector->RemotePort;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Combine old PAD entry with new PAD entry.\r
+\r
+ @param[in, out] OldPadId The pointer to the EFI_IPSEC_PAD_ID structure.\r
+ @param[in, out] OldData The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+ @param[in] NewPadId The pointer to the EFI_IPSEC_PAD_ID structure.\r
+ @param[in] NewData The pointer to the EFI_IPSEC_PAD_DATA structure.\r
+ @param[in] Mask The pointer to the Mask.\r
+ @param[out] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Combined successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+EFI_STATUS\r
+CombinePadEntry (\r
+ IN OUT EFI_IPSEC_PAD_ID *OldPadId,\r
+ IN OUT EFI_IPSEC_PAD_DATA *OldData,\r
+ IN EFI_IPSEC_PAD_ID *NewPadId,\r
+ IN EFI_IPSEC_PAD_DATA *NewData,\r
+ IN UINT32 Mask,\r
+ OUT BOOLEAN *CreateNew\r
+ )\r
+{\r
+\r
+ *CreateNew = FALSE;\r
+\r
+ if ((Mask & (PEER_ID | PEER_ADDRESS)) == 0) {\r
+ CopyMem (NewPadId, OldPadId, sizeof (EFI_IPSEC_PAD_ID));\r
+ } else {\r
+ if ((Mask & PEER_ID) != 0) {\r
+ if (OldPadId->PeerIdValid) {\r
+ if (StrCmp ((CONST CHAR16 *) OldPadId->Id.PeerId, (CONST CHAR16 *) NewPadId->Id.PeerId) != 0) {\r
+ *CreateNew = TRUE;\r
+ }\r
+ } else {\r
+ *CreateNew = TRUE;\r
+ }\r
+ } else {\r
+ //\r
+ // MASK & PEER_ADDRESS\r
+ //\r
+ if (OldPadId->PeerIdValid) {\r
+ *CreateNew = TRUE;\r
+ } else {\r
+ if ((CompareMem (&OldPadId->Id.IpAddress.Address, &NewPadId->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) ||\r
+ (OldPadId->Id.IpAddress.PrefixLength != NewPadId->Id.IpAddress.PrefixLength)) {\r
+ *CreateNew = TRUE;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if ((Mask & AUTH_PROTO) != 0) {\r
+ OldData->AuthProtocol = NewData->AuthProtocol;\r
+ }\r
+\r
+ if ((Mask & AUTH_METHOD) != 0) {\r
+ OldData->AuthMethod = NewData->AuthMethod;\r
+ }\r
+\r
+ if ((Mask & IKE_ID) != 0) {\r
+ OldData->IkeIdFlag = NewData->IkeIdFlag;\r
+ }\r
+\r
+ if ((Mask & AUTH_DATA) != 0) {\r
+ OldData->AuthDataSize = NewData->AuthDataSize;\r
+ OldData->AuthData = NewData->AuthData;\r
+ }\r
+\r
+ if ((Mask & REVOCATION_DATA) != 0) {\r
+ OldData->RevocationDataSize = NewData->RevocationDataSize;\r
+ OldData->RevocationData = NewData->RevocationData;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+COMBINE_POLICY_ENTRY mCombinePolicyEntry[] = {\r
+ (COMBINE_POLICY_ENTRY) CombineSpdEntry,\r
+ (COMBINE_POLICY_ENTRY) CombineSadEntry,\r
+ (COMBINE_POLICY_ENTRY) CombinePadEntry\r
+};\r
+\r
+/**\r
+ Edit entry information in the database.\r
+\r
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.\r
+ @param[in] Data The pointer to the data.\r
+ @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.\r
+\r
+ @retval EFI_SUCCESS Continue the iteration.\r
+ @retval EFI_ABORTED Abort the iteration.\r
+**/\r
+EFI_STATUS\r
+EditOperatePolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN EDIT_POLICY_ENTRY_CONTEXT *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN CreateNew;\r
+\r
+ if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {\r
+ ASSERT (Context->DataType < 3);\r
+\r
+ Status = mCombinePolicyEntry[Context->DataType] (\r
+ Selector,\r
+ Data,\r
+ Context->Selector,\r
+ Context->Data,\r
+ Context->Mask,\r
+ &CreateNew\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ if (CreateNew) {\r
+ //\r
+ // Insert new entry before old entry\r
+ //\r
+ Status = mIpSecConfig->SetData (\r
+ mIpSecConfig,\r
+ Context->DataType,\r
+ Context->Selector,\r
+ Data,\r
+ Selector\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ //\r
+ // Delete old entry\r
+ //\r
+ Status = mIpSecConfig->SetData (\r
+ mIpSecConfig,\r
+ Context->DataType,\r
+ Selector,\r
+ NULL,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ } else {\r
+ Status = mIpSecConfig->SetData (\r
+ mIpSecConfig,\r
+ Context->DataType,\r
+ Context->Selector,\r
+ Data,\r
+ NULL\r
+ );\r
+ }\r
+ }\r
+\r
+ Context->Status = Status;\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Edit entry information in database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Edit entry information successfully.\r
+ @retval EFI_NOT_FOUND Can't find the specified entry.\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+EditPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EDIT_POLICY_ENTRY_CONTEXT Context;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");\r
+ if (ValueStr == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);\r
+ if (!EFI_ERROR (Status)) {\r
+ Context.DataType = DataType;\r
+ Context.Status = EFI_NOT_FOUND;\r
+ Status = mCreatePolicyEntry[DataType] (&Context.Selector, &Context.Data, ParamPackage, &Context.Mask, FALSE);\r
+ if (!EFI_ERROR (Status)) {\r
+ ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) EditOperatePolicyEntry, &Context);\r
+ Status = Context.Status;\r
+ }\r
+\r
+ if (Context.Selector != NULL) {\r
+ gBS->FreePool (Context.Selector);\r
+ }\r
+\r
+ if (Context.Data != NULL) {\r
+ gBS->FreePool (Context.Data);\r
+ }\r
+ }\r
+\r
+ if (Status == EFI_NOT_FOUND) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);\r
+ } else if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_EDIT_FAILED), mHiiHandle, mAppName);\r
+ }\r
+\r
+ return Status;\r
+\r
+}\r
+\r
+/**\r
+ Insert entry information in database.\r
+\r
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.\r
+ @param[in] Data The pointer to the data.\r
+ @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.\r
+\r
+ @retval EFI_SUCCESS Continue the iteration.\r
+ @retval EFI_ABORTED Abort the iteration.\r
+**/\r
+EFI_STATUS\r
+InsertPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN INSERT_POLICY_ENTRY_CONTEXT *Context\r
+ )\r
+{\r
+ //\r
+ // Found the entry which we want to insert before.\r
+ //\r
+ if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {\r
+\r
+ Context->Status = mIpSecConfig->SetData (\r
+ mIpSecConfig,\r
+ Context->DataType,\r
+ Context->Selector,\r
+ Context->Data,\r
+ Selector\r
+ );\r
+ //\r
+ // Abort the iteration after the insertion.\r
+ //\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Insert or add entry information in database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Insert or add entry information successfully.\r
+ @retval EFI_NOT_FOUND Can't find the specified entry.\r
+ @retval EFI_BUFFER_TOO_SMALL The entry already existed.\r
+ @retval EFI_UNSUPPORTED The operation is not supported.\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+AddOrInsertPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;\r
+ VOID *Data;\r
+ INSERT_POLICY_ENTRY_CONTEXT Context;\r
+ UINT32 Mask;\r
+ UINTN DataSize;\r
+ CONST CHAR16 *ValueStr;\r
+\r
+ Status = mCreatePolicyEntry[DataType] (&Selector, &Data, ParamPackage, &Mask, TRUE);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Find if the Selector to be inserted already exists.\r
+ //\r
+ DataSize = 0;\r
+ Status = mIpSecConfig->GetData (\r
+ mIpSecConfig,\r
+ DataType,\r
+ Selector,\r
+ &DataSize,\r
+ NULL\r
+ );\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_EXISTS), mHiiHandle, mAppName);\r
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {\r
+ Status = mIpSecConfig->SetData (\r
+ mIpSecConfig,\r
+ DataType,\r
+ Selector,\r
+ Data,\r
+ NULL\r
+ );\r
+ } else {\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");\r
+ if (ValueStr == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);\r
+ if (!EFI_ERROR (Status)) {\r
+ Context.DataType = DataType;\r
+ Context.Status = EFI_NOT_FOUND;\r
+ Context.Selector = Selector;\r
+ Context.Data = Data;\r
+\r
+ ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) InsertPolicyEntry, &Context);\r
+ Status = Context.Status;\r
+ if (Status == EFI_NOT_FOUND) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);\r
+ }\r
+ }\r
+ }\r
+\r
+ gBS->FreePool (Selector);\r
+ gBS->FreePool (Data);\r
+ }\r
+\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_UNSUPPORT), mHiiHandle, mAppName);\r
+ } else if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_FAILED), mHiiHandle, mAppName);\r
+ }\r
+\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+ The function declaration of policy entry operation in IpSecConfig application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _POLICY_ENTRY_OPERATION_H_\r
+#define _POLICY_ENTRY_OPERATION_H_\r
+\r
+#define LOCAL BIT(0)\r
+#define REMOTE BIT(1)\r
+#define PROTO BIT(2)\r
+#define LOCAL_PORT BIT(3)\r
+#define REMOTE_PORT BIT(4)\r
+#define ICMP_TYPE BIT(5)\r
+#define ICMP_CODE BIT(6)\r
+#define NAME BIT(7)\r
+#define PACKET_FLAG BIT(8)\r
+#define ACTION BIT(9)\r
+#define EXT_SEQUENCE BIT(10)\r
+#define SEQUENCE_OVERFLOW BIT(11)\r
+#define FRAGMENT_CHECK BIT(12)\r
+#define LIFEBYTE BIT(13)\r
+#define LIFETIME_SOFT BIT(14)\r
+#define LIFETIME BIT(15)\r
+#define MODE BIT(16)\r
+#define TUNNEL_LOCAL BIT(17)\r
+#define TUNNEL_REMOTE BIT(18)\r
+#define DONT_FRAGMENT BIT(19)\r
+#define IPSEC_PROTO BIT(20)\r
+#define AUTH_ALGO BIT(21)\r
+#define ENCRYPT_ALGO BIT(22)\r
+#define SPI BIT(23)\r
+#define DEST BIT(24)\r
+#define SEQUENCE_NUMBER BIT(25)\r
+#define ANTIREPLAY_WINDOW BIT(26)\r
+#define AUTH_KEY BIT(27)\r
+#define ENCRYPT_KEY BIT(28)\r
+#define PATH_MTU BIT(29)\r
+\r
+#define PEER_ID BIT(0)\r
+#define PEER_ADDRESS BIT(1)\r
+#define AUTH_PROTO BIT(2)\r
+#define AUTH_METHOD BIT(3)\r
+#define IKE_ID BIT(4)\r
+#define AUTH_DATA BIT(5)\r
+#define REVOCATION_DATA BIT(6)\r
+\r
+typedef struct {\r
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;\r
+ EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted.\r
+ VOID *Data;\r
+ UINT32 Mask;\r
+ POLICY_ENTRY_INDEXER Indexer;\r
+ EFI_STATUS Status; // Indicate whether insertion succeeds.\r
+} EDIT_POLICY_ENTRY_CONTEXT;\r
+\r
+typedef struct {\r
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;\r
+ EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted.\r
+ VOID *Data;\r
+ POLICY_ENTRY_INDEXER Indexer;\r
+ EFI_STATUS Status; // Indicate whether insertion succeeds.\r
+} INSERT_POLICY_ENTRY_CONTEXT;\r
+\r
+/**\r
+ The prototype for the CreateSpdEntry()/CreateSadEntry()/CreatePadEntry().\r
+ Fill in EFI_IPSEC_CONFIG_SELECTOR and corresponding data thru ParamPackage list.\r
+\r
+ @param[out] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.\r
+ @param[out] Data The pointer to corresponding data.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+ @param[out] Mask The pointer to the Mask.\r
+ @param[in] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Filled in EFI_IPSEC_CONFIG_SELECTOR and corresponding data successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*CREATE_POLICY_ENTRY) (\r
+ OUT EFI_IPSEC_CONFIG_SELECTOR **Selector,\r
+ OUT VOID **Data,\r
+ IN LIST_ENTRY *ParamPackage,\r
+ OUT UINT32 *Mask,\r
+ IN BOOLEAN CreateNew\r
+ );\r
+\r
+/**\r
+ The prototype for the CombineSpdEntry()/CombineSadEntry()/CombinePadEntry().\r
+ Combine old SPD/SAD/PAD entry with new SPD/SAD/PAD entry.\r
+\r
+ @param[in, out] OldSelector The pointer to the old EFI_IPSEC_CONFIG_SELECTOR union.\r
+ @param[in, out] OldData The pointer to the corresponding old data.\r
+ @param[in] NewSelector The pointer to the new EFI_IPSEC_CONFIG_SELECTOR union.\r
+ @param[in] NewData The pointer to the corresponding new data.\r
+ @param[in] Mask The pointer to the Mask.\r
+ @param[out] CreateNew The switch to create new.\r
+\r
+ @retval EFI_SUCCESS Combined successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(* COMBINE_POLICY_ENTRY) (\r
+ EFI_IPSEC_CONFIG_SELECTOR *OldSelector,\r
+ VOID *OldData,\r
+ EFI_IPSEC_CONFIG_SELECTOR *NewSelector,\r
+ VOID *NewData,\r
+ UINT32 Mask,\r
+ BOOLEAN *CreateNew\r
+ );\r
+\r
+/**\r
+ Insert or add entry information in database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Insert or add entry information successfully.\r
+ @retval EFI_NOT_FOUND Can't find the specified entry.\r
+ @retval EFI_BUFFER_TOO_SMALL The entry already existed.\r
+ @retval EFI_UNSUPPORTED The operation is not supported./\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+AddOrInsertPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ );\r
+\r
+/**\r
+ Edit entry information in the database according to datatype.\r
+\r
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] ParamPackage The pointer to the ParamPackage list.\r
+\r
+ @retval EFI_SUCCESS Edit entry information successfully.\r
+ @retval EFI_NOT_FOUND Can't find the specified entry.\r
+ @retval Others Some mistaken case.\r
+**/\r
+EFI_STATUS\r
+EditPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN LIST_ENTRY *ParamPackage\r
+ );\r
+#endif\r
--- /dev/null
+/** @file\r
+ The implement to read TSC in IA32 platform.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+\r
+/**\r
+ Reads and returns the current value of the Time Stamp Counter (TSC).\r
+\r
+ @return The current value of TSC.\r
+\r
+**/\r
+UINT64\r
+ReadTime ()\r
+{\r
+ return AsmReadTsc ();\r
+}\r
--- /dev/null
+/** @file\r
+ The implement to read ITC in IA64 platform.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+\r
+/**\r
+ Reads and returns the current value of the Interval Timer Counter Register (ITC).\r
+\r
+ @return The current value of ITC.\r
+\r
+**/\r
+UINT64\r
+ReadTime ()\r
+{\r
+ return AsmReadItc ();\r
+}\r
--- /dev/null
+/** @file\r
+ The implementation for Ping6 application.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/ShellLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/Cpu.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+\r
+#include "Ping6.h"\r
+\r
+SHELL_PARAM_ITEM Ping6ParamList[] = {\r
+ {\r
+ L"-l",\r
+ TypeValue\r
+ },\r
+ {\r
+ L"-n",\r
+ TypeValue\r
+ },\r
+ {\r
+ L"-s",\r
+ TypeValue\r
+ },\r
+ {\r
+ L"-?",\r
+ TypeFlag\r
+ },\r
+ {\r
+ NULL,\r
+ TypeMax\r
+ },\r
+};\r
+\r
+//\r
+// Global Variables in Ping6 application.\r
+//\r
+EFI_HII_HANDLE mHiiHandle;\r
+CONST CHAR16 *mIp6DstString;\r
+CONST CHAR16 *mIp6SrcString;\r
+EFI_GUID mEfiPing6Guid = EFI_PING6_GUID;\r
+UINT32 mFrequency = 0;\r
+/**\r
+ Get and caculate the frequency in tick/ms.\r
+ The result is saved in the globle variable mFrequency\r
+\r
+ @retval EFI_SUCCESS Caculated the frequency successfully.\r
+ @retval Others Failed to caculate the frequency.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6GetFrequency (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_CPU_ARCH_PROTOCOL *Cpu;\r
+ UINT64 CurrentTick;\r
+ UINT32 TimerPeriod;\r
+\r
+ Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, (UINT64 *) &TimerPeriod);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // For NT32 Simulator only. 358049 is a similar value to keep timer granularity.\r
+ // Set the timer period by ourselves.\r
+ //\r
+ TimerPeriod = NTTIMERPERIOD;\r
+ }\r
+ //\r
+ // The timer period is in femtosecond (1 femtosecond is 1e-15 second).\r
+ // So 1e+12 is divided by timer period to produce the freq in tick/ms.\r
+ //\r
+ mFrequency = (UINT32) DivU64x32 (1000000000000ULL, TimerPeriod);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get and caculate the duration in ms.\r
+\r
+ @param[in] Begin The start point of time.\r
+ @param[in] End The end point of time.\r
+\r
+ @return The duration in ms.\r
+\r
+**/\r
+UINT32\r
+Ping6CalculateTick (\r
+ IN UINT64 Begin,\r
+ IN UINT64 End\r
+ )\r
+{\r
+ ASSERT (End > Begin);\r
+ return (UINT32) DivU64x32 (End - Begin, mFrequency);\r
+}\r
+\r
+/**\r
+ Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.\r
+\r
+ @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.\r
+\r
+**/\r
+VOID\r
+Ping6DestroyTxInfo (\r
+ IN PING6_ICMP6_TX_INFO *TxInfo\r
+ )\r
+{\r
+ EFI_IP6_TRANSMIT_DATA *TxData;\r
+ EFI_IP6_FRAGMENT_DATA *FragData;\r
+ UINTN Index;\r
+\r
+ ASSERT (TxInfo != NULL);\r
+\r
+ if (TxInfo->Token != NULL) {\r
+\r
+ if (TxInfo->Token->Event != NULL) {\r
+ gBS->CloseEvent (TxInfo->Token->Event);\r
+ }\r
+\r
+ TxData = TxInfo->Token->Packet.TxData;\r
+ if (TxData != NULL) {\r
+\r
+ if (TxData->OverrideData != NULL) {\r
+ FreePool (TxData->OverrideData);\r
+ }\r
+\r
+ if (TxData->ExtHdrs != NULL) {\r
+ FreePool (TxData->ExtHdrs);\r
+ }\r
+\r
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
+ FragData = TxData->FragmentTable[Index].FragmentBuffer;\r
+ if (FragData != NULL) {\r
+ FreePool (FragData);\r
+ }\r
+ }\r
+ }\r
+\r
+ FreePool (TxInfo->Token);\r
+ }\r
+\r
+ FreePool (TxInfo);\r
+}\r
+\r
+/**\r
+ Match the request, and reply with SequenceNum/TimeStamp.\r
+\r
+ @param[in] Private The pointer to PING6_PRIVATE_DATA.\r
+ @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.\r
+\r
+ @retval EFI_SUCCESS The match is successful.\r
+ @retval EFI_NOT_FOUND The reply can't be matched with any request.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6MatchEchoReply (\r
+ IN PING6_PRIVATE_DATA *Private,\r
+ IN ICMP6_ECHO_REQUEST_REPLY *Packet\r
+ )\r
+{\r
+ PING6_ICMP6_TX_INFO *TxInfo;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+ TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+\r
+ if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {\r
+ Private->RxCount++;\r
+ RemoveEntryList (&TxInfo->Link);\r
+ Ping6DestroyTxInfo (TxInfo);\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ The original intention is to send a request.\r
+ Currently, the application retransmits an icmp6 echo request packet\r
+ per second in sendnumber times that is specified by the user.\r
+ Because nothing can be done here, all things move to the timer rountine.\r
+\r
+ @param[in] Event A EFI_EVENT type event.\r
+ @param[in] Context The pointer to Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnEchoRequestSent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+}\r
+\r
+/**\r
+ receive reply, match and print reply infomation.\r
+\r
+ @param[in] Event A EFI_EVENT type event.\r
+ @param[in] Context The pointer to context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnEchoReplyReceived (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PING6_PRIVATE_DATA *Private;\r
+ EFI_IP6_COMPLETION_TOKEN *RxToken;\r
+ EFI_IP6_RECEIVE_DATA *RxData;\r
+ ICMP6_ECHO_REQUEST_REPLY *Reply;\r
+ UINT32 PayLoad;\r
+ UINT32 Rtt;\r
+ CHAR8 Near;\r
+\r
+ Private = (PING6_PRIVATE_DATA *) Context;\r
+\r
+ if (Private->Status == EFI_ABORTED) {\r
+ return;\r
+ }\r
+\r
+ RxToken = &Private->RxToken;\r
+ RxData = RxToken->Packet.RxData;\r
+ Reply = RxData->FragmentTable[0].FragmentBuffer;\r
+ PayLoad = RxData->DataLength;\r
+\r
+ if (RxData->Header->NextHeader != IP6_ICMP) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (!IP6_IS_MULTICAST (&Private->DstAddress) && \r
+ !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (PayLoad != Private->BufferSize) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Check whether the reply matches the sent request before.\r
+ //\r
+ Status = Ping6MatchEchoReply (Private, Reply);\r
+ if (EFI_ERROR(Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Display statistics on this icmp6 echo reply packet.\r
+ //\r
+ Rtt = Ping6CalculateTick (Reply->TimeStamp, ReadTime ());\r
+ if (Rtt != 0) {\r
+ Near = (CHAR8) '=';\r
+ } else {\r
+ Near = (CHAR8) '<';\r
+ }\r
+\r
+ Private->RttSum += Rtt;\r
+ Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin;\r
+ Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax;\r
+\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_PING6_REPLY_INFO),\r
+ mHiiHandle,\r
+ PayLoad,\r
+ mIp6DstString,\r
+ Reply->SequenceNum,\r
+ RxData->Header->HopLimit,\r
+ Near,\r
+ Rtt\r
+ );\r
+\r
+ON_EXIT:\r
+\r
+ if (Private->RxCount < Private->SendNum) {\r
+ //\r
+ // Continue to receive icmp6 echo reply packets.\r
+ //\r
+ RxToken->Status = EFI_ABORTED;\r
+\r
+ Status = Private->Ip6->Receive (Private->Ip6, RxToken);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Private->Status = EFI_ABORTED;\r
+ }\r
+ } else {\r
+ //\r
+ // All reply have already been received from the dest host.\r
+ //\r
+ Private->Status = EFI_SUCCESS;\r
+ }\r
+ //\r
+ // Singal to recycle the each rxdata here, not at the end of process.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+}\r
+\r
+/**\r
+ Initial EFI_IP6_COMPLETION_TOKEN.\r
+\r
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.\r
+ @param[in] TimeStamp The TimeStamp of request.\r
+ @param[in] SequenceNum The SequenceNum of request.\r
+\r
+ @return The pointer of EFI_IP6_COMPLETION_TOKEN.\r
+\r
+**/\r
+EFI_IP6_COMPLETION_TOKEN *\r
+Ping6GenerateToken (\r
+ IN PING6_PRIVATE_DATA *Private,\r
+ IN UINT64 TimeStamp,\r
+ IN UINT16 SequenceNum\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IP6_COMPLETION_TOKEN *Token;\r
+ EFI_IP6_TRANSMIT_DATA *TxData;\r
+ ICMP6_ECHO_REQUEST_REPLY *Request;\r
+\r
+ Request = AllocateZeroPool (Private->BufferSize);\r
+\r
+ if (Request == NULL) {\r
+ return NULL;\r
+ }\r
+ //\r
+ // Assembly icmp6 echo request packet.\r
+ //\r
+ Request->Type = ICMP_V6_ECHO_REQUEST;\r
+ Request->Code = 0;\r
+ Request->SequenceNum = SequenceNum;\r
+ Request->TimeStamp = TimeStamp;\r
+ Request->Identifier = 0;\r
+ //\r
+ // Leave check sum to ip6 layer, since it has no idea of source address\r
+ // selection.\r
+ //\r
+ Request->Checksum = 0;\r
+\r
+ TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));\r
+\r
+ if (TxData == NULL) {\r
+ FreePool (Request);\r
+ return NULL;\r
+ }\r
+ //\r
+ // Assembly ipv6 token for transmit.\r
+ //\r
+ TxData->OverrideData = 0;\r
+ TxData->ExtHdrsLength = 0;\r
+ TxData->ExtHdrs = NULL;\r
+ TxData->DataLength = Private->BufferSize;\r
+ TxData->FragmentCount = 1;\r
+ TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;\r
+ TxData->FragmentTable[0].FragmentLength = Private->BufferSize;\r
+\r
+ Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));\r
+\r
+ if (Token == NULL) {\r
+ FreePool (Request);\r
+ FreePool (TxData);\r
+ return NULL;\r
+ }\r
+\r
+ Token->Status = EFI_ABORTED;\r
+ Token->Packet.TxData = TxData;\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ Ping6OnEchoRequestSent,\r
+ Private,\r
+ &Token->Event\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Request);\r
+ FreePool (TxData);\r
+ FreePool (Token);\r
+ return NULL;\r
+ }\r
+\r
+ return Token;\r
+}\r
+\r
+/**\r
+ Transmit the EFI_IP6_COMPLETION_TOKEN.\r
+\r
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.\r
+\r
+ @retval EFI_SUCCESS Transmitted successfully.\r
+ @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.\r
+ @retval others Transmitted unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6SendEchoRequest (\r
+ IN PING6_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PING6_ICMP6_TX_INFO *TxInfo;\r
+\r
+ TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));\r
+\r
+ if (TxInfo == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TxInfo->TimeStamp = ReadTime ();\r
+ TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);\r
+\r
+ TxInfo->Token = Ping6GenerateToken (\r
+ Private,\r
+ TxInfo->TimeStamp,\r
+ TxInfo->SequenceNum\r
+ );\r
+\r
+ if (TxInfo->Token == NULL) {\r
+ Ping6DestroyTxInfo (TxInfo);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Ping6DestroyTxInfo (TxInfo);\r
+ return Status;\r
+ }\r
+\r
+ InsertTailList (&Private->TxList, &TxInfo->Link);\r
+ Private->TxCount++;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Place a completion token into the receive packet queue to receive the echo reply.\r
+\r
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.\r
+\r
+ @retval EFI_SUCCESS Put the token into the receive packet queue successfully.\r
+ @retval others Put the token into the receive packet queue unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6ReceiveEchoReply (\r
+ IN PING6_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ Ping6OnEchoReplyReceived,\r
+ Private,\r
+ &Private->RxToken.Event\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Private->RxToken.Status = EFI_NOT_READY;\r
+\r
+ return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);\r
+}\r
+\r
+/**\r
+ Remove the timeout request from the list.\r
+\r
+ @param[in] Event A EFI_EVENT type event.\r
+ @param[in] Context The pointer to Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ping6OnTimerRoutine (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PING6_PRIVATE_DATA *Private;\r
+ PING6_ICMP6_TX_INFO *TxInfo;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ UINT32 Time;\r
+\r
+ Private = (PING6_PRIVATE_DATA *) Context;\r
+\r
+ //\r
+ // Retransmit icmp6 echo request packets per second in sendnumber times.\r
+ //\r
+ if (Private->TxCount < Private->SendNum) {\r
+\r
+ Status = Ping6SendEchoRequest (Private);\r
+ if (Private->TxCount != 0){\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), mHiiHandle, Private->TxCount + 1);\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Check whether any icmp6 echo request in the list timeout.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+ TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+ Time = Ping6CalculateTick (TxInfo->TimeStamp, ReadTime ());\r
+\r
+ //\r
+ // Remove the timeout echo request from txlist.\r
+ //\r
+ if (Time > PING6_DEFAULT_TIMEOUT) {\r
+\r
+ if (EFI_ERROR (TxInfo->Token->Status)) {\r
+ Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
+ }\r
+ //\r
+ // Remove the timeout icmp6 echo request from list.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), mHiiHandle, TxInfo->SequenceNum);\r
+\r
+ RemoveEntryList (&TxInfo->Link);\r
+ Ping6DestroyTxInfo (TxInfo);\r
+\r
+ if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {\r
+ //\r
+ // All the left icmp6 echo request in the list timeout.\r
+ //\r
+ Private->Status = EFI_TIMEOUT;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Create a valid IP6 instance.\r
+\r
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.\r
+\r
+ @retval EFI_SUCCESS Create a valid IP6 instance successfully.\r
+ @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully.\r
+ @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address.\r
+ @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.\r
+ @retval EFI_NOT_FOUND The source address is not found.\r
+**/\r
+EFI_STATUS\r
+Ping6CreateIp6Instance (\r
+ IN PING6_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN HandleIndex;\r
+ UINTN HandleNum;\r
+ EFI_HANDLE *HandleBuffer;\r
+ EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
+ EFI_IP6_CONFIG_DATA Ip6Config;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;\r
+ UINTN IfInfoSize;\r
+ EFI_IPv6_ADDRESS *Addr;\r
+ UINTN AddrIndex;\r
+\r
+ HandleBuffer = NULL;\r
+ Ip6Sb = NULL;\r
+ IfInfo = NULL;\r
+ IfInfoSize = 0;\r
+\r
+ //\r
+ // Locate all the handles with ip6 service binding protocol.\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ &HandleNum,\r
+ &HandleBuffer\r
+ );\r
+ if (EFI_ERROR (Status) || (HandleNum == 0)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ //\r
+ // Source address is required when pinging a link-local address on multi-\r
+ // interfaces host.\r
+ //\r
+ if (NetIp6IsLinkLocalAddr (&Private->DstAddress) &&\r
+ NetIp6IsUnspecifiedAddr (&Private->SrcAddress) &&\r
+ (HandleNum > 1)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), mHiiHandle);\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // For each ip6 protocol, check interface addresses list.\r
+ //\r
+ for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {\r
+\r
+ Ip6Sb = NULL;\r
+ IfInfo = NULL;\r
+ IfInfoSize = 0;\r
+\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[HandleIndex],\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ (VOID **) &Ip6Sb\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {\r
+ //\r
+ // No need to match interface address.\r
+ //\r
+ break;\r
+ } else {\r
+ //\r
+ // Ip6config protocol and ip6 service binding protocol are installed\r
+ // on the same handle.\r
+ //\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[HandleIndex],\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ (VOID **) &Ip6Cfg\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // Get the interface information size.\r
+ //\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &IfInfoSize,\r
+ NULL\r
+ );\r
+\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ IfInfo = AllocateZeroPool (IfInfoSize);\r
+\r
+ if (IfInfo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // Get the interface info.\r
+ //\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &IfInfoSize,\r
+ IfInfo\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+ //\r
+ // Check whether the source address is one of the interface addresses.\r
+ //\r
+ for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {\r
+\r
+ Addr = &(IfInfo->AddressInfo[AddrIndex].Address);\r
+ if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {\r
+ //\r
+ // Match a certain interface address.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (AddrIndex < IfInfo->AddressInfoCount) {\r
+ //\r
+ // Found a nic handle with right interface address.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ FreePool (IfInfo);\r
+ IfInfo = NULL;\r
+ }\r
+ //\r
+ // No exact interface address matched.\r
+ //\r
+\r
+ if (HandleIndex == HandleNum) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), mHiiHandle, mIp6SrcString);\r
+ Status = EFI_NOT_FOUND;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Private->NicHandle = HandleBuffer[HandleIndex];\r
+\r
+ ASSERT (Ip6Sb != NULL);\r
+ Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Ip6ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ (VOID **) &Private->Ip6,\r
+ Private->ImageHandle,\r
+ Private->Ip6ChildHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+\r
+ //\r
+ // Configure the ip6 instance for icmp6 packet exchange.\r
+ //\r
+ Ip6Config.DefaultProtocol = 58;\r
+ Ip6Config.AcceptAnyProtocol = FALSE;\r
+ Ip6Config.AcceptIcmpErrors = TRUE;\r
+ Ip6Config.AcceptPromiscuous = FALSE;\r
+ Ip6Config.TrafficClass = 0;\r
+ Ip6Config.HopLimit = 128;\r
+ Ip6Config.FlowLabel = 0;\r
+ Ip6Config.ReceiveTimeout = 0;\r
+ Ip6Config.TransmitTimeout = 0;\r
+\r
+ IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);\r
+\r
+ IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);\r
+\r
+ Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), mHiiHandle, Status);\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ if (HandleBuffer != NULL) {\r
+ FreePool (HandleBuffer);\r
+ }\r
+\r
+ if (IfInfo != NULL) {\r
+ FreePool (IfInfo);\r
+ }\r
+\r
+ if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {\r
+ Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Destory the IP6 instance.\r
+\r
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+Ping6DestoryIp6Instance (\r
+ IN PING6_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;\r
+\r
+ gBS->CloseProtocol (\r
+ Private->Ip6ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ Private->ImageHandle,\r
+ Private->Ip6ChildHandle\r
+ );\r
+\r
+ Status = gBS->HandleProtocol (\r
+ Private->NicHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ (VOID **) &Ip6Sb\r
+ );\r
+\r
+ if (!EFI_ERROR(Status)) {\r
+ Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
+ }\r
+}\r
+\r
+/**\r
+ The Ping6 Process.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.\r
+ @param[in] SendNumber The send request count.\r
+ @param[in] BufferSize The send buffer size.\r
+ @param[in] SrcAddress The source IPv6 address.\r
+ @param[in] DstAddress The destination IPv6 address.\r
+\r
+ @retval EFI_SUCCESS The ping6 processed successfullly.\r
+ @retval others The ping6 processed unsuccessfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ping6 (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN UINT32 SendNumber,\r
+ IN UINT32 BufferSize,\r
+ IN EFI_IPv6_ADDRESS *SrcAddress,\r
+ IN EFI_IPv6_ADDRESS *DstAddress\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_INPUT_KEY Key;\r
+ PING6_PRIVATE_DATA *Private;\r
+ PING6_ICMP6_TX_INFO *TxInfo;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+\r
+ Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));\r
+\r
+ ASSERT (Private != NULL);\r
+\r
+ Private->ImageHandle = ImageHandle;\r
+ Private->SendNum = SendNumber;\r
+ Private->BufferSize = BufferSize;\r
+ Private->RttMin = 0xFFFF;\r
+ Private->Status = EFI_NOT_READY;\r
+\r
+ InitializeListHead (&Private->TxList);\r
+\r
+ IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);\r
+ IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);\r
+\r
+ //\r
+ // Open and configure a ip6 instance for ping6.\r
+ //\r
+ Status = Ping6CreateIp6Instance (Private);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Print the command line itself.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), mHiiHandle, mIp6DstString, Private->BufferSize);\r
+ //\r
+ // Create a ipv6 token to receive the first icmp6 echo reply packet.\r
+ //\r
+ Status = Ping6ReceiveEchoReply (Private);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Create and start timer to send icmp6 echo request packet per second.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ Ping6OnTimerRoutine,\r
+ Private,\r
+ &Private->Timer\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Create a ipv6 token to send the first icmp6 echo request packet.\r
+ //\r
+ Status = Ping6SendEchoRequest (Private);\r
+ //\r
+ // EFI_NOT_READY for IPsec is enable and IKE is not established.\r
+ //\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {\r
+ if(Status == EFI_NOT_FOUND) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), mHiiHandle, mIp6DstString);\r
+ }\r
+\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = gBS->SetTimer (\r
+ Private->Timer,\r
+ TimerPeriodic,\r
+ PING6_ONE_SECOND\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Control the ping6 process by two factors:\r
+ // 1. Hot key\r
+ // 2. Private->Status\r
+ // 2.1. success means all icmp6 echo request packets get reply packets.\r
+ // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.\r
+ // 2.3. noready means ping6 process is on-the-go.\r
+ //\r
+ while (Private->Status == EFI_NOT_READY) {\r
+ Private->Ip6->Poll (Private->Ip6);\r
+\r
+ //\r
+ // Terminate the ping6 process by 'esc' or 'ctl-c'.\r
+ //\r
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+\r
+ if (!EFI_ERROR(Status)) {\r
+ if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||\r
+ ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {\r
+ goto ON_STAT;\r
+ }\r
+ }\r
+ }\r
+\r
+ON_STAT:\r
+ //\r
+ // Display the statistics in all.\r
+ //\r
+ gBS->SetTimer (Private->Timer, TimerCancel, 0);\r
+\r
+ if (Private->TxCount != 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_PING6_STAT),\r
+ mHiiHandle,\r
+ Private->TxCount,\r
+ Private->RxCount,\r
+ (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,\r
+ Private->RttSum\r
+ );\r
+ }\r
+\r
+ if (Private->RxCount != 0) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_PING6_RTT),\r
+ mHiiHandle,\r
+ Private->RttMin,\r
+ Private->RttMax,\r
+ Private->RttSum / Private->RxCount\r
+ );\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ if (Private != NULL) {\r
+ Private->Status = EFI_ABORTED;\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
+ TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
+\r
+ Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
+\r
+ RemoveEntryList (&TxInfo->Link);\r
+ Ping6DestroyTxInfo (TxInfo);\r
+ }\r
+\r
+ if (Private->Timer != NULL) {\r
+ gBS->CloseEvent (Private->Timer);\r
+ }\r
+\r
+ if (Private->Ip6 != NULL) {\r
+ Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);\r
+ }\r
+\r
+ if (Private->RxToken.Event != NULL) {\r
+ gBS->CloseEvent (Private->RxToken.Event);\r
+ }\r
+\r
+ if (Private->Ip6ChildHandle != NULL) {\r
+ Ping6DestoryIp6Instance (Private);\r
+ }\r
+\r
+ FreePool (Private);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+ both device drivers and bus drivers.\r
+\r
+ The entry point for the Ping6 application that parses the command line input and calls the Ping6 process.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETETR Input parameters combination is invalid.\r
+ @retval Others Some errors occur.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializePing6 (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IPv6_ADDRESS DstAddress;\r
+ EFI_IPv6_ADDRESS SrcAddress;\r
+ UINT64 BufferSize;\r
+ UINTN SendNumber;\r
+ LIST_ENTRY *ParamPackage;\r
+ CONST CHAR16 *ValueStr;\r
+ CONST CHAR16 *ValueStrPtr;\r
+ UINTN NonOptionCount;\r
+\r
+ //\r
+ // Register our string package with HII and return the handle to it.\r
+ //\r
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, Ping6Strings, NULL);\r
+ ASSERT (mHiiHandle != NULL);\r
+\r
+ Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, NULL, TRUE, FALSE);\r
+ if (EFI_ERROR(Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_HELP), mHiiHandle);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ SendNumber = 10;\r
+ BufferSize = 16;\r
+\r
+ //\r
+ // Parse the paramter of count number.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");\r
+ ValueStrPtr = ValueStr;\r
+ if (ValueStr != NULL) {\r
+ SendNumber = ShellStrToUintn (ValueStrPtr);\r
+\r
+ //\r
+ // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
+ //\r
+ if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), mHiiHandle, ValueStr);\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Parse the paramter of buffer size.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");\r
+ ValueStrPtr = ValueStr;\r
+ if (ValueStr != NULL) {\r
+ BufferSize = ShellStrToUintn (ValueStrPtr);\r
+\r
+ //\r
+ // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
+ //\r
+ if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), mHiiHandle, ValueStr);\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+\r
+ ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ //\r
+ // Parse the paramter of source ip address.\r
+ //\r
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");\r
+ ValueStrPtr = ValueStr;\r
+ if (ValueStr != NULL) {\r
+ mIp6SrcString = ValueStr;\r
+ Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Parse the paramter of destination ip address.\r
+ //\r
+ NonOptionCount = ShellCommandLineGetCount();\r
+ ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));\r
+ if (NonOptionCount != 2) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+ ValueStrPtr = ValueStr;\r
+ if (ValueStr != NULL) {\r
+ mIp6DstString = ValueStr;\r
+ Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Get frequency to calculate the time from ticks.\r
+ //\r
+ Status = Ping6GetFrequency ();\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Enter into ping6 process.\r
+ //\r
+ Status = Ping6 (\r
+ ImageHandle,\r
+ (UINT32)SendNumber,\r
+ (UINT32)BufferSize,\r
+ &SrcAddress,\r
+ &DstAddress\r
+ );\r
+\r
+ON_EXIT:\r
+ ShellCommandLineFreeVarList (ParamPackage);\r
+ HiiRemovePackages (mHiiHandle);\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+ The interface function declaration of shell application Ping6 (Ping for v6 series).\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _PING6_H_\r
+#define _PING6_H_\r
+\r
+#define EFI_PING6_GUID \\r
+ { \\r
+ 0x3f0b2478, 0x3619, 0x46c5, {0x81, 0x50, 0xa5, 0xab, 0xdd, 0xb6, 0x6b, 0xd9} \\r
+ }\r
+\r
+#define PING6_DEFAULT_TIMEOUT 5000\r
+#define PING6_MAX_SEND_NUMBER 10000\r
+#define PING6_MAX_BUFFER_SIZE 32768\r
+#define PING6_ONE_SECOND 10000000\r
+\r
+//\r
+// A similar amount of time that passes in femtoseconds\r
+// for each increment of TimerValue. It is for NT32 only.\r
+//\r
+#define NTTIMERPERIOD 358049\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct _ICMP6_ECHO_REQUEST_REPLY {\r
+ UINT8 Type;\r
+ UINT8 Code;\r
+ UINT16 Checksum;\r
+ UINT16 Identifier;\r
+ UINT16 SequenceNum;\r
+ UINT64 TimeStamp;\r
+ UINT8 Data[1];\r
+} ICMP6_ECHO_REQUEST_REPLY;\r
+\r
+#pragma pack()\r
+\r
+typedef struct _PING6_ICMP6_TX_INFO {\r
+ LIST_ENTRY Link;\r
+ UINT16 SequenceNum;\r
+ UINT64 TimeStamp;\r
+ EFI_IP6_COMPLETION_TOKEN *Token;\r
+} PING6_ICMP6_TX_INFO;\r
+\r
+typedef struct _PING6_PRIVATE_DATA {\r
+ EFI_HANDLE ImageHandle;\r
+ EFI_HANDLE NicHandle;\r
+ EFI_HANDLE Ip6ChildHandle;\r
+ EFI_IP6_PROTOCOL *Ip6;\r
+ EFI_EVENT Timer;\r
+\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY TxList;\r
+ EFI_IP6_COMPLETION_TOKEN RxToken;\r
+ UINT16 RxCount;\r
+ UINT16 TxCount;\r
+ UINT32 RttSum;\r
+ UINT32 RttMin;\r
+ UINT32 RttMax;\r
+ UINT32 SequenceNum;\r
+\r
+ EFI_IPv6_ADDRESS SrcAddress;\r
+ EFI_IPv6_ADDRESS DstAddress;\r
+ UINT32 SendNum;\r
+ UINT32 BufferSize;\r
+} PING6_PRIVATE_DATA;\r
+\r
+/**\r
+ Reads and returns the current value of register.\r
+ In IA64, the register is the Interval Timer Vector (ITV).\r
+ In X86(IA32/X64), the register is the Time Stamp Counter (TSC)\r
+\r
+ @return The current value of the register.\r
+\r
+**/\r
+UINT64\r
+ReadTime (\r
+ VOID\r
+ );\r
+\r
+#endif\r
--- /dev/null
+## @file\r
+# Component description file for Ping6 application.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010006\r
+ BASE_NAME = Ping6\r
+ FILE_GUID = F35F733F-5235-4d7b-83FA-97780CEBCB20\r
+ MODULE_TYPE = UEFI_APPLICATION\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = InitializePing6\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF\r
+#\r
+\r
+[Sources]\r
+ Ping6.c\r
+ Ping6Strings.uni\r
+ Ping6.h\r
+\r
+[Sources.IA32]\r
+ Ia32/Tsc.c\r
+\r
+[Sources.X64]\r
+ X64/Tsc.c\r
+\r
+[Sources.IPF]\r
+ Ipf/Itc.c\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+ ShellPkg/ShellPkg.dec\r
+\r
+[LibraryClasses]\r
+ BaseLib\r
+ UefiBootServicesTableLib\r
+ UefiApplicationEntryPoint\r
+ BaseMemoryLib\r
+ ShellLib\r
+ MemoryAllocationLib\r
+ DebugLib\r
+ HiiLib\r
+ NetLib\r
+\r
+[Protocols]\r
+ gEfiCpuArchProtocolGuid ## CONSUMS\r
+ gEfiIp6ProtocolGuid ## CONSUMS\r
+ gEfiIp6ServiceBindingProtocolGuid ## CONSUMS\r
+ gEfiIp6ConfigProtocolGuid ## CONSUMS\r
--- /dev/null
+/** @file\r
+ The implement to read TSC in X64 platform.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+\r
+/**\r
+ Reads and returns the current value of Time Stamp Counter (TSC).\r
+\r
+ @return The current value of TSC\r
+\r
+**/\r
+UINT64\r
+ReadTime ()\r
+{\r
+ return AsmReadTsc ();\r
+}\r
--- /dev/null
+/** @file\r
+ Shell application for VLAN configuration.\r
+\r
+ Copyright (C) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/VlanConfig.h>\r
+\r
+#include <Library/UefiApplicationEntryPoint.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/ShellLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+#define INVALID_NIC_INDEX 0xffff\r
+#define INVALID_VLAN_ID 0xffff\r
+\r
+//\r
+// This is the generated String package data for all .UNI files.\r
+// This data array is ready to be used as input of HiiAddPackages() to\r
+// create a packagelist (which contains Form packages, String packages, etc).\r
+//\r
+extern UINT8 VConfigStrings[];\r
+\r
+EFI_HANDLE mImageHandle = NULL;\r
+EFI_HII_HANDLE mHiiHandle = NULL;\r
+\r
+SHELL_PARAM_ITEM mParamList[] = {\r
+ {\r
+ L"-l",\r
+ TypeValue\r
+ },\r
+ {\r
+ L"-a",\r
+ TypeMaxValue\r
+ },\r
+ {\r
+ L"-d",\r
+ TypeValue\r
+ },\r
+ {\r
+ NULL,\r
+ TypeMax\r
+ }\r
+};\r
+\r
+/**\r
+ Locate the network interface handle buffer.\r
+\r
+ @param[out] NumberOfHandles Pointer to the number of handles.\r
+ @param[out] HandleBuffer Pointer to the buffer to store the returned handles.\r
+\r
+**/\r
+VOID\r
+LocateNicHandleBuffer (\r
+ OUT UINTN *NumberOfHandles,\r
+ OUT EFI_HANDLE **HandleBuffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ *NumberOfHandles = 0;\r
+ *HandleBuffer = NULL;\r
+\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiVlanConfigProtocolGuid,\r
+ NULL,\r
+ NumberOfHandles,\r
+ HandleBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_LOCATE_FAIL), mHiiHandle, Status);\r
+ }\r
+}\r
+\r
+/**\r
+ Extract the decimal index from the network interface name.\r
+\r
+ @param[in] Name Name of the network interface.\r
+\r
+ @retval INVALID_NIC_INDEX Failed to extract the network interface index.\r
+ @return others The network interface index.\r
+\r
+**/\r
+UINTN\r
+NicNameToIndex (\r
+ IN CHAR16 *Name\r
+ )\r
+{\r
+ CHAR16 *Str;\r
+\r
+ Str = Name + 3;\r
+ if ((StrnCmp (Name, L"eth", 3) != 0) || (*Str == 0)) {\r
+ return INVALID_NIC_INDEX;\r
+ }\r
+\r
+ while (*Str != 0) {\r
+ if ((*Str < L'0') || (*Str > L'9')) {\r
+ return INVALID_NIC_INDEX;\r
+ }\r
+\r
+ Str++;\r
+ }\r
+\r
+ return (UINT16) StrDecimalToUintn (Name + 3);\r
+}\r
+\r
+/**\r
+ Find network interface device handle by its name.\r
+\r
+ @param[in] Name Name of the network interface.\r
+\r
+ @retval NULL Cannot find the network interface.\r
+ @return others Handle of the network interface.\r
+\r
+**/\r
+EFI_HANDLE\r
+NicNameToHandle (\r
+ IN CHAR16 *Name\r
+ )\r
+{\r
+ UINTN NumberOfHandles;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN Index;\r
+ EFI_HANDLE Handle;\r
+\r
+ //\r
+ // Find all NIC handles.\r
+ //\r
+ LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);\r
+ if (NumberOfHandles == 0) {\r
+ return NULL;\r
+ }\r
+\r
+ Index = NicNameToIndex (Name);\r
+ if (Index >= NumberOfHandles) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_IF), mHiiHandle, Name);\r
+ Handle = NULL;\r
+ } else {\r
+ Handle = HandleBuffer[Index];\r
+ }\r
+\r
+ FreePool (HandleBuffer);\r
+ return Handle;\r
+}\r
+\r
+/**\r
+ Open VlanConfig protocol from a handle.\r
+\r
+ @param[in] Handle The handle to open the VlanConfig protocol.\r
+\r
+ @return The VlanConfig protocol interface.\r
+\r
+**/\r
+EFI_VLAN_CONFIG_PROTOCOL *\r
+OpenVlanConfigProtocol (\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;\r
+\r
+ VlanConfig = NULL;\r
+ gBS->OpenProtocol (\r
+ Handle,\r
+ &gEfiVlanConfigProtocolGuid,\r
+ (VOID **) &VlanConfig,\r
+ mImageHandle,\r
+ Handle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ return VlanConfig;\r
+}\r
+\r
+/**\r
+ Close VlanConfig protocol of a handle.\r
+\r
+ @param[in] Handle The handle to close the VlanConfig protocol.\r
+\r
+**/\r
+VOID\r
+CloseVlanConfigProtocol (\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ gBS->CloseProtocol (\r
+ Handle,\r
+ &gEfiVlanConfigProtocolGuid,\r
+ mImageHandle,\r
+ Handle\r
+ );\r
+}\r
+\r
+/**\r
+ Display VLAN configuration of a network interface.\r
+\r
+ @param[in] Handle Handle of the network interface.\r
+ @param[in] NicIndex Index of the network interface.\r
+\r
+**/\r
+VOID\r
+ShowNicVlanInfo (\r
+ IN EFI_HANDLE Handle,\r
+ IN UINTN NicIndex\r
+ )\r
+{\r
+ CHAR16 *MacStr;\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;\r
+ UINT16 NumberOfVlan;\r
+ EFI_VLAN_FIND_DATA *VlanData;\r
+\r
+ VlanConfig = OpenVlanConfigProtocol (Handle);\r
+ if (VlanConfig == NULL) {\r
+ return ;\r
+ }\r
+\r
+ MacStr = NULL;\r
+ Status = NetLibGetMacString (Handle, mImageHandle, &MacStr);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_MAC_FAIL), mHiiHandle, Status);\r
+ goto Exit;\r
+ }\r
+\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_ETH_MAC), mHiiHandle, NicIndex, MacStr);\r
+\r
+ Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_NOT_FOUND) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VLAN), mHiiHandle);\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_FIND_FAIL), mHiiHandle, Status);\r
+ }\r
+\r
+ goto Exit;\r
+ }\r
+\r
+ for (Index = 0; Index < NumberOfVlan; Index++) {\r
+ ShellPrintHiiEx (\r
+ -1,\r
+ -1,\r
+ NULL,\r
+ STRING_TOKEN (STR_VCONFIG_VLAN_DISPLAY),\r
+ mHiiHandle,\r
+ VlanData[Index].VlanId,\r
+ VlanData[Index].Priority\r
+ );\r
+ }\r
+\r
+ FreePool (VlanData);\r
+\r
+Exit:\r
+ CloseVlanConfigProtocol (Handle);\r
+\r
+ if (MacStr != NULL) {\r
+ FreePool (MacStr);\r
+ }\r
+}\r
+\r
+/**\r
+ Display the VLAN configuration of all, or a specified network interface.\r
+\r
+ @param[in] Name Name of the network interface. If NULL, the VLAN\r
+ configuration of all network will be displayed.\r
+\r
+**/\r
+VOID\r
+DisplayVlan (\r
+ IN CHAR16 *Name OPTIONAL\r
+ )\r
+{\r
+ UINTN NumberOfHandles;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN Index;\r
+ EFI_HANDLE NicHandle;\r
+\r
+ if (Name != NULL) {\r
+ //\r
+ // Display specified NIC\r
+ //\r
+ NicHandle = NicNameToHandle (Name);\r
+ if (NicHandle == NULL) {\r
+ return ;\r
+ }\r
+\r
+ ShowNicVlanInfo (NicHandle, 0);\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // Find all NIC handles\r
+ //\r
+ LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);\r
+ if (NumberOfHandles == 0) {\r
+ return ;\r
+ }\r
+\r
+ for (Index = 0; Index < NumberOfHandles; Index++) {\r
+ ShowNicVlanInfo (HandleBuffer[Index], Index);\r
+ }\r
+\r
+ FreePool (HandleBuffer);\r
+}\r
+\r
+/**\r
+ Convert a NULL-terminated unicode decimal VLAN ID string to VLAN ID.\r
+\r
+ @param[in] String Pointer to VLAN ID string from user input.\r
+\r
+ @retval Value translated from String, or INVALID_VLAN_ID is string is invalid.\r
+\r
+**/\r
+UINT16\r
+StrToVlanId (\r
+ IN CHAR16 *String\r
+ )\r
+{\r
+ CHAR16 *Str;\r
+\r
+ if (String == NULL) {\r
+ return INVALID_VLAN_ID;\r
+ }\r
+\r
+ Str = String;\r
+ while ((*Str >= '0') && (*Str <= '9')) {\r
+ Str++;\r
+ }\r
+\r
+ if (*Str != 0) {\r
+ return INVALID_VLAN_ID;\r
+ }\r
+\r
+ return (UINT16) StrDecimalToUintn (String);\r
+}\r
+\r
+/**\r
+ Add a VLAN device.\r
+\r
+ @param[in] ParamStr Parameter string from user input.\r
+\r
+**/\r
+VOID\r
+AddVlan (\r
+ IN CHAR16 *ParamStr\r
+ )\r
+{\r
+ CHAR16 *Name;\r
+ CHAR16 *VlanIdStr;\r
+ CHAR16 *PriorityStr;\r
+ CHAR16 *StrPtr;\r
+ BOOLEAN IsSpace;\r
+ UINTN VlanId;\r
+ UINTN Priority;\r
+ EFI_HANDLE Handle;\r
+ EFI_HANDLE VlanHandle;\r
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;\r
+ EFI_STATUS Status;\r
+\r
+ VlanConfig = NULL;\r
+ Priority = 0;\r
+\r
+ if (ParamStr == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);\r
+ return ;\r
+ }\r
+\r
+ StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);\r
+ if (StrPtr == NULL) {\r
+ return ;\r
+ }\r
+\r
+ Name = StrPtr;\r
+ VlanIdStr = NULL;\r
+ PriorityStr = NULL;\r
+ IsSpace = FALSE;\r
+ while (*StrPtr != 0) {\r
+ if (*StrPtr == L' ') {\r
+ *StrPtr = 0;\r
+ IsSpace = TRUE;\r
+ } else {\r
+ if (IsSpace) {\r
+ //\r
+ // Start of a parameter.\r
+ //\r
+ if (VlanIdStr == NULL) {\r
+ //\r
+ // 2nd parameter is VLAN ID.\r
+ //\r
+ VlanIdStr = StrPtr;\r
+ } else if (PriorityStr == NULL) {\r
+ //\r
+ // 3rd parameter is Priority.\r
+ //\r
+ PriorityStr = StrPtr;\r
+ } else {\r
+ //\r
+ // Ignore else parameters.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ IsSpace = FALSE;\r
+ }\r
+\r
+ StrPtr++;\r
+ }\r
+\r
+ Handle = NicNameToHandle (Name);\r
+ if (Handle == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ VlanConfig = OpenVlanConfigProtocol (Handle);\r
+ if (VlanConfig == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Check VLAN ID.\r
+ //\r
+ if ((VlanIdStr == NULL) || (*VlanIdStr == 0)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);\r
+ goto Exit;\r
+ }\r
+\r
+ VlanId = StrToVlanId (VlanIdStr);\r
+ if (VlanId > 4094) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Check Priority.\r
+ //\r
+ if ((PriorityStr != NULL) && (*PriorityStr != 0)) {\r
+ Priority = StrDecimalToUintn (PriorityStr);\r
+ if (Priority > 7) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_PRIORITY), mHiiHandle, PriorityStr);\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Set VLAN\r
+ //\r
+ Status = VlanConfig->Set (VlanConfig, (UINT16) VlanId, (UINT8) Priority);\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_FAIL), mHiiHandle, Status);\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Connect the VLAN device.\r
+ //\r
+ VlanHandle = NetLibGetVlanHandle (Handle, (UINT16) VlanId);\r
+ if (VlanHandle != NULL) {\r
+ gBS->ConnectController (VlanHandle, NULL, NULL, TRUE);\r
+ }\r
+\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_SUCCESS), mHiiHandle);\r
+\r
+Exit:\r
+ if (VlanConfig != NULL) {\r
+ CloseVlanConfigProtocol (Handle);\r
+ }\r
+\r
+ FreePool (Name);\r
+}\r
+\r
+/**\r
+ Remove a VLAN device.\r
+\r
+ @param[in] ParamStr Parameter string from user input.\r
+\r
+**/\r
+VOID\r
+DeleteVlan (\r
+ CHAR16 *ParamStr\r
+ )\r
+{\r
+ CHAR16 *Name;\r
+ CHAR16 *VlanIdStr;\r
+ CHAR16 *StrPtr;\r
+ UINTN VlanId;\r
+ EFI_HANDLE Handle;\r
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;\r
+ EFI_STATUS Status;\r
+ UINT16 NumberOfVlan;\r
+ EFI_VLAN_FIND_DATA *VlanData;\r
+\r
+ VlanConfig = NULL;\r
+\r
+ if (ParamStr == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);\r
+ return ;\r
+ }\r
+\r
+ StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);\r
+ if (StrPtr == NULL) {\r
+ return ;\r
+ }\r
+\r
+ Name = StrPtr;\r
+ VlanIdStr = NULL;\r
+ while (*StrPtr != 0) {\r
+ if (*StrPtr == L'.') {\r
+ *StrPtr = 0;\r
+ VlanIdStr = StrPtr + 1;\r
+ break;\r
+ }\r
+\r
+ StrPtr++;\r
+ }\r
+\r
+ Handle = NicNameToHandle (Name);\r
+ if (Handle == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ VlanConfig = OpenVlanConfigProtocol (Handle);\r
+ if (VlanConfig == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Check VLAN ID\r
+ //\r
+ if (VlanIdStr == NULL || *VlanIdStr == 0) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);\r
+ goto Exit;\r
+ }\r
+\r
+ VlanId = StrToVlanId (VlanIdStr);\r
+ if (VlanId > 4094) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Delete VLAN.\r
+ //\r
+ Status = VlanConfig->Remove (VlanConfig, (UINT16) VlanId);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_NOT_FOUND) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NOT_FOUND), mHiiHandle);\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_FAIL), mHiiHandle, Status);\r
+ }\r
+\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Check whether this is the last VLAN to remove.\r
+ //\r
+ Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // This is the last VLAN to remove, try to connect the controller handle.\r
+ //\r
+ gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
+ } else {\r
+ FreePool (VlanData);\r
+ }\r
+\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_SUCCESS), mHiiHandle);\r
+\r
+Exit:\r
+ if (VlanConfig != NULL) {\r
+ CloseVlanConfigProtocol (Handle);\r
+ }\r
+\r
+ FreePool (Name);\r
+}\r
+\r
+/**\r
+ The actual entry point for the application.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The entry point executed successfully.\r
+ @retval other Some error occur when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VlanConfigMain (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ LIST_ENTRY *List;\r
+ CONST CHAR16 *Str;\r
+\r
+ mImageHandle = ImageHandle;\r
+\r
+ //\r
+ // Register our string package to HII database.\r
+ //\r
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, VConfigStrings, NULL);\r
+ if (mHiiHandle == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ List = NULL;\r
+ ShellCommandLineParseEx (mParamList, &List, NULL, FALSE, FALSE);\r
+ if (List == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);\r
+ goto Exit;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (List, L"-?")) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_HELP), mHiiHandle);\r
+ goto Exit;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (List, L"-l")) {\r
+ Str = ShellCommandLineGetValue (List, L"-l");\r
+ DisplayVlan ((CHAR16 *) Str);\r
+ goto Exit;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (List, L"-a")) {\r
+ Str = ShellCommandLineGetValue (List, L"-a");\r
+ AddVlan ((CHAR16 *) Str);\r
+ goto Exit;\r
+ }\r
+\r
+ if (ShellCommandLineGetFlag (List, L"-d")) {\r
+ Str = ShellCommandLineGetValue (List, L"-d");\r
+ DeleteVlan ((CHAR16 *) Str);\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // No valid argument till now.\r
+ //\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);\r
+\r
+Exit:\r
+ if (List != NULL) {\r
+ ShellCommandLineFreeVarList (List);\r
+ }\r
+\r
+ //\r
+ // Remove our string package from HII database.\r
+ //\r
+ HiiRemovePackages (mHiiHandle);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+## @file\r
+# Component files for VLAN configuration shell application.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = VConfig\r
+ FILE_GUID = 87E36301-0406-44db-AAF3-9E0E591F3725\r
+ MODULE_TYPE = UEFI_APPLICATION\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = VlanConfigMain\r
+\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF\r
+#\r
+\r
+[Sources]\r
+ VConfigStrings.uni\r
+ VConfig.c\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+ ShellPkg/ShellPkg.dec\r
+\r
+[LibraryClasses]\r
+ UefiApplicationEntryPoint\r
+ UefiBootServicesTableLib\r
+ UefiLib\r
+ ShellLib\r
+ NetLib\r
+ MemoryAllocationLib\r
+ HiiLib\r
+\r
+[Protocols]\r
+ gEfiVlanConfigProtocolGuid\r
--- /dev/null
+/** @file\r
+ UEFI Component Name(2) protocol implementation for Dhcp6 driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that attempt to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that attempts to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language, from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r
+\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName = {\r
+ Dhcp6ComponentNameGetDriverName,\r
+ Dhcp6ComponentNameGetControllerName,\r
+ "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Dhcp6ComponentNameGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Dhcp6ComponentNameGetControllerName,\r
+ "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDhcp6DriverNameTable[] = {\r
+ {\r
+ "eng;en",\r
+ L"DHCP6 Protocol Driver"\r
+ },\r
+ {\r
+ NULL,\r
+ NULL\r
+ }\r
+};\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mDhcp6DriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN)(This == &gDhcp6ComponentName)\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in the\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language, from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
--- /dev/null
+/** @file\r
+ Driver Binding functions and Service Binding functions\r
+ implementationfor for Dhcp6 Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = {\r
+ Dhcp6DriverBindingSupported,\r
+ Dhcp6DriverBindingStart,\r
+ Dhcp6DriverBindingStop,\r
+ 0xa,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = {\r
+ Dhcp6ServiceBindingCreateChild,\r
+ Dhcp6ServiceBindingDestroyChild\r
+};\r
+\r
+\r
+/**\r
+ Configure the default Udp6Io to receive all the DHCP6 traffic\r
+ on this network interface.\r
+\r
+ @param[in] UdpIo The pointer to Udp6Io to be configured.\r
+ @param[in] Context The pointer to the context.\r
+\r
+ @retval EFI_SUCCESS The Udp6Io is successfully configured.\r
+ @retval Others Failed to configure the Udp6Io.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ConfigureUdpIo (\r
+ IN UDP_IO *UdpIo,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+ EFI_UDP6_CONFIG_DATA *Config;\r
+\r
+ Udp6 = UdpIo->Protocol.Udp6;\r
+ Config = &(UdpIo->Config.Udp6);\r
+\r
+ ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+ //\r
+ // Set Udp6 configure data for the Dhcp6 instance.\r
+ //\r
+ Config->AcceptPromiscuous = FALSE;\r
+ Config->AcceptAnyPort = FALSE;\r
+ Config->AllowDuplicatePort = FALSE;\r
+ Config->TrafficClass = 0;\r
+ Config->HopLimit = 128;\r
+ Config->ReceiveTimeout = 0;\r
+ Config->TransmitTimeout = 0;\r
+\r
+ //\r
+ // Configure an endpoint of client(0, 546), server(0, 0), the addresses\r
+ // will be overridden later. Note that we MUST not limit RemotePort.\r
+ // More details, refer to RFC 3315 section 5.2.\r
+ //\r
+ Config->StationPort = DHCP6_PORT_CLIENT;\r
+ Config->RemotePort = 0;\r
+\r
+ return Udp6->Configure (Udp6, Config);;\r
+}\r
+\r
+\r
+/**\r
+ Destory the Dhcp6 service. The Dhcp6 service may be partly initialized,\r
+ or partly destroyed. If a resource is destroyed, it is marked as such in\r
+ case the destroy failed and being called again later.\r
+\r
+ @param[in, out] Service The pointer to Dhcp6 service to be destroyed.\r
+\r
+**/\r
+VOID\r
+Dhcp6DestroyService (\r
+ IN OUT DHCP6_SERVICE *Service\r
+ )\r
+{\r
+ //\r
+ // All children instances should have been already destoryed here.\r
+ //\r
+ ASSERT (Service->NumOfChild == 0);\r
+\r
+ if (Service->ClientId != NULL) {\r
+ FreePool (Service->ClientId);\r
+ }\r
+\r
+ if (Service->UdpIo != NULL) {\r
+ UdpIoFreeIo (Service->UdpIo);\r
+ }\r
+\r
+ FreePool (Service);\r
+}\r
+\r
+\r
+/**\r
+ Create a new Dhcp6 service for the Nic controller.\r
+\r
+ @param[in] Controller The controller to be installed DHCP6 service\r
+ binding protocol.\r
+ @param[in] ImageHandle The image handle of the Dhcp6 driver.\r
+ @param[out] Service The return pointer of the new Dhcp6 service.\r
+\r
+ @retval EFI_SUCCESS The Dhcp6 service is created successfully.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CreateService (\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE ImageHandle,\r
+ OUT DHCP6_SERVICE **Service\r
+ )\r
+{\r
+ DHCP6_SERVICE *Dhcp6Srv;\r
+\r
+ *Service = NULL;\r
+ Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE));\r
+\r
+ if (Dhcp6Srv == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Open the SNP protocol to get mode data later.\r
+ //\r
+ Dhcp6Srv->Snp = NULL;\r
+ NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp);\r
+ if (Dhcp6Srv->Snp == NULL) {\r
+ FreePool (Dhcp6Srv);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Initialize the fields of the new Dhcp6 service.\r
+ //\r
+ Dhcp6Srv->Signature = DHCP6_SERVICE_SIGNATURE;\r
+ Dhcp6Srv->InDestory = FALSE;\r
+ Dhcp6Srv->Controller = Controller;\r
+ Dhcp6Srv->Image = ImageHandle;\r
+ Dhcp6Srv->Xid = (0xffffff & NET_RANDOM (NetRandomInitSeed ()));\r
+\r
+ CopyMem (\r
+ &Dhcp6Srv->ServiceBinding,\r
+ &gDhcp6ServiceBindingTemplate,\r
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
+ );\r
+\r
+ //\r
+ // Generate client Duid in the format of Duid-llt.\r
+ //\r
+ Dhcp6Srv->ClientId = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode);\r
+\r
+ if (Dhcp6Srv->ClientId == NULL) {\r
+ FreePool (Dhcp6Srv);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance.\r
+ //\r
+ Dhcp6Srv->UdpIo = UdpIoCreateIo (\r
+ Controller,\r
+ ImageHandle,\r
+ Dhcp6ConfigureUdpIo,\r
+ UDP_IO_UDP6_VERSION,\r
+ NULL\r
+ );\r
+\r
+ if (Dhcp6Srv->UdpIo == NULL) {\r
+ FreePool (Dhcp6Srv->ClientId);\r
+ FreePool (Dhcp6Srv);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ InitializeListHead (&Dhcp6Srv->Child);\r
+\r
+ *Service = Dhcp6Srv;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Destroy the Dhcp6 instance and recycle the resources.\r
+\r
+ @param[in, out] Instance The pointer to the Dhcp6 instance.\r
+\r
+**/\r
+VOID\r
+Dhcp6DestroyInstance (\r
+ IN OUT DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ //\r
+ // Clean up the retry list first.\r
+ //\r
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);\r
+ gBS->CloseEvent (Instance->Timer);\r
+\r
+ //\r
+ // Clean up the current configure data.\r
+ //\r
+ if (Instance->Config != NULL) {\r
+ Dhcp6CleanupConfigData (Instance->Config);\r
+ FreePool (Instance->Config);\r
+ }\r
+\r
+ //\r
+ // Clean up the current Ia.\r
+ //\r
+ if (Instance->IaCb.Ia != NULL) {\r
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+ FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+ }\r
+ FreePool (Instance->IaCb.Ia);\r
+ }\r
+\r
+ if (Instance->Unicast != NULL) {\r
+ FreePool (Instance->Unicast);\r
+ }\r
+\r
+ if (Instance->AdSelect != NULL) {\r
+ FreePool (Instance->AdSelect);\r
+ }\r
+\r
+ FreePool (Instance);\r
+}\r
+\r
+\r
+/**\r
+ Create the Dhcp6 instance and initialize it.\r
+\r
+ @param[in] Service The pointer to the Dhcp6 service.\r
+ @param[out] Instance The pointer to the Dhcp6 instance.\r
+\r
+ @retval EFI_SUCCESS The Dhcp6 instance is created.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CreateInstance (\r
+ IN DHCP6_SERVICE *Service,\r
+ OUT DHCP6_INSTANCE **Instance\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ DHCP6_INSTANCE *Dhcp6Ins;\r
+\r
+ *Instance = NULL;\r
+ Dhcp6Ins = AllocateZeroPool (sizeof (DHCP6_INSTANCE));\r
+\r
+ if (Dhcp6Ins == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Initialize the fields of the new Dhcp6 instance.\r
+ //\r
+ Dhcp6Ins->Signature = DHCP6_INSTANCE_SIGNATURE;\r
+ Dhcp6Ins->UdpSts = EFI_ALREADY_STARTED;\r
+ Dhcp6Ins->Service = Service;\r
+ Dhcp6Ins->InDestory = FALSE;\r
+ Dhcp6Ins->MediaPresent = TRUE;\r
+\r
+ CopyMem (\r
+ &Dhcp6Ins->Dhcp6,\r
+ &gDhcp6ProtocolTemplate,\r
+ sizeof (EFI_DHCP6_PROTOCOL)\r
+ );\r
+\r
+ InitializeListHead (&Dhcp6Ins->TxList);\r
+ InitializeListHead (&Dhcp6Ins->InfList);\r
+\r
+ //\r
+ // There is a timer for each Dhcp6 instance, which is used to track the\r
+ // lease time of Ia and the retransmisson time of all sent packets.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ Dhcp6OnTimerTick,\r
+ Dhcp6Ins,\r
+ &Dhcp6Ins->Timer\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Dhcp6Ins);\r
+ return Status;\r
+ }\r
+\r
+ *Instance = Dhcp6Ins;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Entry point of the DHCP6 driver to install various protocols.\r
+\r
+ @param[in] ImageHandle The handle of the UEFI image file.\r
+ @param[in] SystemTable The pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval Others Unexpected error occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ return EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gDhcp6DriverBinding,\r
+ ImageHandle,\r
+ &gDhcp6ComponentName,\r
+ &gDhcp6ComponentName2\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle. This service\r
+ is called by the EFI boot service ConnectController(). In\r
+ order to make drivers as small as possible, there are a few calling\r
+ restrictions for this service. ConnectController() must\r
+ follow these calling restrictions. If any other agent wishes to call\r
+ Supported() it must also follow these calling restrictions.\r
+\r
+ @param[in] This The pointer to the driver binding protocol.\r
+ @param[in] ControllerHandle The handle of device to be tested.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to be started.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval Others This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ return gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ Start this driver on ControllerHandle. This service is called by the\r
+ EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start() it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This The pointer to the driver binding protocol.\r
+ @param[in] ControllerHandle The handle of device to be started.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to be started.\r
+\r
+ @retval EFI_SUCCESS This driver is installed to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ DHCP6_SERVICE *Service;\r
+\r
+ //\r
+ // Check the Dhcp6 serivce whether already started.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ //\r
+ // Create and initialize the Dhcp6 service.\r
+ //\r
+ Status = Dhcp6CreateService (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &Service\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ ASSERT (Service != NULL);\r
+\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &ControllerHandle,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ &Service->ServiceBinding,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp6DestroyService (Service);\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Stop this driver on ControllerHandle. This service is called by the\r
+ EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop() it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
+ children is zero stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCESS This driver is removed ControllerHandle\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval other This driver was not removed from this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ EFI_HANDLE NicHandle;\r
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;\r
+ DHCP6_SERVICE *Service;\r
+ DHCP6_INSTANCE *Instance;\r
+\r
+ //\r
+ // Find and check the Nic handle by the controller handle.\r
+ //\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);\r
+\r
+ if (NicHandle == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ NicHandle,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ (VOID **) &ServiceBinding,\r
+ This->DriverBindingHandle,\r
+ NicHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+ if (Service->InDestory) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (NumberOfChildren == 0) {\r
+ //\r
+ // Destory the service itself if no child instance left.\r
+ //\r
+ Service->InDestory = TRUE;\r
+\r
+ Status = gBS->UninstallProtocolInterface (\r
+ NicHandle,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ ServiceBinding\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Service->InDestory = FALSE;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Dhcp6DestroyService (Service);\r
+\r
+ } else {\r
+ //\r
+ // Destory all the children instances before destory the service.\r
+ //\r
+ while (!IsListEmpty (&Service->Child)) {\r
+ Instance = NET_LIST_HEAD (&Service->Child, DHCP6_INSTANCE, Link);\r
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
+ }\r
+ //\r
+ // Any of child failed to be destroyed.\r
+ //\r
+ if (Service->NumOfChild != 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Creates a child handle and installs a protocol.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+ then a new handle is created. If it is a pointer to an existing\r
+ UEFI handle, then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ DHCP6_SERVICE *Service;\r
+ DHCP6_INSTANCE *Instance;\r
+ VOID *Udp6;\r
+\r
+ if (This == NULL || ChildHandle == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Service = DHCP6_SERVICE_FROM_THIS (This);\r
+\r
+ Status = Dhcp6CreateInstance (Service, &Instance);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ ASSERT (Instance != NULL);\r
+\r
+ //\r
+ // Start the timer when the instance is ready to use.\r
+ //\r
+ Status = gBS->SetTimer (\r
+ Instance->Timer,\r
+ TimerPeriodic,\r
+ TICKS_PER_SECOND\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Install the DHCP6 protocol onto ChildHandle.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ ChildHandle,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ &Instance->Dhcp6,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Instance->Handle = *ChildHandle;\r
+\r
+ //\r
+ // Open the UDP6 protocol BY_CHILD.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Service->UdpIo->UdpHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ (VOID **) &Udp6,\r
+ gDhcp6DriverBinding.DriverBindingHandle,\r
+ Instance->Handle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Instance->Handle,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ &Instance->Dhcp6,\r
+ NULL\r
+ );\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Add into the children list of its parent service.\r
+ //\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ InsertTailList (&Service->Child, &Instance->Link);\r
+ Service->NumOfChild++;\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ Dhcp6DestroyInstance (Instance);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Destroys a child handle with a protocol installed on it.\r
+\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in] ChildHandle Handle of the child to destroy\r
+\r
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.\r
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle\r
+ because its services are being used.\r
+ @retval other The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ EFI_DHCP6_PROTOCOL *Dhcp6;\r
+ DHCP6_SERVICE *Service;\r
+ DHCP6_INSTANCE *Instance;\r
+\r
+ if (This == NULL || ChildHandle == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Retrieve the private context data structures\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ChildHandle,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ (VOID **) &Dhcp6,\r
+ gDhcp6DriverBinding.DriverBindingHandle,\r
+ ChildHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6);\r
+ Service = DHCP6_SERVICE_FROM_THIS (This);\r
+\r
+ if (Instance->Service != Service) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Instance->InDestory) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Instance->InDestory = TRUE;\r
+\r
+ Status = gBS->CloseProtocol (\r
+ Service->UdpIo->UdpHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ gDhcp6DriverBinding.DriverBindingHandle,\r
+ ChildHandle\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Instance->InDestory = FALSE;\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Uninstall the MTFTP6 protocol first to enable a top down destruction.\r
+ //\r
+ Status = gBS->UninstallProtocolInterface (\r
+ ChildHandle,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ Dhcp6\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Instance->InDestory = FALSE;\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Remove it from the children list of its parent service.\r
+ //\r
+ RemoveEntryList (&Instance->Link);\r
+ Service->NumOfChild--;\r
+\r
+ Dhcp6DestroyInstance (Instance);\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ Driver Binding functions and Service Binding functions\r
+ declaration for Dhcp6 Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_DRIVER_H__\r
+#define __EFI_DHCP6_DRIVER_H__\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+\r
+extern EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2;\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle. This service\r
+ is called by the EFI boot service ConnectController(). In\r
+ order to make drivers as small as possible, there are a few calling\r
+ restrictions for this service. ConnectController() must\r
+ follow these calling restrictions. If any other agent wishes to call\r
+ Supported(), it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to test.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Start this driver on ControllerHandle. This service is called by the\r
+ EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start(), it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Stop this driver on ControllerHandle. This service is called by the\r
+ EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop() it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of\r
+ children is zero, stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.\r
+ @retval other This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ );\r
+\r
+/**\r
+ Creates a child handle and installs a protocol.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+ then a new handle is created. If it is a pointer to an existing UEFI handle,\r
+ then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create\r
+ the child.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ );\r
+\r
+/**\r
+ Destroys a child handle with a protocol installed on it.\r
+\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.\r
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle\r
+ because its services are being used.\r
+ @retval other The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ );\r
+\r
+#endif\r
--- /dev/null
+## @file\r
+# Component description file for Dhcp6 module.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = Dhcp6Dxe\r
+ FILE_GUID = 95E3669D-34BE-4775-A651-7EA41B69D89E\r
+ MODULE_TYPE = UEFI_DRIVER\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = Dhcp6DriverEntryPoint\r
+ UNLOAD_IMAGE = NetLibDefaultUnload\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC\r
+#\r
+# DRIVER_BINDING = gDhcp6DriverBinding\r
+# COMPONENT_NAME = gDhcp6ComponentName\r
+# COMPONENT_NAME2 = gDhcp6ComponentName2\r
+#\r
+\r
+[Sources]\r
+ Dhcp6Driver.c\r
+ Dhcp6Driver.h\r
+ Dhcp6Impl.c\r
+ Dhcp6Impl.h\r
+ Dhcp6Io.c\r
+ Dhcp6Io.h\r
+ Dhcp6Utility.c\r
+ Dhcp6Utility.h\r
+ ComponentName.c\r
+\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+ UefiLib\r
+ BaseLib\r
+ BaseMemoryLib\r
+ MemoryAllocationLib\r
+ UefiDriverEntryPoint\r
+ UefiBootServicesTableLib\r
+ UefiRuntimeServicesTableLib\r
+ DebugLib\r
+ NetLib\r
+ UdpIoLib\r
+\r
+\r
+[Protocols]\r
+ gEfiUdp6ServiceBindingProtocolGuid\r
+ gEfiUdp6ProtocolGuid\r
+ gEfiDhcp6ServiceBindingProtocolGuid\r
+ gEfiDhcp6ProtocolGuid\r
+\r
--- /dev/null
+/** @file\r
+ This EFI_DHCP6_PROTOCOL interface implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+//\r
+// Well-known multi-cast address defined in section-24.1 of rfc-3315\r
+//\r
+// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2\r
+// ALL_DHCP_Servers address: FF05::1:3\r
+//\r
+EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};\r
+EFI_IPv6_ADDRESS mAllDhcpServersAddress = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}};\r
+\r
+EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {\r
+ EfiDhcp6GetModeData,\r
+ EfiDhcp6Configure,\r
+ EfiDhcp6Start,\r
+ EfiDhcp6InfoRequest,\r
+ EfiDhcp6RenewRebind,\r
+ EfiDhcp6Decline,\r
+ EfiDhcp6Release,\r
+ EfiDhcp6Stop,\r
+ EfiDhcp6Parse\r
+};\r
+\r
+/**\r
+ Starts the DHCPv6 standard S.A.R.R. process.\r
+\r
+ The Start() function starts the DHCPv6 standard process. This function can\r
+ be called only when the state of Dhcp6 instance is in the Dhcp6Init state.\r
+ If the DHCP process completes successfully, the state of the Dhcp6 instance\r
+ will be transferred through Dhcp6Selecting and Dhcp6Requesting to the\r
+ Dhcp6Bound state.\r
+ Refer to rfc-3315 for precise state transitions during this process. At the\r
+ time when each event occurs in this process, the callback function that was set\r
+ by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this\r
+ opportunity to control the process.\r
+\r
+ @param[in] This The pointer to Dhcp6 protocol.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 standard process has started, or it has\r
+ completed when CompletionEvent is NULL.\r
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no\r
+ response was received from the server within the\r
+ specified timeout value.\r
+ @retval EFI_ABORTED The user aborted the DHCPv6 process.\r
+ @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6\r
+ standard process.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_NO_MEDIA There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Start (\r
+ IN EFI_DHCP6_PROTOCOL *This\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+\r
+ //\r
+ // The instance hasn't been configured.\r
+ //\r
+ if (Instance->Config == NULL) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+ //\r
+ // The instance has already been started.\r
+ //\r
+ if (Instance->IaCb.Ia->State != Dhcp6Init) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+ //\r
+ // Need to clear initial time to make sure that elapsed-time\r
+ // is set to 0 for first Solicit.\r
+ //\r
+ Instance->StartTime = 0;\r
+\r
+ //\r
+ // Send the solicit message to start S.A.R.R process.\r
+ //\r
+ Status = Dhcp6SendSolicitMsg (Instance);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Register receive callback for the stateful exchange process.\r
+ //\r
+ Status = UdpIoRecvDatagram(\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // Poll udp out of the net tpl if synchronous call.\r
+ //\r
+ if (Instance->Config->IaInfoEvent == NULL) {\r
+\r
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+ }\r
+ return Instance->UdpSts;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Stops the DHCPv6 standard S.A.R.R. process.\r
+\r
+ The Stop() function is used to stop the DHCPv6 standard process. After this\r
+ function is called successfully, the state of Dhcp6 instance is transferred\r
+ into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called\r
+ before DHCPv6 standard process can be started again. This function can be\r
+ called when the Dhcp6 instance is in any state.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+\r
+ @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Stop (\r
+ IN EFI_DHCP6_PROTOCOL *This\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+ Udp6 = Service->UdpIo->Protocol.Udp6;\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // The instance hasn't been configured.\r
+ //\r
+ if (Instance->Config == NULL) {\r
+ return Status;\r
+ }\r
+\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+ //\r
+ // The instance has already been stopped.\r
+ //\r
+ if (Instance->IaCb.Ia->State == Dhcp6Init ||\r
+ Instance->IaCb.Ia->State == Dhcp6Selecting ||\r
+ Instance->IaCb.Ia->State == Dhcp6Requesting\r
+ ) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Release the current ready Ia.\r
+ //\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Instance->UdpSts = EFI_ALREADY_STARTED;\r
+ Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // Poll udp out of the net tpl if synchoronus call.\r
+ //\r
+ if (Instance->Config->IaInfoEvent == NULL) {\r
+ ASSERT (Udp6 != NULL);\r
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+ Udp6->Poll (Udp6);\r
+ }\r
+ Status = Instance->UdpSts;\r
+ }\r
+\r
+ //\r
+ // Clean up the session data for the released Ia.\r
+ //\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Dhcp6CleanupSession (Instance, EFI_SUCCESS);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Returns the current operating mode data for the Dhcp6 instance.\r
+\r
+ The GetModeData() function returns the current operating mode and\r
+ cached data packet for the Dhcp6 instance.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data.\r
+ @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data.\r
+\r
+ @retval EFI_SUCCESS The mode data was returned.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance was not\r
+ configured.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6GetModeData (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL,\r
+ OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_DHCP6_IA *Ia;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+ UINT32 IaSize;\r
+ UINT32 IdSize;\r
+\r
+ if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+\r
+ if (Instance->Config == NULL && Dhcp6ConfigData != NULL) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ ASSERT (Service->ClientId != NULL);\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // User needs a copy of instance config data.\r
+ //\r
+ if (Dhcp6ConfigData != NULL) {\r
+ ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA));\r
+ //\r
+ // Duplicate config data, including all reference buffers.\r
+ //\r
+ if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) {\r
+ goto ON_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // User need a copy of instance mode data.\r
+ //\r
+ if (Dhcp6ModeData != NULL) {\r
+ ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA));\r
+ //\r
+ // Duplicate a copy of EFI_DHCP6_DUID for client Id.\r
+ //\r
+ IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length);\r
+\r
+ Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize);\r
+ if (Dhcp6ModeData->ClientId == NULL) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ CopyMem (\r
+ Dhcp6ModeData->ClientId,\r
+ Service->ClientId,\r
+ IdSize\r
+ );\r
+\r
+ Ia = Instance->IaCb.Ia;\r
+ if (Ia != NULL) {\r
+ //\r
+ // Duplicate a copy of EFI_DHCP6_IA for configured Ia.\r
+ //\r
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+\r
+ Dhcp6ModeData->Ia = AllocateZeroPool (IaSize);\r
+ if (Dhcp6ModeData->Ia == NULL) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ CopyMem (\r
+ Dhcp6ModeData->Ia,\r
+ Ia,\r
+ IaSize\r
+ );\r
+\r
+ //\r
+ // Duplicate a copy of reply packet if has.\r
+ //\r
+ if (Ia->ReplyPacket != NULL) {\r
+ Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size);\r
+ if (Dhcp6ModeData->Ia->ReplyPacket == NULL) {\r
+ goto ON_ERROR;\r
+ }\r
+ CopyMem (\r
+ Dhcp6ModeData->Ia->ReplyPacket,\r
+ Ia->ReplyPacket,\r
+ Ia->ReplyPacket->Size\r
+ );\r
+ }\r
+ }\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ if (Dhcp6ConfigData != NULL) {\r
+ Dhcp6CleanupConfigData (Dhcp6ConfigData);\r
+ }\r
+ if (Dhcp6ModeData != NULL) {\r
+ Dhcp6CleanupModeData (Dhcp6ModeData);\r
+ }\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_OUT_OF_RESOURCES;\r
+}\r
+\r
+\r
+/**\r
+ Initializes, changes, or resets the operational settings for the Dhcp6 instance.\r
+\r
+ The Configure() function is used to initialize or clean up the configuration\r
+ data of the Dhcp6 instance:\r
+ - When Dhcp6CfgData is not NULL and Configure() is called successfully, the\r
+ configuration data will be initialized in the Dhcp6 instance, and the state\r
+ of the configured IA will be transferred into Dhcp6Init.\r
+ - When Dhcp6CfgData is NULL and Configure() is called successfully, the\r
+ configuration data will be cleaned up and no IA will be associated with\r
+ the Dhcp6 instance.\r
+ To update the configuration data for an Dhcp6 instance, the original data\r
+ must be cleaned up before setting the new configuration data.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol\r
+ @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA.\r
+\r
+ @retval EFI_SUCCESS The Dhcp6 is configured successfully with the\r
+ Dhcp6Init state, or cleaned up the original\r
+ configuration setting.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance was already configured.\r
+ The Dhcp6 instance has already started the\r
+ DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.\r
+ @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Configure (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Entry;\r
+ DHCP6_INSTANCE *Other;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+ UINTN Index;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+\r
+ //\r
+ // Check the parameter of configure data.\r
+ //\r
+ if (Dhcp6CfgData != NULL) {\r
+ if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (Dhcp6CfgData->OptionList != NULL) {\r
+ for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) {\r
+ if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId ||\r
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit ||\r
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept ||\r
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana ||\r
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA &&\r
+ Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Dhcp6CfgData->SolicitRetransmission != NULL &&\r
+ Dhcp6CfgData->SolicitRetransmission->Mrc == 0 &&\r
+ Dhcp6CfgData->SolicitRetransmission->Mrd == 0\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Make sure the (IaId, IaType) is unique over all the instances.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &Service->Child) {\r
+ Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);\r
+ if (Other->IaCb.Ia != NULL &&\r
+ Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&\r
+ Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (Dhcp6CfgData != NULL) {\r
+ //\r
+ // It's not allowed to configure one instance twice without configure null.\r
+ //\r
+ if (Instance->Config != NULL) {\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Duplicate config data including all reference buffers.\r
+ //\r
+ Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA));\r
+ if (Instance->Config == NULL) {\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData);\r
+ if (EFI_ERROR(Status)) {\r
+ FreePool (Instance->Config);\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Initialize the Ia descriptor from the config data, and leave the other\r
+ // fields of the Ia as default value 0.\r
+ //\r
+ Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA));\r
+ if (Instance->IaCb.Ia == NULL) {\r
+ Dhcp6CleanupConfigData (Instance->Config);\r
+ FreePool (Instance->Config);\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (\r
+ &Instance->IaCb.Ia->Descriptor,\r
+ &Dhcp6CfgData->IaDescriptor,\r
+ sizeof(EFI_DHCP6_IA_DESCRIPTOR)\r
+ );\r
+\r
+ } else {\r
+\r
+ if (Instance->Config == NULL) {\r
+ ASSERT (Instance->IaCb.Ia == NULL);\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // It's not allowed to configure a started instance as null.\r
+ //\r
+ if (Instance->IaCb.Ia->State != Dhcp6Init) {\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ Dhcp6CleanupConfigData (Instance->Config);\r
+ FreePool (Instance->Config);\r
+ Instance->Config = NULL;\r
+\r
+ FreePool (Instance->IaCb.Ia);\r
+ Instance->IaCb.Ia = NULL;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Request configuration information without the assignment of any\r
+ Ia addresses of the client.\r
+\r
+ The InfoRequest() function is used to request configuration information\r
+ without the assignment of any IPv6 address of the client. The client sends\r
+ out an Information Request packet to obtain the required configuration\r
+ information, and DHCPv6 server responds with a Reply packet containing\r
+ the information for the client. The received Reply packet will be passed\r
+ to the user by ReplyCallback function. If the user returns EFI_NOT_READY from\r
+ ReplyCallback, the Dhcp6 instance will continue to receive other Reply\r
+ packets unless timeout according to the Retransmission parameter.\r
+ Otherwise, the Information Request exchange process will be finished\r
+ successfully if user returns EFI_SUCCESS from ReplyCallback.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client\r
+ Identifier option and include it into Information Request\r
+ packet. Otherwise, Client Identifier option will not be included.\r
+ @param[in] OptionRequest The pointer to the buffer of option request options.\r
+ @param[in] OptionCount The option number in the OptionList.\r
+ @param[in] OptionList The list of appended options.\r
+ @param[in] Retransmission The pointer to the retransmission of the message.\r
+ @param[in] TimeoutEvent The event of timeout.\r
+ @param[in] ReplyCallback The callback function when the reply was received.\r
+ @param[in] CallbackContext The pointer to the parameter passed to the callback.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 information request exchange process\r
+ completed when TimeoutEvent is NULL. Information\r
+ Request packet has been sent to DHCPv6 server when\r
+ TimeoutEvent is not NULL.\r
+ @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed\r
+ because of no response, or not all requested-options\r
+ are responded by DHCPv6 servers when Timeout happened.\r
+ @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted\r
+ by user.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6InfoRequest (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN BOOLEAN SendClientId,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,\r
+ IN UINT32 OptionCount,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,\r
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission,\r
+ IN EFI_EVENT TimeoutEvent OPTIONAL,\r
+ IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,\r
+ IN VOID *CallbackContext OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+ DHCP6_INF_CB *InfCb;\r
+ UINTN Index;\r
+\r
+ if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (OptionCount > 0 && OptionList == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (OptionList != NULL) {\r
+ for (Index = 0; Index < OptionCount; Index++) {\r
+ if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+ //\r
+ // Create and initialize the control block for the info-request.\r
+ //\r
+ InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB));\r
+\r
+ if (InfCb == NULL) {\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ InfCb->ReplyCallback = ReplyCallback;\r
+ InfCb->CallbackContext = CallbackContext;\r
+ InfCb->TimeoutEvent = TimeoutEvent;\r
+\r
+ InsertTailList (&Instance->InfList, &InfCb->Link);\r
+\r
+ //\r
+ // Send the info-request message to start exchange process.\r
+ //\r
+ Status = Dhcp6SendInfoRequestMsg (\r
+ Instance,\r
+ InfCb,\r
+ SendClientId,\r
+ OptionRequest,\r
+ OptionCount,\r
+ OptionList,\r
+ Retransmission\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Register receive callback for the stateless exchange process.\r
+ //\r
+ Status = UdpIoRecvDatagram(\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // Poll udp out of the net tpl if synchoronus call.\r
+ //\r
+ if (TimeoutEvent == NULL) {\r
+\r
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+ }\r
+ return Instance->UdpSts;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ RemoveEntryList (&InfCb->Link);\r
+ FreePool (InfCb);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Manually extend the valid and preferred lifetimes for the IPv6 addresses\r
+ of the configured IA and update other configuration parameters by sending a\r
+ Renew or Rebind packet.\r
+\r
+ The RenewRebind() function is used to manually extend the valid and preferred\r
+ lifetimes for the IPv6 addresses of the configured IA, and update other\r
+ configuration parameters by sending Renew or Rebind packet.\r
+ - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,\r
+ it sends Renew packet to the previously DHCPv6 server and transfer the\r
+ state of the configured IA to Dhcp6Renewing. If valid Reply packet received,\r
+ the state transfers to Dhcp6Bound and the valid and preferred timer restarts.\r
+ If fails, the state transfers to Dhcp6Bound, but the timer continues.\r
+ - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,\r
+ it will send a Rebind packet. If valid Reply packet is received, the state transfers\r
+ to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state\r
+ transfers to Dhcp6Init, and the IA can't be used.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.\r
+ Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process has\r
+ completed and at least one IPv6 address of the\r
+ configured IA has been bound again when\r
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL.\r
+ The EFI DHCPv6 Protocol instance has sent Renew\r
+ or Rebind packet when\r
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the\r
+ state of the configured IA is not in Dhcp6Bound.\r
+ @retval EFI_ALREADY_STARTED The state of the configured IA has already entered\r
+ Dhcp6Renewing when RebindRequest is FALSE.\r
+ The state of the configured IA has already entered\r
+ Dhcp6Rebinding when RebindRequest is TRUE.\r
+ @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted\r
+ by the user.\r
+ @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed\r
+ because of no response.\r
+ @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured\r
+ IA after the DHCPv6 renew/rebind exchange process.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6RenewRebind (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN BOOLEAN RebindRequest\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+\r
+ //\r
+ // The instance hasn't been configured.\r
+ //\r
+ if (Instance->Config == NULL) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+ //\r
+ // The instance has already entered renewing or rebinding state.\r
+ //\r
+ if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) ||\r
+ (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest)\r
+ ) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+ //\r
+ // Send renew/rebind message to start exchange process.\r
+ //\r
+ Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Register receive callback for the stateful exchange process.\r
+ //\r
+ Status = UdpIoRecvDatagram(\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // Poll udp out of the net tpl if synchoronus call.\r
+ //\r
+ if (Instance->Config->IaInfoEvent == NULL) {\r
+\r
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+ }\r
+ return Instance->UdpSts;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Inform that one or more addresses assigned by a server are already\r
+ in use by another node.\r
+\r
+ The Decline() function is used to manually decline the assignment of\r
+ IPv6 addresses, which have been already used by another node. If all\r
+ IPv6 addresses of the configured IA are declined through this function,\r
+ the state of the IA will switch through Dhcp6Declining to Dhcp6Init.\r
+ Otherwise, the state of the IA will restore to Dhcp6Bound after the\r
+ declining process. The Decline() can only be called when the IA is in\r
+ Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,\r
+ this function is a blocking operation. It will return after the\r
+ declining process finishes, or aborted by user.\r
+\r
+ @param[in] This The pointer to EFI_DHCP6_PROTOCOL.\r
+ @param[in] AddressCount The number of declining addresses.\r
+ @param[in] Addresses The pointer to the buffer stored the declining\r
+ addresses.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 decline exchange process completed\r
+ when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.\r
+ The Dhcp6 instance sent Decline packet when\r
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the\r
+ state of the configured IA is not in Dhcp6Bound.\r
+ @retval EFI_ABORTED The DHCPv6 decline exchange process aborted by user.\r
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with\r
+ the configured IA for this instance.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Decline (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ EFI_DHCP6_IA *DecIa;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+\r
+ if (This == NULL || AddressCount == 0 || Addresses == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+\r
+ //\r
+ // The instance hasn't been configured.\r
+ //\r
+ if (Instance->Config == NULL) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Check whether all the declined addresses belongs to the configured Ia.\r
+ //\r
+ Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+ //\r
+ // Deprive of all the declined addresses from the configured Ia, and create a\r
+ // DeclineIa used to create decline message.\r
+ //\r
+ DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+ if (DecIa == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Send the decline message to start exchange process.\r
+ //\r
+ Status = Dhcp6SendDeclineMsg (Instance, DecIa);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Register receive callback for the stateful exchange process.\r
+ //\r
+ Status = UdpIoRecvDatagram(\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ FreePool (DecIa);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // Poll udp out of the net tpl if synchoronus call.\r
+ //\r
+ if (Instance->Config->IaInfoEvent == NULL) {\r
+\r
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+ }\r
+ return Instance->UdpSts;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ if (DecIa != NULL) {\r
+ FreePool (DecIa);\r
+ }\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Release one or more addresses associated with the configured Ia\r
+ for current instance.\r
+\r
+ The Release() function is used to manually release one or more\r
+ IPv6 addresses. If AddressCount is zero, it will release all IPv6\r
+ addresses of the configured IA. If all IPv6 addresses of the IA are\r
+ released through this function, the state of the IA will switch\r
+ through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the\r
+ IA will restore to Dhcp6Bound after the releasing process.\r
+ The Release() can only be called when the IA is in Dhcp6Bound state.\r
+ If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is\r
+ a blocking operation. It will return after the releasing process\r
+ finishes, or is aborted by user.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] AddressCount The number of releasing addresses.\r
+ @param[in] Addresses The pointer to the buffer stored the releasing\r
+ addresses.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 release exchange process\r
+ completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+ was NULL. The Dhcp6 instance was sent Release\r
+ packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+ was not NULL.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the\r
+ state of the configured IA is not in Dhcp6Bound.\r
+ @retval EFI_ABORTED The DHCPv6 release exchange process aborted by user.\r
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with\r
+ the configured IA for this instance.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Release (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ EFI_DHCP6_IA *RelIa;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_SERVICE *Service;\r
+\r
+ if (This == NULL || (AddressCount != 0 && Addresses == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+\r
+ //\r
+ // The instance hasn't been configured.\r
+ //\r
+ if (Instance->Config == NULL) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Check whether all the released addresses belongs to the configured Ia.\r
+ //\r
+ Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance->UdpSts = EFI_ALREADY_STARTED;\r
+\r
+ //\r
+ // Deprive of all the released addresses from the configured Ia, and create a\r
+ // ReleaseIa used to create release message.\r
+ //\r
+ RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);\r
+\r
+ if (RelIa == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Send the release message to start exchange process.\r
+ //\r
+ Status = Dhcp6SendReleaseMsg (Instance, RelIa);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Register receive callback for the stateful exchange process.\r
+ //\r
+ Status = UdpIoRecvDatagram(\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ FreePool (RelIa);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // Poll udp out of the net tpl if synchoronus call.\r
+ //\r
+ if (Instance->Config->IaInfoEvent == NULL) {\r
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {\r
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);\r
+ }\r
+ return Instance->UdpSts;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ if (RelIa != NULL) {\r
+ FreePool (RelIa);\r
+ }\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Parse the option data in the Dhcp6 packet.\r
+\r
+ The Parse() function is used to retrieve the option list in the DHCPv6 packet.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] Packet The pointer to the Dhcp6 packet.\r
+ @param[in, out] OptionCount The number of option in the packet.\r
+ @param[out] PacketOptionList The array of pointers to each option in the packet.\r
+\r
+ @retval EFI_SUCCESS The packet was successfully parsed.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options\r
+ that were found in the Packet.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Parse (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN EFI_DHCP6_PACKET *Packet,\r
+ IN OUT UINT32 *OptionCount,\r
+ OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL\r
+ )\r
+{\r
+ UINT32 OptCnt;\r
+ UINT32 OptLen;\r
+ UINT16 DataLen;\r
+ UINT8 *Start;\r
+ UINT8 *End;\r
+\r
+ if (This == NULL || Packet == NULL || OptionCount == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (*OptionCount != 0 && PacketOptionList == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // The format of Dhcp6 option:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | option-code | option-len (option data) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | option-data |\r
+ // | (option-len octets) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ OptCnt = 0;\r
+ OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER);\r
+ Start = Packet->Dhcp6.Option;\r
+ End = Start + OptLen;\r
+\r
+ //\r
+ // Calculate the number of option in the packet.\r
+ //\r
+ while (Start < End) {\r
+ DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;\r
+ Start += (NTOHS (DataLen) + 4);\r
+ OptCnt++;\r
+ }\r
+\r
+ //\r
+ // It will return buffer too small if pass-in option count is smaller than the\r
+ // actual count of options in the packet.\r
+ //\r
+ if (OptCnt > *OptionCount) {\r
+ *OptionCount = OptCnt;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ ZeroMem (\r
+ PacketOptionList,\r
+ (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *))\r
+ );\r
+\r
+ OptCnt = 0;\r
+ Start = Packet->Dhcp6.Option;\r
+\r
+ while (Start < End) {\r
+\r
+ PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start;\r
+ DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;\r
+ Start += (NTOHS (DataLen) + 4);\r
+ OptCnt++;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Dhcp6 internal data structure and definition declaration.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_IMPL_H__\r
+#define __EFI_DHCP6_IMPL_H__\r
+\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Dhcp6.h>\r
+#include <Protocol/Udp6.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+\r
+#include <Library/UdpIoLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+\r
+typedef struct _DHCP6_IA_CB DHCP6_IA_CB;\r
+typedef struct _DHCP6_INF_CB DHCP6_INF_CB;\r
+typedef struct _DHCP6_TX_CB DHCP6_TX_CB;\r
+typedef struct _DHCP6_SERVICE DHCP6_SERVICE;\r
+typedef struct _DHCP6_INSTANCE DHCP6_INSTANCE;\r
+\r
+#include "Dhcp6Utility.h"\r
+#include "Dhcp6Io.h"\r
+#include "Dhcp6Driver.h"\r
+\r
+#define DHCP6_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'S')\r
+#define DHCP6_INSTANCE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'I')\r
+\r
+//\r
+// Transmit parameters of solicit message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_SOL_MAX_DELAY 1\r
+#define DHCP6_SOL_IRT 1\r
+#define DHCP6_SOL_MRC 0\r
+#define DHCP6_SOL_MRT 120\r
+#define DHCP6_SOL_MRD 0\r
+//\r
+// Transmit parameters of request message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REQ_IRT 1\r
+#define DHCP6_REQ_MRC 10\r
+#define DHCP6_REQ_MRT 30\r
+#define DHCP6_REQ_MRD 0\r
+//\r
+// Transmit parameters of confirm message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_CNF_MAX_DELAY 1\r
+#define DHCP6_CNF_IRT 1\r
+#define DHCP6_CNF_MRC 0\r
+#define DHCP6_CNF_MRT 4\r
+#define DHCP6_CNF_MRD 10\r
+//\r
+// Transmit parameters of renew message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REN_IRT 10\r
+#define DHCP6_REN_MRC 0\r
+#define DHCP6_REN_MRT 600\r
+#define DHCP6_REN_MRD 0\r
+//\r
+// Transmit parameters of rebind message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REB_IRT 10\r
+#define DHCP6_REB_MRC 0\r
+#define DHCP6_REB_MRT 600\r
+#define DHCP6_REB_MRD 0\r
+//\r
+// Transmit parameters of information request message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_INF_MAX_DELAY 1\r
+#define DHCP6_INF_IRT 1\r
+#define DHCP6_INF_MRC 0\r
+#define DHCP6_INF_MRT 120\r
+#define DHCP6_INF_MRD 0\r
+//\r
+// Transmit parameters of release message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_REL_IRT 1\r
+#define DHCP6_REL_MRC 5\r
+#define DHCP6_REL_MRT 0\r
+#define DHCP6_REL_MRD 0\r
+//\r
+// Transmit parameters of decline message, refers to section-5.5 of rfc-3315.\r
+//\r
+#define DHCP6_DEC_IRT 1\r
+#define DHCP6_DEC_MRC 5\r
+#define DHCP6_DEC_MRT 0\r
+#define DHCP6_DEC_MRD 0\r
+\r
+#define DHCP6_PACKET_ALL 0\r
+#define DHCP6_PACKET_STATEFUL 1\r
+#define DHCP6_PACKET_STATELESS 2\r
+\r
+#define DHCP6_BASE_PACKET_SIZE 1024\r
+\r
+#define DHCP6_PORT_CLIENT 546\r
+#define DHCP6_PORT_SERVER 547\r
+\r
+#define DHCP6_INSTANCE_FROM_THIS(Instance) CR ((Instance), DHCP6_INSTANCE, Dhcp6, DHCP6_INSTANCE_SIGNATURE)\r
+#define DHCP6_SERVICE_FROM_THIS(Service) CR ((Service), DHCP6_SERVICE, ServiceBinding, DHCP6_SERVICE_SIGNATURE)\r
+\r
+extern EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress;\r
+extern EFI_IPv6_ADDRESS mAllDhcpServersAddress;\r
+extern EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate;\r
+\r
+//\r
+// Enumeration of Dhcp6 message type, refers to section-5.3 of rfc-3315.\r
+//\r
+typedef enum {\r
+ Dhcp6MsgSolicit = 1,\r
+ Dhcp6MsgAdvertise = 2,\r
+ Dhcp6MsgRequest = 3,\r
+ Dhcp6MsgConfirm = 4,\r
+ Dhcp6MsgRenew = 5,\r
+ Dhcp6MsgRebind = 6,\r
+ Dhcp6MsgReply = 7,\r
+ Dhcp6MsgRelease = 8,\r
+ Dhcp6MsgDecline = 9,\r
+ Dhcp6MsgReconfigure = 10,\r
+ Dhcp6MsgInfoRequest = 11\r
+} DHCP6_MSG_TYPE;\r
+\r
+//\r
+// Enumeration of option code in Dhcp6 packet, refers to section-24.3 of rfc-3315.\r
+//\r
+typedef enum {\r
+ Dhcp6OptClientId = 1,\r
+ Dhcp6OptServerId = 2,\r
+ Dhcp6OptIana = 3,\r
+ Dhcp6OptIata = 4,\r
+ Dhcp6OptIaAddr = 5,\r
+ Dhcp6OptRequestOption = 6,\r
+ Dhcp6OptPreference = 7,\r
+ Dhcp6OptElapsedTime = 8,\r
+ Dhcp6OptReplayMessage = 9,\r
+ Dhcp6OptAuthentication = 11,\r
+ Dhcp6OptServerUnicast = 12,\r
+ Dhcp6OptStatusCode = 13,\r
+ Dhcp6OptRapidCommit = 14,\r
+ Dhcp6OptUserClass = 15,\r
+ Dhcp6OptVendorClass = 16,\r
+ Dhcp6OptVendorInfo = 17,\r
+ Dhcp6OptInterfaceId = 18,\r
+ Dhcp6OptReconfigMessage = 19,\r
+ Dhcp6OptReconfigureAccept = 20\r
+} DHCP6_OPT_CODE;\r
+\r
+//\r
+// Enumeration of status code recorded by IANA, refers to section-24.4 of rfc-3315.\r
+//\r
+typedef enum {\r
+ Dhcp6StsSuccess = 0,\r
+ Dhcp6StsUnspecFail = 1,\r
+ Dhcp6StsNoAddrsAvail = 2,\r
+ Dhcp6StsNoBinding = 3,\r
+ Dhcp6StsNotOnLink = 4,\r
+ Dhcp6StsUseMulticast = 5\r
+} DHCP6_STS_CODE;\r
+\r
+//\r
+// Enumeration of Duid type recorded by IANA, refers to section-24.5 of rfc-3315.\r
+//\r
+typedef enum {\r
+ Dhcp6DuidTypeLlt = 1,\r
+ Dhcp6DuidTypeEn = 2,\r
+ Dhcp6DuidTypeLl = 3\r
+} DHCP6_DUID_TYPE;\r
+\r
+//\r
+// Control block for each IA.\r
+//\r
+struct _DHCP6_IA_CB {\r
+ EFI_DHCP6_IA *Ia;\r
+ UINT32 T1;\r
+ UINT32 T2;\r
+ UINT32 AllExpireTime;\r
+ UINT32 LeaseTime;\r
+};\r
+\r
+//\r
+// Control block for each transmitted message.\r
+//\r
+struct _DHCP6_TX_CB {\r
+ LIST_ENTRY Link;\r
+ UINT32 Xid;\r
+ EFI_DHCP6_PACKET *TxPacket;\r
+ EFI_DHCP6_RETRANSMISSION RetryCtl;\r
+ UINT32 RetryCnt;\r
+ UINT32 RetryExp;\r
+ UINT32 RetryLos;\r
+ UINT32 TickTime;\r
+ UINT16 *Elapsed;\r
+};\r
+\r
+//\r
+// Control block for each info-request message.\r
+//\r
+struct _DHCP6_INF_CB {\r
+ LIST_ENTRY Link;\r
+ UINT32 Xid;\r
+ EFI_DHCP6_INFO_CALLBACK ReplyCallback;\r
+ VOID *CallbackContext;\r
+ EFI_EVENT TimeoutEvent;\r
+};\r
+\r
+//\r
+// Control block for Dhcp6 instance, it's per configuration data.\r
+//\r
+struct _DHCP6_INSTANCE {\r
+ UINT32 Signature;\r
+ EFI_HANDLE Handle;\r
+ DHCP6_SERVICE *Service;\r
+ LIST_ENTRY Link;\r
+ EFI_DHCP6_PROTOCOL Dhcp6;\r
+ EFI_EVENT Timer;\r
+ EFI_DHCP6_CONFIG_DATA *Config;\r
+ EFI_DHCP6_IA *CacheIa;\r
+ DHCP6_IA_CB IaCb;\r
+ LIST_ENTRY TxList;\r
+ LIST_ENTRY InfList;\r
+ EFI_DHCP6_PACKET *AdSelect;\r
+ UINT8 AdPref;\r
+ EFI_IPv6_ADDRESS *Unicast;\r
+ EFI_STATUS UdpSts;\r
+ BOOLEAN InDestory;\r
+ BOOLEAN MediaPresent;\r
+ UINT64 StartTime;\r
+};\r
+\r
+//\r
+// Control block for Dhcp6 service, it's per Nic handle.\r
+//\r
+struct _DHCP6_SERVICE {\r
+ UINT32 Signature;\r
+ EFI_HANDLE Controller;\r
+ EFI_HANDLE Image;\r
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;\r
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ UDP_IO *UdpIo;\r
+ UINT32 Xid;\r
+ LIST_ENTRY Child;\r
+ UINTN NumOfChild;\r
+ BOOLEAN InDestory;\r
+};\r
+\r
+/**\r
+ Starts the DHCPv6 standard S.A.R.R. process.\r
+\r
+ The Start() function starts the DHCPv6 standard process. This function can\r
+ be called only when the state of Dhcp6 instance is in the Dhcp6Init state.\r
+ If the DHCP process completes successfully, the state of the Dhcp6 instance\r
+ will be transferred through Dhcp6Selecting and Dhcp6Requesting to the\r
+ Dhcp6Bound state.\r
+ Refer to rfc-3315 for precise state transitions during this process. At the\r
+ time when each event occurs in this process, the callback function that was set\r
+ by EFI_DHCP6_PROTOCOL.Configure() will be called and the user can take this\r
+ opportunity to control the process.\r
+\r
+ @param[in] This The pointer to Dhcp6 protocol.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 standard process has started, or it\r
+ completed when CompletionEvent was NULL.\r
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no\r
+ response was received from the server within the\r
+ specified timeout value.\r
+ @retval EFI_ABORTED The user aborted the DHCPv6 process.\r
+ @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6\r
+ standard process.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Start (\r
+ IN EFI_DHCP6_PROTOCOL *This\r
+ );\r
+\r
+/**\r
+ Stops the DHCPv6 standard S.A.R.R. process.\r
+\r
+ The Stop() function is used to stop the DHCPv6 standard process. After this\r
+ function is called successfully, the state of Dhcp6 instance is transferred\r
+ into the Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called\r
+ before DHCPv6 standard process can be started again. This function can be\r
+ called when the Dhcp6 instance is in any state.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+\r
+ @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Stop (\r
+ IN EFI_DHCP6_PROTOCOL *This\r
+ );\r
+\r
+/**\r
+ Returns the current operating mode data for the Dhcp6 instance.\r
+\r
+ The GetModeData() function returns the current operating mode and\r
+ cached data packet for the Dhcp6 instance.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data.\r
+ @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data.\r
+\r
+ @retval EFI_SUCCESS The mode data was returned.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance has not\r
+ been configured when Dhcp6ConfigData is\r
+ not NULL.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6GetModeData (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL,\r
+ OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Initializes, changes, or resets the operational settings for the Dhcp6 instance.\r
+\r
+ The Configure() function is used to initialize or clean up the configuration\r
+ data of the Dhcp6 instance:\r
+ - When Dhcp6CfgData is not NULL and Configure() is called successfully, the\r
+ configuration data will be initialized in the Dhcp6 instance and the state\r
+ of the configured IA will be transferred into Dhcp6Init.\r
+ - When Dhcp6CfgData is NULL and Configure() is called successfully, the\r
+ configuration data will be cleaned up and no IA will be associated with\r
+ the Dhcp6 instance.\r
+ To update the configuration data for an Dhcp6 instance, the original data\r
+ must be cleaned up before setting the new configuration data.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol\r
+ @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA.\r
+\r
+ @retval EFI_SUCCESS The Dhcp6 is configured successfully with the\r
+ Dhcp6Init state, or cleaned up the original\r
+ configuration setting.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance has been already configured\r
+ when Dhcp6CfgData is not NULL.\r
+ The Dhcp6 instance has already started the\r
+ DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.\r
+ @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Configure (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Request configuration information without the assignment of any\r
+ Ia addresses of the client.\r
+\r
+ The InfoRequest() function is used to request configuration information\r
+ without the assignment of any IPv6 address of the client. Client sends\r
+ out Information Request packet to obtain the required configuration\r
+ information, and DHCPv6 server responds with Reply packet containing\r
+ the information for the client. The received Reply packet will be passed\r
+ to the user by ReplyCallback function. If user returns EFI_NOT_READY from\r
+ ReplyCallback, the Dhcp6 instance will continue to receive other Reply\r
+ packets unless timeout according to the Retransmission parameter.\r
+ Otherwise, the Information Request exchange process will be finished\r
+ successfully if user returns EFI_SUCCESS from ReplyCallback.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client\r
+ Identifier option and include it into Information Request\r
+ packet. Otherwise, Client Identifier option will not be included.\r
+ @param[in] OptionRequest The pointer to the buffer of option request options.\r
+ @param[in] OptionCount The option number in the OptionList.\r
+ @param[in] OptionList The list of appended options.\r
+ @param[in] Retransmission The pointer to the retransmission of the message.\r
+ @param[in] TimeoutEvent The event of timeout.\r
+ @param[in] ReplyCallback The callback function when a reply was received.\r
+ @param[in] CallbackContext The pointer to the parameter passed to the callback.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 information request exchange process\r
+ completed when TimeoutEvent is NULL. Information\r
+ Request packet has been sent to DHCPv6 server when\r
+ TimeoutEvent is not NULL.\r
+ @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed\r
+ because of no response, or not all requested-options\r
+ are responded to by DHCPv6 servers when Timeout happened.\r
+ @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted\r
+ by the user.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6InfoRequest (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN BOOLEAN SendClientId,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,\r
+ IN UINT32 OptionCount,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,\r
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission,\r
+ IN EFI_EVENT TimeoutEvent OPTIONAL,\r
+ IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,\r
+ IN VOID *CallbackContext OPTIONAL\r
+ );\r
+\r
+/**\r
+ Manually extend the valid and preferred lifetimes for the IPv6 addresses\r
+ of the configured IA and update other configuration parameters by sending\r
+ Renew or Rebind packet.\r
+\r
+ The RenewRebind() function is used to manually extend the valid and preferred\r
+ lifetimes for the IPv6 addresses of the configured IA and update other\r
+ configuration parameters by sending a Renew or Rebind packet.\r
+ - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,\r
+ it will send Renew packet to the previously DHCPv6 server and transfer the\r
+ state of the configured IA to Dhcp6Renewing. If valid Reply packet received,\r
+ the state transfers to Dhcp6Bound and the valid and preferred timer restarts.\r
+ If fails, the state transfers to Dhcp6Bound but the timer continues.\r
+ - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,\r
+ it will send a Rebind packet. If a valid Reply packet is received, the state transfers\r
+ to Dhcp6Bound, and the valid and preferred timer restarts. If it fails, the state\r
+ transfers to Dhcp6Init, and the IA can't be used.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.\r
+ Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process\r
+ completed and at least one IPv6 address of the\r
+ configured IA was bound again when\r
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.\r
+ The EFI DHCPv6 Protocol instance has sent Renew\r
+ or Rebind packet when\r
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the\r
+ state of the configured IA is not in Dhcp6Bound.\r
+ @retval EFI_ALREADY_STARTED The state of the configured IA has already entered\r
+ Dhcp6Renewing when RebindRequest is FALSE.\r
+ The state of the configured IA has already entered\r
+ Dhcp6Rebinding when RebindRequest is TRUE.\r
+ @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted\r
+ by user.\r
+ @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed\r
+ because of no response.\r
+ @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured\r
+ IA after the DHCPv6 renew/rebind exchange process.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6RenewRebind (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN BOOLEAN RebindRequest\r
+ );\r
+\r
+/**\r
+ Inform that one or more addresses assigned by a server are already\r
+ in use by another node.\r
+\r
+ The Decline() function is used to manually decline the assignment of\r
+ IPv6 addresses, which have been already used by another node. If all\r
+ IPv6 addresses of the configured IA are declined through this function,\r
+ the state of the IA will switch through Dhcp6Declining to Dhcp6Init.\r
+ Otherwise, the state of the IA will restore to Dhcp6Bound after the\r
+ declining process. The Decline() can only be called when the IA is in\r
+ Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,\r
+ this function is a blocking operation. It will return after the\r
+ declining process finishes, or aborted by user.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] AddressCount The number of declining addresses.\r
+ @param[in] Addresses The pointer to the buffer stored the declining\r
+ addresses.\r
+\r
+ @retval EFI_SUCCESS The DHCPv6 decline exchange process completed\r
+ when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.\r
+ The Dhcp6 instance has sent Decline packet when\r
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the\r
+ state of the configured IA is not in Dhcp6Bound.\r
+ @retval EFI_ABORTED The DHCPv6 decline exchange process was aborted by the user.\r
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with\r
+ the configured IA for this instance.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Decline (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ );\r
+\r
+/**\r
+ Release one or more addresses associated with the configured Ia\r
+ for the current instance.\r
+\r
+ The Release() function is used to manually release the one or more\r
+ IPv6 address. If AddressCount is zero, it will release all IPv6\r
+ addresses of the configured IA. If all IPv6 addresses of the IA are\r
+ released through this function, the state of the IA will switch\r
+ through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the\r
+ IA will restore to Dhcp6Bound after the releasing process.\r
+ The Release() can only be called when the IA is in a Dhcp6Bound state.\r
+ If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is\r
+ a blocking operation. It will return after the releasing process\r
+ finishes, or aborted by user.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] AddressCount The number of releasing addresses.\r
+ @param[in] Addresses The pointer to the buffer stored the releasing\r
+ addresses.\r
+ @retval EFI_SUCCESS The DHCPv6 release exchange process has\r
+ completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+ is NULL. The Dhcp6 instance has sent Release\r
+ packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent\r
+ is not NULL.\r
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the\r
+ state of the configured IA is not in Dhcp6Bound.\r
+ @retval EFI_ABORTED The DHCPv6 release exchange process was aborted by the user.\r
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with\r
+ the configured IA for this instance.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Release (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ );\r
+\r
+/**\r
+ Parse the option data in the Dhcp6 packet.\r
+\r
+ The Parse() function is used to retrieve the option list in the DHCPv6 packet.\r
+\r
+ @param[in] This The pointer to the Dhcp6 protocol.\r
+ @param[in] Packet The pointer to the Dhcp6 packet.\r
+ @param[in, out] OptionCount The number of option in the packet.\r
+ @param[out] PacketOptionList The array of pointers to the each option in the packet.\r
+\r
+ @retval EFI_SUCCESS The packet was successfully parsed.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options\r
+ that were found in the Packet.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiDhcp6Parse (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN EFI_DHCP6_PACKET *Packet,\r
+ IN OUT UINT32 *OptionCount,\r
+ OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Dhcp6 internal functions implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+/**\r
+ Enqueue the packet into the retry list in case of timeout.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Packet The pointer to the Dhcp6 packet to retry.\r
+ @param[in] Elapsed The pointer to the elapsed time value in the packet.\r
+ @param[in] RetryCtl The pointer to the transmission control of the packet.\r
+ This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS Successfully enqueued the packet into the retry list according\r
+ to its message type.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected message type.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6EnqueueRetry (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet,\r
+ IN UINT16 *Elapsed,\r
+ IN EFI_DHCP6_RETRANSMISSION *RetryCtl OPTIONAL\r
+ )\r
+{\r
+ DHCP6_TX_CB *TxCb;\r
+ DHCP6_IA_CB *IaCb;\r
+\r
+ ASSERT (Packet != NULL);\r
+\r
+ IaCb = &Instance->IaCb;\r
+ TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB));\r
+\r
+ if (TxCb == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Save tx packet pointer, and it will be destoryed when reply received.\r
+ //\r
+ TxCb->TxPacket = Packet;\r
+ TxCb->Xid = Packet->Dhcp6.Header.TransactionId;\r
+\r
+ //\r
+ // Save pointer to elapsed-time value so we can update it on retransmits.\r
+ //\r
+ TxCb->Elapsed = Elapsed;\r
+\r
+ //\r
+ // Calculate the retransmission according to the the message type.\r
+ //\r
+ switch (Packet->Dhcp6.Header.MessageType) {\r
+ case Dhcp6MsgSolicit:\r
+ //\r
+ // Calculate the retransmission threshold value for solicit packet.\r
+ // Use the default value by rfc-3315 if user doesn't configure.\r
+ //\r
+ if (RetryCtl == NULL) {\r
+ TxCb->RetryCtl.Irt = DHCP6_SOL_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT;\r
+ TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD;\r
+ } else {\r
+ TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT;\r
+ TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC;\r
+ TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT;\r
+ TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD;\r
+ }\r
+\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ FALSE\r
+ );\r
+ break;\r
+\r
+ case Dhcp6MsgRequest:\r
+ //\r
+ // Calculate the retransmission threshold value for request packet.\r
+ //\r
+ TxCb->RetryCtl.Irt = DHCP6_REQ_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT;\r
+ TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD;\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ break;\r
+\r
+ case Dhcp6MsgConfirm:\r
+ //\r
+ // Calculate the retransmission threshold value for confirm packet.\r
+ //\r
+ TxCb->RetryCtl.Irt = DHCP6_CNF_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT;\r
+ TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD;\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ break;\r
+\r
+ case Dhcp6MsgRenew:\r
+ //\r
+ // Calculate the retransmission threshold value for renew packet.\r
+ //\r
+ TxCb->RetryCtl.Irt = DHCP6_REB_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_REB_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_REB_MRT;\r
+ TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1;\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ break;\r
+\r
+ case Dhcp6MsgRebind:\r
+ //\r
+ // Calculate the retransmission threshold value for rebind packet.\r
+ //\r
+ TxCb->RetryCtl.Irt = DHCP6_REN_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_REN_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_REN_MRT;\r
+ TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2;\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ break;\r
+\r
+ case Dhcp6MsgDecline:\r
+ //\r
+ // Calculate the retransmission threshold value for decline packet.\r
+ //\r
+ TxCb->RetryCtl.Irt = DHCP6_DEC_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT;\r
+ TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD;\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ break;\r
+\r
+ case Dhcp6MsgRelease:\r
+ //\r
+ // Calculate the retransmission threshold value for release packet.\r
+ //\r
+ TxCb->RetryCtl.Irt = DHCP6_REL_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_REL_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_REL_MRT;\r
+ TxCb->RetryCtl.Mrd = DHCP6_REL_MRD;\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ break;\r
+\r
+ case Dhcp6MsgInfoRequest:\r
+ //\r
+ // Calculate the retransmission threshold value for info-request packet.\r
+ // Use the default value by rfc-3315 if user doesn't configure.\r
+ //\r
+ if (RetryCtl == NULL) {\r
+ TxCb->RetryCtl.Irt = DHCP6_INF_IRT;\r
+ TxCb->RetryCtl.Mrc = DHCP6_INF_MRC;\r
+ TxCb->RetryCtl.Mrt = DHCP6_INF_MRT;\r
+ TxCb->RetryCtl.Mrd = DHCP6_INF_MRD;\r
+ } else {\r
+ TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT;\r
+ TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC;\r
+ TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT;\r
+ TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD;\r
+ }\r
+\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Irt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Unexpected message type.\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Insert into the retransmit list of the instance.\r
+ //\r
+ InsertTailList (&Instance->TxList, &TxCb->Link);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Dequeue the packet from retry list if reply received or timeout at last.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] PacketXid The packet transaction id to match.\r
+ @param[in] NeedSignal If TRUE, then an timeout event need be signaled when it is existed.\r
+ Otherwise, this parameter is ignored.\r
+\r
+ @retval EFI_SUCCESS Successfully dequeued the packet into retry list .\r
+ @retval EFI_NOT_FOUND There is no xid matched in retry list.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6DequeueRetry (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN UINT32 PacketXid,\r
+ IN BOOLEAN NeedSignal\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ DHCP6_TX_CB *TxCb;\r
+ DHCP6_INF_CB *InfCb;\r
+\r
+ //\r
+ // Seek the retransmit node in the retransmit list by packet xid.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+ ASSERT(TxCb->TxPacket);\r
+\r
+ if (TxCb->Xid == PacketXid) {\r
+\r
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+\r
+ //\r
+ // Seek the info-request node in the info-request list by packet xid.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {\r
+\r
+ InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);\r
+\r
+ if (InfCb->Xid == PacketXid) {\r
+ //\r
+ // Remove the info-request node, and signal the event if timeout.\r
+ //\r
+ if (InfCb->TimeoutEvent != NULL && NeedSignal) {\r
+ gBS->SignalEvent (InfCb->TimeoutEvent);\r
+ }\r
+\r
+ RemoveEntryList (&InfCb->Link);\r
+ FreePool (InfCb);\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Remove the retransmit node.\r
+ //\r
+ RemoveEntryList (&TxCb->Link);\r
+ ASSERT(TxCb->TxPacket);\r
+ FreePool (TxCb->TxPacket);\r
+ FreePool (TxCb);\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+ Clean up the specific nodes in the retry list.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Scope The scope of cleanup nodes.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupRetry (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN UINT32 Scope\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ DHCP6_TX_CB *TxCb;\r
+ DHCP6_INF_CB *InfCb;\r
+\r
+ //\r
+ // Clean up all the stateful messages from the retransmit list.\r
+ //\r
+ if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) {\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+ ASSERT(TxCb->TxPacket);\r
+\r
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) {\r
+ RemoveEntryList (&TxCb->Link);\r
+ FreePool (TxCb->TxPacket);\r
+ FreePool (TxCb);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Clean up all the stateless messages from the retransmit list.\r
+ //\r
+ if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) {\r
+\r
+ //\r
+ // Clean up all the retransmit list for stateless messages.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+ ASSERT(TxCb->TxPacket);\r
+\r
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+ RemoveEntryList (&TxCb->Link);\r
+ FreePool (TxCb->TxPacket);\r
+ FreePool (TxCb);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Clean up all the info-request messages list.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {\r
+\r
+ InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);\r
+\r
+ if (InfCb->TimeoutEvent != NULL) {\r
+ gBS->SignalEvent (InfCb->TimeoutEvent);\r
+ }\r
+ RemoveEntryList (&InfCb->Link);\r
+ FreePool (InfCb);\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Clean up the session of the instance stateful exchange.\r
+\r
+ @param[in, out] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Status The return status from udp.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupSession (\r
+ IN OUT DHCP6_INSTANCE *Instance,\r
+ IN EFI_STATUS Status\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_DHCP6_IA *Ia;\r
+\r
+ ASSERT(Instance->Config);\r
+ ASSERT(Instance->IaCb.Ia);\r
+\r
+ //\r
+ // Clean up the retransmit list for stateful messages.\r
+ //\r
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL);\r
+\r
+ if (Instance->Unicast != NULL) {\r
+ FreePool (Instance->Unicast);\r
+ }\r
+\r
+ if (Instance->AdSelect != NULL) {\r
+ FreePool (Instance->AdSelect);\r
+ }\r
+\r
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+ FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+ }\r
+\r
+ //\r
+ // Reinitialize the Ia fields of the instance.\r
+ //\r
+ Instance->UdpSts = Status;\r
+ Instance->AdSelect = NULL;\r
+ Instance->AdPref = 0;\r
+ Instance->Unicast = NULL;\r
+ Instance->IaCb.T1 = 0;\r
+ Instance->IaCb.T2 = 0;\r
+ Instance->IaCb.AllExpireTime = 0;\r
+ Instance->IaCb.LeaseTime = 0;\r
+\r
+ //\r
+ // Clear start time\r
+ //\r
+ Instance->StartTime = 0;\r
+\r
+ Ia = Instance->IaCb.Ia;\r
+ Ia->State = Dhcp6Init;\r
+ Ia->ReplyPacket = NULL;\r
+\r
+ //\r
+ // Set the addresses as zero lifetime, and then the notify\r
+ // function in Ip6Config will remove these timeout address.\r
+ //\r
+ for (Index = 0; Index < Ia->IaAddressCount; Index++) {\r
+ Ia->IaAddress[Index].PreferredLifetime = 0;\r
+ Ia->IaAddress[Index].ValidLifetime = 0;\r
+ }\r
+\r
+ //\r
+ //\r
+ // Signal the Ia information updated event to informal user.\r
+ //\r
+ if (Instance->Config->IaInfoEvent != NULL) {\r
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Callback to user when Dhcp6 transmit/receive occurs.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Event The current Dhcp6 event.\r
+ @param[in, out] Packet The pointer to the packet sending or received.\r
+\r
+ @retval EFI_SUCCESS The user function returns success.\r
+ @retval EFI_NOT_READY Direct the caller to continue collecting the offer.\r
+ @retval EFI_ABORTED The user function ask it to abort.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Dhcp6CallbackUser (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_EVENT Event,\r
+ IN OUT EFI_DHCP6_PACKET **Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PACKET *NewPacket;\r
+ EFI_DHCP6_CALLBACK Callback;\r
+ VOID *Context;\r
+\r
+ ASSERT (Packet != NULL);\r
+ ASSERT (Instance->Config != NULL);\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+ NewPacket = NULL;\r
+ Status = EFI_SUCCESS;\r
+ Callback = Instance->Config->Dhcp6Callback;\r
+ Context = Instance->Config->CallbackContext;\r
+\r
+ //\r
+ // Callback to user with the new message if has.\r
+ //\r
+ if (Callback != NULL) {\r
+\r
+ Status = Callback (\r
+ &Instance->Dhcp6,\r
+ Context,\r
+ Instance->IaCb.Ia->State,\r
+ Event,\r
+ *Packet,\r
+ &NewPacket\r
+ );\r
+ //\r
+ // Updated the new packet from user to replace the original one.\r
+ //\r
+ if (NewPacket != NULL) {\r
+ ASSERT (*Packet != NULL);\r
+ FreePool (*Packet);\r
+ *Packet = NewPacket;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Update Ia according to the new reply message.\r
+\r
+ @param[in, out] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Packet The pointer to reply messages.\r
+\r
+ @retval EFI_SUCCESS Updated the Ia information successfully.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6UpdateIaInfo (\r
+ IN OUT DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_STATE State;\r
+ UINT8 *Option;\r
+ UINT8 *IaInnerOpt;\r
+ UINT16 IaInnerLen;\r
+ UINT16 StsCode;\r
+ UINT32 T1;\r
+ UINT32 T2;\r
+\r
+ ASSERT (Instance->Config != NULL);\r
+ //\r
+ // If the reply was received in reponse to a solicit with rapid commit option,\r
+ // request, renew or rebind message, the client updates the information it has\r
+ // recorded about IAs from the IA options contained in the reply message:\r
+ // 1. record the T1 and T2 times\r
+ // 2. add any new addresses in the IA\r
+ // 3. discard any addresses from the IA, that have a valid lifetime of 0\r
+ // 4. update lifetimes for any addresses that alread recorded\r
+ // 5. leave unchanged any information about addresses\r
+ //\r
+ // See details in the section-18.1.8 of rfc-3315.\r
+ //\r
+ State = Dhcp6Init;\r
+ Option = Dhcp6SeekIaOption (\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - sizeof (EFI_DHCP6_HEADER),\r
+ &Instance->Config->IaDescriptor\r
+ );\r
+ if (Option == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // The format of the IA_NA option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_IA_NA | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | IAID (4 octets) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | T1 |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | T2 |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // . IA_NA-options .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+ // The format of the IA_TA option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_IA_TA | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | IAID (4 octets) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // . IA_TA-options .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // sizeof (option-code + option-len + IaId) = 8\r
+ // sizeof (option-code + option-len + IaId + T1) = 12\r
+ // sizeof (option-code + option-len + IaId + T1 + T2) = 16\r
+ //\r
+ // The inner options still start with 2 bytes option-code and 2 bytes option-len.\r
+ //\r
+ if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {\r
+ T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8)));\r
+ T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12)));\r
+ IaInnerOpt = Option + 16;\r
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12);\r
+ } else {\r
+ T1 = 0;\r
+ T2 = 0;\r
+ IaInnerOpt = Option + 8;\r
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4);\r
+ }\r
+\r
+ //\r
+ // The format of the Status Code option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_STATUS_CODE | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | status-code | |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+ // . .\r
+ // . status-message .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // sizeof (option-code + option-len) = 4\r
+ //\r
+ StsCode = Dhcp6StsSuccess;\r
+ Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);\r
+\r
+ if (Option != NULL) {\r
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));\r
+ if (StsCode != Dhcp6StsSuccess) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Generate control block for the Ia.\r
+ //\r
+ Status = Dhcp6GenerateIaCb (\r
+ Instance,\r
+ IaInnerOpt,\r
+ IaInnerLen,\r
+ T1,\r
+ T2\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+ Seek StatusCode Option in package. A Status Code option may appear in the\r
+ options field of a DHCP message and/or in the options field of another option.\r
+ See details in section 22.13, RFC3315.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Packet The pointer to reply messages.\r
+ @param[out] Option The pointer to status code option.\r
+\r
+ @retval EFI_SUCCESS Seek status code option successfully.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SeekStsOption (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet,\r
+ OUT UINT8 **Option\r
+ )\r
+{\r
+ UINT8 *IaInnerOpt;\r
+ UINT16 IaInnerLen;\r
+ UINT16 StsCode;\r
+\r
+ //\r
+ // Seek StatusCode option directly in DHCP message body. That is, search in\r
+ // non-encapsulated option fields.\r
+ //\r
+ *Option = Dhcp6SeekOption (\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptStatusCode\r
+ );\r
+\r
+ if (*Option != NULL) {\r
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));\r
+ if (StsCode != Dhcp6StsSuccess) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Seek in encapsulated options, IA_NA and IA_TA.\r
+ //\r
+ *Option = Dhcp6SeekIaOption (\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - sizeof (EFI_DHCP6_HEADER),\r
+ &Instance->Config->IaDescriptor\r
+ );\r
+ if (*Option == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // The format of the IA_NA option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_IA_NA | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | IAID (4 octets) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | T1 |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | T2 |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // . IA_NA-options .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+ // The format of the IA_TA option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_IA_TA | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | IAID (4 octets) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // . IA_TA-options .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // sizeof (option-code + option-len + IaId) = 8\r
+ // sizeof (option-code + option-len + IaId + T1) = 12\r
+ // sizeof (option-code + option-len + IaId + T1 + T2) = 16\r
+ //\r
+ // The inner options still start with 2 bytes option-code and 2 bytes option-len.\r
+ //\r
+ if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {\r
+ IaInnerOpt = *Option + 16;\r
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12);\r
+ } else {\r
+ IaInnerOpt = *Option + 8;\r
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4);\r
+ }\r
+\r
+ //\r
+ // The format of the Status Code option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_STATUS_CODE | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | status-code | |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+ // . .\r
+ // . status-message .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // sizeof (option-code + option-len) = 4\r
+ //\r
+ *Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);\r
+ if (*Option != NULL) {\r
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));\r
+ if (StsCode != Dhcp6StsSuccess) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Transmit Dhcp6 message by udpio.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Packet The pointer to transmit message.\r
+ @param[in] Elapsed The pointer to the elapsed time value to fill in.\r
+\r
+ @retval EFI_SUCCESS Successfully transmitted the packet.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6TransmitPacket (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet,\r
+ IN UINT16 *Elapsed\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ NET_BUF *Wrap;\r
+ NET_FRAGMENT Frag;\r
+ UDP_END_POINT EndPt;\r
+ DHCP6_SERVICE *Service;\r
+\r
+ Service = Instance->Service;\r
+\r
+ //\r
+ // Wrap it into a netbuf then send it.\r
+ //\r
+ Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header;\r
+ Frag.Len = Packet->Length;\r
+\r
+ //\r
+ // Do not register free packet here, which will be handled in retry list.\r
+ //\r
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL);\r
+\r
+ if (Wrap == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Multicast the Dhcp6 message, unless get the unicast server address by option.\r
+ //\r
+ ZeroMem (&EndPt, sizeof (UDP_END_POINT));\r
+\r
+ if (Instance->Unicast != NULL) {\r
+ CopyMem (\r
+ &EndPt.RemoteAddr,\r
+ Instance->Unicast,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ } else {\r
+ CopyMem (\r
+ &EndPt.RemoteAddr,\r
+ &mAllDhcpRelayAndServersAddress,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ }\r
+\r
+ EndPt.RemotePort = DHCP6_PORT_SERVER;\r
+ EndPt.LocalPort = DHCP6_PORT_CLIENT;\r
+\r
+ //\r
+ // Update the elapsed time value.\r
+ //\r
+ if (Elapsed != NULL) {\r
+ SetElapsedTime (Elapsed, Instance);\r
+ }\r
+\r
+ //\r
+ // Send out the message by the configured Udp6Io.\r
+ //\r
+ Status = UdpIoSendDatagram (\r
+ Service->UdpIo,\r
+ Wrap,\r
+ &EndPt,\r
+ NULL,\r
+ Dhcp6OnTransmitted,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ NetbufFree (Wrap);\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Create the solicit message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+\r
+ @retval EFI_SUCCESS Created and sent the solicit message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Failed to send the solicit message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendSolicitMsg (\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ EFI_DHCP6_PACKET_OPTION *UserOpt;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ DHCP6_SERVICE *Service;\r
+ UINT8 *Cursor;\r
+ UINT16 *Elapsed;\r
+ UINT32 UserLen;\r
+ UINTN Index;\r
+ UINT16 Length;\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+ UserLen = 0;\r
+\r
+ ASSERT (Service->ClientId != NULL);\r
+ ASSERT (Instance->Config != NULL);\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+\r
+ //\r
+ // Calculate the added length of customized option list.\r
+ //\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+ }\r
+\r
+ //\r
+ // Create the Dhcp6 packet and initialize commone fields.\r
+ //\r
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);\r
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgSolicit;\r
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+ //\r
+ // Assembly Dhcp6 options for solicit message.\r
+ //\r
+ Cursor = Packet->Dhcp6.Option;\r
+\r
+ Length = HTONS (ClientId->Length);\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptClientId),\r
+ Length,\r
+ ClientId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendETOption (\r
+ Cursor,\r
+ Instance,\r
+ &Elapsed\r
+ );\r
+\r
+ Cursor = Dhcp6AppendIaOption (\r
+ Cursor,\r
+ Instance->IaCb.Ia,\r
+ Instance->IaCb.T1,\r
+ Instance->IaCb.T2\r
+ );\r
+\r
+ //\r
+ // Append user-defined when configurate Dhcp6 service.\r
+ //\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+\r
+ UserOpt = Instance->Config->OptionList[Index];\r
+ Cursor = Dhcp6AppendOption(\r
+ Cursor,\r
+ UserOpt->OpCode,\r
+ UserOpt->OpLen,\r
+ UserOpt->Data\r
+ );\r
+ }\r
+\r
+ //\r
+ // Determine the size/length of packet.\r
+ //\r
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+ ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+ //\r
+ // Callback to user with the packet to be sent and check the user's feedback.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Send solicit packet with the state transition from Dhcp6init to\r
+ // Dhcp6selecting.\r
+ //\r
+ Instance->IaCb.Ia->State = Dhcp6Selecting;\r
+\r
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Enqueue the sent packet for the retransmission in case reply timeout.\r
+ //\r
+ return Dhcp6EnqueueRetry (\r
+ Instance,\r
+ Packet,\r
+ Elapsed,\r
+ Instance->Config->SolicitRetransmission\r
+ );\r
+}\r
+\r
+/**\r
+ Configure some parameter to initiate SolicitMsg.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+\r
+ @retval EFI_SUCCESS Created and sent the solicit message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Failed to send the solicit message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6InitSolicitMsg (\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ Instance->IaCb.T1 = 0;\r
+ Instance->IaCb.T2 = 0;\r
+ Instance->IaCb.Ia->IaAddressCount = 0;\r
+\r
+ return Dhcp6SendSolicitMsg (Instance);\r
+}\r
+\r
+\r
+/**\r
+ Create the request message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+\r
+ @retval EFI_SUCCESS Created and sent the request message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRequestMsg (\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ EFI_DHCP6_PACKET_OPTION *UserOpt;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ EFI_DHCP6_DUID *ServerId;\r
+ DHCP6_SERVICE *Service;\r
+ UINT8 *Option;\r
+ UINT8 *Cursor;\r
+ UINT16 *Elapsed;\r
+ UINT32 UserLen;\r
+ UINTN Index;\r
+ UINT16 Length;\r
+\r
+ ASSERT(Instance->AdSelect != NULL);\r
+ ASSERT(Instance->Config != NULL);\r
+ ASSERT(Instance->IaCb.Ia != NULL);\r
+ ASSERT(Instance->Service != NULL);\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+\r
+ ASSERT(ClientId != NULL);\r
+\r
+ //\r
+ // Get the server Id from the selected advertisement message.\r
+ //\r
+ Option = Dhcp6SeekOption (\r
+ Instance->AdSelect->Dhcp6.Option,\r
+ Instance->AdSelect->Length - 4,\r
+ Dhcp6OptServerId\r
+ );\r
+ if (Option == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+ //\r
+ // Calculate the added length of customized option list.\r
+ //\r
+ UserLen = 0;\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+ }\r
+\r
+ //\r
+ // Create the Dhcp6 packet and initialize commone fields.\r
+ //\r
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);\r
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgRequest;\r
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+ //\r
+ // Assembly Dhcp6 options for request message.\r
+ //\r
+ Cursor = Packet->Dhcp6.Option;\r
+\r
+ Length = HTONS (ClientId->Length);\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptClientId),\r
+ Length,\r
+ ClientId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendETOption (\r
+ Cursor,\r
+ Instance,\r
+ &Elapsed\r
+ );\r
+\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptServerId),\r
+ ServerId->Length,\r
+ ServerId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendIaOption (\r
+ Cursor,\r
+ Instance->IaCb.Ia,\r
+ Instance->IaCb.T1,\r
+ Instance->IaCb.T2\r
+ );\r
+\r
+ //\r
+ // Append user-defined when configurate Dhcp6 service.\r
+ //\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+\r
+ UserOpt = Instance->Config->OptionList[Index];\r
+ Cursor = Dhcp6AppendOption(\r
+ Cursor,\r
+ UserOpt->OpCode,\r
+ UserOpt->OpLen,\r
+ UserOpt->Data\r
+ );\r
+ }\r
+\r
+ //\r
+ // Determine the size/length of packet.\r
+ //\r
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+ ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+ //\r
+ // Callback to user with the packet to be sent and check the user's feedback.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Send request packet with the state transition from Dhcp6selecting to\r
+ // Dhcp6requesting.\r
+ //\r
+ Instance->IaCb.Ia->State = Dhcp6Requesting;\r
+\r
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Enqueue the sent packet for the retransmission in case reply timeout.\r
+ //\r
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+ Create the decline message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] DecIa The pointer to the decline Ia.\r
+\r
+ @retval EFI_SUCCESS Created and sent the decline message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the decline message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendDeclineMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_IA *DecIa\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ EFI_DHCP6_PACKET *LastReply;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ EFI_DHCP6_DUID *ServerId;\r
+ DHCP6_SERVICE *Service;\r
+ UINT8 *Option;\r
+ UINT8 *Cursor;\r
+ UINT16 *Elapsed;\r
+ UINT16 Length;\r
+\r
+ ASSERT (Instance->Config != NULL);\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+ ASSERT (Instance->Service != NULL);\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+ LastReply = Instance->IaCb.Ia->ReplyPacket;\r
+\r
+ ASSERT (ClientId != NULL);\r
+ ASSERT (LastReply != NULL);\r
+\r
+ //\r
+ // Get the server Id from the last reply message.\r
+ //\r
+ Option = Dhcp6SeekOption (\r
+ LastReply->Dhcp6.Option,\r
+ LastReply->Length - 4,\r
+ Dhcp6OptServerId\r
+ );\r
+ if (Option == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // EFI_DHCP6_DUID contains a length field of 2 bytes.\r
+ //\r
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+ //\r
+ // Create the Dhcp6 packet and initialize commone fields.\r
+ //\r
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet->Size = DHCP6_BASE_PACKET_SIZE;\r
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);\r
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgDecline;\r
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+ //\r
+ // Assembly Dhcp6 options for rebind/renew message.\r
+ //\r
+ Cursor = Packet->Dhcp6.Option;\r
+\r
+ Length = HTONS (ClientId->Length);\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptClientId),\r
+ Length,\r
+ ClientId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendETOption (\r
+ Cursor,\r
+ Instance,\r
+ &Elapsed\r
+ );\r
+\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptServerId),\r
+ ServerId->Length,\r
+ ServerId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0);\r
+\r
+ //\r
+ // Determine the size/length of packet.\r
+ //\r
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+ ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+ //\r
+ // Callback to user with the packet to be sent and check the user's feedback.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Send decline packet with the state transition from Dhcp6bound to\r
+ // Dhcp6declining.\r
+ //\r
+ Instance->IaCb.Ia->State = Dhcp6Declining;\r
+\r
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Enqueue the sent packet for the retransmission in case reply timeout.\r
+ //\r
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+ Create the release message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] RelIa The pointer to the release Ia.\r
+\r
+ @retval EFI_SUCCESS Created and sent the release message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the release message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendReleaseMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_IA *RelIa\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ EFI_DHCP6_PACKET *LastReply;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ EFI_DHCP6_DUID *ServerId;\r
+ DHCP6_SERVICE *Service;\r
+ UINT8 *Option;\r
+ UINT8 *Cursor;\r
+ UINT16 *Elapsed;\r
+ UINT16 Length;\r
+\r
+ ASSERT(Instance->Config);\r
+ ASSERT(Instance->IaCb.Ia);\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+ LastReply = Instance->IaCb.Ia->ReplyPacket;\r
+\r
+ ASSERT(ClientId);\r
+ ASSERT(LastReply);\r
+\r
+ //\r
+ // Get the server Id from the last reply message.\r
+ //\r
+ Option = Dhcp6SeekOption (\r
+ LastReply->Dhcp6.Option,\r
+ LastReply->Length - 4,\r
+ Dhcp6OptServerId\r
+ );\r
+ if (Option == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+ //\r
+ // Create the Dhcp6 packet and initialize commone fields.\r
+ //\r
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet->Size = DHCP6_BASE_PACKET_SIZE;\r
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);\r
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgRelease;\r
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+ //\r
+ // Assembly Dhcp6 options for rebind/renew message\r
+ //\r
+ Cursor = Packet->Dhcp6.Option;\r
+\r
+ Length = HTONS (ClientId->Length);\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptClientId),\r
+ Length,\r
+ ClientId->Duid\r
+ );\r
+\r
+ //\r
+ // ServerId is extracted from packet, it's network order.\r
+ //\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptServerId),\r
+ ServerId->Length,\r
+ ServerId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendETOption (\r
+ Cursor,\r
+ Instance,\r
+ &Elapsed\r
+ );\r
+\r
+ Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0);\r
+\r
+ //\r
+ // Determine the size/length of packet\r
+ //\r
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+ ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+ //\r
+ // Callback to user with the packet to be sent and check the user's feedback.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Send release packet with the state transition from Dhcp6bound to\r
+ // Dhcp6releasing.\r
+ //\r
+ Instance->IaCb.Ia->State = Dhcp6Releasing;\r
+\r
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Enqueue the sent packet for the retransmission in case reply timeout.\r
+ //\r
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+ Create the renew/rebind message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] RebindRequest If TRUE, it is a Rebind type message.\r
+ Otherwise, it is a Renew type message.\r
+\r
+ @retval EFI_SUCCESS Created and sent the renew/rebind message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the renew/rebind message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRenewRebindMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN BOOLEAN RebindRequest\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ EFI_DHCP6_PACKET *LastReply;\r
+ EFI_DHCP6_PACKET_OPTION *UserOpt;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ EFI_DHCP6_DUID *ServerId;\r
+ EFI_DHCP6_STATE State;\r
+ EFI_DHCP6_EVENT Event;\r
+ DHCP6_SERVICE *Service;\r
+ UINT8 *Option;\r
+ UINT8 *Cursor;\r
+ UINT16 *Elapsed;\r
+ UINT32 UserLen;\r
+ UINTN Index;\r
+ UINT16 Length;\r
+\r
+ ASSERT(Instance->Config);\r
+ ASSERT(Instance->IaCb.Ia);\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+\r
+ ASSERT(ClientId);\r
+\r
+ //\r
+ // Calculate the added length of customized option list.\r
+ //\r
+ UserLen = 0;\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+ }\r
+\r
+ //\r
+ // Create the Dhcp6 packet and initialize commone fields.\r
+ //\r
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);\r
+ Packet->Dhcp6.Header.MessageType = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew;\r
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+ //\r
+ // Assembly Dhcp6 options for rebind/renew message.\r
+ //\r
+ Cursor = Packet->Dhcp6.Option;\r
+\r
+ Length = HTONS (ClientId->Length);\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptClientId),\r
+ Length,\r
+ ClientId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendETOption (\r
+ Cursor,\r
+ Instance,\r
+ &Elapsed\r
+ );\r
+\r
+ Cursor = Dhcp6AppendIaOption (\r
+ Cursor,\r
+ Instance->IaCb.Ia,\r
+ Instance->IaCb.T1,\r
+ Instance->IaCb.T2\r
+ );\r
+\r
+ if (!RebindRequest) {\r
+ //\r
+ // Get the server Id from the last reply message and\r
+ // insert it for rebind request.\r
+ //\r
+ LastReply = Instance->IaCb.Ia->ReplyPacket;\r
+ ASSERT (LastReply);\r
+\r
+ Option = Dhcp6SeekOption (\r
+ LastReply->Dhcp6.Option,\r
+ LastReply->Length - 4,\r
+ Dhcp6OptServerId\r
+ );\r
+ if (Option == NULL) {\r
+ FreePool (Packet);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);\r
+\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptServerId),\r
+ ServerId->Length,\r
+ ServerId->Duid\r
+ );\r
+ }\r
+\r
+ //\r
+ // Append user-defined when configurate Dhcp6 service.\r
+ //\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+\r
+ UserOpt = Instance->Config->OptionList[Index];\r
+ Cursor = Dhcp6AppendOption(\r
+ Cursor,\r
+ UserOpt->OpCode,\r
+ UserOpt->OpLen,\r
+ UserOpt->Data\r
+ );\r
+ }\r
+\r
+ //\r
+ // Determine the size/length of packet.\r
+ //\r
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+ ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+ //\r
+ // Callback to user with the packet to be sent and check the user's feedback.\r
+ //\r
+ State = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing;\r
+ Event = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing;\r
+\r
+ Status = Dhcp6CallbackUser (Instance, Event, &Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Send renew/rebind packet with the state transition from Dhcp6bound to\r
+ // Dhcp6renew/rebind.\r
+ // And sync the lease time when send renew/rebind, in case that user send\r
+ // renew/rebind actively.\r
+ //\r
+ Instance->IaCb.Ia->State = State;\r
+ Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1;\r
+\r
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Enqueue the sent packet for the retransmission in case reply timeout.\r
+ //\r
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+/**\r
+ Create the information request message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] InfCb The pointer to the information request control block.\r
+ @param[in] SendClientId If TRUE, the client identifier option will be included in\r
+ information request message. Otherwise, the client identifier\r
+ option will not be included.\r
+ @param[in] OptionRequest The pointer to the option request option.\r
+ @param[in] OptionCount The number options in the OptionList.\r
+ @param[in] OptionList The array pointers to the appended options.\r
+ @param[in] Retransmission The pointer to the retransmission control.\r
+\r
+ @retval EFI_SUCCESS Created and sent the info-request message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Failed to send the info-request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendInfoRequestMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN DHCP6_INF_CB *InfCb,\r
+ IN BOOLEAN SendClientId,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,\r
+ IN UINT32 OptionCount,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[],\r
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ EFI_DHCP6_PACKET_OPTION *UserOpt;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ DHCP6_SERVICE *Service;\r
+ UINT8 *Cursor;\r
+ UINT16 *Elapsed;\r
+ UINT32 UserLen;\r
+ UINTN Index;\r
+ UINT16 Length;\r
+\r
+ ASSERT(OptionRequest);\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+ UserLen = NTOHS (OptionRequest->OpLen) + 4;\r
+\r
+ ASSERT(ClientId);\r
+\r
+ //\r
+ // Calculate the added length of customized option list.\r
+ //\r
+ for (Index = 0; Index < OptionCount; Index++) {\r
+ UserLen += (NTOHS (OptionList[Index]->OpLen) + 4);\r
+ }\r
+\r
+ //\r
+ // Create the Dhcp6 packet and initialize commone fields.\r
+ //\r
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);\r
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgInfoRequest;\r
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+ InfCb->Xid = Packet->Dhcp6.Header.TransactionId;\r
+\r
+ //\r
+ // Assembly Dhcp6 options for info-request message.\r
+ //\r
+ Cursor = Packet->Dhcp6.Option;\r
+\r
+ if (SendClientId) {\r
+ Length = HTONS (ClientId->Length);\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptClientId),\r
+ Length,\r
+ ClientId->Duid\r
+ );\r
+ }\r
+\r
+ Cursor = Dhcp6AppendETOption (\r
+ Cursor,\r
+ Instance,\r
+ &Elapsed\r
+ );\r
+\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ OptionRequest->OpCode,\r
+ OptionRequest->OpLen,\r
+ OptionRequest->Data\r
+ );\r
+\r
+ //\r
+ // Append user-defined when configurate Dhcp6 service.\r
+ //\r
+ for (Index = 0; Index < OptionCount; Index++) {\r
+\r
+ UserOpt = OptionList[Index];\r
+ Cursor = Dhcp6AppendOption(\r
+ Cursor,\r
+ UserOpt->OpCode,\r
+ UserOpt->OpLen,\r
+ UserOpt->Data\r
+ );\r
+ }\r
+\r
+ //\r
+ // Determine the size/length of packet.\r
+ //\r
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+ ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+ //\r
+ // Send info-request packet with no state.\r
+ //\r
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Enqueue the sent packet for the retransmission in case reply timeout.\r
+ //\r
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission);\r
+}\r
+\r
+\r
+/**\r
+ Create the Confirm message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+\r
+ @retval EFI_SUCCESS Created and sent the confirm message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the confirm message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendConfirmMsg (\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ UINT8 *Cursor;\r
+ UINTN Index;\r
+ UINT16 Length;\r
+ UINT32 UserLen;\r
+ EFI_STATUS Status;\r
+ DHCP6_SERVICE *Service;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ EFI_DHCP6_PACKET_OPTION *UserOpt;\r
+ UINT16 *Elapsed;\r
+\r
+ ASSERT (Instance->Config != NULL);\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+ ASSERT (Instance->Service != NULL);\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+ ASSERT (ClientId != NULL);\r
+\r
+ //\r
+ // Calculate the added length of customized option list.\r
+ //\r
+ UserLen = 0;\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);\r
+ }\r
+\r
+ //\r
+ // Create the Dhcp6 packet and initialize common fields.\r
+ //\r
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;\r
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);\r
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgConfirm;\r
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;\r
+\r
+ //\r
+ // Assembly Dhcp6 options for solicit message.\r
+ //\r
+ Cursor = Packet->Dhcp6.Option;\r
+\r
+ Length = HTONS (ClientId->Length);\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ HTONS (Dhcp6OptClientId),\r
+ Length,\r
+ ClientId->Duid\r
+ );\r
+\r
+ Cursor = Dhcp6AppendETOption (\r
+ Cursor,\r
+ Instance,\r
+ &Elapsed\r
+ );\r
+\r
+ Cursor = Dhcp6AppendIaOption (\r
+ Cursor,\r
+ Instance->IaCb.Ia,\r
+ Instance->IaCb.T1,\r
+ Instance->IaCb.T2\r
+ );\r
+\r
+ //\r
+ // Append user-defined when configurate Dhcp6 service.\r
+ //\r
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {\r
+ UserOpt = Instance->Config->OptionList[Index];\r
+ Cursor = Dhcp6AppendOption (\r
+ Cursor,\r
+ UserOpt->OpCode,\r
+ UserOpt->OpLen,\r
+ UserOpt->Data\r
+ );\r
+ }\r
+\r
+ //\r
+ // Determine the size/length of packet.\r
+ //\r
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);\r
+ ASSERT (Packet->Size > Packet->Length + 8);\r
+\r
+ //\r
+ // Callback to user with the packet to be sent and check the user's feedback.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Send confirm packet with the state transition from Dhcp6Bound to\r
+ // Dhcp6Confirming.\r
+ //\r
+ Instance->IaCb.Ia->State = Dhcp6Confirming;\r
+\r
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Enqueue the sent packet for the retransmission in case reply timeout.\r
+ //\r
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);\r
+}\r
+\r
+\r
+\r
+/**\r
+ Handle with the Dhcp6 reply message.\r
+\r
+ @param[in] Instance The pointer to Dhcp6 instance.\r
+ @param[in] Packet The pointer to the Dhcp6 reply message.\r
+\r
+ @retval EFI_SUCCESS Processed the reply message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to process the reply message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6HandleReplyMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Option;\r
+ UINT16 StsCode;\r
+\r
+ ASSERT (Instance->Config != NULL);\r
+ ASSERT (Instance->IaCb.Ia != NULL);\r
+ ASSERT (Packet != NULL);\r
+\r
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // If the client subsequently receives a valid reply message that includes a\r
+ // rapid commit option since send a solicit with rapid commit option before,\r
+ // preocess the reply message and discard any reply messages received in\r
+ // response to the request message.\r
+ // See details in the section-17.1.4 of rfc-3315.\r
+ //\r
+ Option = Dhcp6SeekOption (\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptRapidCommit\r
+ );\r
+\r
+ if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // As to a valid reply packet in response to a request/renew/rebind packet,\r
+ // ignore the packet if not contains the Ia option\r
+ //\r
+ if (Instance->IaCb.Ia->State == Dhcp6Requesting ||\r
+ Instance->IaCb.Ia->State == Dhcp6Renewing ||\r
+ Instance->IaCb.Ia->State == Dhcp6Rebinding\r
+ ) {\r
+\r
+ Option = Dhcp6SeekIaOption (\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length,\r
+ &Instance->Config->IaDescriptor\r
+ );\r
+ if (Option == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Callback to user with the received packet and check the user's feedback.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Dequeue the sent packet from retransmit list since reply received.\r
+ //\r
+ Status = Dhcp6DequeueRetry (\r
+ Instance,\r
+ Packet->Dhcp6.Header.TransactionId,\r
+ FALSE\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // When receive a valid reply packet in response to a decline/release packet,\r
+ // the client considers the decline/release event completed regardless of the\r
+ // status code.\r
+ //\r
+ if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) {\r
+\r
+ if (Instance->IaCb.Ia->IaAddressCount != 0) {\r
+ Instance->IaCb.Ia->State = Dhcp6Bound;\r
+ } else {\r
+ ASSERT (Instance->IaCb.Ia->ReplyPacket);\r
+ FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+ Instance->IaCb.Ia->ReplyPacket = NULL;\r
+ Instance->IaCb.Ia->State = Dhcp6Init;\r
+ }\r
+\r
+ //\r
+ // For sync, set the success flag out of polling in decline/release.\r
+ //\r
+ Instance->UdpSts = EFI_SUCCESS;\r
+\r
+ //\r
+ // For async, signal the Ia event to inform Ia infomation update.\r
+ //\r
+ if (Instance->Config->IaInfoEvent != NULL) {\r
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+ }\r
+\r
+ //\r
+ // Reset start time for next exchange.\r
+ //\r
+ Instance->StartTime = 0;\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Upon the receipt of a valid reply packet in response to a solicit, request,\r
+ // confirm, renew and rebind, the behavior depends on the status code option.\r
+ // See the details in the section-18.1.8 of rfc-3315.\r
+ //\r
+ Option = NULL;\r
+ Status = Dhcp6SeekStsOption (\r
+ Instance,\r
+ Packet,\r
+ &Option\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Reset start time for next exchange.\r
+ //\r
+ Instance->StartTime = 0;\r
+\r
+ //\r
+ // No status code or no error status code means succeed to reply.\r
+ //\r
+ Status = Dhcp6UpdateIaInfo (Instance, Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Set bound state and store the reply packet.\r
+ //\r
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+ FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+ }\r
+\r
+ Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);\r
+\r
+ if (Instance->IaCb.Ia->ReplyPacket == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);\r
+\r
+ Instance->IaCb.Ia->State = Dhcp6Bound;\r
+\r
+ //\r
+ // For sync, set the success flag out of polling in start/renewrebind.\r
+ //\r
+ Instance->UdpSts = EFI_SUCCESS;\r
+\r
+ //\r
+ // Maybe this is a new round DHCP process due to some reason, such as NotOnLink\r
+ // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that\r
+ // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP\r
+ // consumers can be notified to flush old address.\r
+ //\r
+ Dhcp6AppendCacheIa (Instance);\r
+\r
+ //\r
+ // For async, signal the Ia event to inform Ia infomation update.\r
+ //\r
+ if (Instance->Config->IaInfoEvent != NULL) {\r
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);\r
+ }\r
+ } else if (Option != NULL) {\r
+ //\r
+ // Any error status code option is found.\r
+ //\r
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));\r
+ switch (StsCode) {\r
+ case Dhcp6StsUnspecFail:\r
+ //\r
+ // It indicates the server is unable to process the message due to an\r
+ // unspecified failure condition, so just retry if possible.\r
+ //\r
+ break;\r
+\r
+ case Dhcp6StsUseMulticast:\r
+ //\r
+ // It indicates the server receives a message via unicast from a client\r
+ // to which the server has not sent a unicast option, so retry it by\r
+ // multi-cast address.\r
+ //\r
+ if (Instance->Unicast != NULL) {\r
+ FreePool (Instance->Unicast);\r
+ Instance->Unicast = NULL;\r
+ }\r
+ break;\r
+\r
+ case Dhcp6StsNotOnLink:\r
+ if (Instance->IaCb.Ia->State == Dhcp6Confirming) {\r
+ //\r
+ // Before initiate new round DHCP, cache the current IA.\r
+ //\r
+ Status = Dhcp6CacheIa (Instance);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Restart S.A.R.R process to acquire new address.\r
+ //\r
+ Status = Dhcp6InitSolicitMsg (Instance);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // The other status code, just restart solicitation.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Select the appointed Dhcp6 advertisement message.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] AdSelect The pointer to the selected Dhcp6 advertisement message.\r
+\r
+ @retval EFI_SUCCESS Selected the right advertisement message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Failed to select the advertise message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SelectAdvertiseMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *AdSelect\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Option;\r
+\r
+ ASSERT (AdSelect != NULL);\r
+\r
+ //\r
+ // Callback to user with the selected advertisement packet, and the user\r
+ // might overwrite it.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Instance->AdSelect = AdSelect;\r
+\r
+ //\r
+ // Dequeue the sent packet for the retransmission since advertisement selected.\r
+ //\r
+ Status = Dhcp6DequeueRetry (\r
+ Instance,\r
+ AdSelect->Dhcp6.Header.TransactionId,\r
+ FALSE\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check whether there is server unicast option in the selected advertise\r
+ // packet, and update it.\r
+ //\r
+ Option = Dhcp6SeekOption(\r
+ AdSelect->Dhcp6.Option,\r
+ AdSelect->Length - 4,\r
+ Dhcp6OptServerUnicast\r
+ );\r
+\r
+ if (Option != NULL) {\r
+\r
+ Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS));\r
+\r
+ if (Instance->Unicast == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS));\r
+ }\r
+\r
+ //\r
+ // Update the information of the Ia by the selected advertisement message.\r
+ //\r
+ Status = Dhcp6UpdateIaInfo (Instance, AdSelect);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Send the request message to continue the S.A.R.R. process.\r
+ //\r
+ return Dhcp6SendRequestMsg (Instance);\r
+}\r
+\r
+\r
+/**\r
+ Handle with the Dhcp6 advertisement message.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Packet The pointer to the Dhcp6 advertisement message.\r
+\r
+ @retval EFI_SUCCESS Processed the advertisement message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to process the advertise message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6HandleAdvertiseMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Option;\r
+ UINT16 StsCode;\r
+ BOOLEAN Timeout;\r
+\r
+ ASSERT(Instance->Config);\r
+ ASSERT(Instance->IaCb.Ia);\r
+\r
+ Timeout = FALSE;\r
+ StsCode = Dhcp6StsSuccess;\r
+\r
+ //\r
+ // If the client does receives a valid reply message that includes a rapid\r
+ // commit option since a solicit with rapid commit optioin sent before, select\r
+ // this reply message. Or else, process the advertise messages as normal.\r
+ // See details in the section-17.1.4 of rfc-3315.\r
+ //\r
+ Option = Dhcp6SeekOption(\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptRapidCommit\r
+ );\r
+\r
+ if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) {\r
+\r
+ return Dhcp6HandleReplyMsg (Instance, Packet);\r
+ }\r
+\r
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Client must ignore any advertise message that includes a status code option\r
+ // containing the value noaddrsavail, with the exception that the client may\r
+ // display the associated status message to the user.\r
+ // See the details in the section-17.1.3 of rfc-3315.\r
+ //\r
+ Option = Dhcp6SeekOption(\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptStatusCode\r
+ );\r
+\r
+ if (Option != NULL) {\r
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));\r
+ if (StsCode != Dhcp6StsSuccess) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Callback to user with the received packet and check the user's feedback.\r
+ //\r
+ Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Success means user choose the current advertisement packet.\r
+ //\r
+ if (Instance->AdSelect != NULL) {\r
+ FreePool (Instance->AdSelect);\r
+ }\r
+\r
+ //\r
+ // Store the selected advertisement packet and set a flag.\r
+ //\r
+ Instance->AdSelect = AllocateZeroPool (Packet->Size);\r
+\r
+ if (Instance->AdSelect == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (Instance->AdSelect, Packet, Packet->Size);\r
+\r
+ Instance->AdPref = 0xff;\r
+\r
+ } else if (Status == EFI_NOT_READY) {\r
+ //\r
+ // Not_ready means user wants to continue to receive more advertise packets.\r
+ //\r
+ if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) {\r
+ //\r
+ // It's a tricky point. The timer routine set adpref as 0xff if the first\r
+ // rt timeout and no advertisement received, which means any advertisement\r
+ // received will be selected after the first rt.\r
+ //\r
+ Timeout = TRUE;\r
+ }\r
+\r
+ //\r
+ // Check whether the current packet has a 255 preference option or not.\r
+ // Take non-preference option as 0 value.\r
+ //\r
+ Option = Dhcp6SeekOption(\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptPreference\r
+ );\r
+\r
+ if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) {\r
+ //\r
+ // No advertisements received before or preference is more than other\r
+ // advertisements received before. Then store the new packet and the\r
+ // preference value.\r
+ //\r
+ if (Instance->AdSelect != NULL) {\r
+ FreePool (Instance->AdSelect);\r
+ }\r
+\r
+ Instance->AdSelect = AllocateZeroPool (Packet->Size);\r
+\r
+ if (Instance->AdSelect == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (Instance->AdSelect, Packet, Packet->Size);\r
+\r
+ if (Option != NULL) {\r
+ Instance->AdPref = *(Option + 4);\r
+ }\r
+ } else {\r
+ //\r
+ // Non-preference and other advertisements received before or current\r
+ // preference is less than other advertisements received before.\r
+ // Leave the packet alone.\r
+ }\r
+\r
+ } else {\r
+ //\r
+ // Other error status means termination.\r
+ //\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Client must collect advertise messages as more as possible until the first\r
+ // RT has elapsed, or get a highest preference 255 advertise.\r
+ // See details in the section-17.1.2 of rfc-3315.\r
+ //\r
+ if (Instance->AdPref == 0xff || Timeout) {\r
+ Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ The Dhcp6 stateful exchange process routine.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Packet The pointer to the received Dhcp6 message.\r
+\r
+**/\r
+VOID\r
+Dhcp6HandleStateful (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_DUID *ClientId;\r
+ DHCP6_SERVICE *Service;\r
+ UINT8 *Option;\r
+\r
+ Service = Instance->Service;\r
+ ClientId = Service->ClientId;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (Instance->InDestory || Instance->Config == NULL) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
+ ASSERT (ClientId);\r
+ ASSERT (Instance->Config);\r
+ ASSERT (Instance->IaCb.Ia);\r
+\r
+ //\r
+ // Discard the packet if not advertisement or reply packet.\r
+ //\r
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
+ //\r
+ // Check whether include client Id or not.\r
+ //\r
+ Option = Dhcp6SeekOption(\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptClientId\r
+ );\r
+\r
+ if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
+ //\r
+ // Check whether include server Id or not.\r
+ //\r
+ Option = Dhcp6SeekOption(\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptServerId\r
+ );\r
+\r
+ if (Option == NULL) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
+ switch (Instance->IaCb.Ia->State) {\r
+ case Dhcp6Selecting:\r
+ //\r
+ // Handle the advertisement message when in the Dhcp6Selecting state.\r
+ // Do not need check return status, if failed, just continue to the next.\r
+ //\r
+ Dhcp6HandleAdvertiseMsg (Instance, Packet);\r
+ break;\r
+\r
+ case Dhcp6Requesting:\r
+ case Dhcp6Confirming:\r
+ case Dhcp6Renewing:\r
+ case Dhcp6Rebinding:\r
+ case Dhcp6Releasing:\r
+ case Dhcp6Declining:\r
+ //\r
+ // Handle the reply message when in the Dhcp6Requesting, Dhcp6Renewing\r
+ // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state.\r
+ // If failed here, it should reset the current session.\r
+ //\r
+ Status = Dhcp6HandleReplyMsg (Instance, Packet);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ break;\r
+ default:\r
+ //\r
+ // Other state has not supported yet.\r
+ //\r
+ break;\r
+ }\r
+\r
+ON_CONTINUE:\r
+ //\r
+ // Continue to receive the following Dhcp6 message.\r
+ //\r
+ Status = UdpIoRecvDatagram (\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+ON_EXIT:\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp6CleanupSession (Instance, Status);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ The Dhcp6 stateless exchange process routine.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Packet The pointer to the received Dhcp6 message.\r
+\r
+**/\r
+VOID\r
+Dhcp6HandleStateless (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ DHCP6_SERVICE *Service;\r
+ DHCP6_INF_CB *InfCb;\r
+ UINT8 *Option;\r
+ BOOLEAN IsMatched;\r
+\r
+ Service = Instance->Service;\r
+ Status = EFI_SUCCESS;\r
+ IsMatched = FALSE;\r
+ InfCb = NULL;\r
+\r
+ if (Instance->InDestory) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Check whether it's a desired Info-request message by Xid.\r
+ //\r
+ while (!IsListEmpty (&Instance->InfList)) {\r
+ InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link);\r
+ if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) {\r
+ IsMatched = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!IsMatched) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Check whether include server Id or not.\r
+ //\r
+ Option = Dhcp6SeekOption (\r
+ Packet->Dhcp6.Option,\r
+ Packet->Length - 4,\r
+ Dhcp6OptServerId\r
+ );\r
+\r
+ if (Option == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Callback to user with the received packet and check the user's feedback.\r
+ //\r
+ Status = InfCb->ReplyCallback (\r
+ &Instance->Dhcp6,\r
+ InfCb->CallbackContext,\r
+ Packet\r
+ );\r
+\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // Success or aborted will both stop this info-request exchange process,\r
+ // but not ready means user wants to continue to receive reply.\r
+ //\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Dequeue the sent packet from the txlist if the xid matched, and ignore\r
+ // if no xid matched.\r
+ //\r
+ Dhcp6DequeueRetry (\r
+ Instance,\r
+ Packet->Dhcp6.Header.TransactionId,\r
+ FALSE\r
+ );\r
+\r
+ //\r
+ // For sync, set the status out of polling for info-request.\r
+ //\r
+ Instance->UdpSts = Status;\r
+\r
+ON_EXIT:\r
+\r
+ Status = UdpIoRecvDatagram (\r
+ Service->UdpIo,\r
+ Dhcp6ReceivePacket,\r
+ Service,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ The receive callback function for Dhcp6 exchange process.\r
+\r
+ @param[in] Udp6Wrap The pointer to the received net buffer.\r
+ @param[in] EndPoint The pointer to the udp end point.\r
+ @param[in] IoStatus The return status from udp io.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6ReceivePacket (\r
+ IN NET_BUF *Udp6Wrap,\r
+ IN UDP_END_POINT *EndPoint,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_DHCP6_HEADER *Head;\r
+ EFI_DHCP6_PACKET *Packet;\r
+ DHCP6_SERVICE *Service;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_TX_CB *TxCb;\r
+ UINT32 Size;\r
+ BOOLEAN IsDispatched;\r
+ BOOLEAN IsStateless;\r
+ LIST_ENTRY *Entry1;\r
+ LIST_ENTRY *Next1;\r
+ LIST_ENTRY *Entry2;\r
+ LIST_ENTRY *Next2;\r
+\r
+ ASSERT (Udp6Wrap != NULL);\r
+ ASSERT (Context != NULL);\r
+\r
+ Service = (DHCP6_SERVICE *) Context;\r
+ Instance = NULL;\r
+ Packet = NULL;\r
+ IsDispatched = FALSE;\r
+ IsStateless = FALSE;\r
+\r
+ if (EFI_ERROR (IoStatus)) {\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // Copy the net buffer received from upd6 to a Dhcp6 packet.\r
+ //\r
+ Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize;\r
+ Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size);\r
+\r
+ if (Packet == NULL) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
+ Packet->Size = Size;\r
+ Head = &Packet->Dhcp6.Header;\r
+ Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head);\r
+\r
+ if (Packet->Length == 0) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
+ //\r
+ // Dispatch packet to right instance by transaction id.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {\r
+\r
+ Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) {\r
+\r
+ TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link);\r
+\r
+ if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) {\r
+ //\r
+ // Find the corresponding packet in tx list, and check it whether belongs\r
+ // to stateful exchange process.\r
+ //\r
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+ IsStateless = TRUE;\r
+ }\r
+ IsDispatched = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (IsDispatched) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Skip this packet if not dispatched to any instance.\r
+ //\r
+ if (!IsDispatched) {\r
+ goto ON_CONTINUE;\r
+ }\r
+\r
+ //\r
+ // Dispatch the received packet ot the right instance.\r
+ //\r
+ if (IsStateless) {\r
+ Dhcp6HandleStateless (Instance, Packet);\r
+ } else {\r
+ Dhcp6HandleStateful (Instance, Packet);\r
+ }\r
+\r
+ON_CONTINUE:\r
+\r
+ NetbufFree (Udp6Wrap);\r
+\r
+ if (Packet != NULL) {\r
+ FreePool (Packet);\r
+ }\r
+}\r
+\r
+/**\r
+ Detect Link movement for specified network device.\r
+\r
+ This routine will try to invoke Snp->GetStatus() to get the media status.\r
+ If media present status switches from unpresent to present, a link movement\r
+ is detected. Note that the underlying UNDI driver may not support reporting\r
+ media status from GET_STATUS command. If that, fail to detect link movement.\r
+\r
+ @param[in] Instance The pointer to DHCP6_INSTANCE.\r
+\r
+ @retval TRUE A link movement is detected.\r
+ @retval FALSE A link movement is not detected.\r
+\r
+**/\r
+BOOLEAN\r
+Dhcp6LinkMovDetect (\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ UINT32 InterruptStatus;\r
+ BOOLEAN MediaPresent;\r
+ EFI_STATUS Status;\r
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+\r
+ ASSERT (Instance != NULL);\r
+ Snp = Instance->Service->Snp;\r
+ MediaPresent = Instance->MediaPresent;\r
+\r
+ //\r
+ // Check whether SNP support media detection\r
+ //\r
+ if (!Snp->Mode->MediaPresentSupported) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data\r
+ //\r
+ Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+\r
+ Instance->MediaPresent = Snp->Mode->MediaPresent;\r
+ //\r
+ // Media transimit Unpresent to Present means new link movement is detected.\r
+ //\r
+ if (!MediaPresent && Instance->MediaPresent) {\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ The timer routine of the Dhcp6 instance for each second.\r
+\r
+ @param[in] Event The timer event.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTimerTick (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ DHCP6_INSTANCE *Instance;\r
+ DHCP6_TX_CB *TxCb;\r
+ DHCP6_IA_CB *IaCb;\r
+ UINT32 LossTime;\r
+\r
+ ASSERT (Context != NULL);\r
+\r
+ Instance = (DHCP6_INSTANCE *) Context;\r
+\r
+ //\r
+ // 1. Loop the tx list, count live time of every tx packet to check whether\r
+ // need re-transmit or not.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {\r
+\r
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);\r
+\r
+ TxCb->TickTime++;\r
+\r
+ if (TxCb->TickTime > TxCb->RetryExp) {\r
+ //\r
+ // Handle the first rt in the transmission of solicit specially.\r
+ //\r
+ if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {\r
+ if (Instance->AdSelect == NULL) {\r
+ //\r
+ // Set adpref as 0xff here to indicate select any advertisement\r
+ // afterwards.\r
+ //\r
+ Instance->AdPref = 0xff;\r
+ } else {\r
+ //\r
+ // Select the advertisement received before.\r
+ //\r
+ Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);\r
+ return;\r
+ }\r
+ }\r
+ //\r
+ // Increase the retry count for the packet and add up the total loss time.\r
+ //\r
+ TxCb->RetryCnt++;\r
+ TxCb->RetryLos += TxCb->RetryExp;\r
+\r
+ //\r
+ // Check whether overflow the max retry count limit for this packet\r
+ //\r
+ if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {\r
+ goto ON_CLOSE;\r
+ }\r
+\r
+ //\r
+ // Check whether overflow the max retry duration for this packet\r
+ //\r
+ if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {\r
+ goto ON_CLOSE;\r
+ }\r
+\r
+ //\r
+ // Re-calculate retry expire timeout for the next time.\r
+ //\r
+ // Firstly, Check the new calculated time whether overflow the max retry\r
+ // expire time.\r
+ //\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryExp,\r
+ FALSE,\r
+ TRUE\r
+ );\r
+\r
+ if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) {\r
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (\r
+ TxCb->RetryCtl.Mrt,\r
+ TRUE,\r
+ TRUE\r
+ );\r
+ }\r
+\r
+ //\r
+ // Secondly, Check the new calculated time whether overflow the max retry\r
+ // duration time.\r
+ //\r
+ LossTime = TxCb->RetryLos + TxCb->RetryExp;\r
+ if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) {\r
+ TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos;\r
+ }\r
+\r
+ //\r
+ // Reset the tick time for the next retransmission\r
+ //\r
+ TxCb->TickTime = 0;\r
+\r
+ //\r
+ // Retransmit the last sent packet again.\r
+ //\r
+ Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);\r
+ }\r
+ }\r
+\r
+ //\r
+ // 2. Check the configured Ia, count lease time of every valid Ia to check\r
+ // whether need to renew or rebind this Ia.\r
+ //\r
+ IaCb = &Instance->IaCb;\r
+\r
+ if (Instance->Config == NULL || IaCb->Ia == NULL) {\r
+ return;\r
+ }\r
+\r
+ if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) {\r
+\r
+ IaCb->LeaseTime++;\r
+\r
+ if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) {\r
+ //\r
+ // Exceed t2, send rebind packet to extend the Ia lease.\r
+ //\r
+ Dhcp6SendRenewRebindMsg (Instance, TRUE);\r
+\r
+ } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) {\r
+\r
+ //\r
+ // Exceed t1, send renew packet to extend the Ia lease.\r
+ //\r
+ Dhcp6SendRenewRebindMsg (Instance, FALSE);\r
+ }\r
+ }\r
+\r
+ //\r
+ // 3. In any situation when a client may have moved to a new link, the\r
+ // client MUST initiate a Confirm/Reply message exchange.\r
+ //\r
+ if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) {\r
+ Dhcp6SendConfirmMsg (Instance);\r
+ }\r
+\r
+ return;\r
+\r
+ ON_CLOSE:\r
+\r
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||\r
+ TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew ||\r
+ TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm\r
+ ) {\r
+ //\r
+ // The failure of renew/Confirm will still switch to the bound state.\r
+ //\r
+ if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) ||\r
+ (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) {\r
+ ASSERT (Instance->IaCb.Ia);\r
+ Instance->IaCb.Ia->State = Dhcp6Bound;\r
+ }\r
+ //\r
+ // The failure of info-request will return no response.\r
+ //\r
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {\r
+ Instance->UdpSts = EFI_NO_RESPONSE;\r
+ }\r
+ Dhcp6DequeueRetry (\r
+ Instance,\r
+ TxCb->Xid,\r
+ TRUE\r
+ );\r
+ } else {\r
+ //\r
+ // The failure of the others will terminate current state machine if timeout.\r
+ //\r
+ Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE);\r
+ }\r
+}\r
--- /dev/null
+/** @file\r
+ Dhcp6 internal functions declaration.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_IO_H__\r
+#define __EFI_DHCP6_IO_H__\r
+\r
+\r
+/**\r
+ Clean up the specific nodes in the retry list.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Scope The scope of cleanup nodes.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupRetry (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN UINT32 Scope\r
+ );\r
+\r
+/**\r
+ Clean up the session of the instance stateful exchange.\r
+\r
+ @param[in, out] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] Status The return status from udp.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupSession (\r
+ IN OUT DHCP6_INSTANCE *Instance,\r
+ IN EFI_STATUS Status\r
+ );\r
+\r
+/**\r
+ Create the solicit message and send it.\r
+\r
+ @param[in] Instance The pointer to Dhcp6 instance.\r
+\r
+ @retval EFI_SUCCESS Create and send the solicit message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Failed to send the solicit message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendSolicitMsg (\r
+ IN DHCP6_INSTANCE *Instance\r
+ );\r
+\r
+/**\r
+ Create the request message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+\r
+ @retval EFI_SUCCESS Create and send the request message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRequestMsg (\r
+ IN DHCP6_INSTANCE *Instance\r
+ );\r
+\r
+/**\r
+ Create the renew/rebind message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] RebindRequest If TRUE, it is a Rebind type message.\r
+ Otherwise, it is a Renew type message.\r
+\r
+ @retval EFI_SUCCESS Create and send the renew/rebind message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the renew/rebind message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendRenewRebindMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN BOOLEAN RebindRequest\r
+ );\r
+\r
+/**\r
+ Create the decline message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] DecIa The pointer to the decline Ia.\r
+\r
+ @retval EFI_SUCCESS Create and send the decline message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the decline message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendDeclineMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_IA *DecIa\r
+ );\r
+\r
+/**\r
+ Create the release message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] RelIa The pointer to the release Ia.\r
+\r
+ @retval EFI_SUCCESS Create and send the release message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others Failed to send the release message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendReleaseMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN EFI_DHCP6_IA *RelIa\r
+ );\r
+\r
+/**\r
+ Create the information request message and send it.\r
+\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[in] InfCb The pointer to the information request control block.\r
+ @param[in] SendClientId If TRUE, the client identifier option will be included in\r
+ information request message. Otherwise, the client identifier\r
+ option will not be included.\r
+ @param[in] OptionRequest The pointer to the option request option.\r
+ @param[in] OptionCount The number options in the OptionList.\r
+ @param[in] OptionList The array pointers to the appended options.\r
+ @param[in] Retransmission The pointer to the retransmission control.\r
+\r
+ @retval EFI_SUCCESS Create and send the info-request message successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Failed to send the info-request message.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6SendInfoRequestMsg (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN DHCP6_INF_CB *InfCb,\r
+ IN BOOLEAN SendClientId,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,\r
+ IN UINT32 OptionCount,\r
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[],\r
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission\r
+ );\r
+\r
+/**\r
+ The receive callback function for the Dhcp6 exchange process.\r
+\r
+ @param[in] Udp6Wrap The pointer to the received net buffer.\r
+ @param[in] EndPoint The pointer to the udp end point.\r
+ @param[in] IoStatus The return status from udp io.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6ReceivePacket (\r
+ IN NET_BUF *Udp6Wrap,\r
+ IN UDP_END_POINT *EndPoint,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ The timer routine of the Dhcp6 instance for each second.\r
+\r
+ @param[in] Event The timer event.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTimerTick (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Dhcp6 support functions implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Dhcp6Impl.h"\r
+\r
+\r
+/**\r
+ Generate client Duid in the format of Duid-llt.\r
+\r
+ @param[in] Mode The pointer to the mode of SNP.\r
+\r
+ @retval NULL If it failed to generate a client Id.\r
+ @retval others The pointer to the new client id.\r
+\r
+**/\r
+EFI_DHCP6_DUID *\r
+Dhcp6GenerateClientId (\r
+ IN EFI_SIMPLE_NETWORK_MODE *Mode\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_DUID *Duid;\r
+ EFI_TIME Time;\r
+ UINT32 Stamp;\r
+\r
+ //\r
+ // Attempt to get client Id from variable to keep it constant.\r
+ // See details in section-9 of rfc-3315.\r
+ //\r
+ Duid = GetVariable (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid);\r
+ if (Duid != NULL) {\r
+ return Duid;\r
+ }\r
+\r
+ //\r
+ // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.\r
+ //\r
+ gRT->GetTime (&Time, NULL);\r
+ Stamp = (UINT32)\r
+ (\r
+ (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *\r
+ 60 +\r
+ Time.Second\r
+ );\r
+\r
+ //\r
+ // The format of client identifier option:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_CLIENTID | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // . .\r
+ // . DUID .\r
+ // . (variable length) .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+ //\r
+ // The format of DUID-LLT:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | Duid type (1) | hardware type (16 bits) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | time (32 bits) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // . .\r
+ // . link-layer address (variable length) .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes\r
+ //\r
+ Duid = AllocateZeroPool (10 + Mode->HwAddressSize);\r
+ if (Duid == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // sizeof (Duid-type + hardware-type + time) = 8 bytes\r
+ //\r
+ Duid->Length = (UINT16) (Mode->HwAddressSize + 8);\r
+\r
+ //\r
+ // Set the Duid-type, hardware-type, time and copy the hardware address.\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt));\r
+ WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET));\r
+ WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp));\r
+\r
+ CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);\r
+\r
+ Status = gRT->SetVariable (\r
+ L"ClientId",\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),\r
+ Duid->Length + 2,\r
+ (VOID *) Duid\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Duid;\r
+}\r
+\r
+\r
+/**\r
+ Copy the Dhcp6 configure data.\r
+\r
+ @param[in] DstCfg The pointer to the destination configure data.\r
+ @param[in] SorCfg The pointer to the source configure data.\r
+\r
+ @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CopyConfigData (\r
+ IN EFI_DHCP6_CONFIG_DATA *DstCfg,\r
+ IN EFI_DHCP6_CONFIG_DATA *SorCfg\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN OptionListSize;\r
+ UINTN OptionSize;\r
+\r
+ CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+\r
+ //\r
+ // Allocate another buffer for solicitretransmission, and copy it.\r
+ //\r
+ if (SorCfg->SolicitRetransmission != NULL) {\r
+\r
+ DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
+\r
+ if (DstCfg->SolicitRetransmission == NULL) {\r
+ //\r
+ // Error will be handled out of this function.\r
+ //\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (\r
+ DstCfg->SolicitRetransmission,\r
+ SorCfg->SolicitRetransmission,\r
+ sizeof (EFI_DHCP6_RETRANSMISSION)\r
+ );\r
+ }\r
+\r
+ if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) {\r
+\r
+ OptionListSize = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *);\r
+ DstCfg->OptionList = AllocateZeroPool (OptionListSize);\r
+\r
+ if (DstCfg->OptionList == NULL) {\r
+ //\r
+ // Error will be handled out of this function.\r
+ //\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ for (Index = 0; Index < SorCfg->OptionCount; Index++) {\r
+\r
+ OptionSize = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4;\r
+ DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize);\r
+\r
+ if (DstCfg->OptionList[Index] == NULL) {\r
+ //\r
+ // Error will be handled out of this function.\r
+ //\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (\r
+ DstCfg->OptionList[Index],\r
+ SorCfg->OptionList[Index],\r
+ OptionSize\r
+ );\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Clean up the configure data.\r
+\r
+ @param[in, out] CfgData The pointer to the configure data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupConfigData (\r
+ IN OUT EFI_DHCP6_CONFIG_DATA *CfgData\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ ASSERT (CfgData != NULL);\r
+ //\r
+ // Clean up all fields in config data including the reference buffers, but do\r
+ // not free the config data buffer itself.\r
+ //\r
+ if (CfgData->OptionList != NULL) {\r
+ for (Index = 0; Index < CfgData->OptionCount; Index++) {\r
+ if (CfgData->OptionList[Index] != NULL) {\r
+ FreePool (CfgData->OptionList[Index]);\r
+ }\r
+ }\r
+ FreePool (CfgData->OptionList);\r
+ }\r
+\r
+ if (CfgData->SolicitRetransmission != NULL) {\r
+ FreePool (CfgData->SolicitRetransmission);\r
+ }\r
+\r
+ ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+}\r
+\r
+\r
+/**\r
+ Clean up the mode data.\r
+\r
+ @param[in, out] ModeData The pointer to the mode data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupModeData (\r
+ IN OUT EFI_DHCP6_MODE_DATA *ModeData\r
+ )\r
+{\r
+ ASSERT (ModeData != NULL);\r
+ //\r
+ // Clean up all fields in mode data including the reference buffers, but do\r
+ // not free the mode data buffer itself.\r
+ //\r
+ if (ModeData->ClientId != NULL) {\r
+ FreePool (ModeData->ClientId);\r
+ }\r
+\r
+ if (ModeData->Ia != NULL) {\r
+\r
+ if (ModeData->Ia->ReplyPacket != NULL) {\r
+ FreePool (ModeData->Ia->ReplyPacket);\r
+ }\r
+ FreePool (ModeData->Ia);\r
+ }\r
+\r
+ ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA));\r
+}\r
+\r
+\r
+/**\r
+ Calculate the expire time by the algorithm defined in rfc.\r
+\r
+ @param[in] Base The base value of the time.\r
+ @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.\r
+ @param[in] NeedSigned If TRUE, the the signed factor is needed.\r
+\r
+ @return Expire The calculated result for the new expire time.\r
+\r
+**/\r
+UINT32\r
+Dhcp6CalculateExpireTime (\r
+ IN UINT32 Base,\r
+ IN BOOLEAN IsFirstRt,\r
+ IN BOOLEAN NeedSigned\r
+ )\r
+{\r
+ EFI_TIME Time;\r
+ BOOLEAN Signed;\r
+ UINT32 Seed;\r
+ UINT32 Expire;\r
+\r
+ //\r
+ // Take the 10bits of microsecond in system time as a uniform distribution.\r
+ // Take the 10th bit as a flag to determine it's signed or not.\r
+ //\r
+ gRT->GetTime (&Time, NULL);\r
+ Seed = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK);\r
+ Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE);\r
+ Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE);\r
+\r
+ //\r
+ // Calculate expire by the following algo:\r
+ // 1. base + base * (-0.1 ~ 0) for the first solicit\r
+ // 2. base + base * (-0.1 ~ 0.1) for the first other messages\r
+ // 3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages\r
+ // 4. base + base * (-0.1 ~ 0) for the more than mrt timeout\r
+ //\r
+ // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1).\r
+ //\r
+ if (IsFirstRt && Signed) {\r
+\r
+ Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+\r
+ } else if (IsFirstRt && !Signed) {\r
+\r
+ Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+\r
+ } else if (!IsFirstRt && Signed) {\r
+\r
+ Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+\r
+ } else {\r
+\r
+ Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);\r
+ }\r
+\r
+ Expire = (Expire != 0) ? Expire : 1;\r
+\r
+ return Expire;\r
+}\r
+\r
+\r
+/**\r
+ Calculate the lease time by the algorithm defined in rfc.\r
+\r
+ @param[in] IaCb The pointer to the Ia control block.\r
+\r
+**/\r
+VOID\r
+Dhcp6CalculateLeaseTime (\r
+ IN DHCP6_IA_CB *IaCb\r
+ )\r
+{\r
+ EFI_DHCP6_IA_ADDRESS *IaAddr;\r
+ UINT32 MinLt;\r
+ UINT32 MaxLt;\r
+ UINTN Index;\r
+\r
+ ASSERT (IaCb->Ia->IaAddressCount > 0);\r
+\r
+ MinLt = (UINT32) (-1);\r
+ MaxLt = 0;\r
+\r
+ //\r
+ // Calculate minlt as min of all valid life time, and maxlt as max of all\r
+ // valid life time.\r
+ //\r
+ for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {\r
+ IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+ MinLt = MIN (MinLt, IaAddr->ValidLifetime);\r
+ MaxLt = MAX (MinLt, IaAddr->ValidLifetime);\r
+ }\r
+\r
+ //\r
+ // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer\r
+ // such information.\r
+ //\r
+ IaCb->T1 = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10);\r
+ IaCb->T2 = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10);\r
+ IaCb->AllExpireTime = MaxLt;\r
+ IaCb->LeaseTime = 0;\r
+}\r
+\r
+\r
+/**\r
+ Check whether the addresses are all included by the configured Ia.\r
+\r
+ @param[in] Ia The pointer to the Ia.\r
+ @param[in] AddressCount The number of addresses.\r
+ @param[in] Addresses The pointer to the addresses buffer.\r
+\r
+ @retval EFI_SUCCESS The addresses are all included by the configured IA.\r
+ @retval EFI_NOT_FOUND The addresses are not included by the configured IA.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CheckAddress (\r
+ IN EFI_DHCP6_IA *Ia,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ )\r
+{\r
+ UINTN Index1;\r
+ UINTN Index2;\r
+ BOOLEAN Found;\r
+\r
+ //\r
+ // Check whether the addresses are all included by the configured IA. And it\r
+ // will return success if address count is zero, which means all addresses.\r
+ //\r
+ for (Index1 = 0; Index1 < AddressCount; Index1++) {\r
+\r
+ Found = FALSE;\r
+\r
+ for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {\r
+\r
+ if (CompareMem (\r
+ &Addresses[Index1],\r
+ &Ia->IaAddress[Index2],\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ ) == 0) {\r
+\r
+ Found = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!Found) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Deprive the addresses from current Ia, and generate another eliminated Ia.\r
+\r
+ @param[in] Ia The pointer to the Ia.\r
+ @param[in] AddressCount The number of addresses.\r
+ @param[in] Addresses The pointer to the addresses buffer.\r
+\r
+ @retval NULL If it failed to generate the deprived Ia.\r
+ @retval others The pointer to the deprived Ia.\r
+\r
+**/\r
+EFI_DHCP6_IA *\r
+Dhcp6DepriveAddress (\r
+ IN EFI_DHCP6_IA *Ia,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ )\r
+{\r
+ EFI_DHCP6_IA *IaCopy;\r
+ UINTN IaCopySize;\r
+ UINTN Index1;\r
+ UINTN Index2;\r
+ BOOLEAN Found;\r
+\r
+ if (AddressCount == 0) {\r
+ //\r
+ // It means release all Ia addresses if address count is zero.\r
+ //\r
+ AddressCount = Ia->IaAddressCount;\r
+ }\r
+\r
+ ASSERT (AddressCount != 0);\r
+\r
+ IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+ IaCopy = AllocateZeroPool (IaCopySize);\r
+\r
+ if (IaCopy == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ if (AddressCount == Ia->IaAddressCount) {\r
+ //\r
+ // If release all Ia addresses, just copy the configured Ia and then set\r
+ // its address count as zero.\r
+ // We may decline/release part of addresses at the begining. So it's a\r
+ // forwarding step to update address infor for decline/release, while the\r
+ // other infor such as Ia state will be updated when receiving reply.\r
+ //\r
+ CopyMem (IaCopy, Ia, IaCopySize);\r
+ Ia->IaAddressCount = 0;\r
+ return IaCopy;\r
+ }\r
+\r
+ CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA));\r
+\r
+ //\r
+ // Move the addresses from the Ia of instance to the deprived Ia.\r
+ //\r
+ for (Index1 = 0; Index1 < AddressCount; Index1++) {\r
+\r
+ Found = FALSE;\r
+\r
+ for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {\r
+\r
+ if (CompareMem (\r
+ &Addresses[Index1],\r
+ &Ia->IaAddress[Index2],\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ ) == 0) {\r
+ //\r
+ // Copy the deprived address to the copy of Ia\r
+ //\r
+ CopyMem (\r
+ &IaCopy->IaAddress[Index1],\r
+ &Ia->IaAddress[Index2],\r
+ sizeof (EFI_DHCP6_IA_ADDRESS)\r
+ );\r
+ //\r
+ // Delete the deprived address from the instance Ia\r
+ //\r
+ if (Index2 + 1 < Ia->IaAddressCount) {\r
+ CopyMem (\r
+ &Ia->IaAddress[Index2],\r
+ &Ia->IaAddress[Index2 + 1],\r
+ (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS)\r
+ );\r
+ }\r
+ Found = TRUE;\r
+ break;\r
+ }\r
+ }\r
+ ASSERT (Found == TRUE);\r
+ }\r
+\r
+ Ia->IaAddressCount -= AddressCount;\r
+ IaCopy->IaAddressCount = AddressCount;\r
+\r
+ return IaCopy;\r
+}\r
+\r
+\r
+/**\r
+ The dummy ext buffer free callback routine.\r
+\r
+ @param[in] Arg The pointer to the parameter.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6DummyExtFree (\r
+ IN VOID *Arg\r
+ )\r
+{\r
+}\r
+\r
+\r
+/**\r
+ The callback routine once message transmitted.\r
+\r
+ @param[in] Udp6Wrap The pointer to the received net buffer.\r
+ @param[in] EndPoint The pointer to the udp end point.\r
+ @param[in] IoStatus The return status from udp io.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTransmitted (\r
+ IN NET_BUF *Wrap,\r
+ IN UDP_END_POINT *EndPoint,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ NetbufFree (Wrap);\r
+}\r
+\r
+\r
+/**\r
+ Append the option to Buf, and move Buf to the end.\r
+\r
+ @param[in, out] Buf The pointer to the buffer.\r
+ @param[in] OptType The option type.\r
+ @param[in] OptLen The length of option contents.\r
+ @param[in] Data The pointer to the option content.\r
+\r
+ @return Buf The position to append the next option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendOption (\r
+ IN OUT UINT8 *Buf,\r
+ IN UINT16 OptType,\r
+ IN UINT16 OptLen,\r
+ IN UINT8 *Data\r
+ )\r
+{\r
+ //\r
+ // The format of Dhcp6 option:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | option-code | option-len (option data) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | option-data |\r
+ // | (option-len octets) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ ASSERT (OptLen != 0);\r
+\r
+ WriteUnaligned16 ((UINT16 *) Buf, OptType);\r
+ Buf += 2;\r
+ WriteUnaligned16 ((UINT16 *) Buf, OptLen);\r
+ Buf += 2;\r
+ CopyMem (Buf, Data, NTOHS (OptLen));\r
+ Buf += NTOHS (OptLen);\r
+\r
+ return Buf;\r
+}\r
+\r
+\r
+/**\r
+ Append the appointed Ia option to Buf, and move Buf to the end.\r
+\r
+ @param[in, out] Buf The pointer to the position to append.\r
+ @param[in] Ia The pointer to the Ia.\r
+ @param[in] T1 The time of T1.\r
+ @param[in] T2 The time of T2.\r
+\r
+ @return Buf The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendIaOption (\r
+ IN OUT UINT8 *Buf,\r
+ IN EFI_DHCP6_IA *Ia,\r
+ IN UINT32 T1,\r
+ IN UINT32 T2\r
+ )\r
+{\r
+ UINT8 *AddrOpt;\r
+ UINT16 *Len;\r
+ UINTN Index;\r
+ UINT16 Length;\r
+\r
+ //\r
+ // The format of IA_NA and IA_TA option:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_IA_NA | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | IAID (4 octets) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | T1 (only for IA_NA) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | T2 (only for IA_NA) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // . IA_NA-options/IA_TA-options .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // Fill the value of Ia option type\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type));\r
+ Buf += 2;\r
+\r
+ //\r
+ // Fill the len of Ia option later, keep the pointer first\r
+ //\r
+ Len = (UINT16 *) Buf;\r
+ Buf += 2;\r
+\r
+ //\r
+ // Fill the value of iaid\r
+ //\r
+ WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId));\r
+ Buf += 4;\r
+\r
+ //\r
+ // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.\r
+ //\r
+ if (Ia->Descriptor.Type == Dhcp6OptIana) {\r
+ WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff));\r
+ Buf += 4;\r
+ WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff));\r
+ Buf += 4;\r
+ }\r
+\r
+ //\r
+ // Fill all the addresses belong to the Ia\r
+ //\r
+ for (Index = 0; Index < Ia->IaAddressCount; Index++) {\r
+\r
+ AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+ Length = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS));\r
+ Buf = Dhcp6AppendOption (\r
+ Buf,\r
+ HTONS (Dhcp6OptIaAddr),\r
+ Length,\r
+ AddrOpt\r
+ );\r
+ }\r
+\r
+ //\r
+ // Fill the value of Ia option length\r
+ //\r
+ *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2));\r
+\r
+ return Buf;\r
+}\r
+\r
+/**\r
+ Append the appointed Elapsed time option to Buf, and move Buf to the end.\r
+\r
+ @param[in, out] Buf The pointer to the position to append.\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[out] Elapsed The pointer to the elapsed time value in\r
+ the generated packet.\r
+\r
+ @return Buf The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendETOption (\r
+ IN OUT UINT8 *Buf,\r
+ IN DHCP6_INSTANCE *Instance,\r
+ OUT UINT16 **Elapsed\r
+ )\r
+{\r
+ //\r
+ // The format of elapsed time option:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_ELAPSED_TIME | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | elapsed-time |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // Fill the value of elapsed-time option type.\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime));\r
+ Buf += 2;\r
+\r
+ //\r
+ // Fill the len of elapsed-time option, which is fixed.\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS(2));\r
+ Buf += 2;\r
+\r
+ //\r
+ // Fill in elapsed time value with 0 value for now. The actual value is\r
+ // filled in later just before the packet is transmitted.\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS(0));\r
+ *Elapsed = (UINT16 *) Buf;\r
+ Buf += 2;\r
+\r
+ return Buf;\r
+}\r
+\r
+/**\r
+ Set the elapsed time based on the given instance and the pointer to the\r
+ elapsed time option.\r
+\r
+ @param[in] Elapsed The pointer to the position to append.\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+\r
+**/\r
+VOID\r
+SetElapsedTime (\r
+ IN UINT16 *Elapsed,\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ EFI_TIME Time;\r
+ UINT64 CurrentStamp;\r
+ UINT64 ElapsedTimeValue;\r
+\r
+ //\r
+ // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month.\r
+ //\r
+ gRT->GetTime (&Time, NULL);\r
+ CurrentStamp = (UINT64)\r
+ (\r
+ ((((((Time.Year - 2000) * 360 +\r
+ (Time.Month - 1)) * 30 +\r
+ (Time.Day - 1)) * 24 + Time.Hour) * 60 +\r
+ Time.Minute) * 60 + Time.Second) * 100\r
+ + DivU64x32(Time.Nanosecond, 10000000)\r
+ );\r
+\r
+ //\r
+ // Sentinel value of 0 means that this is the first DHCP packet that we are\r
+ // sending and that we need to initialize the value. First DHCP Solicit\r
+ // gets 0 elapsed-time. Otherwise, calculate based on StartTime.\r
+ //\r
+ if (Instance->StartTime == 0) {\r
+ ElapsedTimeValue = 0;\r
+ Instance->StartTime = CurrentStamp;\r
+ } else {\r
+ ElapsedTimeValue = CurrentStamp - Instance->StartTime;\r
+\r
+ //\r
+ // If elapsed time cannot fit in two bytes, set it to 0xffff.\r
+ //\r
+ if (ElapsedTimeValue > 0xffff) {\r
+ ElapsedTimeValue = 0xffff;\r
+ }\r
+ }\r
+ WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue));\r
+}\r
+\r
+\r
+/**\r
+ Seek the address of the first byte of the option header.\r
+\r
+ @param[in] Buf The pointer to the buffer.\r
+ @param[in] SeekLen The length to seek.\r
+ @param[in] OptType The option type.\r
+\r
+ @retval NULL If it failed to seek the option.\r
+ @retval others The position to the option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekOption (\r
+ IN UINT8 *Buf,\r
+ IN UINT32 SeekLen,\r
+ IN UINT16 OptType\r
+ )\r
+{\r
+ UINT8 *Cursor;\r
+ UINT8 *Option;\r
+ UINT16 DataLen;\r
+ UINT16 OpCode;\r
+\r
+ Option = NULL;\r
+ Cursor = Buf;\r
+\r
+ //\r
+ // The format of Dhcp6 option refers to Dhcp6AppendOption().\r
+ //\r
+ while (Cursor < Buf + SeekLen) {\r
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
+ if (OpCode == HTONS (OptType)) {\r
+ Option = Cursor;\r
+ break;\r
+ }\r
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
+ Cursor += (DataLen + 4);\r
+ }\r
+\r
+ return Option;\r
+}\r
+\r
+\r
+/**\r
+ Seek the address of the first byte of the Ia option header.\r
+\r
+ @param[in] Buf The pointer to the buffer.\r
+ @param[in] SeekLen The length to seek.\r
+ @param[in] IaDesc The pointer to the Ia descriptor.\r
+\r
+ @retval NULL If it failed to seek the Ia option.\r
+ @retval others The position to the Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekIaOption (\r
+ IN UINT8 *Buf,\r
+ IN UINT32 SeekLen,\r
+ IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc\r
+ )\r
+{\r
+ UINT8 *Cursor;\r
+ UINT8 *Option;\r
+ UINT16 DataLen;\r
+ UINT16 OpCode;\r
+ UINT32 IaId;\r
+\r
+ //\r
+ // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption().\r
+ //\r
+ Option = NULL;\r
+ Cursor = Buf;\r
+\r
+ while (Cursor < Buf + SeekLen) {\r
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
+ IaId = ReadUnaligned32 ((UINT32 *) (Cursor + 4));\r
+ if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) {\r
+ Option = Cursor;\r
+ break;\r
+ }\r
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
+ Cursor += (DataLen + 4);\r
+ }\r
+\r
+ return Option;\r
+}\r
+\r
+\r
+/**\r
+ Parse the address option and update the address infomation.\r
+\r
+ @param[in] IaInnerOpt The pointer to the buffer.\r
+ @param[in] IaInnerLen The length to parse.\r
+ @param[out] AddrNum The number of addresses.\r
+ @param[in, out] AddrBuf The pointer to the address buffer.\r
+\r
+**/\r
+VOID\r
+Dhcp6ParseAddrOption (\r
+ IN UINT8 *IaInnerOpt,\r
+ IN UINT16 IaInnerLen,\r
+ OUT UINT32 *AddrNum,\r
+ IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf\r
+ )\r
+{\r
+ UINT8 *Cursor;\r
+ UINT16 DataLen;\r
+ UINT16 OpCode;\r
+ UINT32 ValidLt;\r
+\r
+ //\r
+ // The format of the IA Address option:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_IAADDR | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // | IPv6 address |\r
+ // | |\r
+ // | |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | preferred-lifetime |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | valid-lifetime |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // . .\r
+ // . IAaddr-options .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // Two usage model:\r
+ //\r
+ // 1. Pass addrbuf == null, to get the addrnum over the Ia inner options.\r
+ // 2. Pass addrbuf != null, to resolve the addresses over the Ia inner\r
+ // options to the addrbuf.\r
+ //\r
+\r
+ Cursor = IaInnerOpt;\r
+ *AddrNum = 0;\r
+\r
+ while (Cursor < IaInnerOpt + IaInnerLen) {\r
+ //\r
+ // Count the Ia address option with non-0 valid time.\r
+ //\r
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
+ ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24));\r
+ if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) {\r
+\r
+ if (AddrBuf != NULL) {\r
+ CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS));\r
+ AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime);\r
+ AddrBuf->ValidLifetime = NTOHL (AddrBuf->ValidLifetime);\r
+ AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));\r
+ }\r
+\r
+ (*AddrNum)++;\r
+ }\r
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
+ Cursor += (DataLen + 4);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Create a control blcok for the Ia according to the corresponding options.\r
+\r
+ @param[in] Instance The pointer to DHCP6 Instance.\r
+ @param[in] IaInnerOpt The pointer to the inner options in the Ia option.\r
+ @param[in] IaInnerLen The length of all the inner options in the Ia option.\r
+ @param[in] T1 T1 time in the Ia option.\r
+ @param[in] T2 T2 time in the Ia option.\r
+\r
+ @retval EFI_NOT_FOUND No valid IA option is found.\r
+ @retval EFI_SUCCESS Create an IA control block successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6GenerateIaCb (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN UINT8 *IaInnerOpt,\r
+ IN UINT16 IaInnerLen,\r
+ IN UINT32 T1,\r
+ IN UINT32 T2\r
+ )\r
+{\r
+ UINT32 AddrNum;\r
+ UINT32 IaSize;\r
+ EFI_DHCP6_IA *Ia;\r
+\r
+ if (Instance->IaCb.Ia == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Calculate the number of addresses for this Ia, excluding the addresses with\r
+ // the value 0 of valid lifetime.\r
+ //\r
+ Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL);\r
+\r
+ if (AddrNum == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Allocate for new IA.\r
+ //\r
+ IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+ Ia = AllocateZeroPool (IaSize);\r
+\r
+ if (Ia == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Fill up this new IA fields.\r
+ //\r
+ Ia->State = Instance->IaCb.Ia->State;\r
+ Ia->IaAddressCount = AddrNum;\r
+ CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));\r
+ Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);\r
+\r
+ //\r
+ // Free original IA resource.\r
+ //\r
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {\r
+ FreePool (Instance->IaCb.Ia->ReplyPacket);\r
+ }\r
+ FreePool (Instance->IaCb.Ia);\r
+\r
+\r
+ ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB));\r
+\r
+ //\r
+ // Update IaCb to use new IA.\r
+ //\r
+ Instance->IaCb.Ia = Ia;\r
+\r
+ //\r
+\r
+ // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime.\r
+ //\r
+ Instance->IaCb.T1 = T1;\r
+ Instance->IaCb.T2 = T2;\r
+ Dhcp6CalculateLeaseTime (&Instance->IaCb);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Cache the current IA configuration information.\r
+\r
+ @param[in] Instance The pointer to DHCP6 Instance.\r
+\r
+ @retval EFI_SUCCESS Cache the current IA successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CacheIa (\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ UINTN IaSize;\r
+ EFI_DHCP6_IA *Ia;\r
+\r
+ Ia = Instance->IaCb.Ia;\r
+\r
+ if ((Instance->CacheIa == NULL) && (Ia != NULL)) {\r
+ //\r
+ // Cache the current IA.\r
+ //\r
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+\r
+ Instance->CacheIa = AllocateZeroPool (IaSize);\r
+ if (Instance->CacheIa == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (Instance->CacheIa, Ia, IaSize);\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.\r
+\r
+ @param[in] Instance The pointer to DHCP6 instance.\r
+\r
+**/\r
+VOID\r
+Dhcp6AppendCacheIa (\r
+ IN DHCP6_INSTANCE *Instance\r
+ )\r
+{\r
+ UINT8 *Ptr;\r
+ UINTN Index;\r
+ UINTN IaSize;\r
+ UINTN NewIaSize;\r
+ EFI_DHCP6_IA *Ia;\r
+ EFI_DHCP6_IA *NewIa;\r
+ EFI_DHCP6_IA *CacheIa;\r
+\r
+ Ia = Instance->IaCb.Ia;\r
+ CacheIa = Instance->CacheIa;\r
+\r
+ if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) {\r
+ //\r
+ // There are old addresses existing. Merge with current addresses.\r
+ //\r
+ NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+ NewIa = AllocateZeroPool (NewIaSize);\r
+ if (NewIa == NULL) {\r
+ return;\r
+ }\r
+\r
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+ CopyMem (NewIa, Ia, IaSize);\r
+\r
+ //\r
+ // Clear old address.ValidLifetime\r
+ //\r
+ for (Index = 0; Index < CacheIa->IaAddressCount; Index++) {\r
+ CacheIa->IaAddress[Index].ValidLifetime = 0;\r
+ }\r
+\r
+ NewIa->IaAddressCount += CacheIa->IaAddressCount;\r
+ Ptr = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount];\r
+ CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS));\r
+\r
+ //\r
+ // Migrate to the NewIa and free previous.\r
+ //\r
+ FreePool (Instance->CacheIa);\r
+ FreePool (Instance->IaCb.Ia);\r
+ Instance->CacheIa = NULL;\r
+ Instance->IaCb.Ia = NewIa;\r
+ }\r
+}\r
--- /dev/null
+/** @file\r
+ Dhcp6 support functions declaration.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_DHCP6_UTILITY_H__\r
+#define __EFI_DHCP6_UTILITY_H__\r
+\r
+\r
+#define DHCP6_10_BIT_MASK 0x3ff\r
+\r
+/**\r
+ Generate client Duid in the format of Duid-llt.\r
+\r
+ @param[in] Mode The pointer to the mode of SNP.\r
+\r
+ @retval NULL if failed to generate client Id.\r
+ @retval Others The pointer to the new client id.\r
+\r
+**/\r
+EFI_DHCP6_DUID *\r
+Dhcp6GenerateClientId (\r
+ IN EFI_SIMPLE_NETWORK_MODE *Mode\r
+ );\r
+\r
+/**\r
+ Copy the Dhcp6 configure data.\r
+\r
+ @param[in] DstCfg The pointer to the destination configure data.\r
+ @param[in] SorCfg The pointer to the source configure data.\r
+\r
+ @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CopyConfigData (\r
+ IN EFI_DHCP6_CONFIG_DATA *DstCfg,\r
+ IN EFI_DHCP6_CONFIG_DATA *SorCfg\r
+ );\r
+\r
+/**\r
+ Clean up the configure data.\r
+\r
+ @param[in, out] CfgData The pointer to the configure data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupConfigData (\r
+ IN OUT EFI_DHCP6_CONFIG_DATA *CfgData\r
+ );\r
+\r
+/**\r
+ Clean up the mode data.\r
+\r
+ @param[in, out] ModeData The pointer to the mode data.\r
+\r
+**/\r
+VOID\r
+Dhcp6CleanupModeData (\r
+ IN OUT EFI_DHCP6_MODE_DATA *ModeData\r
+ );\r
+\r
+/**\r
+ Calculate the expire time by the algorithm defined in rfc.\r
+\r
+ @param[in] Base The base value of the time.\r
+ @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.\r
+ @param[in] NeedSigned If TRUE, the the signed factor is needed.\r
+\r
+ @return Expire The calculated result for the new expire time.\r
+\r
+**/\r
+UINT32\r
+Dhcp6CalculateExpireTime (\r
+ IN UINT32 Base,\r
+ IN BOOLEAN IsFirstRt,\r
+ IN BOOLEAN NeedSigned\r
+ );\r
+\r
+/**\r
+ Calculate the lease time by the algorithm defined in rfc.\r
+\r
+ @param[in] IaCb The pointer to the Ia control block.\r
+\r
+**/\r
+VOID\r
+Dhcp6CalculateLeaseTime (\r
+ IN DHCP6_IA_CB *IaCb\r
+ );\r
+\r
+/**\r
+ Check whether the addresses are all included by the configured Ia.\r
+\r
+ @param[in] Ia The pointer to the Ia.\r
+ @param[in] AddressCount The number of addresses.\r
+ @param[in] Addresses The pointer to the addresses buffer.\r
+\r
+ @retval EFI_SUCCESS The addresses are all included by the configured IA.\r
+ @retval EFI_NOT_FOUND The addresses are not included by the configured IA.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CheckAddress (\r
+ IN EFI_DHCP6_IA *Ia,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ );\r
+\r
+/**\r
+ Deprive the addresses from current Ia, and generate another eliminated Ia.\r
+\r
+ @param[in] Ia The pointer to the Ia.\r
+ @param[in] AddressCount The number of addresses.\r
+ @param[in] Addresses The pointer to the addresses buffer.\r
+\r
+ @retval NULL If failed to generate the deprived Ia.\r
+ @retval others The pointer to the deprived Ia.\r
+\r
+**/\r
+EFI_DHCP6_IA *\r
+Dhcp6DepriveAddress (\r
+ IN EFI_DHCP6_IA *Ia,\r
+ IN UINT32 AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Addresses\r
+ );\r
+\r
+/**\r
+ The dummy ext buffer free callback routine.\r
+\r
+ @param[in] Arg The pointer to the parameter.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6DummyExtFree (\r
+ IN VOID *Arg\r
+ );\r
+\r
+/**\r
+ The callback routine once message transmitted.\r
+\r
+ @param[in] Udp6Wrap The pointer to the received net buffer.\r
+ @param[in] EndPoint The pointer to the udp end point.\r
+ @param[in] IoStatus The return status from udp io.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Dhcp6OnTransmitted (\r
+ IN NET_BUF *Wrap,\r
+ IN UDP_END_POINT *EndPoint,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Append the appointed option to the buf, and move the buf to the end.\r
+\r
+ @param[in, out] Buf The pointer to buffer.\r
+ @param[in] OptType The option type.\r
+ @param[in] OptLen The lenght of option content.s\r
+ @param[in] Data The pointer to the option content.\r
+\r
+ @return Buf The position to append the next option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendOption (\r
+ IN OUT UINT8 *Buf,\r
+ IN UINT16 OptType,\r
+ IN UINT16 OptLen,\r
+ IN UINT8 *Data\r
+ );\r
+\r
+/**\r
+ Append the Ia option to Buf, and move Buf to the end.\r
+\r
+ @param[in, out] Buf The pointer to the position to append.\r
+ @param[in] Ia The pointer to the Ia.\r
+ @param[in] T1 The time of T1.\r
+ @param[in] T2 The time of T2.\r
+\r
+ @return Buf The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendIaOption (\r
+ IN OUT UINT8 *Buf,\r
+ IN EFI_DHCP6_IA *Ia,\r
+ IN UINT32 T1,\r
+ IN UINT32 T2\r
+ );\r
+\r
+/**\r
+ Append the appointed Elapsed time option to Buf, and move Buf to the end.\r
+\r
+ @param[in, out] Buf The pointer to the position to append.\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+ @param[out] Elapsed The pointer to the elapsed time value in\r
+ the generated packet.\r
+\r
+ @return Buf The position to append the next Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendETOption (\r
+ IN OUT UINT8 *Buf,\r
+ IN DHCP6_INSTANCE *Instance,\r
+ OUT UINT16 **Elapsed\r
+ );\r
+\r
+/**\r
+ Set the elapsed time based on the given instance and the pointer to the\r
+ elapsed time option.\r
+\r
+ @param[in] Elapsed The pointer to the position to append.\r
+ @param[in] Instance The pointer to the Dhcp6 instance.\r
+**/\r
+VOID\r
+SetElapsedTime (\r
+ IN UINT16 *Elapsed,\r
+ IN DHCP6_INSTANCE *Instance\r
+ );\r
+\r
+/**\r
+ Seek the address of the first byte of the option header.\r
+\r
+ @param[in] Buf The pointer to buffer.\r
+ @param[in] SeekLen The length to seek.\r
+ @param[in] OptType The option type.\r
+\r
+ @retval NULL If failed to seek the option.\r
+ @retval others The position to the option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekOption (\r
+ IN UINT8 *Buf,\r
+ IN UINT32 SeekLen,\r
+ IN UINT16 OptType\r
+ );\r
+\r
+/**\r
+ Seek the address of the first byte of the Ia option header.\r
+\r
+ @param[in] Buf The pointer to the buffer.\r
+ @param[in] SeekLen The length to seek.\r
+ @param[in] IaDesc The pointer to the Ia descriptor.\r
+\r
+ @retval NULL If failed to seek the Ia option.\r
+ @retval others The position to the Ia option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6SeekIaOption (\r
+ IN UINT8 *Buf,\r
+ IN UINT32 SeekLen,\r
+ IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc\r
+ );\r
+\r
+/**\r
+ Parse the address option and update the address info.\r
+\r
+ @param[in] IaInnerOpt The pointer to the buffer.\r
+ @param[in] IaInnerLen The length to parse.\r
+ @param[out] AddrNum The number of addresses.\r
+ @param[in, out] AddrBuf The pointer to the address buffer.\r
+\r
+**/\r
+VOID\r
+Dhcp6ParseAddrOption (\r
+ IN UINT8 *IaInnerOpt,\r
+ IN UINT16 IaInnerLen,\r
+ OUT UINT32 *AddrNum,\r
+ IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf\r
+ );\r
+\r
+/**\r
+ Create a control blcok for the Ia according to the corresponding options.\r
+\r
+ @param[in] Instance The pointer to DHCP6 Instance.\r
+ @param[in] IaInnerOpt The pointer to the inner options in the Ia option.\r
+ @param[in] IaInnerLen The length of all the inner options in the Ia option.\r
+ @param[in] T1 T1 time in the Ia option.\r
+ @param[in] T2 T2 time in the Ia option.\r
+\r
+ @retval EFI_NOT_FOUND No valid IA option is found.\r
+ @retval EFI_SUCCESS Create an IA control block successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6GenerateIaCb (\r
+ IN DHCP6_INSTANCE *Instance,\r
+ IN UINT8 *IaInnerOpt,\r
+ IN UINT16 IaInnerLen,\r
+ IN UINT32 T1,\r
+ IN UINT32 T2\r
+ );\r
+\r
+\r
+/**\r
+ Cache the current IA configuration information.\r
+\r
+ @param[in] Instance The pointer to DHCP6 Instance.\r
+\r
+ @retval EFI_SUCCESS Cache the current IA successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+Dhcp6CacheIa (\r
+ IN DHCP6_INSTANCE *Instance\r
+ );\r
+\r
+\r
+/**\r
+ Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.\r
+\r
+ @param[in] Instance The pointer to DHCP6 instance.\r
+\r
+**/\r
+VOID\r
+Dhcp6AppendCacheIa (\r
+ IN DHCP6_INSTANCE *Instance\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Implementation of EFI_COMPONENT_NAME_PROTOCOL and\r
+ EFI_COMPONENT_NAME2_PROTOCOL protocol.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r
+\r
+//\r
+// EFI Component Name Protocol.\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName = {\r
+ Ip6ComponentNameGetDriverName,\r
+ Ip6ComponentNameGetControllerName,\r
+ "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol.\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip6ComponentNameGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip6ComponentNameGetControllerName,\r
+ "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIp6DriverNameTable[] = {\r
+ {\r
+ "eng;en",\r
+ L"IP6 Network Service Driver"\r
+ },\r
+ {\r
+ NULL,\r
+ NULL\r
+ }\r
+};\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mIp6DriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN) (This == &gIp6ComponentName)\r
+ );\r
+\r
+}\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
--- /dev/null
+/** @file\r
+ The implementation of common functions shared by IP6 driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+ of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,\r
+ only the address count is returned.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[out] AddressCount The number of returned addresses.\r
+ @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO.\r
+ This is an optional parameter.\r
+\r
+\r
+ @retval EFI_SUCCESS The address array successfully built.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiAddressList (\r
+ IN IP6_SERVICE *IpSb,\r
+ OUT UINT32 *AddressCount,\r
+ OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL\r
+ )\r
+{\r
+ UINT32 Count;\r
+ LIST_ENTRY *Entry;\r
+ EFI_IP6_ADDRESS_INFO *EfiAddrInfo;\r
+ IP6_ADDRESS_INFO *AddrInfo;\r
+\r
+ if (AddressCount == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (IpSb->LinkLocalOk) {\r
+ Count = 1 + IpSb->DefaultInterface->AddressCount;\r
+ } else {\r
+ Count = 0;\r
+ }\r
+\r
+ *AddressCount = Count;\r
+\r
+ if ((AddressList == NULL) || (Count == 0)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (*AddressList == NULL) {\r
+ *AddressList = AllocatePool (sizeof (EFI_IP6_ADDRESS_INFO) * Count);\r
+ if (*AddressList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ EfiAddrInfo = *AddressList;\r
+\r
+ IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &IpSb->LinkLocalAddr);\r
+ EfiAddrInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+\r
+ EfiAddrInfo++;\r
+ Count = 1;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) {\r
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+ IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &AddrInfo->Address);\r
+ EfiAddrInfo->PrefixLength = AddrInfo->PrefixLength;\r
+\r
+ EfiAddrInfo++;\r
+ Count++;\r
+ }\r
+\r
+ ASSERT (Count == *AddressCount);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Generate the multicast addresses identify the group of all IPv6 nodes or IPv6\r
+ routers defined in RFC4291.\r
+\r
+ All Nodes Addresses: FF01::1, FF02::1.\r
+ All Router Addresses: FF01::2, FF02::2, FF05::2.\r
+\r
+ @param[in] Router If TRUE, generate all routers addresses,\r
+ else generate all node addresses.\r
+ @param[in] Scope interface-local(1), link-local(2), or site-local(5)\r
+ @param[out] Ip6Addr The generated multicast address.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_SUCCESS The address is generated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetToAllNodeMulticast (\r
+ IN BOOLEAN Router,\r
+ IN UINT8 Scope,\r
+ OUT EFI_IPv6_ADDRESS *Ip6Addr\r
+ )\r
+{\r
+ if (Ip6Addr == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!Router && Scope == IP6_SITE_LOCAL_SCOPE) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ZeroMem (Ip6Addr, sizeof (EFI_IPv6_ADDRESS));\r
+ Ip6Addr->Addr[0] = 0xFF;\r
+ Ip6Addr->Addr[1] = Scope;\r
+\r
+ if (!Router) {\r
+ Ip6Addr->Addr[15] = 0x1;\r
+ } else {\r
+ Ip6Addr->Addr[15] = 0x2;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function converts MAC address to 64 bits interface ID according to RFC4291\r
+ and returns the interface ID. Currently only 48-bit MAC address is supported by\r
+ this function.\r
+\r
+ @param[in, out] IpSb The IP6 service binding instance.\r
+\r
+ @retval NULL The operation fails.\r
+ @return Pointer to the generated interface ID.\r
+\r
+**/\r
+UINT8 *\r
+Ip6CreateInterfaceID (\r
+ IN OUT IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ UINT8 InterfaceId[8];\r
+ UINT8 Byte;\r
+ EFI_MAC_ADDRESS *MacAddr;\r
+ UINT32 AddrLen;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ AddrLen = IpSb->SnpMode.HwAddressSize;\r
+\r
+ //\r
+ // Currently only IEEE 802 48-bit MACs are supported to create link local address.\r
+ //\r
+ if (AddrLen != IP6_MAC_LEN || IpSb->InterfaceIdLen != IP6_IF_ID_LEN) {\r
+ return NULL;\r
+ }\r
+\r
+ MacAddr = &IpSb->SnpMode.CurrentAddress;\r
+\r
+ //\r
+ // Convert MAC address to 64 bits interface ID according to Appendix A of RFC4291:\r
+ // 1. Insert 0xFFFE to the middle\r
+ // 2. Invert the universal/local bit - bit 6 in network order\r
+ //\r
+ CopyMem (InterfaceId, MacAddr, 3);\r
+ InterfaceId[3] = 0xFF;\r
+ InterfaceId[4] = 0xFE;\r
+ CopyMem (&InterfaceId[5], &MacAddr->Addr[3], 3);\r
+\r
+ Byte = (UINT8) (InterfaceId[0] & IP6_U_BIT);\r
+ if (Byte == IP6_U_BIT) {\r
+ InterfaceId[0] &= ~IP6_U_BIT;\r
+ } else {\r
+ InterfaceId[0] |= IP6_U_BIT;\r
+ }\r
+\r
+ //\r
+ // Return the interface ID.\r
+ //\r
+ return AllocateCopyPool (IpSb->InterfaceIdLen, InterfaceId);\r
+}\r
+\r
+/**\r
+ This function creates link-local address from interface identifier. The\r
+ interface identifier is normally created from MAC address. It might be manually\r
+ configured by administrator if the link-local address created from MAC address\r
+ is a duplicate address.\r
+\r
+ @param[in, out] IpSb The IP6 service binding instance.\r
+\r
+ @retval NULL If the operation fails.\r
+ @return The generated Link Local address, in network order.\r
+\r
+**/\r
+EFI_IPv6_ADDRESS *\r
+Ip6CreateLinkLocalAddr (\r
+ IN OUT IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ EFI_IPv6_ADDRESS *Ip6Addr;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
+ UINTN DataSize;\r
+ EFI_IP6_CONFIG_INTERFACE_ID InterfaceId;\r
+ EFI_STATUS Status;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ if (IpSb->InterfaceId != NULL) {\r
+ FreePool (IpSb->InterfaceId);\r
+ }\r
+\r
+ //\r
+ // Get the interface id if it is manully configured.\r
+ //\r
+ Ip6Config = &IpSb->Ip6ConfigInstance.Ip6Config;\r
+ DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);\r
+ ZeroMem (&InterfaceId, DataSize);\r
+\r
+ Status = Ip6Config->GetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeAltInterfaceId,\r
+ &DataSize,\r
+ &InterfaceId\r
+ );\r
+ if (Status == EFI_NOT_FOUND) {\r
+ //\r
+ // Since the interface id is not configured, generate the interface id from\r
+ // MAC address.\r
+ //\r
+ IpSb->InterfaceId = Ip6CreateInterfaceID (IpSb);\r
+ if (IpSb->InterfaceId == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ CopyMem (&InterfaceId, IpSb->InterfaceId, IpSb->InterfaceIdLen);\r
+ //\r
+ // Record the interface id.\r
+ //\r
+ Status = Ip6Config->SetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeAltInterfaceId,\r
+ DataSize,\r
+ &InterfaceId\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (IpSb->InterfaceId);\r
+ IpSb->InterfaceId = NULL;\r
+ return NULL;\r
+ }\r
+ } else if (!EFI_ERROR (Status)) {\r
+ IpSb->InterfaceId = AllocateCopyPool (DataSize, &InterfaceId);\r
+ if (IpSb->InterfaceId == NULL) {\r
+ return NULL;\r
+ }\r
+ } else {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Append FE80::/64 to the left of IPv6 address then return.\r
+ //\r
+ Ip6Addr = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS));\r
+ if (Ip6Addr == NULL) {\r
+ FreePool (IpSb->InterfaceId);\r
+ IpSb->InterfaceId = NULL;\r
+ return NULL;\r
+ }\r
+\r
+ CopyMem (&Ip6Addr->Addr[8], IpSb->InterfaceId, IpSb->InterfaceIdLen);\r
+ Ip6Addr->Addr[1] = 0x80;\r
+ Ip6Addr->Addr[0] = 0xFE;\r
+\r
+ return Ip6Addr;\r
+}\r
+\r
+/**\r
+ Compute the solicited-node multicast address for an unicast or anycast address,\r
+ by taking the low-order 24 bits of this address, and appending those bits to\r
+ the prefix FF02:0:0:0:0:1:FF00::/104.\r
+\r
+ @param[in] Ip6Addr The unicast or anycast address, in network order.\r
+ @param[out] MulticastAddr The generated solicited-node multicast address,\r
+ in network order.\r
+\r
+**/\r
+VOID\r
+Ip6CreateSNMulticastAddr (\r
+ IN EFI_IPv6_ADDRESS *Ip6Addr,\r
+ OUT EFI_IPv6_ADDRESS *MulticastAddr\r
+ )\r
+{\r
+ ASSERT (Ip6Addr != NULL && MulticastAddr != NULL);\r
+\r
+ ZeroMem (MulticastAddr, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ MulticastAddr->Addr[0] = 0xFF;\r
+ MulticastAddr->Addr[1] = 0x02;\r
+ MulticastAddr->Addr[11] = 0x1;\r
+ MulticastAddr->Addr[12] = 0xFF;\r
+\r
+ CopyMem (&MulticastAddr->Addr[13], &Ip6Addr->Addr[13], 3);\r
+}\r
+\r
+/**\r
+ Insert a node IP6_ADDRESS_INFO to an IP6 interface.\r
+\r
+ @param[in, out] IpIf Points to an IP6 interface.\r
+ @param[in] AddrInfo Points to IP6_ADDRESS_INFO\r
+\r
+**/\r
+VOID\r
+Ip6AddAddr (\r
+ IN OUT IP6_INTERFACE *IpIf,\r
+ IN IP6_ADDRESS_INFO *AddrInfo\r
+ )\r
+{\r
+ InsertHeadList (&IpIf->AddressList, &AddrInfo->Link);\r
+ IpIf->AddressCount++;\r
+}\r
+\r
+/**\r
+ Destroy the IP instance if its StationAddress is removed. It is the help function\r
+ for Ip6RemoveAddr().\r
+\r
+ @param[in, out] IpSb Points to an IP6 service binding instance.\r
+ @param[in] Address The to be removed address\r
+\r
+**/\r
+VOID\r
+Ip6DestroyInstanceByAddress (\r
+ IN OUT IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Address\r
+ )\r
+{\r
+ BOOLEAN OneDestroyed;\r
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;\r
+ LIST_ENTRY *Entry;\r
+ IP6_PROTOCOL *Instance;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ ServiceBinding = &IpSb->ServiceBinding;\r
+\r
+ //\r
+ // Upper layer IP protocol consumers may have tight relationship between several\r
+ // IP protocol instances, in other words, calling ServiceBinding->DestroyChild to\r
+ // destroy one IP child may cause other related IP children destroyed too. This\r
+ // will probably leave hole in the children list when we iterate it. So everytime\r
+ // we just destroy one child then back to the start point to iterate the list.\r
+ //\r
+ do {\r
+ OneDestroyed = FALSE;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+ Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+\r
+ if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) {\r
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);\r
+ OneDestroyed = TRUE;\r
+ break;\r
+ }\r
+ }\r
+ } while (OneDestroyed);\r
+}\r
+\r
+/**\r
+ Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.\r
+\r
+ This function removes the matching IPv6 addresses from the address list and\r
+ adjusts the address count of the address list. If IpSb is not NULL, this function\r
+ calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the\r
+ its solicited-node multicast MAC address from the filter list and sends out\r
+ a Multicast Listener Done. If Prefix is NULL, all address in the address list\r
+ will be removed. If Prefix is not NULL, the address that matching the Prefix\r
+ with PrefixLength in the address list will be removed.\r
+\r
+ @param[in] IpSb NULL or points to IP6 service binding instance.\r
+ @param[in, out] AddressList Address list array.\r
+ @param[in, out] AddressCount The count of addresses in address list array.\r
+ @param[in] Prefix NULL or an IPv6 address prefix.\r
+ @param[in] PrefixLength The length of Prefix.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength\r
+ cannot be found in the address list.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6RemoveAddr (\r
+ IN IP6_SERVICE *IpSb OPTIONAL,\r
+ IN OUT LIST_ENTRY *AddressList,\r
+ IN OUT UINT32 *AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Prefix OPTIONAL,\r
+ IN UINT8 PrefixLength\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_ADDRESS_INFO *AddrInfo;\r
+ EFI_IPv6_ADDRESS SnMCastAddr;\r
+\r
+ if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_NUM) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = EFI_NOT_FOUND;\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, AddressList) {\r
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+ if (Prefix == NULL ||\r
+ (PrefixLength == 128 && EFI_IP6_EQUAL (Prefix, &AddrInfo->Address)) ||\r
+ (PrefixLength == AddrInfo->PrefixLength && NetIp6IsNetEqual (Prefix, &AddrInfo->Address, PrefixLength))\r
+ ) {\r
+ if (IpSb != NULL) {\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ Ip6CreateSNMulticastAddr (&AddrInfo->Address, &SnMCastAddr);\r
+ Ip6LeaveGroup (IpSb, &SnMCastAddr);\r
+\r
+ //\r
+ // Destroy any instance who is using the dying address as the source address.\r
+ //\r
+ Ip6DestroyInstanceByAddress (IpSb, &AddrInfo->Address);\r
+ }\r
+\r
+ RemoveEntryList (Entry);\r
+ FreePool (AddrInfo);\r
+ (*AddressCount)--;\r
+\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check whether the incoming Ipv6 address is a solicited-node multicast address.\r
+\r
+ @param[in] Ip6 Ip6 address, in network order.\r
+\r
+ @retval TRUE Yes, solicited-node multicast address\r
+ @retval FALSE No\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsSNMulticastAddr (\r
+ IN EFI_IPv6_ADDRESS *Ip6\r
+ )\r
+{\r
+ EFI_IPv6_ADDRESS Sn;\r
+ BOOLEAN Flag;\r
+\r
+ Ip6CreateSNMulticastAddr (Ip6, &Sn);\r
+ Flag = FALSE;\r
+\r
+ if (CompareMem (Sn.Addr, Ip6->Addr, 13) == 0) {\r
+ Flag = TRUE;\r
+ }\r
+\r
+ return Flag;\r
+}\r
+\r
+/**\r
+ Check whether the incoming IPv6 address is one of the maintained addresses in\r
+ the IP6 service binding instance.\r
+\r
+ @param[in] IpSb Points to a IP6 service binding instance.\r
+ @param[in] Address The IP6 address to be checked.\r
+ @param[out] Interface If not NULL, output the IP6 interface which\r
+ maintains the Address.\r
+ @param[out] AddressInfo If not NULL, output the IP6 address information\r
+ of the Address.\r
+\r
+ @retval TRUE Yes, it is one of the maintained address.\r
+ @retval FALSE No, it is not one of the maintained address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsOneOfSetAddress (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Address,\r
+ OUT IP6_INTERFACE **Interface OPTIONAL,\r
+ OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Entry2;\r
+ IP6_INTERFACE *IpIf;\r
+ IP6_ADDRESS_INFO *TmpAddressInfo;\r
+\r
+ //\r
+ // Check link-local address first\r
+ //\r
+ if (IpSb->LinkLocalOk && EFI_IP6_EQUAL (&IpSb->LinkLocalAddr, Address)) {\r
+ if (Interface != NULL) {\r
+ *Interface = IpSb->DefaultInterface;\r
+ }\r
+\r
+ if (AddressInfo != NULL) {\r
+ *AddressInfo = NULL;\r
+ }\r
+\r
+ return TRUE;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+ NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {\r
+ TmpAddressInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+ if (EFI_IP6_EQUAL (&TmpAddressInfo->Address, Address)) {\r
+ if (Interface != NULL) {\r
+ *Interface = IpIf;\r
+ }\r
+\r
+ if (AddressInfo != NULL) {\r
+ *AddressInfo = TmpAddressInfo;\r
+ }\r
+\r
+ return TRUE;\r
+ }\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Check whether the incoming MAC address is valid.\r
+\r
+ @param[in] IpSb Points to a IP6 service binding instance.\r
+ @param[in] LinkAddress The MAC address.\r
+\r
+ @retval TRUE Yes, it is valid.\r
+ @retval FALSE No, it is not valid.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidLinkAddress (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_MAC_ADDRESS *LinkAddress\r
+ )\r
+{\r
+ UINT32 Index;\r
+\r
+ //\r
+ // TODO: might be updated later to be more acceptable.\r
+ //\r
+ for (Index = IpSb->SnpMode.HwAddressSize; Index < sizeof (EFI_MAC_ADDRESS); Index++) {\r
+ if (LinkAddress->Addr[Index] != 0) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Copy the PrefixLength bits from Src to Dest.\r
+\r
+ @param[out] Dest A pointer to the buffer to copy to.\r
+ @param[in] Src A pointer to the buffer to copy from.\r
+ @param[in] PrefixLength The number of bits to copy.\r
+\r
+**/\r
+VOID\r
+Ip6CopyAddressByPrefix (\r
+ OUT EFI_IPv6_ADDRESS *Dest,\r
+ IN EFI_IPv6_ADDRESS *Src,\r
+ IN UINT8 PrefixLength\r
+ )\r
+{\r
+ UINT8 Byte;\r
+ UINT8 Bit;\r
+ UINT8 Mask;\r
+\r
+ ASSERT (Dest != NULL && Src != NULL);\r
+ ASSERT (PrefixLength < IP6_PREFIX_NUM);\r
+\r
+ Byte = (UINT8) (PrefixLength / 8);\r
+ Bit = (UINT8) (PrefixLength % 8);\r
+\r
+ ZeroMem (Dest, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ CopyMem (Dest, Src, Byte);\r
+\r
+ if (Bit > 0) {\r
+ Mask = (UINT8) (0xFF << (8 - Bit));\r
+ ASSERT (Byte < 16);\r
+ Dest->Addr[Byte] = (UINT8) (Src->Addr[Byte] & Mask);\r
+ }\r
+}\r
+\r
+/**\r
+ Get the MAC address for a multicast IP address. Call\r
+ Mnp's McastIpToMac to find the MAC address instead of\r
+ hard-coding the NIC to be Ethernet.\r
+\r
+ @param[in] Mnp The Mnp instance to get the MAC address.\r
+ @param[in] Multicast The multicast IP address to translate.\r
+ @param[out] Mac The buffer to hold the translated address.\r
+\r
+ @retval EFI_SUCCESS The multicast IP successfully\r
+ translated to a multicast MAC address.\r
+ @retval Other The address is not converted because an error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6GetMulticastMac (\r
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,\r
+ IN EFI_IPv6_ADDRESS *Multicast,\r
+ OUT EFI_MAC_ADDRESS *Mac\r
+ )\r
+{\r
+ EFI_IP_ADDRESS EfiIp;\r
+\r
+ IP6_COPY_ADDRESS (&EfiIp.v6, Multicast);\r
+\r
+ return Mnp->McastIpToMac (Mnp, TRUE, &EfiIp, Mac);\r
+}\r
+\r
+/**\r
+ Set the Ip6 variable data.\r
+\r
+ @param[in] IpSb Points to an IP6 service binding instance.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetVariableData (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ UINT32 NumConfiguredInstance;\r
+ LIST_ENTRY *Entry;\r
+ UINTN VariableDataSize;\r
+ EFI_IP6_VARIABLE_DATA *Ip6VariableData;\r
+ EFI_IP6_ADDRESS_PAIR *Ip6AddressPair;\r
+ IP6_PROTOCOL *IpInstance;\r
+ CHAR16 *NewMacString;\r
+ EFI_STATUS Status;\r
+\r
+ NumConfiguredInstance = 0;\r
+\r
+ //\r
+ // Go through the children list to count the configured children.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+\r
+ if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+ NumConfiguredInstance++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Calculate the size of the Ip6VariableData. As there may be no IP child,\r
+ // we should add extra buffer for the address paris only if the number of configured\r
+ // children is more than 1.\r
+ //\r
+ VariableDataSize = sizeof (EFI_IP6_VARIABLE_DATA);\r
+\r
+ if (NumConfiguredInstance > 1) {\r
+ VariableDataSize += sizeof (EFI_IP6_ADDRESS_PAIR) * (NumConfiguredInstance - 1);\r
+ }\r
+\r
+ Ip6VariableData = AllocatePool (VariableDataSize);\r
+ if (Ip6VariableData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Ip6VariableData->DriverHandle = IpSb->Image;\r
+ Ip6VariableData->AddressCount = NumConfiguredInstance;\r
+\r
+ Ip6AddressPair = &Ip6VariableData->AddressPairs[0];\r
+\r
+ //\r
+ // Go through the children list to fill the configured children's address pairs.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+\r
+ if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+ Ip6AddressPair->InstanceHandle = IpInstance->Handle;\r
+ Ip6AddressPair->PrefixLength = IpInstance->PrefixLength;\r
+ IP6_COPY_ADDRESS (&Ip6AddressPair->Ip6Address, &IpInstance->ConfigData.StationAddress);\r
+\r
+ Ip6AddressPair++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get the mac string.\r
+ //\r
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (IpSb->MacString != NULL) {\r
+ //\r
+ // The variable is set already, we're going to update it.\r
+ //\r
+ if (StrCmp (IpSb->MacString, NewMacString) != 0) {\r
+ //\r
+ // The mac address is changed, delete the previous variable first.\r
+ //\r
+ gRT->SetVariable (\r
+ IpSb->MacString,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ FreePool (IpSb->MacString);\r
+ }\r
+\r
+ IpSb->MacString = NewMacString;\r
+\r
+ Status = gRT->SetVariable (\r
+ IpSb->MacString,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ VariableDataSize,\r
+ (VOID *) Ip6VariableData\r
+ );\r
+\r
+Exit:\r
+ FreePool (Ip6VariableData);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param[in] IpSb Ip6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6ClearVariableData (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ ASSERT (IpSb->MacString != NULL);\r
+\r
+ gRT->SetVariable (\r
+ IpSb->MacString,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ FreePool (IpSb->MacString);\r
+ IpSb->MacString = NULL;\r
+}\r
+\r
+/**\r
+ Convert the multibyte field in IP header's byter order.\r
+ In spite of its name, it can also be used to convert from\r
+ host to network byte order.\r
+\r
+ @param[in, out] Head The IP head to convert.\r
+\r
+ @return Point to the converted IP head.\r
+\r
+**/\r
+EFI_IP6_HEADER *\r
+Ip6NtohHead (\r
+ IN OUT EFI_IP6_HEADER *Head\r
+ )\r
+{\r
+ Head->FlowLabelL = NTOHS (Head->FlowLabelL);\r
+ Head->PayloadLength = NTOHS (Head->PayloadLength);\r
+\r
+ return Head;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Common definition and functions for IP6 driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_COMMON_H__\r
+#define __EFI_IP6_COMMON_H__\r
+\r
+#define IP6_LINK_EQUAL(Mac1, Mac2) (CompareMem ((Mac1), (Mac2), sizeof (EFI_MAC_ADDRESS)) == 0)\r
+\r
+//\r
+// Convert the Microsecond to second. IP transmit/receive time is\r
+// in the unit of microsecond. IP ticks once per second.\r
+//\r
+#define IP6_US_TO_SEC(Us) (((Us) + 999999) / 1000000)\r
+\r
+#define IP6_ETHER_PROTO 0x86DD\r
+\r
+#define IP6_MAC_LEN 6\r
+#define IP6_IF_ID_LEN 8\r
+\r
+#define IP6_INTERFACE_LOCAL_SCOPE 1\r
+#define IP6_LINK_LOCAL_SCOPE 2\r
+#define IP6_SITE_LOCAL_SCOPE 5\r
+\r
+#define IP6_INFINIT_LIFETIME 0xFFFFFFFF\r
+\r
+#define IP6_HOP_LIMIT 255\r
+//\r
+// Make it to 64 since all 54 bits are zero.\r
+//\r
+#define IP6_LINK_LOCAL_PREFIX_LENGTH 64\r
+\r
+#define IP6_TIMER_INTERVAL_IN_MS 100\r
+#define IP6_ONE_SECOND_IN_MS 1000\r
+\r
+//\r
+// The packet is received as link level broadcast/multicast/promiscuous.\r
+//\r
+#define IP6_LINK_BROADCAST 0x00000001\r
+#define IP6_LINK_MULTICAST 0x00000002\r
+#define IP6_LINK_PROMISC 0x00000004\r
+\r
+#define IP6_U_BIT 0x02\r
+\r
+typedef enum {\r
+ Ip6Promiscuous = 1,\r
+ Ip6Unicast,\r
+ Ip6Multicast,\r
+ Ip6AnyCast\r
+} IP6_ADDRESS_TYPE;\r
+\r
+typedef struct _IP6_INTERFACE IP6_INTERFACE;\r
+typedef struct _IP6_PROTOCOL IP6_PROTOCOL;\r
+typedef struct _IP6_SERVICE IP6_SERVICE;\r
+typedef struct _IP6_ADDRESS_INFO IP6_ADDRESS_INFO;\r
+\r
+/**\r
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+ of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,\r
+ only the address count is returned.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[out] AddressCount The number of returned addresses.\r
+ @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO.\r
+ This is an optional parameter.\r
+\r
+\r
+ @retval EFI_SUCCESS The address array is successfully build\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiAddressList (\r
+ IN IP6_SERVICE *IpSb,\r
+ OUT UINT32 *AddressCount,\r
+ OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL\r
+ );\r
+\r
+/**\r
+ Generate the multicast addresses identify the group of all IPv6 nodes or IPv6\r
+ routers defined in RFC4291.\r
+\r
+ All Nodes Addresses: FF01::1, FF02::1.\r
+ All Router Addresses: FF01::2, FF02::2, FF05::2.\r
+\r
+ @param[in] Router If TRUE, generate all routers addresses,\r
+ else generate all node addresses.\r
+ @param[in] Scope interface-local(1), link-local(2), or site-local(5)\r
+ @param[out] Ip6Addr The generated multicast address.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_SUCCESS The address is generated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetToAllNodeMulticast (\r
+ IN BOOLEAN Router,\r
+ IN UINT8 Scope,\r
+ OUT EFI_IPv6_ADDRESS *Ip6Addr\r
+ );\r
+\r
+/**\r
+ This function converts MAC address to 64 bits interface ID according to RFC4291\r
+ and returns the interface ID. Currently only 48-bit MAC address is supported by\r
+ this function.\r
+\r
+ @param[in, out] IpSb The IP6 service binding instance.\r
+\r
+ @retval NULL The operation fails.\r
+ @return Pointer to the generated interface ID.\r
+\r
+**/\r
+UINT8 *\r
+Ip6CreateInterfaceID (\r
+ IN OUT IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ This function creates link-local address from interface identifier. The\r
+ interface identifier is normally created from MAC address. It might be manually\r
+ configured by administrator if the link-local address created from MAC address\r
+ is a duplicate address.\r
+\r
+ @param[in, out] IpSb The IP6 service binding instance.\r
+\r
+ @retval NULL If the operation fails.\r
+ @return The generated Link Local address, in network order.\r
+\r
+**/\r
+EFI_IPv6_ADDRESS *\r
+Ip6CreateLinkLocalAddr (\r
+ IN OUT IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ Compute the solicited-node multicast address for an unicast or anycast address,\r
+ by taking the low-order 24 bits of this address, and appending those bits to\r
+ the prefix FF02:0:0:0:0:1:FF00::/104.\r
+\r
+ @param Ip6Addr The unicast or anycast address, in network order.\r
+ @param MulticastAddr The generated solicited-node multicast address,\r
+ in network order.\r
+\r
+**/\r
+VOID\r
+Ip6CreateSNMulticastAddr (\r
+ IN EFI_IPv6_ADDRESS *Ip6Addr,\r
+ OUT EFI_IPv6_ADDRESS *MulticastAddr\r
+ );\r
+\r
+/**\r
+ Check whether the incoming Ipv6 address is a solicited-node multicast address.\r
+\r
+ @param[in] Ip6 Ip6 address, in network order.\r
+\r
+ @retval TRUE Yes, solicited-node multicast address\r
+ @retval FALSE No\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsSNMulticastAddr (\r
+ IN EFI_IPv6_ADDRESS *Ip6\r
+ );\r
+\r
+/**\r
+ Check whether the incoming IPv6 address is one of the maintained address in\r
+ the IP6 service binding instance.\r
+\r
+ @param[in] IpSb Points to a IP6 service binding instance\r
+ @param[in] Address The IP6 address to be checked.\r
+ @param[out] Interface If not NULL, output the IP6 interface which\r
+ maintains the Address.\r
+ @param[out] AddressInfo If not NULL, output the IP6 address information\r
+ of the Address.\r
+\r
+ @retval TRUE Yes, it is one of the maintained addresses.\r
+ @retval FALSE No, it is not one of the maintained addresses.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsOneOfSetAddress (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Address,\r
+ OUT IP6_INTERFACE **Interface OPTIONAL,\r
+ OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL\r
+ );\r
+\r
+/**\r
+ Check whether the incoming MAC address is valid.\r
+\r
+ @param[in] IpSb Points to a IP6 service binding instance.\r
+ @param[in] LinkAddress The MAC address.\r
+\r
+ @retval TRUE Yes, it is valid.\r
+ @retval FALSE No, it is not valid.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidLinkAddress (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_MAC_ADDRESS *LinkAddress\r
+ );\r
+\r
+\r
+/**\r
+ Copy the PrefixLength bits from Src to Dest.\r
+\r
+ @param[out] Dest A pointer to the buffer to copy to.\r
+ @param[in] Src A pointer to the buffer to copy from.\r
+ @param[in] PrefixLength The number of bits to copy.\r
+\r
+**/\r
+VOID\r
+Ip6CopyAddressByPrefix (\r
+ OUT EFI_IPv6_ADDRESS *Dest,\r
+ IN EFI_IPv6_ADDRESS *Src,\r
+ IN UINT8 PrefixLength\r
+ );\r
+\r
+/**\r
+ Insert a node IP6_ADDRESS_INFO to an IP6 interface.\r
+\r
+ @param[in, out] IpIf Points to an IP6 interface.\r
+ @param[in] AddrInfo Points to an IP6_ADDRESS_INFO.\r
+\r
+**/\r
+VOID\r
+Ip6AddAddr (\r
+ IN OUT IP6_INTERFACE *IpIf,\r
+ IN IP6_ADDRESS_INFO *AddrInfo\r
+ );\r
+\r
+/**\r
+ Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.\r
+\r
+ This function removes the matching IPv6 addresses from the address list and\r
+ adjusts the address count of the address list. If IpSb is not NULL, this function\r
+ calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the\r
+ its solicited-node multicast MAC address from the filter list and sends out\r
+ a Multicast Listener Done. If Prefix is NULL, all address in the address list\r
+ will be removed. If Prefix is not NULL, the address that matching the Prefix\r
+ with PrefixLength in the address list will be removed.\r
+\r
+ @param[in] IpSb NULL or points to IP6 service binding instance.\r
+ @param[in, out] AddressList address list array\r
+ @param[in, out] AddressCount the count of addresses in address list array\r
+ @param[in] Prefix NULL or an IPv6 address prefix\r
+ @param[in] PrefixLength the length of Prefix\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength\r
+ cannot be found in address list.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6RemoveAddr (\r
+ IN IP6_SERVICE *IpSb OPTIONAL,\r
+ IN OUT LIST_ENTRY *AddressList,\r
+ IN OUT UINT32 *AddressCount,\r
+ IN EFI_IPv6_ADDRESS *Prefix OPTIONAL,\r
+ IN UINT8 PrefixLength\r
+ );\r
+\r
+/**\r
+ Set the Ip6 variable data.\r
+\r
+ @param[in] IpSb Points to an IP6 service binding instance\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetVariableData (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param[in] IpSb Ip6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6ClearVariableData (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ Get the MAC address for a multicast IP address. Call\r
+ Mnp's McastIpToMac to find the MAC address instead of\r
+ hard-coding the NIC to be Ethernet.\r
+\r
+ @param[in] Mnp The Mnp instance to get the MAC address.\r
+ @param[in] Multicast The multicast IP address to translate.\r
+ @param[out] Mac The buffer to hold the translated address.\r
+\r
+ @retval EFI_SUCCESS The multicast IP is successfully\r
+ translated to a multicast MAC address.\r
+ @retval Other The address is not converted because an error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6GetMulticastMac (\r
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,\r
+ IN EFI_IPv6_ADDRESS *Multicast,\r
+ OUT EFI_MAC_ADDRESS *Mac\r
+ );\r
+\r
+/**\r
+ Convert the multibyte field in IP header's byter order.\r
+ In spite of its name, it can also be used to convert from\r
+ host to network byte order.\r
+\r
+ @param[in, out] Head The IP head to convert.\r
+\r
+ @return Point to the converted IP head.\r
+\r
+**/\r
+EFI_IP6_HEADER *\r
+Ip6NtohHead (\r
+ IN OUT EFI_IP6_HEADER *Head\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ VFR file used by the IP6 configuration component.\r
+\r
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6NvData.h"\r
+\r
+#define EFI_NETWORK_DEVICE_CLASS 0x04\r
+\r
+formset\r
+ guid = IP6_CONFIG_NVDATA_GUID,\r
+ title = STRING_TOKEN(STR_IP6_CONFIG_FORM_TITLE),\r
+ help = STRING_TOKEN(STR_IP6_CONFIG_FORM_HELP),\r
+ class = EFI_NETWORK_DEVICE_CLASS,\r
+ subclass = 0x03,\r
+\r
+ varstore IP6_CONFIG_IFR_NVDATA,\r
+ name = IP6_CONFIG_IFR_NVDATA,\r
+ guid = IP6_CONFIG_NVDATA_GUID;\r
+\r
+ form formid = FORMID_MAIN_FORM,\r
+ title = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE);\r
+\r
+ text\r
+ help = STRING_TOKEN(STR_IP6_INTERFACE_NAME_HELP),\r
+ text = STRING_TOKEN(STR_IP6_INTERFACE_NAME),\r
+ text = STRING_TOKEN(STR_IP6_INTERFACE_NAME_CONTENT);\r
+\r
+ text\r
+ help = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_HELP),\r
+ text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE),\r
+ text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_CONTENT);\r
+\r
+ text\r
+ help = STRING_TOKEN(STR_IP6_MAC_ADDRESS_HELP),\r
+ text = STRING_TOKEN(STR_IP6_MAC_ADDRESS),\r
+ text = STRING_TOKEN(STR_IP6_MAC_ADDRESS_CONTENT);\r
+\r
+ text\r
+ help = STRING_TOKEN(STR_IP6_HOST_ADDRESS_HELP),\r
+ text = STRING_TOKEN(STR_IP6_HOST_ADDRESS),\r
+ text = STRING_TOKEN(STR_NULL);\r
+\r
+ label HOST_ADDRESS_LABEL;\r
+ label LABEL_END;\r
+\r
+ text\r
+ help = STRING_TOKEN(STR_IP6_ROUTE_TABLE_HELP),\r
+ text = STRING_TOKEN(STR_IP6_ROUTE_TABLE),\r
+ text = STRING_TOKEN(STR_NULL);\r
+\r
+ label ROUTE_TABLE_LABEL;\r
+ label LABEL_END;\r
+\r
+ text\r
+ help = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS_HELP),\r
+ text = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS),\r
+ text = STRING_TOKEN(STR_NULL);\r
+\r
+ label GATEWAY_ADDRESS_LABEL;\r
+ label LABEL_END;\r
+\r
+ text\r
+ help = STRING_TOKEN(STR_IP6_DNS_ADDRESS_HELP),\r
+ text = STRING_TOKEN(STR_IP6_DNS_ADDRESS),\r
+ text = STRING_TOKEN(STR_NULL);\r
+\r
+ label DNS_ADDRESS_LABEL;\r
+ label LABEL_END;\r
+\r
+ string varid = IP6_CONFIG_IFR_NVDATA.InterfaceId,\r
+ prompt = STRING_TOKEN(STR_IP6_INTERFACE_ID),\r
+ help = STRING_TOKEN(STR_IP6_INTERFACE_ID_HELP),\r
+ flags = INTERACTIVE,\r
+ key = KEY_INTERFACE_ID,\r
+ minsize = INTERFACE_ID_STR_MIN_SIZE,\r
+ maxsize = INTERFACE_ID_STR_MAX_SIZE,\r
+ endstring;\r
+\r
+ numeric varid = IP6_CONFIG_IFR_NVDATA.DadTransmitCount,\r
+ prompt = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT),\r
+ help = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT_HELP),\r
+ flags = 0,\r
+ minimum = 0,\r
+ maximum = DAD_MAX_TRANSMIT_COUNT,\r
+ step = 0,\r
+ endnumeric;\r
+\r
+ oneof varid = IP6_CONFIG_IFR_NVDATA.Policy,\r
+ prompt = STRING_TOKEN(STR_POLICY_TYPE_PROMPT),\r
+ help = STRING_TOKEN(STR_POLICY_TYPE_HELP),\r
+ option text = STRING_TOKEN(STR_POLICY_TYPE_AUTO), value = IP6_POLICY_AUTO, flags = DEFAULT;\r
+ option text = STRING_TOKEN(STR_POLICY_TYPE_MANUAL), value = IP6_POLICY_MANUAL, flags = 0;\r
+ endoneof;\r
+\r
+ subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+ suppressif ideqval IP6_CONFIG_IFR_NVDATA.Policy == IP6_POLICY_AUTO;\r
+ goto FORMID_MANUAL_CONFIG_FORM,\r
+ prompt = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM),\r
+ help = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM_HELP),\r
+ flags = 0;\r
+ subtitle text = STRING_TOKEN(STR_NULL);\r
+ endif;\r
+\r
+ text\r
+ help = STRING_TOKEN (STR_SAVE_CHANGES_HELP),\r
+ text = STRING_TOKEN (STR_SAVE_CHANGES),\r
+ text = STRING_TOKEN (STR_NULL),\r
+ flags = INTERACTIVE,\r
+ key = KEY_SAVE_CHANGES;\r
+\r
+ endform;\r
+\r
+ form formid = FORMID_MANUAL_CONFIG_FORM,\r
+ title = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM);\r
+\r
+ string varid = IP6_CONFIG_IFR_NVDATA.ManualAddress,\r
+ prompt = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS),\r
+ help = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS_HELP),\r
+ flags = INTERACTIVE,\r
+ key = KEY_MANUAL_ADDRESS,\r
+ minsize = ADDRESS_STR_MIN_SIZE,\r
+ maxsize = ADDRESS_STR_MAX_SIZE,\r
+ endstring;\r
+\r
+ string varid = IP6_CONFIG_IFR_NVDATA.GatewayAddress,\r
+ prompt = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDRESS),\r
+ help = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDR_HELP),\r
+ flags = INTERACTIVE,\r
+ key = KEY_GATEWAY_ADDRESS,\r
+ minsize = ADDRESS_STR_MIN_SIZE,\r
+ maxsize = ADDRESS_STR_MAX_SIZE,\r
+ endstring;\r
+\r
+ string varid = IP6_CONFIG_IFR_NVDATA.DnsAddress,\r
+ prompt = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS),\r
+ help = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS_HELP),\r
+ flags = INTERACTIVE,\r
+ key = KEY_DNS_ADDRESS,\r
+ minsize = ADDRESS_STR_MIN_SIZE,\r
+ maxsize = ADDRESS_STR_MAX_SIZE,\r
+ endstring;\r
+\r
+ goto FORMID_MAIN_FORM,\r
+ prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+ help = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+ flags = INTERACTIVE,\r
+ key = KEY_SAVE_CONFIG_CHANGES;\r
+\r
+ goto FORMID_MAIN_FORM,\r
+ prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+ help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+ flags = INTERACTIVE,\r
+ key = KEY_IGNORE_CONFIG_CHANGES;\r
+\r
+ endform;\r
+\r
+endformset;\r
+\r
--- /dev/null
+/** @file\r
+ The implementation of EFI IPv6 Configuration Protocol.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+LIST_ENTRY mIp6ConfigInstanceList = {&mIp6ConfigInstanceList, &mIp6ConfigInstanceList};\r
+\r
+/**\r
+ The event process routine when the DHCPv6 service binding protocol is installed\r
+ in the system.\r
+\r
+ @param[in] Event Not used.\r
+ @param[in] Context Pointer to the IP6 config instance data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigOnDhcp6SbInstalled (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Update the current policy to NewPolicy. During the transition\r
+ period, the default router list, on-link prefix list, autonomous prefix list\r
+ and address list in all interfaces will be released.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] NewPolicy The new policy to be updated to.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigOnPolicyChanged (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_CONFIG_POLICY NewPolicy\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Entry2;\r
+ LIST_ENTRY *Next;\r
+ IP6_INTERFACE *IpIf;\r
+ IP6_DAD_ENTRY *DadEntry;\r
+\r
+ //\r
+ // Currently there are only two policies: Manual and Automatic. Regardless of\r
+ // what transition is going on, i.e., Manual -> Automatic and Automatic ->\r
+ // Manual, we have to free default router list, on-link prefix list, autonomous\r
+ // prefix list, address list in all the interfaces and destroy any IPv6 child\r
+ // instance whose local IP is neither 0 nor the link-local address.\r
+ //\r
+ Ip6CleanDefaultRouterList (IpSb);\r
+ Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);\r
+ Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);\r
+\r
+ //\r
+ // It's tricky... If the LinkLocal address is O.K., add back the link-local\r
+ // prefix to the on-link prefix table.\r
+ //\r
+ if (IpSb->LinkLocalOk) {\r
+ Ip6CreatePrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ IP6_LINK_LOCAL_PREFIX_LENGTH,\r
+ &IpSb->LinkLocalAddr\r
+ );\r
+ }\r
+\r
+ //\r
+ // All IPv6 children that use global unicast address as it's source address\r
+ // should be destryoed now. The survivers are those use the link-local address\r
+ // or the unspecified address as the source address.\r
+ // TODO: Conduct a check here.\r
+ Ip6RemoveAddr (\r
+ IpSb,\r
+ &IpSb->DefaultInterface->AddressList,\r
+ &IpSb->DefaultInterface->AddressCount,\r
+ NULL,\r
+ 0\r
+ );\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ //\r
+ // remove all pending DAD entries for the global addresses.\r
+ //\r
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {\r
+ DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);\r
+\r
+ if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) {\r
+ //\r
+ // Fail this DAD entry if the address is not link-local.\r
+ //\r
+ Ip6OnDADFinished (FALSE, IpIf, DadEntry);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (NewPolicy == Ip6ConfigPolicyAutomatic) {\r
+ //\r
+ // Set paramters to trigger router solicitation sending in timer handler.\r
+ //\r
+ IpSb->RouterAdvertiseReceived = FALSE;\r
+ IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS;\r
+ //\r
+ // delay 1 second\r
+ //\r
+ IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.\r
+\r
+ @param[in] Instance Pointer to the IP6 config instance data.\r
+ @param[in] OtherInfoOnly If FALSE, get stateful address and other information\r
+ via DHCPv6. Otherwise, only get the other information.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_UNSUPPORTED The DHCP6 driver is not available.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigStartStatefulAutoConfig (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN BOOLEAN OtherInfoOnly\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP6_SERVICE *IpSb;\r
+ EFI_DHCP6_CONFIG_DATA Dhcp6CfgData;\r
+ EFI_DHCP6_PROTOCOL *Dhcp6;\r
+ EFI_DHCP6_PACKET_OPTION *OptList[1];\r
+ UINT16 OptBuf[4];\r
+ EFI_DHCP6_PACKET_OPTION *Oro;\r
+ EFI_DHCP6_RETRANSMISSION InfoReqReXmit;\r
+\r
+ //\r
+ // A host must not invoke stateful address configuration if it is already\r
+ // participating in the statuful protocol as a result of an earlier advertisement.\r
+ //\r
+ if (Instance->Dhcp6Handle != NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+ Instance->OtherInfoOnly = OtherInfoOnly;\r
+\r
+ Status = NetLibCreateServiceChild (\r
+ IpSb->Controller,\r
+ IpSb->Image,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ &Instance->Dhcp6Handle\r
+ );\r
+\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ //\r
+ // No DHCPv6 Service Binding protocol, register a notify.\r
+ //\r
+ if (Instance->Dhcp6SbNotifyEvent == NULL) {\r
+ Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent (\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ TPL_CALLBACK,\r
+ Ip6ConfigOnDhcp6SbInstalled,\r
+ (VOID *) Instance,\r
+ &Instance->Registration\r
+ );\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Instance->Dhcp6SbNotifyEvent != NULL) {\r
+ gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent);\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Instance->Dhcp6Handle,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ (VOID **) &Instance->Dhcp6,\r
+ IpSb->Image,\r
+ IpSb->Controller,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Dhcp6 = Instance->Dhcp6;\r
+ Dhcp6->Configure (Dhcp6, NULL);\r
+\r
+ //\r
+ // Set the exta options to send. Here we only want the option request option\r
+ // with DNS SERVERS.\r
+ //\r
+ Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf;\r
+ Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);\r
+ Oro->OpLen = HTONS (2);\r
+ *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);\r
+ OptList[0] = Oro;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (!OtherInfoOnly) {\r
+ //\r
+ // Get stateful address and other information via DHCPv6.\r
+ //\r
+ Dhcp6CfgData.Dhcp6Callback = NULL;\r
+ Dhcp6CfgData.CallbackContext = NULL;\r
+ Dhcp6CfgData.OptionCount = 1;\r
+ Dhcp6CfgData.OptionList = &OptList[0];\r
+ Dhcp6CfgData.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;\r
+ Dhcp6CfgData.IaDescriptor.IaId = Instance->IaId;\r
+ Dhcp6CfgData.IaInfoEvent = Instance->Dhcp6Event;\r
+ Dhcp6CfgData.ReconfigureAccept = FALSE;\r
+ Dhcp6CfgData.RapidCommit = FALSE;\r
+ Dhcp6CfgData.SolicitRetransmission = NULL;\r
+\r
+ Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ if (IpSb->LinkLocalOk) {\r
+ Status = Dhcp6->Start (Dhcp6);\r
+ } else {\r
+ IpSb->Dhcp6NeedStart = TRUE;\r
+ }\r
+\r
+ }\r
+ } else {\r
+ //\r
+ // Only get other information via DHCPv6, this doesn't require a config\r
+ // action.\r
+ //\r
+ InfoReqReXmit.Irt = 4;\r
+ InfoReqReXmit.Mrc = 64;\r
+ InfoReqReXmit.Mrt = 60;\r
+ InfoReqReXmit.Mrd = 0;\r
+\r
+ if (IpSb->LinkLocalOk) {\r
+ Status = Dhcp6->InfoRequest (\r
+ Dhcp6,\r
+ TRUE,\r
+ Oro,\r
+ 0,\r
+ NULL,\r
+ &InfoReqReXmit,\r
+ Instance->Dhcp6Event,\r
+ Ip6ConfigOnDhcp6Reply,\r
+ Instance\r
+ );\r
+ } else {\r
+ IpSb->Dhcp6NeedInfoRequest = TRUE;\r
+ }\r
+\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Signal the registered event. It is the callback routine for NetMapIterate.\r
+\r
+ @param[in] Map Points to the list of registered event.\r
+ @param[in] Item The registered event.\r
+ @param[in] Arg Not used.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ConfigSignalEvent (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg\r
+ )\r
+{\r
+ gBS->SignalEvent ((EFI_EVENT) Item->Key);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Read the configuration data from variable storage according to the VarName and\r
+ gEfiIp6ConfigProtocolGuid. It checks the integrity of variable data. If the\r
+ data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the\r
+ configuration data to IP6_CONFIG_INSTANCE.\r
+\r
+ @param[in] VarName The pointer to the variable name\r
+ @param[in, out] Instance The pointer to the IP6 config instance data.\r
+\r
+ @retval EFI_NOT_FOUND The variable can not be found or already corrupted.\r
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.\r
+ @retval EFI_SUCCESS The configuration data was retrieved successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigReadConfigData (\r
+ IN CHAR16 *VarName,\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN VarSize;\r
+ IP6_CONFIG_VARIABLE *Variable;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+ UINTN Index;\r
+ IP6_CONFIG_DATA_RECORD DataRecord;\r
+ CHAR8 *Data;\r
+\r
+ //\r
+ // Try to read the configuration variable.\r
+ //\r
+ VarSize = 0;\r
+ Status = gRT->GetVariable (\r
+ VarName,\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ NULL,\r
+ &VarSize,\r
+ NULL\r
+ );\r
+\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ //\r
+ // Allocate buffer and read the config variable.\r
+ //\r
+ Variable = AllocatePool (VarSize);\r
+ if (Variable == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = gRT->GetVariable (\r
+ VarName,\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ NULL,\r
+ &VarSize,\r
+ Variable\r
+ );\r
+ if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) {\r
+ //\r
+ // GetVariable still error or the variable is corrupted.\r
+ // Fall back to the default value.\r
+ //\r
+ FreePool (Variable);\r
+\r
+ //\r
+ // Remove the problematic variable and return EFI_NOT_FOUND, a new\r
+ // variable will be set again.\r
+ //\r
+ gRT->SetVariable (\r
+ VarName,\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ IP6_CONFIG_VARIABLE_ATTRIBUTE,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Get the IAID we use.\r
+ //\r
+ Instance->IaId = Variable->IaId;\r
+\r
+ for (Index = 0; Index < Variable->DataRecordCount; Index++) {\r
+\r
+ CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord));\r
+\r
+ DataItem = &Instance->DataItem[DataRecord.DataType];\r
+ if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) &&\r
+ (DataItem->DataSize != DataRecord.DataSize)\r
+ ) {\r
+ //\r
+ // Perhaps a corrupted data record...\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {\r
+ //\r
+ // This data item has variable length data.\r
+ //\r
+ DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize);\r
+ if (DataItem->Data.Ptr == NULL) {\r
+ //\r
+ // no memory resource\r
+ //\r
+ continue;\r
+ }\r
+ }\r
+\r
+ Data = (CHAR8 *) Variable + DataRecord.Offset;\r
+ CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize);\r
+\r
+ DataItem->DataSize = DataRecord.DataSize;\r
+ DataItem->Status = EFI_SUCCESS;\r
+ }\r
+\r
+ FreePool (Variable);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Write the configuration data from IP6_CONFIG_INSTANCE to variable storage.\r
+\r
+ @param[in] VarName The pointer to the variable name.\r
+ @param[in] Instance The pointer to the IP6 configuration instance data.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.\r
+ @retval EFI_SUCCESS The configuration data is written successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigWriteConfigData (\r
+ IN CHAR16 *VarName,\r
+ IN IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN VarSize;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+ IP6_CONFIG_VARIABLE *Variable;\r
+ IP6_CONFIG_DATA_RECORD *DataRecord;\r
+ CHAR8 *Heap;\r
+ EFI_STATUS Status;\r
+\r
+ VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD);\r
+\r
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+\r
+ DataItem = &Instance->DataItem[Index];\r
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {\r
+\r
+ VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize;\r
+ }\r
+ }\r
+\r
+ Variable = AllocatePool (VarSize);\r
+ if (Variable == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Variable->IaId = Instance->IaId;\r
+ Heap = (CHAR8 *) Variable + VarSize;\r
+ Variable->DataRecordCount = 0;\r
+\r
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+\r
+ DataItem = &Instance->DataItem[Index];\r
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {\r
+\r
+ Heap -= DataItem->DataSize;\r
+ CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize);\r
+\r
+ DataRecord = &Variable->DataRecord[Variable->DataRecordCount];\r
+ DataRecord->DataType = (EFI_IP6_CONFIG_DATA_TYPE) Index;\r
+ DataRecord->DataSize = DataItem->DataSize;\r
+ DataRecord->Offset = (UINT16) (Heap - (CHAR8 *) Variable);\r
+\r
+ Variable->DataRecordCount++;\r
+ }\r
+ }\r
+\r
+ Variable->Checksum = 0;\r
+ Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize);\r
+\r
+ Status = gRT->SetVariable (\r
+ VarName,\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ IP6_CONFIG_VARIABLE_ATTRIBUTE,\r
+ VarSize,\r
+ Variable\r
+ );\r
+\r
+ FreePool (Variable);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The work function for EfiIp6ConfigGetData() to get the interface information\r
+ of the communication device this IP6Config instance manages.\r
+\r
+ @param[in] Instance Pointer to the IP6 config instance data.\r
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in\r
+ bytes, the size of buffer required to store the specified\r
+ configuration data.\r
+ @param[in] Data The data buffer in which the configuration data is returned.\r
+ Ignored if DataSize is ZERO.\r
+\r
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified\r
+ configuration data, and the required size is\r
+ returned in DataSize.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigGetIfInfo (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN OUT UINTN *DataSize,\r
+ IN VOID *Data OPTIONAL\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ UINTN Length;\r
+ IP6_CONFIG_DATA_ITEM *Item;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;\r
+ UINT32 AddressCount;\r
+ UINT32 RouteCount;\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO);\r
+\r
+ //\r
+ // Calculate the required length, add the buffer size for AddressInfo and\r
+ // RouteTable\r
+ //\r
+ Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL);\r
+ Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL);\r
+\r
+ Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE);\r
+\r
+ if (*DataSize < Length) {\r
+ *DataSize = Length;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ //\r
+ // Copy the fixed size part of the interface info.\r
+ //\r
+ Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];\r
+ IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;\r
+ CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO));\r
+\r
+ //\r
+ // AddressInfo\r
+ //\r
+ IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *) (IfInfo + 1);\r
+ Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo);\r
+\r
+ //\r
+ // RouteTable\r
+ //\r
+ IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *) (IfInfo->AddressInfo + IfInfo->AddressInfoCount);\r
+ Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable);\r
+\r
+ if (IfInfo->AddressInfoCount == 0) {\r
+ IfInfo->AddressInfo = NULL;\r
+ }\r
+\r
+ if (IfInfo->RouteCount == 0) {\r
+ IfInfo->RouteTable = NULL;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The work function for EfiIp6ConfigSetData() to set the alternative inteface ID\r
+ for the communication device managed by this IP6Config instance, if the link local\r
+ IPv6 addresses generated from the interface ID based on the default source the\r
+ EFI IPv6 Protocol uses is a duplicate address.\r
+\r
+ @param[in] Instance Pointer to the IP6 configuration instance data.\r
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.\r
+ @param[in] Data The data buffer to set.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type,\r
+ 8 bytes.\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetAltIfId (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ EFI_IP6_CONFIG_INTERFACE_ID *OldIfId;\r
+ EFI_IP6_CONFIG_INTERFACE_ID *NewIfId;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+\r
+ if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];\r
+ OldIfId = DataItem->Data.AltIfId;\r
+ NewIfId = (EFI_IP6_CONFIG_INTERFACE_ID *) Data;\r
+\r
+ CopyMem (OldIfId, NewIfId, DataSize);\r
+ DataItem->Status = EFI_SUCCESS;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The work function for EfiIp6ConfigSetData() to set the general configuration\r
+ policy for the EFI IPv6 network stack that is running on the communication device\r
+ managed by this IP6Config instance. The policy will affect other configuration settings.\r
+\r
+ @param[in] Instance Pointer to the IP6 config instance data.\r
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.\r
+ @param[in] Data The data buffer to set.\r
+\r
+ @retval EFI_INVALID_PARAMETER The to be set policy is invalid.\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.\r
+ @retval EFI_ABORTED The new policy equals the current policy.\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetPolicy (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ EFI_IP6_CONFIG_POLICY NewPolicy;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ NewPolicy = *((EFI_IP6_CONFIG_POLICY *) Data);\r
+\r
+ if (NewPolicy > Ip6ConfigPolicyAutomatic) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (NewPolicy == Instance->Policy) {\r
+\r
+ return EFI_ABORTED;\r
+ } else {\r
+\r
+ if (NewPolicy == Ip6ConfigPolicyAutomatic) {\r
+ //\r
+ // Clean the ManualAddress, Gateway and DnsServers, shrink the variable\r
+ // data size, and fire up all the related events.\r
+ //\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+ if (DataItem->Data.Ptr != NULL) {\r
+ FreePool (DataItem->Data.Ptr);\r
+ }\r
+ DataItem->Data.Ptr = NULL;\r
+ DataItem->DataSize = 0;\r
+ DataItem->Status = EFI_NOT_FOUND;\r
+ NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway];\r
+ if (DataItem->Data.Ptr != NULL) {\r
+ FreePool (DataItem->Data.Ptr);\r
+ }\r
+ DataItem->Data.Ptr = NULL;\r
+ DataItem->DataSize = 0;\r
+ DataItem->Status = EFI_NOT_FOUND;\r
+ NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+ DataItem->Data.Ptr = NULL;\r
+ DataItem->DataSize = 0;\r
+ DataItem->Status = EFI_NOT_FOUND;\r
+ NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);\r
+ } else {\r
+ //\r
+ // The policy is changed from automatic to manual. Stop the DHCPv6 process\r
+ // and destroy the DHCPv6 child.\r
+ //\r
+ if (Instance->Dhcp6Handle != NULL) {\r
+ Ip6ConfigDestroyDhcp6 (Instance);\r
+ }\r
+ }\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ Ip6ConfigOnPolicyChanged (IpSb, NewPolicy);\r
+\r
+ Instance->Policy = NewPolicy;\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ The work function for EfiIp6ConfigSetData() to set the number of consecutive\r
+ Neighbor Solicitation messages sent while performing Duplicate Address Detection\r
+ on a tentative address. A value of ZERO indicates that Duplicate Address Detection\r
+ will not be performed on a tentative address.\r
+\r
+ @param[in] The Instance Pointer to the IP6 config instance data.\r
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.\r
+ @param[in] Data The data buffer to set.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.\r
+ @retval EFI_ABORTED The new transmit count equals the current configuration.\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetDadXmits (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *OldDadXmits;\r
+\r
+ if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits;\r
+\r
+ if ((*(UINT32 *) Data) == OldDadXmits->DupAddrDetectTransmits) {\r
+\r
+ return EFI_ABORTED;\r
+ } else {\r
+\r
+ OldDadXmits->DupAddrDetectTransmits = *((UINT32 *) Data);\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ The callback function for Ip6SetAddr. The prototype is defined\r
+ as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed\r
+ for the manual address set by Ip6ConfigSetMaunualAddress.\r
+\r
+ @param[in] IsDadPassed If TRUE, Duplicate Address Detection passed.\r
+ @param[in] TargetAddress The tentative IPv6 address to be checked.\r
+ @param[in] Context Pointer to the IP6 configuration instance data.\r
+\r
+**/\r
+VOID\r
+Ip6ManualAddrDadCallback (\r
+ IN BOOLEAN IsDadPassed,\r
+ IN EFI_IPv6_ADDRESS *TargetAddress,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+ UINTN Index;\r
+ IP6_CONFIG_DATA_ITEM *Item;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddr;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *PassedAddr;\r
+ UINTN DadPassCount;\r
+ UINTN DadFailCount;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ Instance = (IP6_CONFIG_INSTANCE *) Context;\r
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+ Item = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+ ManualAddr = NULL;\r
+\r
+ for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) {\r
+ //\r
+ // Find the original tag used to place into the NET_MAP.\r
+ //\r
+ ManualAddr = Item->Data.ManualAddress + Index;\r
+ if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+\r
+ if (IsDadPassed) {\r
+ NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL);\r
+ } else {\r
+ NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL);\r
+ }\r
+\r
+ DadPassCount = NetMapGetCount (&Instance->DadPassedMap);\r
+ DadFailCount = NetMapGetCount (&Instance->DadFailedMap);\r
+\r
+ if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) {\r
+ //\r
+ // All addresses have finished the configuration process.\r
+ //\r
+ if (DadFailCount != 0) {\r
+ //\r
+ // There is at least one duplicate address.\r
+ //\r
+ FreePool (Item->Data.Ptr);\r
+\r
+ Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+ if (Item->DataSize == 0) {\r
+ //\r
+ // All failed, bad luck.\r
+ //\r
+ Item->Data.Ptr = NULL;\r
+ Item->Status = EFI_NOT_FOUND;\r
+ } else {\r
+ //\r
+ // Part of addresses are detected to be duplicates, so update the\r
+ // data with those passed.\r
+ //\r
+ PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AllocatePool (Item->DataSize);\r
+ ASSERT (PassedAddr != NULL);\r
+\r
+ Item->Data.Ptr = PassedAddr;\r
+ Item->Status = EFI_SUCCESS;\r
+\r
+ while (!NetMapIsEmpty (&Instance->DadPassedMap)) {\r
+ ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) NetMapRemoveHead (&Instance->DadPassedMap, NULL);\r
+ CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+\r
+ PassedAddr++;\r
+ }\r
+\r
+ ASSERT ((UINTN) PassedAddr - (UINTN) Item->Data.Ptr == Item->DataSize);\r
+ }\r
+ } else {\r
+ //\r
+ // All addresses are valid.\r
+ //\r
+ Item->Status = EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Remove the tags we put in the NET_MAPs.\r
+ //\r
+ while (!NetMapIsEmpty (&Instance->DadFailedMap)) {\r
+ NetMapRemoveHead (&Instance->DadFailedMap, NULL);\r
+ }\r
+\r
+ while (!NetMapIsEmpty (&Instance->DadPassedMap)) {\r
+ NetMapRemoveHead (&Instance->DadPassedMap, NULL);\r
+ }\r
+\r
+ //\r
+ // Signal the waiting events.\r
+ //\r
+ NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ Ip6ConfigWriteConfigData (IpSb->MacString, Instance);\r
+ }\r
+}\r
+\r
+/**\r
+ The work function for EfiIp6ConfigSetData() to set the station addresses manually\r
+ for the EFI IPv6 network stack. It is only configurable when the policy is\r
+ Ip6ConfigPolicyManual.\r
+\r
+ @param[in] Instance Pointer to the IP6 configuration instance data.\r
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.\r
+ @param[in] Data The data buffer to set.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.\r
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set\r
+ under the current policy.\r
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.\r
+ @retval EFI_NOT_READY An asynchrous process is invoked to set the specified\r
+ configuration data, and the process is not finished.\r
+ @retval EFI_ABORTED The manual addresses to be set equal current\r
+ configuration.\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetMaunualAddress (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *NewAddress;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *TmpAddress;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+ UINTN NewAddressCount;\r
+ UINTN Index1;\r
+ UINTN Index2;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_ADDRESS_INFO *CurrentAddrInfo;\r
+ IP6_ADDRESS_INFO *Copy;\r
+ LIST_ENTRY CurrentSourceList;\r
+ UINT32 CurrentSourceCount;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Entry2;\r
+ IP6_INTERFACE *IpIf;\r
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsUpdated;\r
+\r
+ ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY);\r
+\r
+ if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ if (Instance->Policy != Ip6ConfigPolicyManual) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+ NewAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) Data;\r
+\r
+ for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {\r
+\r
+ if (NetIp6IsLinkLocalAddr (&NewAddress->Address) ||\r
+ !NetIp6IsValidUnicast (&NewAddress->Address) ||\r
+ (NewAddress->PrefixLength > 128)\r
+ ) {\r
+ //\r
+ // make sure the IPv6 address is unicast and not link-local address &&\r
+ // the prefix length is valid.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TmpAddress = NewAddress + 1;\r
+ for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) {\r
+ //\r
+ // Any two addresses in the array can't be equal.\r
+ //\r
+ if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) {\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+ //\r
+ // Build the current source address list.\r
+ //\r
+ InitializeListHead (&CurrentSourceList);\r
+ CurrentSourceCount = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+ NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {\r
+ CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+ Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo);\r
+ if (Copy == NULL) {\r
+ break;\r
+ }\r
+\r
+ InsertTailList (&CurrentSourceList, &Copy->Link);\r
+ CurrentSourceCount++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Update the value... a long journey starts\r
+ //\r
+ NewAddress = AllocateCopyPool (DataSize, Data);\r
+ if (NewAddress == NULL) {\r
+ Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0);\r
+\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Store the new data, and init the DataItem status to EFI_NOT_READY because\r
+ // we may have an asynchronous configuration process.\r
+ //\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+ if (DataItem->Data.Ptr != NULL) {\r
+ FreePool (DataItem->Data.Ptr);\r
+ }\r
+ DataItem->Data.Ptr = NewAddress;\r
+ DataItem->DataSize = DataSize;\r
+ DataItem->Status = EFI_NOT_READY;\r
+\r
+ //\r
+ // Trigger DAD, it's an asynchronous process.\r
+ //\r
+ IsUpdated = FALSE;\r
+\r
+ for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {\r
+ if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) {\r
+ ASSERT (CurrentAddrInfo != NULL);\r
+ //\r
+ // Remove this already existing source address from the CurrentSourceList\r
+ // built before.\r
+ //\r
+ Ip6RemoveAddr (\r
+ NULL,\r
+ &CurrentSourceList,\r
+ &CurrentSourceCount,\r
+ &CurrentAddrInfo->Address,\r
+ 128\r
+ );\r
+\r
+ //\r
+ // This manual address is already in use, see whether prefix length is changed.\r
+ //\r
+ if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) {\r
+ //\r
+ // Remove the on-link prefix table, the route entry will be removed\r
+ // implicitly.\r
+ //\r
+ PrefixEntry = Ip6FindPrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ CurrentAddrInfo->PrefixLength,\r
+ &CurrentAddrInfo->Address\r
+ );\r
+ if (PrefixEntry != NULL) {\r
+ Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);\r
+ }\r
+\r
+ //\r
+ // Save the prefix length.\r
+ //\r
+ CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength;\r
+ IsUpdated = TRUE;\r
+ }\r
+\r
+ //\r
+ // create a new on-link prefix entry.\r
+ //\r
+ PrefixEntry = Ip6FindPrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ NewAddress->PrefixLength,\r
+ &NewAddress->Address\r
+ );\r
+ if (PrefixEntry == NULL) {\r
+ Ip6CreatePrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ NewAddress->PrefixLength,\r
+ &NewAddress->Address\r
+ );\r
+ }\r
+\r
+ CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast;\r
+ //\r
+ // Artificially mark this address passed DAD be'coz it is already in use.\r
+ //\r
+ Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance);\r
+ } else {\r
+ //\r
+ // A new address.\r
+ //\r
+ IsUpdated = TRUE;\r
+\r
+ //\r
+ // Set the new address, this will trigger DAD and activate the address if\r
+ // DAD succeeds.\r
+ //\r
+ Ip6SetAddress (\r
+ IpSb->DefaultInterface,\r
+ &NewAddress->Address,\r
+ NewAddress->IsAnycast,\r
+ NewAddress->PrefixLength,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ Ip6ManualAddrDadCallback,\r
+ Instance\r
+ );\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check the CurrentSourceList, it now contains those addresses currently in\r
+ // use and will be removed.\r
+ //\r
+ IpIf = IpSb->DefaultInterface;\r
+\r
+ while (!IsListEmpty (&CurrentSourceList)) {\r
+ IsUpdated = TRUE;\r
+\r
+ CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link);\r
+\r
+ //\r
+ // This local address is going to be removed, the IP instances that are\r
+ // currently using it will be destroyed.\r
+ //\r
+ Ip6RemoveAddr (\r
+ IpSb,\r
+ &IpIf->AddressList,\r
+ &IpIf->AddressCount,\r
+ &CurrentAddrInfo->Address,\r
+ 128\r
+ );\r
+\r
+ //\r
+ // Remove the on-link prefix table, the route entry will be removed\r
+ // implicitly.\r
+ //\r
+ PrefixEntry = Ip6FindPrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ CurrentAddrInfo->PrefixLength,\r
+ &CurrentAddrInfo->Address\r
+ );\r
+ if (PrefixEntry != NULL) {\r
+ Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);\r
+ }\r
+\r
+ RemoveEntryList (&CurrentAddrInfo->Link);\r
+ FreePool (CurrentAddrInfo);\r
+ }\r
+\r
+ if (IsUpdated) {\r
+ if (DataItem->Status == EFI_NOT_READY) {\r
+ //\r
+ // If DAD is disabled on this interface, the configuration process is\r
+ // actually synchronous, and the data item's status will be changed to\r
+ // the final status before we reach here, just check it.\r
+ //\r
+ Status = EFI_NOT_READY;\r
+ } else {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ } else {\r
+ //\r
+ // No update is taken, reset the status to success and return EFI_ABORTED.\r
+ //\r
+ DataItem->Status = EFI_SUCCESS;\r
+ Status = EFI_ABORTED;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The work function for EfiIp6ConfigSetData() to set the gateway addresses manually\r
+ for the EFI IPv6 network stack that is running on the communication device that\r
+ this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is\r
+ Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses.\r
+\r
+ @param[in] Instance The pointer to the IP6 config instance data.\r
+ @param[in] DataSize The size of the buffer pointed to by Data in bytes.\r
+ @param[in] Data The data buffer to set. This points to an array of\r
+ EFI_IPv6_ADDRESS instances.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.\r
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set\r
+ under the current policy.\r
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to complete the operation.\r
+ @retval EFI_ABORTED The manual gateway addresses to be set equal the\r
+ current configuration.\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetGateway (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ UINTN Index1;\r
+ UINTN Index2;\r
+ EFI_IPv6_ADDRESS *OldGateway;\r
+ EFI_IPv6_ADDRESS *NewGateway;\r
+ UINTN OldGatewayCount;\r
+ UINTN NewGatewayCount;\r
+ IP6_CONFIG_DATA_ITEM *Item;\r
+ BOOLEAN OneRemoved;\r
+ BOOLEAN OneAdded;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+ VOID *Tmp;\r
+\r
+ if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ if (Instance->Policy != Ip6ConfigPolicyManual) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ NewGateway = (EFI_IPv6_ADDRESS *) Data;\r
+ NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+ for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {\r
+\r
+ if (!NetIp6IsValidUnicast (NewGateway + Index1)) {\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) {\r
+ if (EFI_IP6_EQUAL (NewGateway + Index1, NewGateway + Index2)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ Item = &Instance->DataItem[Ip6ConfigDataTypeGateway];\r
+ OldGateway = Item->Data.Gateway;\r
+ OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+ OneRemoved = FALSE;\r
+ OneAdded = FALSE;\r
+\r
+ if (NewGatewayCount != OldGatewayCount) {\r
+ Tmp = AllocatePool (DataSize);\r
+ if (Tmp == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ } else {\r
+ Tmp = NULL;\r
+ }\r
+\r
+ for (Index1 = 0; Index1 < OldGatewayCount; Index1++) {\r
+ //\r
+ // Find the gateways that are no long in the new setting and remove them.\r
+ //\r
+ for (Index2 = 0; Index2 < NewGatewayCount; Index2++) {\r
+ if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) {\r
+ OneRemoved = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Index2 == NewGatewayCount) {\r
+ //\r
+ // Remove this default router.\r
+ //\r
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1);\r
+ if (DefaultRouter != NULL) {\r
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+ }\r
+ }\r
+ }\r
+\r
+ for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {\r
+\r
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1);\r
+ if (DefaultRouter == NULL) {\r
+ Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME);\r
+ OneAdded = TRUE;\r
+ }\r
+ }\r
+\r
+ if (!OneRemoved && !OneAdded) {\r
+ Item->Status = EFI_SUCCESS;\r
+ return EFI_ABORTED;\r
+ } else {\r
+\r
+ if (Tmp != NULL) {\r
+ if (Item->Data.Ptr != NULL) {\r
+ FreePool (Item->Data.Ptr);\r
+ }\r
+ Item->Data.Ptr = Tmp;\r
+ }\r
+\r
+ CopyMem (Item->Data.Ptr, Data, DataSize);\r
+ Item->DataSize = DataSize;\r
+ Item->Status = EFI_SUCCESS;\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ The work function for EfiIp6ConfigSetData() to set the DNS server list for the\r
+ EFI IPv6 network stack running on the communication device that this EFI IPv6\r
+ Configuration Protocol manages. It is not configurable when the policy is\r
+ Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses.\r
+\r
+ @param[in] Instance The pointer to the IP6 config instance data.\r
+ @param[in] DataSize The size of the buffer pointed to by Data in bytes.\r
+ @param[in] Data The data buffer to set, points to an array of\r
+ EFI_IPv6_ADDRESS instances.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.\r
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set\r
+ under the current policy.\r
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.\r
+ @retval EFI_ABORTED The DNS server addresses to be set equal the current\r
+ configuration.\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigSetDnsServer (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ UINTN OldIndex;\r
+ UINTN NewIndex;\r
+ UINTN Index1;\r
+ EFI_IPv6_ADDRESS *OldDns;\r
+ EFI_IPv6_ADDRESS *NewDns;\r
+ UINTN OldDnsCount;\r
+ UINTN NewDnsCount;\r
+ IP6_CONFIG_DATA_ITEM *Item;\r
+ BOOLEAN OneAdded;\r
+ VOID *Tmp;\r
+\r
+ if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ if (Instance->Policy != Ip6ConfigPolicyManual) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+ NewDns = (EFI_IPv6_ADDRESS *) Data;\r
+ OldDns = Item->Data.DnsServers;\r
+ NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+ OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);\r
+ OneAdded = FALSE;\r
+\r
+ if (NewDnsCount != OldDnsCount) {\r
+ Tmp = AllocatePool (DataSize);\r
+ if (Tmp == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ } else {\r
+ Tmp = NULL;\r
+ }\r
+\r
+ for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) {\r
+\r
+ if (!NetIp6IsValidUnicast (NewDns + NewIndex)) {\r
+ //\r
+ // The dns server address must be unicast.\r
+ //\r
+ FreePool (Tmp);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {\r
+ if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) {\r
+ FreePool (Tmp);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if (OneAdded) {\r
+ //\r
+ // If any address in the new setting is not in the old settings, skip the\r
+ // comparision below.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) {\r
+ if (EFI_IP6_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) {\r
+ //\r
+ // If found break out.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (OldIndex == OldDnsCount) {\r
+ OneAdded = TRUE;\r
+ }\r
+ }\r
+\r
+ if (!OneAdded && (DataSize == Item->DataSize)) {\r
+ //\r
+ // No new item is added and the size is the same.\r
+ //\r
+ Item->Status = EFI_SUCCESS;\r
+ return EFI_ABORTED;\r
+ } else {\r
+ if (Tmp != NULL) {\r
+ if (Item->Data.Ptr != NULL) {\r
+ FreePool (Item->Data.Ptr);\r
+ }\r
+ Item->Data.Ptr = Tmp;\r
+ }\r
+\r
+ CopyMem (Item->Data.Ptr, Data, DataSize);\r
+ Item->DataSize = DataSize;\r
+ Item->Status = EFI_SUCCESS;\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ Generate the operational state of the interface this IP6 config instance manages\r
+ and output in EFI_IP6_CONFIG_INTERFACE_INFO.\r
+\r
+ @param[in] IpSb The pointer to the IP6 service binding instance.\r
+ @param[out] IfInfo The pointer to the IP6 configuration interface information structure.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigInitIfInfo (\r
+ IN IP6_SERVICE *IpSb,\r
+ OUT EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo\r
+ )\r
+{\r
+ IfInfo->Name[0] = L'e';\r
+ IfInfo->Name[1] = L't';\r
+ IfInfo->Name[2] = L'h';\r
+ IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex);\r
+ IfInfo->Name[4] = 0;\r
+\r
+ IfInfo->IfType = IpSb->SnpMode.IfType;\r
+ IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize;\r
+ CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize);\r
+}\r
+\r
+/**\r
+ Parse DHCPv6 reply packet to get the DNS server list.\r
+ It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event.\r
+\r
+ @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL instance.\r
+ @param[in, out] Instance The pointer to the IP6 configuration instance data.\r
+ @param[in] Reply The pointer to the DHCPv6 reply packet.\r
+\r
+ @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet.\r
+ @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or\r
+ the DNS server address is not valid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigParseDhcpReply (\r
+ IN EFI_DHCP6_PROTOCOL *Dhcp6,\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance,\r
+ IN EFI_DHCP6_PACKET *Reply\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 OptCount;\r
+ EFI_DHCP6_PACKET_OPTION **OptList;\r
+ UINT16 OpCode;\r
+ UINT16 Length;\r
+ UINTN Index;\r
+ UINTN Index2;\r
+ EFI_IPv6_ADDRESS *DnsServer;\r
+ IP6_CONFIG_DATA_ITEM *Item;\r
+\r
+ //\r
+ // A DHCPv6 reply packet is received as the response to our InfoRequest\r
+ // packet.\r
+ //\r
+ OptCount = 0;\r
+ Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL);\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *));\r
+ if (OptList == NULL) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_NOT_READY;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ for (Index = 0; Index < OptCount; Index++) {\r
+ //\r
+ // Go through all the options to check the ones we are interested in.\r
+ // The OpCode and Length are in network byte-order and may not be naturally\r
+ // aligned.\r
+ //\r
+ CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode));\r
+ OpCode = NTOHS (OpCode);\r
+\r
+ if (OpCode == IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS) {\r
+ CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length));\r
+ Length = NTOHS (Length);\r
+\r
+ if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) {\r
+ //\r
+ // The length should be a multiple of 16 bytes.\r
+ //\r
+ Status = EFI_NOT_READY;\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Validate the DnsServers: whether they are unicast addresses.\r
+ //\r
+ DnsServer = (EFI_IPv6_ADDRESS *) OptList[Index]->Data;\r
+ for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) {\r
+ if (!NetIp6IsValidUnicast (DnsServer)) {\r
+ Status = EFI_NOT_READY;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ DnsServer++;\r
+ }\r
+\r
+ Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+\r
+ if (Item->DataSize != Length) {\r
+ if (Item->Data.Ptr != NULL) {\r
+ FreePool (Item->Data.Ptr);\r
+ }\r
+\r
+ Item->Data.Ptr = AllocatePool (Length);\r
+ ASSERT (Item->Data.Ptr != NULL);\r
+ }\r
+\r
+ CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length);\r
+ Item->DataSize = Length;\r
+ Item->Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Signal the waiting events.\r
+ //\r
+ NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ FreePool (OptList);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The callback function for Ip6SetAddr. The prototype is defined\r
+ as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed\r
+ on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event().\r
+\r
+ @param[in] IsDadPassed If TRUE, Duplicate Address Detection passes.\r
+ @param[in] TargetAddress The tentative IPv6 address to be checked.\r
+ @param[in] Context Pointer to the IP6 configuration instance data.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigSetStatefulAddrCallback (\r
+ IN BOOLEAN IsDadPassed,\r
+ IN EFI_IPv6_ADDRESS *TargetAddress,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+\r
+ Instance = (IP6_CONFIG_INSTANCE *) Context;\r
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+ //\r
+ // We should record the addresses that fail the DAD, and DECLINE them.\r
+ //\r
+ if (IsDadPassed) {\r
+ //\r
+ // Decrease the count, no interests in those passed DAD.\r
+ //\r
+ if (Instance->FailedIaAddressCount > 0 ) {\r
+ Instance->FailedIaAddressCount--;\r
+ }\r
+ } else {\r
+ //\r
+ // Record it.\r
+ //\r
+ IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress);\r
+ Instance->DeclineAddressCount++;\r
+ }\r
+\r
+ if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) {\r
+ //\r
+ // The checking on all addresses are finished.\r
+ //\r
+ if (Instance->DeclineAddressCount != 0) {\r
+ //\r
+ // Decline those duplicates.\r
+ //\r
+ Instance->Dhcp6->Decline (\r
+ Instance->Dhcp6,\r
+ Instance->DeclineAddressCount,\r
+ Instance->DeclineAddress\r
+ );\r
+ }\r
+\r
+ if (Instance->DeclineAddress != NULL) {\r
+ FreePool (Instance->DeclineAddress);\r
+ }\r
+ Instance->DeclineAddress = NULL;\r
+ Instance->DeclineAddressCount = 0;\r
+ }\r
+}\r
+\r
+/**\r
+ The event handle routine when DHCPv6 process is finished or is updated.\r
+\r
+ @param[in] Event Not used.\r
+ @param[in] Context The pointer to the IP6 configuration instance data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigOnDhcp6Event (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+ EFI_DHCP6_PROTOCOL *Dhcp6;\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_MODE_DATA Dhcp6ModeData;\r
+ EFI_DHCP6_IA *Ia;\r
+ EFI_DHCP6_IA_ADDRESS *IaAddr;\r
+ UINT32 Index;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_ADDRESS_INFO *AddrInfo;\r
+ IP6_INTERFACE *IpIf;\r
+\r
+ Instance = (IP6_CONFIG_INSTANCE *) Context;\r
+\r
+ if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) {\r
+ //\r
+ // IPv6 is not operating in the automatic policy now or\r
+ // the DHCPv6 information request message exchange is aborted.\r
+ //\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // The stateful address autoconfiguration is done or updated.\r
+ //\r
+ Dhcp6 = Instance->Dhcp6;\r
+\r
+ Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ return ;\r
+ }\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ IpIf = IpSb->DefaultInterface;\r
+ Ia = Dhcp6ModeData.Ia;\r
+ IaAddr = Ia->IaAddress;\r
+\r
+ if (Instance->DeclineAddress != NULL) {\r
+ FreePool (Instance->DeclineAddress);\r
+ }\r
+\r
+ Instance->DeclineAddress = (EFI_IPv6_ADDRESS *) AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS));\r
+ if (Instance->DeclineAddress == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Instance->FailedIaAddressCount = Ia->IaAddressCount;\r
+ Instance->DeclineAddressCount = 0;\r
+\r
+ for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) {\r
+ if (Ia->IaAddress[Index].ValidLifetime != 0 && Ia->State == Dhcp6Bound) {\r
+ //\r
+ // Set this address, either it's a new address or with updated lifetimes.\r
+ // An appropriate prefix length will be set.\r
+ //\r
+ Ip6SetAddress (\r
+ IpIf,\r
+ &IaAddr->IpAddress,\r
+ FALSE,\r
+ 0,\r
+ IaAddr->ValidLifetime,\r
+ IaAddr->PreferredLifetime,\r
+ Ip6ConfigSetStatefulAddrCallback,\r
+ Instance\r
+ );\r
+ } else {\r
+ //\r
+ // discard this address, artificially decrease the count as if this address\r
+ // passed DAD.\r
+ //\r
+ if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) {\r
+ ASSERT (AddrInfo != NULL);\r
+ Ip6RemoveAddr (\r
+ IpSb,\r
+ &IpIf->AddressList,\r
+ &IpIf->AddressCount,\r
+ &AddrInfo->Address,\r
+ AddrInfo->PrefixLength\r
+ );\r
+ }\r
+\r
+ if (Instance->FailedIaAddressCount > 0) {\r
+ Instance->FailedIaAddressCount--;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Parse the Reply packet to get the options we need.\r
+ //\r
+ if (Dhcp6ModeData.Ia->ReplyPacket != NULL) {\r
+ Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket);\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ FreePool (Dhcp6ModeData.ClientId);\r
+ FreePool (Dhcp6ModeData.Ia);\r
+}\r
+\r
+/**\r
+ The event process routine when the DHCPv6 server is answered with a reply packet\r
+ for an information request.\r
+\r
+ @param[in] This Points to the EFI_DHCP6_PROTOCOL.\r
+ @param[in] Context The pointer to the IP6 configuration instance data.\r
+ @param[in] Packet The DHCPv6 reply packet.\r
+\r
+ @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet.\r
+ @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or\r
+ the DNS server address is not valid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ConfigOnDhcp6Reply (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN VOID *Context,\r
+ IN EFI_DHCP6_PACKET *Packet\r
+ )\r
+{\r
+ return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *) Context, Packet);\r
+}\r
+\r
+/**\r
+ The event process routine when the DHCPv6 service binding protocol is installed\r
+ in the system.\r
+\r
+ @param[in] Event Not used.\r
+ @param[in] Context The pointer to the IP6 config instance data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigOnDhcp6SbInstalled (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+\r
+ Instance = (IP6_CONFIG_INSTANCE *) Context;\r
+\r
+ if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) {\r
+ //\r
+ // The DHCP6 child is already created or the policy is no longer AUTOMATIC.\r
+ //\r
+ return ;\r
+ }\r
+\r
+ Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly);\r
+}\r
+\r
+/**\r
+ Set the configuration for the EFI IPv6 network stack running on the communication\r
+ device this EFI IPv6 Configuration Protocol instance manages.\r
+\r
+ This function is used to set the configuration data of type DataType for the EFI\r
+ IPv6 network stack that is running on the communication device that this EFI IPv6\r
+ Configuration Protocol instance manages.\r
+\r
+ DataSize is used to calculate the count of structure instances in the Data for\r
+ a DataType in which multiple structure instances are allowed.\r
+\r
+ This function is always non-blocking. When setting some type of configuration data,\r
+ an asynchronous process is invoked to check the correctness of the data, such as\r
+ performing Duplicate Address Detection on the manually set local IPv6 addresses.\r
+ EFI_NOT_READY is returned immediately to indicate that such an asynchronous process\r
+ is invoked, and the process is not finished yet. The caller wanting to get the result\r
+ of the asynchronous process is required to call RegisterDataNotify() to register an\r
+ event on the specified configuration data. Once the event is signaled, the caller\r
+ can call GetData() to obtain the configuration data and know the result.\r
+ For other types of configuration data that do not require an asynchronous configuration\r
+ process, the result of the operation is immediately returned.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to set.\r
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.\r
+ @param[in] Data The data buffer to set. The type of the data buffer is\r
+ associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - Data is NULL.\r
+ - One or more fields in Data do not match the requirement of the\r
+ data type indicated by DataType.\r
+ @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified\r
+ configuration data cannot be set under the current policy.\r
+ @retval EFI_ACCESS_DENIED Another set operation on the specified configuration\r
+ data is already in process.\r
+ @retval EFI_NOT_READY An asynchronous process was invoked to set the specified\r
+ configuration data, and the process is not finished yet.\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type\r
+ indicated by DataType.\r
+ @retval EFI_UNSUPPORTED This DataType is not supported.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigSetData (\r
+ IN EFI_IP6_CONFIG_PROTOCOL *This,\r
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ if ((This == NULL) || (Data == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Status = Instance->DataItem[DataType].Status;\r
+ if (Status != EFI_NOT_READY) {\r
+\r
+ if (Instance->DataItem[DataType].SetData == NULL) {\r
+ //\r
+ // This type of data is readonly.\r
+ //\r
+ Status = EFI_WRITE_PROTECTED;\r
+ } else {\r
+\r
+ Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Fire up the events registered with this type of data.\r
+ //\r
+ NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);\r
+ Ip6ConfigWriteConfigData (IpSb->MacString, Instance);\r
+ } else if (Status == EFI_ABORTED) {\r
+ //\r
+ // The SetData is aborted because the data to set is the same with\r
+ // the one maintained.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // Another asynchornous process is on the way.\r
+ //\r
+ Status = EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get the configuration data for the EFI IPv6 network stack running on the communication\r
+ device that this EFI IPv6 Configuration Protocol instance manages.\r
+\r
+ This function returns the configuration data of type DataType for the EFI IPv6 network\r
+ stack running on the communication device that this EFI IPv6 Configuration Protocol instance\r
+ manages.\r
+\r
+ The caller is responsible for allocating the buffer used to return the specified\r
+ configuration data. The required size will be returned to the caller if the size of\r
+ the buffer is too small.\r
+\r
+ EFI_NOT_READY is returned if the specified configuration data is not ready due to an\r
+ asynchronous configuration process already in progress. The caller can call RegisterDataNotify()\r
+ to register an event on the specified configuration data. Once the asynchronous configuration\r
+ process is finished, the event will be signaled, and a subsequent GetData() call will return\r
+ the specified configuration data.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to get.\r
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in bytes, the\r
+ size of buffer required to store the specified configuration data.\r
+ @param[in] Data The data buffer in which the configuration data is returned. The\r
+ type of the data buffer is associated with the DataType.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+ - This is NULL.\r
+ - DataSize is NULL.\r
+ - Data is NULL if *DataSize is not zero.\r
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data,\r
+ and the required size is returned in DataSize.\r
+ @retval EFI_NOT_READY The specified configuration data is not ready due to an\r
+ asynchronous configuration process already in progress.\r
+ @retval EFI_NOT_FOUND The specified configuration data is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigGetData (\r
+ IN EFI_IP6_CONFIG_PROTOCOL *This,\r
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,\r
+ IN OUT UINTN *DataSize,\r
+ IN VOID *Data OPTIONAL\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+\r
+ if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+ DataItem = &Instance->DataItem[DataType];\r
+\r
+ Status = Instance->DataItem[DataType].Status;\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ if (DataItem->GetData != NULL) {\r
+\r
+ Status = DataItem->GetData (Instance, DataSize, Data);\r
+ } else if (*DataSize < Instance->DataItem[DataType].DataSize) {\r
+ //\r
+ // Update the buffer length.\r
+ //\r
+ *DataSize = Instance->DataItem[DataType].DataSize;\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ } else {\r
+\r
+ *DataSize = Instance->DataItem[DataType].DataSize;\r
+ CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize);\r
+ }\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Register an event that is signaled whenever a configuration process on the specified\r
+ configuration data is done.\r
+\r
+ This function registers an event that is to be signaled whenever a configuration\r
+ process on the specified configuration data is performed. An event can be registered\r
+ for a different DataType simultaneously. The caller is responsible for determining\r
+ which type of configuration data causes the signaling of the event in such an event.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to unregister the event for.\r
+ @param[in] Event The event to register.\r
+\r
+ @retval EFI_SUCCESS The notification event for the specified configuration data is\r
+ registered.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+ @retval EFI_UNSUPPORTED The configuration data type specified by DataType is not\r
+ supported.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_ACCESS_DENIED The Event is already registered for the DataType.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigRegisterDataNotify (\r
+ IN EFI_IP6_CONFIG_PROTOCOL *This,\r
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_EVENT Event\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+ NET_MAP *EventMap;\r
+ NET_MAP_ITEM *Item;\r
+\r
+ if ((This == NULL) || (Event == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+ EventMap = &Instance->DataItem[DataType].EventMap;\r
+\r
+ //\r
+ // Check whether this event is already registered for this DataType.\r
+ //\r
+ Item = NetMapFindKey (EventMap, Event);\r
+ if (Item == NULL) {\r
+\r
+ Status = NetMapInsertTail (EventMap, Event, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ } else {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Remove a previously registered event for the specified configuration data.\r
+\r
+ @param This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+ @param DataType The type of data to remove from the previously\r
+ registered event.\r
+ @param Event The event to be unregistered.\r
+\r
+ @retval EFI_SUCCESS The event registered for the specified\r
+ configuration data was removed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+ @retval EFI_NOT_FOUND The Event has not been registered for the\r
+ specified DataType.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6ConfigUnregisterDataNotify (\r
+ IN EFI_IP6_CONFIG_PROTOCOL *This,\r
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_EVENT Event\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+ NET_MAP_ITEM *Item;\r
+\r
+ if ((This == NULL) || (Event == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (DataType >= Ip6ConfigDataTypeMaximum) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);\r
+\r
+ Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event);\r
+ if (Item != NULL) {\r
+\r
+ NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL);\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Initialize an IP6_CONFIG_INSTANCE.\r
+\r
+ @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.\r
+ @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigInitInstance (\r
+ OUT IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_CONFIG_INSTANCE *TmpInstance;\r
+ LIST_ENTRY *Entry;\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ UINT16 IfIndex;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+ Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE;\r
+\r
+ //\r
+ // Determine the index of this interface.\r
+ //\r
+ IfIndex = 0;\r
+ NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) {\r
+ TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+ if (TmpInstance->IfIndex > IfIndex) {\r
+ //\r
+ // There is a sequence hole because some interface is down.\r
+ //\r
+ break;\r
+ }\r
+\r
+ IfIndex++;\r
+ }\r
+\r
+ Instance->IfIndex = IfIndex;\r
+ NetListInsertBefore (Entry, &Instance->Link);\r
+\r
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+ //\r
+ // Initialize the event map for each data item.\r
+ //\r
+ NetMapInit (&Instance->DataItem[Index].EventMap);\r
+ }\r
+\r
+ //\r
+ // Initialize the NET_MAPs used for DAD on manually configured source addresses.\r
+ //\r
+ NetMapInit (&Instance->DadFailedMap);\r
+ NetMapInit (&Instance->DadPassedMap);\r
+\r
+ //\r
+ // Initialize each data type: associate storage and set data size for the\r
+ // fixed size data types, hook the SetData function, set the data attribute.\r
+ //\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];\r
+ DataItem->GetData = Ip6ConfigGetIfInfo;\r
+ DataItem->Data.Ptr = &Instance->InterfaceInfo;\r
+ DataItem->DataSize = sizeof (Instance->InterfaceInfo);\r
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE);\r
+ Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo);\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];\r
+ DataItem->SetData = Ip6ConfigSetAltIfId;\r
+ DataItem->Data.Ptr = &Instance->AltIfId;\r
+ DataItem->DataSize = sizeof (Instance->AltIfId);\r
+ DataItem->Status = EFI_NOT_FOUND;\r
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypePolicy];\r
+ DataItem->SetData = Ip6ConfigSetPolicy;\r
+ DataItem->Data.Ptr = &Instance->Policy;\r
+ DataItem->DataSize = sizeof (Instance->Policy);\r
+ Instance->Policy = Ip6ConfigPolicyAutomatic;\r
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits];\r
+ DataItem->SetData = Ip6ConfigSetDadXmits;\r
+ DataItem->Data.Ptr = &Instance->DadXmits;\r
+ DataItem->DataSize = sizeof (Instance->DadXmits);\r
+ Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS;\r
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];\r
+ DataItem->SetData = Ip6ConfigSetMaunualAddress;\r
+ DataItem->Status = EFI_NOT_FOUND;\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway];\r
+ DataItem->SetData = Ip6ConfigSetGateway;\r
+ DataItem->Status = EFI_NOT_FOUND;\r
+\r
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];\r
+ DataItem->SetData = Ip6ConfigSetDnsServer;\r
+ DataItem->Status = EFI_NOT_FOUND;\r
+\r
+ //\r
+ // Create the event used for DHCP.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ Ip6ConfigOnDhcp6Event,\r
+ Instance,\r
+ &Instance->Dhcp6Event\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Instance->Configured = TRUE;\r
+\r
+ //\r
+ // Try to read the config data from NV variable.\r
+ //\r
+ Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance);\r
+ if (Status == EFI_NOT_FOUND) {\r
+ //\r
+ // The NV variable is not set, so generate a random IAID, and write down the\r
+ // fresh new configuration as the NV variable now.\r
+ //\r
+ Instance->IaId = NET_RANDOM (NetRandomInitSeed ());\r
+\r
+ for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) {\r
+ Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31));\r
+ }\r
+\r
+ Ip6ConfigWriteConfigData (IpSb->MacString, Instance);\r
+ } else if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Instance->Ip6Config.SetData = EfiIp6ConfigSetData;\r
+ Instance->Ip6Config.GetData = EfiIp6ConfigGetData;\r
+ Instance->Ip6Config.RegisterDataNotify = EfiIp6ConfigRegisterDataNotify;\r
+ Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify;\r
+\r
+\r
+ //\r
+ // Publish the IP6 configuration form\r
+ //\r
+ return Ip6ConfigFormInit (Instance);\r
+}\r
+\r
+/**\r
+ Release an IP6_CONFIG_INSTANCE.\r
+\r
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigCleanInstance (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ UINTN Index;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+\r
+ if (Instance->DeclineAddress != NULL) {\r
+ FreePool (Instance->DeclineAddress);\r
+ }\r
+\r
+ if (!Instance->Configured) {\r
+ return ;\r
+ }\r
+\r
+ if (Instance->Dhcp6Handle != NULL) {\r
+\r
+ Ip6ConfigDestroyDhcp6 (Instance);\r
+ }\r
+\r
+ //\r
+ // Close the event.\r
+ //\r
+ if (Instance->Dhcp6Event != NULL) {\r
+ gBS->CloseEvent (Instance->Dhcp6Event);\r
+ }\r
+\r
+ NetMapClean (&Instance->DadPassedMap);\r
+ NetMapClean (&Instance->DadFailedMap);\r
+\r
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {\r
+\r
+ DataItem = &Instance->DataItem[Index];\r
+\r
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {\r
+ if (DataItem->Data.Ptr != NULL) {\r
+ FreePool (DataItem->Data.Ptr);\r
+ }\r
+ DataItem->Data.Ptr = NULL;\r
+ DataItem->DataSize = 0;\r
+ }\r
+\r
+ NetMapClean (&Instance->DataItem[Index].EventMap);\r
+ }\r
+\r
+ Ip6ConfigFormUnload (Instance);\r
+\r
+ RemoveEntryList (&Instance->Link);\r
+}\r
+\r
+/**\r
+ Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.\r
+\r
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+\r
+ @retval EFI_SUCCESS The child was successfully destroyed.\r
+ @retval Others Failed to destory the child.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigDestroyDhcp6 (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ EFI_STATUS Status;\r
+ EFI_DHCP6_PROTOCOL *Dhcp6;\r
+\r
+ Dhcp6 = Instance->Dhcp6;\r
+ ASSERT (Dhcp6 != NULL);\r
+\r
+ Dhcp6->Stop (Dhcp6);\r
+ Dhcp6->Configure (Dhcp6, NULL);\r
+ Instance->Dhcp6 = NULL;\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+\r
+ //\r
+ // Close DHCPv6 protocol and destroy the child.\r
+ //\r
+ Status = gBS->CloseProtocol (\r
+ Instance->Dhcp6Handle,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ IpSb->Image,\r
+ IpSb->Controller\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = NetLibDestroyServiceChild (\r
+ IpSb->Controller,\r
+ IpSb->Image,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ Instance->Dhcp6Handle\r
+ );\r
+\r
+ Instance->Dhcp6Handle = NULL;\r
+\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Definitions for EFI IPv6 Configuartion Protocol implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __IP6_CONFIG_IMPL_H__\r
+#define __IP6_CONFIG_IMPL_H__\r
+\r
+#define IP6_CONFIG_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'C')\r
+#define IP6_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I')\r
+#define IP6_CONFIG_VARIABLE_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)\r
+\r
+#define IP6_CONFIG_DEFAULT_DAD_XMITS 1\r
+#define IP6_CONFIG_DHCP6_OPTION_ORO 6\r
+#define IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS 23\r
+\r
+#define DATA_ATTRIB_SIZE_FIXED 0x1\r
+#define DATA_ATTRIB_VOLATILE 0x2\r
+\r
+#define DATA_ATTRIB_SET(Attrib, Bits) (BOOLEAN)((Attrib) & (Bits))\r
+#define SET_DATA_ATTRIB(Attrib, Bits) ((Attrib) |= (Bits))\r
+\r
+typedef struct _IP6_CONFIG_INSTANCE IP6_CONFIG_INSTANCE;\r
+\r
+#define IP6_CONFIG_INSTANCE_FROM_PROTOCOL(Proto) \\r
+ CR ((Proto), \\r
+ IP6_CONFIG_INSTANCE, \\r
+ Ip6Config, \\r
+ IP6_CONFIG_INSTANCE_SIGNATURE \\r
+ )\r
+\r
+\r
+#define IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK(Callback) \\r
+ CR ((Callback), \\r
+ IP6_CONFIG_INSTANCE, \\r
+ CallbackInfo, \\r
+ IP6_CONFIG_INSTANCE_SIGNATURE \\r
+ )\r
+\r
+#define IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE(Instance) \\r
+ CR ((Instance), \\r
+ IP6_SERVICE, \\r
+ Ip6ConfigInstance, \\r
+ IP6_SERVICE_SIGNATURE \\r
+ )\r
+\r
+#define IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \\r
+ CR ((ConfigAccess), \\r
+ IP6_FORM_CALLBACK_INFO, \\r
+ HiiConfigAccess, \\r
+ IP6_FORM_CALLBACK_INFO_SIGNATURE \\r
+ )\r
+\r
+/**\r
+ The prototype of work function for EfiIp6ConfigSetData().\r
+\r
+ @param[in] Instance The pointer to the IP6 config instance data.\r
+ @param[in] DataSize In bytes, the size of the buffer pointed to by Data.\r
+ @param[in] Data The data buffer to set.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type,\r
+ 8 bytes.\r
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6\r
+ network stack was set successfully.\r
+ \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IP6_CONFIG_SET_DATA) (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ );\r
+\r
+/**\r
+ The prototype of work function for EfiIp6ConfigGetData().\r
+\r
+ @param[in] Instance The pointer to the IP6 config instance data.\r
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in\r
+ bytes, the size of buffer required to store the specified\r
+ configuration data.\r
+ @param[in] Data The data buffer in which the configuration data is returned. \r
+ Ignored if DataSize is ZERO.\r
+\r
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified\r
+ configuration data, and the required size is \r
+ returned in DataSize.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully. \r
+ \r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IP6_CONFIG_GET_DATA) (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN OUT UINTN *DataSize,\r
+ IN VOID *Data OPTIONAL\r
+ );\r
+\r
+typedef union {\r
+ VOID *Ptr;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;\r
+ EFI_IP6_CONFIG_INTERFACE_ID *AltIfId;\r
+ EFI_IP6_CONFIG_POLICY *Policy;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress;\r
+ EFI_IPv6_ADDRESS *Gateway;\r
+ EFI_IPv6_ADDRESS *DnsServers;\r
+} IP6_CONFIG_DATA;\r
+\r
+typedef struct {\r
+ IP6_CONFIG_SET_DATA SetData;\r
+ IP6_CONFIG_GET_DATA GetData;\r
+ EFI_STATUS Status;\r
+ UINT8 Attribute;\r
+ NET_MAP EventMap;\r
+ IP6_CONFIG_DATA Data;\r
+ UINTN DataSize;\r
+} IP6_CONFIG_DATA_ITEM;\r
+\r
+typedef struct {\r
+ UINT16 Offset;\r
+ UINTN DataSize;\r
+ EFI_IP6_CONFIG_DATA_TYPE DataType;\r
+} IP6_CONFIG_DATA_RECORD;\r
+\r
+#pragma pack(1)\r
+\r
+//\r
+// heap data that contains the data for each data record.\r
+//\r
+// BOOLEAN IsAltIfIdSet;\r
+// EFI_IP6_CONFIG_POLICY Policy;\r
+// EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;\r
+// UINT32 ManualaddressCount;\r
+// UINT32 GatewayCount;\r
+// UINT32 DnsServersCount;\r
+// EFI_IP6_CONFIG_INTERFACE_ID AltIfId;\r
+// EFI_IP6_CONFIG_MANUAL_ADDRESS ManualAddress[];\r
+// EFI_IPv6_ADDRESS Gateway[];\r
+// EFI_IPv6_ADDRESS DnsServers[];\r
+//\r
+typedef struct {\r
+ UINT32 IaId;\r
+ UINT16 Checksum;\r
+ UINT16 DataRecordCount;\r
+ IP6_CONFIG_DATA_RECORD DataRecord[1];\r
+} IP6_CONFIG_VARIABLE;\r
+\r
+#pragma pack()\r
+\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ EFI_IP6_ADDRESS_INFO AddrInfo;\r
+} IP6_ADDRESS_INFO_ENTRY;\r
+\r
+typedef struct {\r
+ EFI_IP6_CONFIG_POLICY Policy; ///< manual or automatic \r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadTransmitCount; ///< dad transmits count\r
+ EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; ///< alternative interface id \r
+ LIST_ENTRY ManualAddress; ///< IP addresses\r
+ UINT32 ManualAddressCount; ///< IP addresses count\r
+ LIST_ENTRY GatewayAddress; ///< Gateway address\r
+ UINT32 GatewayAddressCount; ///< Gateway address count\r
+ LIST_ENTRY DnsAddress; ///< DNS server address\r
+ UINT32 DnsAddressCount; ///< DNS server address count\r
+} IP6_CONFIG_NVDATA;\r
+\r
+typedef struct _IP6_FORM_CALLBACK_INFO {\r
+ UINT32 Signature;\r
+ EFI_HANDLE ChildHandle;\r
+ EFI_HII_CONFIG_ACCESS_PROTOCOL HiiConfigAccess;\r
+ EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath;\r
+ EFI_HII_HANDLE RegisteredHandle;\r
+} IP6_FORM_CALLBACK_INFO;\r
+\r
+struct _IP6_CONFIG_INSTANCE {\r
+ UINT32 Signature;\r
+ BOOLEAN Configured;\r
+ LIST_ENTRY Link;\r
+ UINT16 IfIndex;\r
+\r
+ EFI_IP6_CONFIG_INTERFACE_INFO InterfaceInfo;\r
+ EFI_IP6_CONFIG_INTERFACE_ID AltIfId;\r
+ EFI_IP6_CONFIG_POLICY Policy;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;\r
+\r
+ IP6_CONFIG_DATA_ITEM DataItem[Ip6ConfigDataTypeMaximum];\r
+ NET_MAP DadFailedMap;\r
+ NET_MAP DadPassedMap;\r
+\r
+ EFI_IP6_CONFIG_PROTOCOL Ip6Config;\r
+\r
+ EFI_EVENT Dhcp6SbNotifyEvent;\r
+ VOID *Registration;\r
+ EFI_HANDLE Dhcp6Handle;\r
+ EFI_DHCP6_PROTOCOL *Dhcp6;\r
+ BOOLEAN OtherInfoOnly;\r
+ UINT32 IaId;\r
+ EFI_EVENT Dhcp6Event;\r
+ UINT32 FailedIaAddressCount;\r
+ EFI_IPv6_ADDRESS *DeclineAddress;\r
+ UINT32 DeclineAddressCount;\r
+\r
+ IP6_FORM_CALLBACK_INFO CallbackInfo;\r
+ IP6_CONFIG_NVDATA Ip6NvData;\r
+};\r
+\r
+/**\r
+ The event process routine when the DHCPv6 server is answered with a reply packet\r
+ for an information request.\r
+ \r
+ @param[in] This Points to the EFI_DHCP6_PROTOCOL.\r
+ @param[in] Context The pointer to the IP6 configuration instance data.\r
+ @param[in] Packet The DHCPv6 reply packet.\r
+\r
+ @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet.\r
+ @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or \r
+ the DNS server address is not valid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ConfigOnDhcp6Reply (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN VOID *Context,\r
+ IN EFI_DHCP6_PACKET *Packet\r
+ );\r
+\r
+/**\r
+ The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.\r
+ \r
+ @param[in] Instance Pointer to the IP6 config instance data.\r
+ @param[in] OtherInfoOnly If FALSE, get stateful address and other information\r
+ via DHCPv6. Otherwise, only get the other information.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_UNSUPPORTED The DHCP6 driver is not available.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigStartStatefulAutoConfig (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN BOOLEAN OtherInfoOnly\r
+ );\r
+\r
+/**\r
+ Initialize an IP6_CONFIG_INSTANCE.\r
+\r
+ @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized.\r
+ \r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.\r
+ @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully.\r
+ \r
+**/\r
+EFI_STATUS\r
+Ip6ConfigInitInstance (\r
+ OUT IP6_CONFIG_INSTANCE *Instance\r
+ );\r
+\r
+/**\r
+ Release an IP6_CONFIG_INSTANCE.\r
+\r
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+ \r
+**/\r
+VOID\r
+Ip6ConfigCleanInstance (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ );\r
+\r
+/**\r
+ Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.\r
+\r
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.\r
+\r
+ @retval EFI_SUCCESS The child was successfully destroyed.\r
+ @retval Others Failed to destory the child.\r
+ \r
+**/\r
+EFI_STATUS\r
+Ip6ConfigDestroyDhcp6 (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Helper functions for configuring or obtaining the parameters relating to IP6.\r
+\r
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_GUID mIp6HiiVendorDevicePathGuid = IP6_HII_VENDOR_DEVICE_PATH_GUID;\r
+EFI_GUID mIp6ConfigNvDataGuid = IP6_CONFIG_NVDATA_GUID;\r
+CHAR16 mIp6ConfigStorageName[] = L"IP6_CONFIG_IFR_NVDATA";\r
+\r
+/**\r
+ The notify function of create event when performing a manual configuration.\r
+\r
+ @param[in] Event The pointer of Event.\r
+ @param[in] Context The pointer of Context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6ConfigManualAddressNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+/**\r
+ Get the configuration data for the EFI IPv6 network stack running on the\r
+ communication. It is a help function to the call EfiIp6ConfigGetData().\r
+\r
+ @param[in] Ip6Config The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to get.\r
+ @param[out] DataSize The size of buffer required in bytes.\r
+ @param[out] Data The data buffer in which the configuration data is returned. The\r
+ type of the data buffer associated with the DataType.\r
+ It is the caller's responsibility to free the resource.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+ - Ip6Config is NULL or invalid.\r
+ - DataSize is NULL.\r
+ - Data is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resources.\r
+ @retval EFI_NOT_READY The specified configuration data is not ready due to an\r
+ asynchronous configuration process already in progress.\r
+ @retval EFI_NOT_FOUND The specified configuration data was not found.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigNvGetData (\r
+ IN EFI_IP6_CONFIG_PROTOCOL *Ip6Config,\r
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,\r
+ OUT UINTN *DataSize,\r
+ OUT VOID **Data\r
+ )\r
+{\r
+ UINTN BufferSize;\r
+ VOID *Buffer;\r
+ EFI_STATUS Status;\r
+\r
+ if ((Ip6Config == NULL) || (Data == NULL) || (DataSize == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ BufferSize = 0;\r
+ Status = Ip6Config->GetData (\r
+ Ip6Config,\r
+ DataType,\r
+ &BufferSize,\r
+ NULL\r
+ );\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return Status;\r
+ }\r
+\r
+ Buffer = AllocateZeroPool (BufferSize);\r
+ if (Buffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Ip6Config->GetData (\r
+ Ip6Config,\r
+ DataType,\r
+ &BufferSize,\r
+ Buffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Buffer);\r
+ return Status;\r
+ }\r
+\r
+ *DataSize = BufferSize;\r
+ *Data = Buffer;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Free all nodes in IP6_ADDRESS_INFO_ENTRY in the list array specified\r
+ with ListHead.\r
+\r
+ @param[in] ListHead The head of the list array in IP6_ADDRESS_INFO_ENTRY.\r
+\r
+**/\r
+VOID\r
+Ip6FreeAddressInfoList (\r
+ IN LIST_ENTRY *ListHead\r
+ )\r
+{\r
+ IP6_ADDRESS_INFO_ENTRY *Node;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ListHead) {\r
+ Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);\r
+ RemoveEntryList (&Node->Link);\r
+ FreePool (Node);\r
+ }\r
+}\r
+\r
+/**\r
+ Convert the IPv6 address into a formatted string.\r
+\r
+ @param[in] Ip6 The IPv6 address.\r
+ @param[out] Str The formatted IP string.\r
+\r
+**/\r
+VOID\r
+Ip6ToStr (\r
+ IN EFI_IPv6_ADDRESS *Ip6,\r
+ OUT CHAR16 *Str\r
+ )\r
+{\r
+ UINTN Index;\r
+ BOOLEAN Short;\r
+ UINTN Number;\r
+ CHAR16 FormatString[8];\r
+\r
+ Short = FALSE;\r
+\r
+ for (Index = 0; Index < 15; Index = Index + 2) {\r
+ if (!Short &&\r
+ Index % 2 == 0 &&\r
+ Ip6->Addr[Index] == 0 &&\r
+ Ip6->Addr[Index + 1] == 0\r
+ ) {\r
+ //\r
+ // Deal with the case of ::.\r
+ //\r
+ if (Index == 0) {\r
+ *Str = L':';\r
+ *(Str + 1) = L':';\r
+ Str = Str + 2;\r
+ } else {\r
+ *Str = L':';\r
+ Str = Str + 1;\r
+ }\r
+\r
+ while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) {\r
+ Index = Index + 2;\r
+ }\r
+\r
+ Short = TRUE;\r
+\r
+ if (Index == 16) {\r
+ //\r
+ // :: is at the end of the address.\r
+ //\r
+ *Str = L'\0';\r
+ break;\r
+ }\r
+ }\r
+\r
+ ASSERT (Index < 15);\r
+\r
+ if (Ip6->Addr[Index] == 0) {\r
+ Number = UnicodeSPrint (Str, 2 * IP6_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]);\r
+ } else {\r
+ if (Ip6->Addr[Index + 1] < 0x10) {\r
+ CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:"));\r
+ } else {\r
+ CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:"));\r
+ }\r
+\r
+ Number = UnicodeSPrint (\r
+ Str,\r
+ 2 * IP6_STR_MAX_SIZE,\r
+ (CONST CHAR16 *) FormatString,\r
+ (UINTN) Ip6->Addr[Index],\r
+ (UINTN) Ip6->Addr[Index + 1]\r
+ );\r
+ }\r
+\r
+ Str = Str + Number;\r
+\r
+ if (Index + 2 == 16) {\r
+ *Str = L'\0';\r
+ if (*(Str - 1) == L':') {\r
+ *(Str - 1) = L'\0';\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Convert EFI_IP6_CONFIG_INTERFACE_ID to string format.\r
+\r
+ @param[out] String The buffer to store the converted string.\r
+ @param[in] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+ @retval EFI_SUCCESS The string converted successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertInterfaceIdToString (\r
+ OUT CHAR16 *String,\r
+ IN EFI_IP6_CONFIG_INTERFACE_ID *IfId\r
+ )\r
+{\r
+ UINT8 Index;\r
+ UINTN Number;\r
+\r
+ if ((String == NULL) || (IfId == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ for (Index = 0; Index < 8; Index++) {\r
+ Number = UnicodeSPrint (\r
+ String,\r
+ 2 * INTERFACE_ID_STR_STORAGE,\r
+ L"%x:",\r
+ (UINTN) IfId->Id[Index]\r
+ );\r
+ String = String + Number;\r
+ }\r
+\r
+ *(String - 1) = '\0';\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Parse InterfaceId in string format and convert it to EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+ @param[in] String The buffer of the string to be parsed.\r
+ @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ParseInterfaceIdFromString (\r
+ IN CONST CHAR16 *String,\r
+ OUT EFI_IP6_CONFIG_INTERFACE_ID *IfId\r
+ )\r
+{\r
+ UINT8 Index;\r
+ CHAR16 *IfIdStr;\r
+ CHAR16 *TempStr;\r
+ UINTN NodeVal;\r
+\r
+ if ((String == NULL) || (IfId == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IfIdStr = (CHAR16 *) String;\r
+\r
+ ZeroMem (IfId, sizeof (EFI_IP6_CONFIG_INTERFACE_ID));\r
+\r
+ for (Index = 0; Index < 8; Index++) {\r
+ TempStr = IfIdStr;\r
+\r
+ while ((*IfIdStr != L'\0') && (*IfIdStr != L':')) {\r
+ IfIdStr++;\r
+ }\r
+\r
+ //\r
+ // The InterfaceId format is X:X:X:X, the number of X should not exceed 8.\r
+ // If the number of X is less than 8, zero is appended to the InterfaceId.\r
+ //\r
+ if ((*IfIdStr == ':') && (Index == 7)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Convert the string to interface id. AsciiStrHexToUintn stops at the\r
+ // first character that is not a valid hex character, ':' or '\0' here.\r
+ //\r
+ NodeVal = StrHexToUintn (TempStr);\r
+ if (NodeVal > 0xFF) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IfId->Id[Index] = (UINT8) NodeVal;\r
+\r
+ IfIdStr++;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create Hii Extend Label OpCode as the start opcode and end opcode. It is\r
+ a help function.\r
+\r
+ @param[in] StartLabelNumber The number of start label.\r
+ @param[out] StartOpCodeHandle Points to the start opcode handle.\r
+ @param[out] StartLabel Points to the created start opcode.\r
+ @param[out] EndOpCodeHandle Points to the end opcode handle.\r
+ @param[out] EndLabel Points to the created end opcode.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this\r
+ operation.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CreateOpCode (\r
+ IN UINT16 StartLabelNumber,\r
+ OUT VOID **StartOpCodeHandle,\r
+ OUT EFI_IFR_GUID_LABEL **StartLabel,\r
+ OUT VOID **EndOpCodeHandle,\r
+ OUT EFI_IFR_GUID_LABEL **EndLabel\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IFR_GUID_LABEL *InternalStartLabel;\r
+ EFI_IFR_GUID_LABEL *InternalEndLabel;\r
+\r
+ if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *StartOpCodeHandle = NULL;\r
+ *EndOpCodeHandle = NULL;\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+\r
+ //\r
+ // Initialize the container for dynamic opcodes.\r
+ //\r
+ *StartOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+ if (*StartOpCodeHandle == NULL) {\r
+ return Status;\r
+ }\r
+\r
+ *EndOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+ if (*EndOpCodeHandle == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Create Hii Extend Label OpCode as the start opcode.\r
+ //\r
+ InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (\r
+ *StartOpCodeHandle,\r
+ &gEfiIfrTianoGuid,\r
+ NULL,\r
+ sizeof (EFI_IFR_GUID_LABEL)\r
+ );\r
+ if (InternalStartLabel == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+ InternalStartLabel->Number = StartLabelNumber;\r
+\r
+ //\r
+ // Create Hii Extend Label OpCode as the end opcode.\r
+ //\r
+ InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (\r
+ *EndOpCodeHandle,\r
+ &gEfiIfrTianoGuid,\r
+ NULL,\r
+ sizeof (EFI_IFR_GUID_LABEL)\r
+ );\r
+ if (InternalEndLabel == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+ InternalEndLabel->Number = LABEL_END;\r
+\r
+ *StartLabel = InternalStartLabel;\r
+ *EndLabel = InternalEndLabel;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Exit:\r
+\r
+ if (*StartOpCodeHandle != NULL) {\r
+ HiiFreeOpCodeHandle (*StartOpCodeHandle);\r
+ }\r
+\r
+ if (*EndOpCodeHandle != NULL) {\r
+ HiiFreeOpCodeHandle (*EndOpCodeHandle);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This function converts the different format of address list to string format and\r
+ then generates the corresponding text opcode to illustarate the address info in\r
+ IP6 configuration page. Currently, the following formats are supported:\r
+ EFI_IP6_ADDRESS_INFO AddressType: Ip6ConfigNvHostAddress;\r
+ EFI_IPv6_ADDRESS AddressType: Ip6ConfigNvGatewayAddress and Ip6ConfigNvDnsAddress;\r
+ EFI_IP6_ROUTE_TABLE AddressType: Ip6ConfigNvRouteTable.\r
+\r
+ @param[in, out] String The pointer to the buffer to store the converted\r
+ string.\r
+ @param[in] HiiHandle A handle that was previously registered in the\r
+ HII Database.\r
+ @param[in] AddressType The address type.\r
+ @param[in] AddressInfo Pointer to the address list.\r
+ @param[in] AddressCount The address count of the address list.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_UNSUPPORTED The AddressType is not supported.\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertAddressListToString (\r
+ IN OUT CHAR16 *String,\r
+ IN EFI_HII_HANDLE HiiHandle,\r
+ IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType,\r
+ IN VOID *AddressInfo,\r
+ IN UINTN AddressCount\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN Number;\r
+ CHAR16 *TempStr;\r
+ EFI_STATUS Status;\r
+ VOID *StartOpCodeHandle;\r
+ EFI_IFR_GUID_LABEL *StartLabel;\r
+ VOID *EndOpCodeHandle;\r
+ EFI_IFR_GUID_LABEL *EndLabel;\r
+ UINT16 StartLabelNumber;\r
+ EFI_STRING_ID TextTwo;\r
+ UINT8 *AddressHead;\r
+ UINT8 PrefixLength;\r
+ EFI_IPv6_ADDRESS *Address;\r
+\r
+ if ((String == NULL) || (HiiHandle == NULL) || (AddressInfo == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (AddressType == Ip6ConfigNvHostAddress) {\r
+ StartLabelNumber = HOST_ADDRESS_LABEL;\r
+ } else if (AddressType == Ip6ConfigNvGatewayAddress) {\r
+ StartLabelNumber = GATEWAY_ADDRESS_LABEL;\r
+ } else if (AddressType == Ip6ConfigNvDnsAddress) {\r
+ StartLabelNumber = DNS_ADDRESS_LABEL;\r
+ } else if (AddressType == Ip6ConfigNvRouteTable) {\r
+ StartLabelNumber = ROUTE_TABLE_LABEL;\r
+ } else {\r
+ ASSERT (FALSE);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Status = Ip6CreateOpCode (\r
+ StartLabelNumber,\r
+ &StartOpCodeHandle,\r
+ &StartLabel,\r
+ &EndOpCodeHandle,\r
+ &EndLabel\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ AddressHead = (UINT8 *) AddressInfo;\r
+\r
+ for (Index = 0; Index < AddressCount; Index++) {\r
+ if (AddressType == Ip6ConfigNvHostAddress) {\r
+ AddressInfo = AddressHead + sizeof (EFI_IP6_ADDRESS_INFO) * Index;\r
+ Address = &((EFI_IP6_ADDRESS_INFO *) AddressInfo)->Address;\r
+ } else if (AddressType == Ip6ConfigNvRouteTable) {\r
+ AddressInfo = AddressHead + sizeof (EFI_IP6_ROUTE_TABLE) * Index;\r
+ Address = &((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Destination;\r
+ } else {\r
+ AddressInfo = AddressHead + sizeof (EFI_IPv6_ADDRESS) * Index;\r
+ Address = AddressInfo;\r
+ }\r
+\r
+ //\r
+ // Convert the IP address info to string.\r
+ //\r
+ Ip6ToStr (Address, String);\r
+ TempStr = String + StrLen (String);\r
+\r
+ if ((AddressType == Ip6ConfigNvHostAddress) || (AddressType == Ip6ConfigNvRouteTable)) {\r
+ if (AddressType == Ip6ConfigNvHostAddress) {\r
+ PrefixLength = ((EFI_IP6_ADDRESS_INFO *) AddressInfo)->PrefixLength;\r
+ } else {\r
+ PrefixLength = ((EFI_IP6_ROUTE_TABLE *) AddressInfo)->PrefixLength;\r
+ }\r
+\r
+ //\r
+ // Append the prefix length to the string.\r
+ //\r
+ *TempStr = L'/';\r
+ TempStr++;\r
+ Number = UnicodeSPrint (TempStr, 6, L"%d", PrefixLength);\r
+ TempStr = TempStr + Number;\r
+ }\r
+\r
+ if (AddressType == Ip6ConfigNvRouteTable) {\r
+ //\r
+ // Append " >> " to the string.\r
+ //\r
+ Number = UnicodeSPrint (TempStr, 8, L" >> ");\r
+ TempStr = TempStr + Number;\r
+\r
+ //\r
+ // Append the gateway address to the string.\r
+ //\r
+ Ip6ToStr (&((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Gateway, TempStr);\r
+ TempStr = TempStr + StrLen (TempStr);\r
+ }\r
+\r
+ //\r
+ // Generate a text opcode and update the UI.\r
+ //\r
+ TextTwo = HiiSetString (HiiHandle, 0, String, NULL);\r
+ if (TextTwo == 0) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Exit;\r
+ }\r
+\r
+ HiiCreateTextOpCode (StartOpCodeHandle, STR_NULL, STR_NULL, TextTwo);\r
+\r
+ String = TempStr;\r
+ *String = IP6_ADDRESS_DELIMITER;\r
+ String++;\r
+ }\r
+\r
+ *(String - 1) = '\0';\r
+\r
+ Status = HiiUpdateForm (\r
+ HiiHandle, // HII handle\r
+ &mIp6ConfigNvDataGuid, // Formset GUID\r
+ FORMID_MAIN_FORM, // Form ID\r
+ StartOpCodeHandle, // Label for where to insert opcodes\r
+ EndOpCodeHandle // Replace data\r
+ );\r
+\r
+Exit:\r
+ HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+ HiiFreeOpCodeHandle (EndOpCodeHandle);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Parse address list in string format and convert it to a list array of node in\r
+ IP6_ADDRESS_INFO_ENTRY.\r
+\r
+ @param[in] String The buffer to string to be parsed.\r
+ @param[out] ListHead The list head of array.\r
+ @param[out] AddressCount The number of list nodes in the array.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of resource.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ParseAddressListFromString (\r
+ IN CONST CHAR16 *String,\r
+ OUT LIST_ENTRY *ListHead,\r
+ OUT UINT32 *AddressCount\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *LocalString;\r
+ CHAR16 *Temp;\r
+ CHAR16 *TempStr;\r
+ EFI_IP6_ADDRESS_INFO AddressInfo;\r
+ IP6_ADDRESS_INFO_ENTRY *Node;\r
+ BOOLEAN Last;\r
+ UINT32 Count;\r
+\r
+ if ((String == NULL) || (ListHead == NULL) || (AddressCount == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ LocalString = (CHAR16 *) AllocateCopyPool (StrSize (String), String);\r
+ if (LocalString == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Clean the original address list.\r
+ //\r
+ Ip6FreeAddressInfoList (ListHead);\r
+\r
+ Temp = LocalString;\r
+ Last = FALSE;\r
+ Count = 0;\r
+\r
+ while (*LocalString != L'\0') {\r
+ TempStr = LocalString;\r
+ while ((*LocalString != L'\0') && (*LocalString != IP6_ADDRESS_DELIMITER)) {\r
+ LocalString++;\r
+ }\r
+\r
+ if (*LocalString == L'\0') {\r
+ Last = TRUE;\r
+ }\r
+\r
+ *LocalString = L'\0';\r
+\r
+ Status = NetLibStrToIp6andPrefix (TempStr, &AddressInfo.Address, &AddressInfo.PrefixLength);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ if (AddressInfo.PrefixLength == 0xFF) {\r
+ AddressInfo.PrefixLength = 0;\r
+ }\r
+\r
+ if (!NetIp6IsValidUnicast (&AddressInfo.Address)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Error;\r
+ }\r
+\r
+ Node = AllocatePool (sizeof (IP6_ADDRESS_INFO_ENTRY));\r
+ if (Node == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ CopyMem (&Node->AddrInfo, &AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO));\r
+ InsertTailList (ListHead, &Node->Link);\r
+ Count++;\r
+\r
+ if (Last) {\r
+ break;\r
+ }\r
+\r
+ LocalString++;\r
+ }\r
+\r
+ FreePool (Temp);\r
+ *AddressCount = Count;\r
+ return EFI_SUCCESS;\r
+\r
+Error:\r
+ Ip6FreeAddressInfoList (ListHead);\r
+ FreePool (Temp);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This function converts the interface info to string and draws it to the IP6 UI.\r
+ The interface information includes interface name, interface type, hardware address,\r
+ address info, and route table information. The address information is also used as the\r
+ content of manual addresses in IP6 UI.\r
+\r
+ @param[in] IfInfo The pointer of EFI_IP6_CONFIG_INTERFACE_INFO.\r
+ @param[in] HiiHandle The handle that was previously registered in the\r
+ HII Database.\r
+ @param[in, out] IfrNvData Points to IP6_CONFIG_IFR_NVDATA.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertInterfaceInfoToString (\r
+ IN EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo,\r
+ IN EFI_HII_HANDLE HiiHandle,\r
+ IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINTN Number;\r
+ CHAR16 *String;\r
+ CHAR16 *LinkLocalStr;\r
+ CHAR16 PortString[ADDRESS_STR_MAX_SIZE];\r
+ CHAR16 FormatString[8];\r
+ EFI_STRING_ID StringId;\r
+ EFI_STATUS Status;\r
+\r
+ if ((IfInfo == NULL) || (HiiHandle == NULL) || (IfrNvData == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Print the interface name.\r
+ //\r
+ StringId = HiiSetString (\r
+ HiiHandle,\r
+ STRING_TOKEN (STR_IP6_INTERFACE_NAME_CONTENT),\r
+ IfInfo->Name,\r
+ NULL\r
+ );\r
+ if (StringId == 0) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Print the interface type.\r
+ //\r
+ if (IfInfo->IfType == Ip6InterfaceTypeEthernet) {\r
+ StrCpy (PortString, IP6_ETHERNET);\r
+ } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) {\r
+ StrCpy (PortString, IP6_EXPERIMENTAL_ETHERNET);\r
+ } else {\r
+ //\r
+ // Refer to RFC1700, chapter Number Hardware Type.\r
+ //\r
+ UnicodeSPrint (PortString, 6, L"%d", IfInfo->IfType);\r
+ }\r
+\r
+ StringId = HiiSetString (\r
+ HiiHandle,\r
+ STRING_TOKEN (STR_IP6_INTERFACE_TYPE_CONTENT),\r
+ PortString,\r
+ NULL\r
+ );\r
+ if (StringId == 0) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Convert the hardware address.\r
+ //\r
+ String = PortString;\r
+ ASSERT (IfInfo->HwAddressSize <= 32);\r
+\r
+ for (Index = 0; Index < IfInfo->HwAddressSize; Index++) {\r
+\r
+ if (IfInfo->HwAddress.Addr[Index] < 0x10) {\r
+ StrCpy (FormatString, L"0%x-");\r
+ } else {\r
+ StrCpy (FormatString, L"%x-");\r
+ }\r
+\r
+ Number = UnicodeSPrint (\r
+ String,\r
+ 8,\r
+ (CONST CHAR16 *) FormatString,\r
+ (UINTN) IfInfo->HwAddress.Addr[Index]\r
+ );\r
+ String = String + Number;\r
+ }\r
+\r
+ if (Index != 0) {\r
+ ASSERT (String > PortString);\r
+ String--;\r
+ *String = '\0';\r
+ }\r
+\r
+ //\r
+ // Print the hardware address.\r
+ //\r
+ StringId = HiiSetString (\r
+ HiiHandle,\r
+ STRING_TOKEN (STR_IP6_MAC_ADDRESS_CONTENT),\r
+ PortString,\r
+ NULL\r
+ );\r
+ if (StringId == 0) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Print the host address Information.\r
+ //\r
+ Status = Ip6ConvertAddressListToString (\r
+ PortString,\r
+ HiiHandle,\r
+ Ip6ConfigNvHostAddress,\r
+ IfInfo->AddressInfo,\r
+ IfInfo->AddressInfoCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Copy the Host Address Info to manual address field.\r
+ // Do not copy the link local address.\r
+ //\r
+ LinkLocalStr = StrStr (PortString, IP6_LINK_LOCAL_PREFIX);\r
+ if (LinkLocalStr != NULL) {\r
+ Number = LinkLocalStr - PortString;\r
+ if (Number > 0) {\r
+ CopyMem (IfrNvData->ManualAddress, PortString, Number * sizeof (CHAR16));\r
+ }\r
+\r
+ while ((*LinkLocalStr != L' ') && (*LinkLocalStr != L'\0')) {\r
+ LinkLocalStr++;\r
+ }\r
+\r
+ if (*LinkLocalStr != L'\0') {\r
+ LinkLocalStr++;\r
+ StrCat (IfrNvData->ManualAddress, LinkLocalStr);\r
+ }\r
+ } else {\r
+ StrCpy (IfrNvData->ManualAddress, PortString);\r
+ }\r
+\r
+ //\r
+ // Print the route table information.\r
+ //\r
+ Status = Ip6ConvertAddressListToString (\r
+ PortString,\r
+ HiiHandle,\r
+ Ip6ConfigNvRouteTable,\r
+ IfInfo->RouteTable,\r
+ IfInfo->RouteCount\r
+ );\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Build the address info list from list array of node in IP6_ADDRESS_INFO_ENTRY.\r
+\r
+ @param[in] Instance Points to IP6 config instance data.\r
+ @param[in] AddressType The address type.\r
+ @param[out] AddressInfo The pointer to the buffer to store the address list.\r
+ @param[out] AddressSize The address size of the address list.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_UNSUPPORTED The AddressType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildNvAddressInfo (\r
+ IN IP6_CONFIG_INSTANCE *Instance,\r
+ IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType,\r
+ OUT VOID **AddressInfo,\r
+ OUT UINTN *AddressSize\r
+ )\r
+{\r
+ IP6_CONFIG_NVDATA *Ip6NvData;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *ListHead;\r
+ IP6_ADDRESS_INFO_ENTRY *Node;\r
+ VOID *AddressList;\r
+ VOID *TmpStr;\r
+ UINTN DataSize;\r
+ EFI_IPv6_ADDRESS *Ip6Address;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress;\r
+\r
+ if ((Instance == NULL) || (AddressInfo == NULL) || (AddressSize == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+ Ip6NvData = &Instance->Ip6NvData;\r
+\r
+ if (AddressType == Ip6ConfigNvHostAddress) {\r
+ ListHead = &Ip6NvData->ManualAddress;\r
+ DataSize = sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS) * Ip6NvData->ManualAddressCount;\r
+ } else if (AddressType == Ip6ConfigNvGatewayAddress) {\r
+ ListHead = &Ip6NvData->GatewayAddress;\r
+ DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->GatewayAddressCount;\r
+ } else if (AddressType == Ip6ConfigNvDnsAddress) {\r
+ ListHead = &Ip6NvData->DnsAddress;\r
+ DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->DnsAddressCount;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ AddressList = AllocateZeroPool (DataSize);\r
+ if (AddressList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TmpStr = AddressList;\r
+\r
+ NET_LIST_FOR_EACH (Entry, ListHead) {\r
+ Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);\r
+ if (AddressType == Ip6ConfigNvHostAddress) {\r
+ ManualAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AddressList;\r
+ IP6_COPY_ADDRESS (&ManualAddress->Address, &Node->AddrInfo.Address);\r
+ ManualAddress->PrefixLength = Node->AddrInfo.PrefixLength;\r
+ AddressList = (UINT8 *) AddressList + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);\r
+ } else {\r
+ Ip6Address = (EFI_IPv6_ADDRESS *) AddressList;\r
+ IP6_COPY_ADDRESS (Ip6Address, &Node->AddrInfo.Address);\r
+ AddressList = (UINT8 *) AddressList + sizeof (EFI_IPv6_ADDRESS);\r
+ }\r
+ }\r
+\r
+ *AddressInfo = TmpStr;\r
+ *AddressSize = DataSize;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Convert the IP6 configuration data into the IFR data.\r
+\r
+ @param[in, out] IfrNvData The IFR NV data.\r
+ @param[in] Instance The IP6 config instance data.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_UNSUPPORTED The policy is not supported in the current implementation.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertConfigNvDataToIfrNvData (\r
+ IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData,\r
+ IN IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
+ UINTN DataSize;\r
+ VOID *Data;\r
+ EFI_STATUS Status;\r
+ EFI_IP6_CONFIG_INTERFACE_ID InterfaceId;\r
+ EFI_IP6_CONFIG_POLICY Policy;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;\r
+ EFI_HII_HANDLE HiiHandle;\r
+\r
+ if ((IfrNvData == NULL) || (Instance == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+\r
+ Ip6Config = &Instance->Ip6Config;\r
+ Data = NULL;\r
+ DataSize = 0;\r
+ HiiHandle = Instance->CallbackInfo.RegisteredHandle;\r
+\r
+ //\r
+ // Get the current interface info.\r
+ //\r
+ Status = Ip6ConfigNvGetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &DataSize,\r
+ (VOID **) &Data\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Convert the interface info to string and print.\r
+ //\r
+ Status = Ip6ConvertInterfaceInfoToString (\r
+ (EFI_IP6_CONFIG_INTERFACE_INFO *) Data,\r
+ HiiHandle,\r
+ IfrNvData\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Get the interface id.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);\r
+ ZeroMem (&InterfaceId, DataSize);\r
+ Status = Ip6Config->GetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeAltInterfaceId,\r
+ &DataSize,\r
+ &InterfaceId\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &InterfaceId);\r
+\r
+ //\r
+ // Get current policy.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
+ Status = Ip6Config->GetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypePolicy,\r
+ &DataSize,\r
+ &Policy\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (Policy == Ip6ConfigPolicyManual) {\r
+ IfrNvData->Policy = IP6_POLICY_MANUAL;\r
+ } else if (Policy == Ip6ConfigPolicyAutomatic) {\r
+ IfrNvData->Policy = IP6_POLICY_AUTO;\r
+ } else {\r
+ ASSERT (FALSE);\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Get Duplicate Address Detection Transmits count.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+ Status = Ip6Config->GetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+ &DataSize,\r
+ &DadXmits\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ IfrNvData->DadTransmitCount = DadXmits.DupAddrDetectTransmits;\r
+\r
+ //\r
+ // Get DNS server list.\r
+ //\r
+ FreePool (Data);\r
+ Data = NULL;\r
+ DataSize = 0;\r
+ Status = Ip6ConfigNvGetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeDnsServer,\r
+ &DataSize,\r
+ (VOID **) &Data\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (DataSize > 0) {\r
+ //\r
+ // Convert the DNS server address to string and draw it to UI.\r
+ //\r
+ Status = Ip6ConvertAddressListToString (\r
+ IfrNvData->DnsAddress,\r
+ HiiHandle,\r
+ Ip6ConfigNvDnsAddress,\r
+ Data,\r
+ DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ FreePool (Data);\r
+ Data = NULL;\r
+ }\r
+\r
+ //\r
+ // Get gateway adderss list.\r
+ //\r
+ DataSize = 0;\r
+ Status = Ip6ConfigNvGetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeGateway,\r
+ &DataSize,\r
+ (VOID **) &Data\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (DataSize > 0) {\r
+ //\r
+ // Convert the gateway address to string and draw it to UI.\r
+ //\r
+ Status = Ip6ConvertAddressListToString (\r
+ IfrNvData->GatewayAddress,\r
+ HiiHandle,\r
+ Ip6ConfigNvGatewayAddress,\r
+ Data,\r
+ DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+ if (Data != NULL) {\r
+ FreePool (Data);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Convert IFR data into IP6 configuration data. The policy, alternative interface\r
+ ID, and DAD transmit counts, and will be saved. If under manual policy, the configured\r
+ manual address, gateway address, and DNS server address will be saved.\r
+\r
+ @param[in] IfrNvData The IFR NV data.\r
+ @param[in, out] Instance The IP6 config instance data.\r
+\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConvertIfrNvDataToConfigNvData (\r
+ IN IP6_CONFIG_IFR_NVDATA *IfrNvData,\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ IP6_CONFIG_NVDATA *Ip6NvData;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
+ EFI_STATUS Status;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress;\r
+ EFI_IPv6_ADDRESS *Address;\r
+ BOOLEAN IsAddressOk;\r
+ EFI_EVENT SetAddressEvent;\r
+ EFI_EVENT TimeoutEvent;\r
+ UINTN DataSize;\r
+\r
+ if ((IfrNvData == NULL) || (Instance == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);\r
+ Ip6NvData = &Instance->Ip6NvData;\r
+ Ip6Config = &Instance->Ip6Config;\r
+\r
+ //\r
+ // Update those fields which don't have INTERACTIVE attribute.\r
+ //\r
+ if (IfrNvData->Policy == IP6_POLICY_AUTO) {\r
+ Ip6NvData->Policy = Ip6ConfigPolicyAutomatic;\r
+ } else if (IfrNvData->Policy == IP6_POLICY_MANUAL) {\r
+ Ip6NvData->Policy = Ip6ConfigPolicyManual;\r
+ }\r
+\r
+ Ip6NvData->DadTransmitCount.DupAddrDetectTransmits = IfrNvData->DadTransmitCount;\r
+\r
+ //\r
+ // Set the configured policy.\r
+ //\r
+ Status = Ip6Config->SetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypePolicy,\r
+ sizeof (EFI_IP6_CONFIG_POLICY),\r
+ &Ip6NvData->Policy\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Set the duplicate address detection transmits count.\r
+ //\r
+ Status = Ip6Config->SetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+ sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),\r
+ &Ip6NvData->DadTransmitCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Set the alternative interface ID\r
+ //\r
+ Status = Ip6Config->SetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeAltInterfaceId,\r
+ sizeof (EFI_IP6_CONFIG_INTERFACE_ID),\r
+ &Ip6NvData->InterfaceId\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+\r
+ if (Ip6NvData->Policy == Ip6ConfigPolicyAutomatic) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Create events & timers for asynchronous settings.\r
+ //\r
+ SetAddressEvent = NULL;\r
+ TimeoutEvent = NULL;\r
+ ManualAddress = NULL;\r
+ Address = NULL;\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ Ip6ConfigManualAddressNotify,\r
+ &IsAddressOk,\r
+ &SetAddressEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &TimeoutEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Set the manual address list. This is an asynchronous process.\r
+ //\r
+ if (!IsListEmpty (&Ip6NvData->ManualAddress) && (Ip6NvData->ManualAddressCount != 0)) {\r
+ Status = Ip6BuildNvAddressInfo (\r
+ Instance,\r
+ Ip6ConfigNvHostAddress,\r
+ (VOID **) &ManualAddress,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ IsAddressOk = FALSE;\r
+\r
+ Status = Ip6Config->RegisterDataNotify (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ SetAddressEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Ip6Config->SetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ DataSize,\r
+ (VOID *) ManualAddress\r
+ );\r
+ if (Status == EFI_NOT_READY) {\r
+ gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000);\r
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+ if (IsAddressOk) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ Status = Ip6Config->UnregisterDataNotify (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ SetAddressEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Set gateway address list.\r
+ //\r
+ if (!IsListEmpty (&Ip6NvData->GatewayAddress) && (Ip6NvData->GatewayAddressCount != 0)) {\r
+ Status = Ip6BuildNvAddressInfo (\r
+ Instance,\r
+ Ip6ConfigNvGatewayAddress,\r
+ (VOID **) &Address,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Ip6Config->SetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeGateway,\r
+ DataSize,\r
+ (VOID *) Address\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ FreePool (Address);\r
+ Address = NULL;\r
+ }\r
+\r
+ //\r
+ // Set DNS server address list.\r
+ //\r
+ if (!IsListEmpty (&Ip6NvData->DnsAddress) && (Ip6NvData->DnsAddressCount != 0)) {\r
+ Status = Ip6BuildNvAddressInfo (\r
+ Instance,\r
+ Ip6ConfigNvDnsAddress,\r
+ (VOID **) &Address,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Ip6Config->SetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeDnsServer,\r
+ DataSize,\r
+ (VOID *) Address\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+ if (SetAddressEvent != NULL) {\r
+ gBS->CloseEvent (SetAddressEvent);\r
+ }\r
+\r
+ if (TimeoutEvent != NULL) {\r
+ gBS->CloseEvent (TimeoutEvent);\r
+ }\r
+\r
+ if (ManualAddress != NULL) {\r
+ FreePool (ManualAddress);\r
+ }\r
+\r
+ if (Address != NULL) {\r
+ FreePool (Address);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This function allows the caller to request the current\r
+ configuration for one or more named elements. The resulting\r
+ string is in <ConfigAltResp> format. Any and all alternative\r
+ configuration strings shall also be appended to the end of the\r
+ current configuration string. If they are, they must appear\r
+ after the current configuration. They must contain the same\r
+ routing (GUID, NAME, PATH) as the current configuration string.\r
+ They must have an additional description indicating the type of\r
+ alternative configuration the string represents,\r
+ "ALTCFG=<StringToken>". That <StringToken> (when\r
+ converted from Hex UNICODE to binary) is a reference to a\r
+ string in the associated string pack.\r
+\r
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+ @param[in] Request A null-terminated Unicode string in\r
+ <ConfigRequest> format. Note that this\r
+ includes the routing information as well as\r
+ the configurable name / value pairs. It is\r
+ invalid for this string to be in\r
+ <MultiConfigRequest> format.\r
+ @param[out] Progress On return, points to a character in the\r
+ Request string. Points to the string's null\r
+ terminator if request was successful. Points\r
+ to the most recent "&" before the first\r
+ failing name / value pair (or the beginning\r
+ of the string if the failure is in the first\r
+ name / value pair) if the request was not\r
+ successful.\r
+ @param[out] Results A null-terminated Unicode string in\r
+ <ConfigAltResp> format which has all values\r
+ filled in for the names in the Request string.\r
+ String to be allocated by the called function.\r
+\r
+ @retval EFI_SUCCESS The Results string is filled with the\r
+ values corresponding to all requested\r
+ names.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the\r
+ parts of the results that must be\r
+ stored awaiting possible future\r
+ protocols.\r
+ @retval EFI_INVALID_PARAMETER For example, passing in a NULL\r
+ for the Request parameter\r
+ would result in this type of\r
+ error. In this case, the\r
+ Progress parameter would be\r
+ set to NULL.\r
+ @retval EFI_NOT_FOUND Routing data doesn't match any\r
+ known driver. Progress set to the\r
+ first character in the routing header.\r
+ Note: There is no requirement that the\r
+ driver validate the routing data. It\r
+ must skip the <ConfigHdr> in order to\r
+ process the names.\r
+ @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set\r
+ to most recent & before the\r
+ error or the beginning of the\r
+ string.\r
+ @retval EFI_INVALID_PARAMETER Unknown name. Progress points\r
+ to the & before the name in\r
+ question. Currently not implemented.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6FormExtractConfig (\r
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,\r
+ IN CONST EFI_STRING Request,\r
+ OUT EFI_STRING *Progress,\r
+ OUT EFI_STRING *Results\r
+ )\r
+{\r
+\r
+ EFI_STATUS Status;\r
+ IP6_FORM_CALLBACK_INFO *Private;\r
+ IP6_CONFIG_INSTANCE *Ip6ConfigInstance;\r
+ IP6_CONFIG_IFR_NVDATA *IfrNvData;\r
+ EFI_STRING ConfigRequestHdr;\r
+ EFI_STRING ConfigRequest;\r
+ BOOLEAN AllocatedRequest;\r
+ UINTN Size;\r
+ UINTN BufferSize;\r
+\r
+ if (This == NULL || Progress == NULL || Results == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *Progress = Request;\r
+ if ((Request != NULL) &&\r
+ !HiiIsConfigHdrMatch (Request, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ ConfigRequestHdr = NULL;\r
+ ConfigRequest = NULL;\r
+ AllocatedRequest = FALSE;\r
+ Size = 0;\r
+\r
+ Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);\r
+ Ip6ConfigInstance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);\r
+ BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);\r
+\r
+ IfrNvData = (IP6_CONFIG_IFR_NVDATA *) AllocateZeroPool (BufferSize);\r
+ if (IfrNvData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Ip6ConvertConfigNvDataToIfrNvData (IfrNvData, Ip6ConfigInstance);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ ConfigRequest = Request;\r
+ if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {\r
+ //\r
+ // Request has no request element, construct full request string.\r
+ // Allocate and fill a buffer large enough to hold the <ConfigHdr> template\r
+ // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator.\r
+ //\r
+ ConfigRequestHdr = HiiConstructConfigHdr (\r
+ &mIp6ConfigNvDataGuid,\r
+ mIp6ConfigStorageName,\r
+ Private->ChildHandle\r
+ );\r
+ Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);\r
+ ConfigRequest = AllocateZeroPool (Size);\r
+ ASSERT (ConfigRequest != NULL);\r
+ AllocatedRequest = TRUE;\r
+ UnicodeSPrint (\r
+ ConfigRequest,\r
+ Size,\r
+ L"%s&OFFSET=0&WIDTH=%016LX",\r
+ ConfigRequestHdr,\r
+ (UINT64) BufferSize\r
+ );\r
+ FreePool (ConfigRequestHdr);\r
+ }\r
+\r
+ //\r
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()\r
+ //\r
+ Status = gHiiConfigRouting->BlockToConfig (\r
+ gHiiConfigRouting,\r
+ ConfigRequest,\r
+ (UINT8 *) IfrNvData,\r
+ BufferSize,\r
+ Results,\r
+ Progress\r
+ );\r
+\r
+Exit:\r
+ FreePool (IfrNvData);\r
+ //\r
+ // Free the allocated config request string.\r
+ //\r
+ if (AllocatedRequest) {\r
+ FreePool (ConfigRequest);\r
+ ConfigRequest = NULL;\r
+ }\r
+ //\r
+ // Set Progress string to the original request string.\r
+ //\r
+ if (Request == NULL) {\r
+ *Progress = NULL;\r
+ } else if (StrStr (Request, L"OFFSET") == NULL) {\r
+ *Progress = Request + StrLen (Request);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This function applies changes in a driver's configuration.\r
+ Input is a Configuration, which has the routing data for this\r
+ driver followed by name / value configuration pairs. The driver\r
+ must apply those pairs to its configurable storage. If the\r
+ driver's configuration is stored in a linear block of data\r
+ and the driver's name / value pairs are in <BlockConfig>\r
+ format, it may use the ConfigToBlock helper function (above) to\r
+ simplify the job. Currently not implemented.\r
+\r
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+ @param[in] Configuration A null-terminated Unicode string in\r
+ <ConfigString> format.\r
+ @param[out] Progress A pointer to a string filled in with the\r
+ offset of the most recent '&' before the\r
+ first failing name / value pair (or the\r
+ beginn ing of the string if the failure\r
+ is in the first name / value pair) or\r
+ the terminating NULL if all was\r
+ successful.\r
+\r
+ @retval EFI_SUCCESS The results have been distributed or are\r
+ awaiting distribution.\r
+ @retval EFI_OUT_OF_MEMORY Not enough memory to store the\r
+ parts of the results that must be\r
+ stored awaiting possible future\r
+ protocols.\r
+ @retval EFI_INVALID_PARAMETERS Passing in a NULL for the\r
+ Results parameter would result\r
+ in this type of error.\r
+ @retval EFI_NOT_FOUND Target for the specified routing data\r
+ was not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6FormRouteConfig (\r
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,\r
+ IN CONST EFI_STRING Configuration,\r
+ OUT EFI_STRING *Progress\r
+ )\r
+{\r
+ if (This == NULL || Configuration == NULL || Progress == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check routing data in <ConfigHdr>.\r
+ // Note: if only one Storage is used, then this checking could be skipped.\r
+ //\r
+ if (!HiiIsConfigHdrMatch (Configuration, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {\r
+ *Progress = Configuration;\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ *Progress = Configuration + StrLen (Configuration);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function is called to provide results data to the driver.\r
+ This data consists of a unique key that is used to identify\r
+ which data is either being passed back or being asked for.\r
+\r
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+ @param[in] Action Specifies the type of action taken by the browser.\r
+ @param[in] QuestionId A unique value which is sent to the original\r
+ exporting driver so that it can identify the type\r
+ of data to expect. The format of the data tends to\r
+ vary based on the opcode that generated the callback.\r
+ @param[in] Type The type of value for the question.\r
+ @param[in] Value A pointer to the data being sent to the original\r
+ exporting driver.\r
+ @param[out] ActionRequest On return, points to the action requested by the\r
+ callback function.\r
+\r
+ @retval EFI_SUCCESS The callback successfully handled the action.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the\r
+ variable and its data.\r
+ @retval EFI_DEVICE_ERROR The variable could not be saved.\r
+ @retval EFI_UNSUPPORTED The specified Action is not supported by the\r
+ callback. Currently not implemented.\r
+ @retval EFI_INVALID_PARAMETER Passed in the wrong parameter.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6FormCallback (\r
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,\r
+ IN EFI_BROWSER_ACTION Action,\r
+ IN EFI_QUESTION_ID QuestionId,\r
+ IN UINT8 Type,\r
+ IN EFI_IFR_TYPE_VALUE *Value,\r
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest\r
+ )\r
+{\r
+ IP6_FORM_CALLBACK_INFO *Private;\r
+ UINTN BufferSize;\r
+ IP6_CONFIG_IFR_NVDATA *IfrNvData;\r
+ IP6_CONFIG_IFR_NVDATA OldIfrNvData;\r
+ EFI_STATUS Status;\r
+ EFI_INPUT_KEY Key;\r
+ IP6_CONFIG_INSTANCE *Instance;\r
+ IP6_CONFIG_NVDATA *Ip6NvData;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *Data;\r
+ UINTN DataSize;\r
+ CHAR16 PortString[ADDRESS_STR_MAX_SIZE];\r
+ EFI_HII_HANDLE HiiHandle;\r
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);\r
+ Instance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);\r
+ Ip6NvData = &Instance->Ip6NvData;\r
+\r
+ if (Action == EFI_BROWSER_ACTION_FORM_OPEN) {\r
+ //\r
+ // Update main Form when main Form is opened.\r
+ // This will be done only in FORM_OPEN CallBack of question with KEY_INTERFACE_ID from main Form.\r
+ //\r
+ if (QuestionId != KEY_INTERFACE_ID) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Ip6Config = &Instance->Ip6Config;\r
+ HiiHandle = Instance->CallbackInfo.RegisteredHandle;\r
+\r
+ //\r
+ // Get the current interface info.\r
+ //\r
+ Status = Ip6ConfigNvGetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeInterfaceInfo,\r
+ &DataSize,\r
+ (VOID **) &Data\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Generate the dynamic text opcode for host address and draw it.\r
+ //\r
+ IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;\r
+ Status = Ip6ConvertAddressListToString (\r
+ PortString,\r
+ HiiHandle,\r
+ Ip6ConfigNvHostAddress,\r
+ IfInfo->AddressInfo,\r
+ IfInfo->AddressInfoCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Generate the dynamic text opcode for route table and draw it.\r
+ //\r
+ Status = Ip6ConvertAddressListToString (\r
+ PortString,\r
+ HiiHandle,\r
+ Ip6ConfigNvRouteTable,\r
+ IfInfo->RouteTable,\r
+ IfInfo->RouteCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Get DNS server list.\r
+ //\r
+ DataSize = 0;\r
+ Status = Ip6ConfigNvGetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeDnsServer,\r
+ &DataSize,\r
+ (VOID **) &Data\r
+ );\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (DataSize > 0) {\r
+ //\r
+ // Generate the dynamic text opcode for DNS server and draw it.\r
+ //\r
+ Status = Ip6ConvertAddressListToString (\r
+ PortString,\r
+ HiiHandle,\r
+ Ip6ConfigNvDnsAddress,\r
+ Data,\r
+ DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get gateway adderss list.\r
+ //\r
+ DataSize = 0;\r
+ Status = Ip6ConfigNvGetData (\r
+ Ip6Config,\r
+ Ip6ConfigDataTypeGateway,\r
+ &DataSize,\r
+ (VOID **) &Data\r
+ );\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (DataSize > 0) {\r
+ //\r
+ // Generate the dynamic text opcode for gateway and draw it.\r
+ //\r
+ Status = Ip6ConvertAddressListToString (\r
+ PortString,\r
+ HiiHandle,\r
+ Ip6ConfigNvGatewayAddress,\r
+ Data,\r
+ DataSize / sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+Exit:\r
+ FreePool (Data);\r
+ return Status;\r
+ }\r
+\r
+ if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) {\r
+ //\r
+ // Do nothing for UEFI FORM_CLOSE action\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if ((Value == NULL) || (ActionRequest == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Retrieve uncommitted data from Browser\r
+ //\r
+\r
+ BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);\r
+ IfrNvData = AllocateZeroPool (BufferSize);\r
+ if (IfrNvData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ ZeroMem (&OldIfrNvData, BufferSize);\r
+\r
+ HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);\r
+\r
+ CopyMem (&OldIfrNvData, IfrNvData, BufferSize);\r
+\r
+ switch (QuestionId) {\r
+ case KEY_INTERFACE_ID:\r
+ Status = Ip6ParseInterfaceIdFromString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId);\r
+ if (EFI_ERROR (Status)) {\r
+ CreatePopUp (\r
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+ &Key,\r
+ L"Invalid Interface ID!",\r
+ NULL\r
+ );\r
+ }\r
+\r
+ break;\r
+\r
+ case KEY_MANUAL_ADDRESS:\r
+ Status = Ip6ParseAddressListFromString (\r
+ IfrNvData->ManualAddress,\r
+ &Ip6NvData->ManualAddress,\r
+ &Ip6NvData->ManualAddressCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ CreatePopUp (\r
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+ &Key,\r
+ L"Invalid Host Addresses!",\r
+ NULL\r
+ );\r
+ }\r
+\r
+ break;\r
+\r
+ case KEY_GATEWAY_ADDRESS:\r
+ Status = Ip6ParseAddressListFromString (\r
+ IfrNvData->GatewayAddress,\r
+ &Ip6NvData->GatewayAddress,\r
+ &Ip6NvData->GatewayAddressCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ CreatePopUp (\r
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+ &Key,\r
+ L"Invalid Gateway Addresses!",\r
+ NULL\r
+ );\r
+ }\r
+\r
+ break;\r
+\r
+ case KEY_DNS_ADDRESS:\r
+ Status = Ip6ParseAddressListFromString (\r
+ IfrNvData->DnsAddress,\r
+ &Ip6NvData->DnsAddress,\r
+ &Ip6NvData->DnsAddressCount\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ CreatePopUp (\r
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+ &Key,\r
+ L"Invalid DNS Addresses!",\r
+ NULL\r
+ );\r
+ }\r
+\r
+ break;\r
+\r
+ case KEY_SAVE_CONFIG_CHANGES:\r
+ CopyMem (&OldIfrNvData, IfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));\r
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+ break;\r
+\r
+ case KEY_IGNORE_CONFIG_CHANGES:\r
+ CopyMem (IfrNvData, &OldIfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));\r
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+ break;\r
+\r
+ case KEY_SAVE_CHANGES:\r
+ Status = Ip6ConvertIfrNvDataToConfigNvData (IfrNvData, Instance);\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Pass changed uncommitted data back to Form Browser.\r
+ //\r
+ BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);\r
+ HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL);\r
+ }\r
+\r
+ FreePool (IfrNvData);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Install HII Config Access protocol for network device and allocate resources.\r
+\r
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form.\r
+\r
+ @retval EFI_SUCCESS The HII Config Access protocol is installed.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigFormInit (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_FORM_CALLBACK_INFO *CallbackInfo;\r
+ EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;\r
+ VENDOR_DEVICE_PATH VendorDeviceNode;\r
+ EFI_SERVICE_BINDING_PROTOCOL *MnpSb;\r
+ CHAR16 *MacString;\r
+ CHAR16 MenuString[128];\r
+ CHAR16 PortString[128];\r
+ CHAR16 *OldMenuString;\r
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ ASSERT (IpSb != NULL);\r
+\r
+ Status = gBS->HandleProtocol (\r
+ IpSb->Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **) &ParentDevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ CallbackInfo = &Instance->CallbackInfo;\r
+ CallbackInfo->Signature = IP6_FORM_CALLBACK_INFO_SIGNATURE;\r
+\r
+ //\r
+ // Construct device path node for EFI HII Config Access protocol,\r
+ // which consists of controller physical device path and one hardware\r
+ // vendor guid node.\r
+ //\r
+ ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));\r
+ VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH;\r
+ VendorDeviceNode.Header.SubType = HW_VENDOR_DP;\r
+\r
+ CopyGuid (&VendorDeviceNode.Guid, &mIp6HiiVendorDevicePathGuid);\r
+\r
+ SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));\r
+ CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (\r
+ ParentDevicePath,\r
+ (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode\r
+ );\r
+ if (CallbackInfo->HiiVendorDevicePath == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ ConfigAccess = &CallbackInfo->HiiConfigAccess;\r
+ ConfigAccess->ExtractConfig = Ip6FormExtractConfig;\r
+ ConfigAccess->RouteConfig = Ip6FormRouteConfig;\r
+ ConfigAccess->Callback = Ip6FormCallback;\r
+\r
+ //\r
+ // Install Device Path Protocol and Config Access protocol on new handle\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &CallbackInfo->ChildHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ CallbackInfo->HiiVendorDevicePath,\r
+ &gEfiHiiConfigAccessProtocolGuid,\r
+ ConfigAccess,\r
+ NULL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Open the Parent Handle for the child\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ IpSb->Controller,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ (VOID **) &MnpSb,\r
+ IpSb->Image,\r
+ CallbackInfo->ChildHandle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Publish our HII data\r
+ //\r
+ CallbackInfo->RegisteredHandle = HiiAddPackages (\r
+ &mIp6ConfigNvDataGuid,\r
+ CallbackInfo->ChildHandle,\r
+ Ip6DxeStrings,\r
+ Ip6ConfigBin,\r
+ NULL\r
+ );\r
+ if (CallbackInfo->RegisteredHandle == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Append MAC string in the menu string and tile string\r
+ //\r
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString);\r
+ if (!EFI_ERROR (Status)) {\r
+ OldMenuString = HiiGetString (\r
+ CallbackInfo->RegisteredHandle,\r
+ STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),\r
+ NULL)\r
+ ;\r
+ UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);\r
+ HiiSetString (\r
+ CallbackInfo->RegisteredHandle,\r
+ STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),\r
+ MenuString,\r
+ NULL\r
+ );\r
+ UnicodeSPrint (PortString, 128, L"MAC:%s", MacString);\r
+ HiiSetString (\r
+ CallbackInfo->RegisteredHandle,\r
+ STRING_TOKEN (STR_IP6_DEVICE_FORM_TITLE),\r
+ PortString,\r
+ NULL\r
+ );\r
+\r
+ FreePool (MacString);\r
+ FreePool (OldMenuString);\r
+\r
+ InitializeListHead (&Instance->Ip6NvData.ManualAddress);\r
+ InitializeListHead (&Instance->Ip6NvData.GatewayAddress);\r
+ InitializeListHead (&Instance->Ip6NvData.DnsAddress);\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+Error:\r
+ Ip6ConfigFormUnload (Instance);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Uninstall the HII Config Access protocol for network devices and free up the resources.\r
+\r
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigFormUnload (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_FORM_CALLBACK_INFO *CallbackInfo;\r
+ IP6_CONFIG_NVDATA *Ip6NvData;\r
+\r
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);\r
+ ASSERT (IpSb != NULL);\r
+\r
+ CallbackInfo = &Instance->CallbackInfo;\r
+\r
+ if (CallbackInfo->ChildHandle != NULL) {\r
+\r
+ //\r
+ // Close the child handle\r
+ //\r
+ gBS->CloseProtocol (\r
+ IpSb->Controller,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ IpSb->Image,\r
+ CallbackInfo->ChildHandle\r
+ );\r
+ //\r
+ // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL\r
+ //\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ CallbackInfo->ChildHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ CallbackInfo->HiiVendorDevicePath,\r
+ &gEfiHiiConfigAccessProtocolGuid,\r
+ &CallbackInfo->HiiConfigAccess,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ if (CallbackInfo->HiiVendorDevicePath != NULL) {\r
+ FreePool (CallbackInfo->HiiVendorDevicePath);\r
+ }\r
+\r
+ if (CallbackInfo->RegisteredHandle != NULL) {\r
+ //\r
+ // Remove HII package list\r
+ //\r
+ HiiRemovePackages (CallbackInfo->RegisteredHandle);\r
+ }\r
+\r
+ Ip6NvData = &Instance->Ip6NvData;\r
+\r
+ Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress);\r
+ Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress);\r
+ Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress);\r
+\r
+ Ip6NvData->ManualAddressCount = 0;\r
+ Ip6NvData->GatewayAddressCount = 0;\r
+ Ip6NvData->DnsAddressCount = 0;\r
+}\r
--- /dev/null
+/** @file\r
+ The header file of Ip6ConfigNv.c.\r
+\r
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _IP6_CONFIGNV_H_\r
+#define _IP6_CONFIGNV_H_\r
+\r
+#include "Ip6NvData.h"\r
+#include "Ip6ConfigImpl.h"\r
+\r
+extern UINT8 Ip6ConfigBin[];\r
+extern UINT8 Ip6DxeStrings[];\r
+\r
+#define IP6_HII_VENDOR_DEVICE_PATH_GUID \\r
+ { \\r
+ 0x13288098, 0xb11f, 0x45b9, { 0xbc, 0x4f, 0x91, 0xb5, 0x4b, 0xa3, 0x39, 0xb9 } \\r
+ }\r
+\r
+#define IP6_ETHERNET L"Ethernet"\r
+#define IP6_EXPERIMENTAL_ETHERNET L"Experimental Ethernet"\r
+#define IP6_ADDRESS_DELIMITER L' '\r
+#define IP6_LINK_LOCAL_PREFIX L"FE80::"\r
+\r
+typedef enum {\r
+ Ip6InterfaceTypeEthernet = 1,\r
+ Ip6InterfaceTypeExperimentalEthernet\r
+} IP6_INTERFACE_TYPE;\r
+\r
+typedef enum {\r
+ Ip6ConfigNvHostAddress,\r
+ Ip6ConfigNvGatewayAddress,\r
+ Ip6ConfigNvDnsAddress,\r
+ Ip6ConfigNvRouteTable\r
+} IP6_CONFIG_NV_ADDRESS_TYPE;\r
+\r
+/**\r
+ Install HII Config Access protocol for network device and allocate resources.\r
+\r
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form.\r
+\r
+ @retval EFI_SUCCESS The HII Config Access protocol is installed.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+ @retval Others Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigFormInit (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ );\r
+\r
+/**\r
+ Uninstall HII Config Access protocol for network device and free resource.\r
+\r
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form.\r
+\r
+**/\r
+VOID\r
+Ip6ConfigFormUnload (\r
+ IN OUT IP6_CONFIG_INSTANCE *Instance\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The driver binding and service binding protocol for IP6 driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = {\r
+ Ip6DriverBindingSupported,\r
+ Ip6DriverBindingStart,\r
+ Ip6DriverBindingStop,\r
+ 0xa,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including\r
+ both device drivers and bus drivers.\r
+\r
+ The entry point for IP6 driver which installs the driver\r
+ binding and component name protocol on its image.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ return EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gIp6DriverBinding,\r
+ ImageHandle,\r
+ &gIp6ComponentName,\r
+ &gIp6ComponentName2\r
+ );\r
+}\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to test.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ //\r
+ // Test for the MNP service binding Protocol\r
+ //\r
+ return gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+}\r
+\r
+/**\r
+ Clean up an IP6 service binding instance. It releases all\r
+ the resource allocated by the instance. The instance may be\r
+ partly initialized, or partly destroyed. If a resource is\r
+ destroyed, it is marked as that in case the destory failed and\r
+ being called again later.\r
+\r
+ @param[in] IpSb The IP6 service binding instance to clean up.\r
+\r
+ @retval EFI_SUCCESS The resource used by the instance are cleaned up.\r
+ @retval Others Failed to clean up some of the resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanService (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IPv6_ADDRESS AllNodes;\r
+ IP6_NEIGHBOR_ENTRY *NeighborCache;\r
+\r
+ Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance);\r
+\r
+ //\r
+ // Leave link-scope all-nodes multicast address (FF02::1)\r
+ //\r
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
+\r
+ Status = Ip6LeaveGroup (IpSb, &AllNodes);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (IpSb->DefaultInterface != NULL) {\r
+ Ip6CleanInterface (IpSb->DefaultInterface, NULL);\r
+ IpSb->DefaultInterface = NULL;\r
+ }\r
+\r
+ Ip6CleanDefaultRouterList (IpSb);\r
+\r
+ Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);\r
+ Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);\r
+\r
+ if (IpSb->RouteTable != NULL) {\r
+ Ip6CleanRouteTable (IpSb->RouteTable);\r
+ IpSb->RouteTable = NULL;\r
+ }\r
+\r
+ if (IpSb->InterfaceId != NULL) {\r
+ FreePool (IpSb->InterfaceId);\r
+ }\r
+\r
+ IpSb->InterfaceId = NULL;\r
+\r
+ Ip6CleanAssembleTable (&IpSb->Assemble);\r
+\r
+ if (IpSb->MnpChildHandle != NULL) {\r
+ if (IpSb->Mnp != NULL) {\r
+ IpSb->Mnp->Cancel (IpSb->Mnp, NULL);\r
+ IpSb->Mnp->Configure (IpSb->Mnp, NULL);\r
+ gBS->CloseProtocol (\r
+ IpSb->MnpChildHandle,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ IpSb->Image,\r
+ IpSb->Controller\r
+ );\r
+\r
+ IpSb->Mnp = NULL;\r
+ }\r
+\r
+ NetLibDestroyServiceChild (\r
+ IpSb->Controller,\r
+ IpSb->Image,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ IpSb->MnpChildHandle\r
+ );\r
+\r
+ IpSb->MnpChildHandle = NULL;\r
+ }\r
+\r
+ if (IpSb->RecvRequest.MnpToken.Event != NULL) {\r
+ gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event);\r
+ }\r
+\r
+ if (IpSb->Timer != NULL) {\r
+ gBS->SetTimer (IpSb->Timer, TimerCancel, 0);\r
+ gBS->CloseEvent (IpSb->Timer);\r
+\r
+ IpSb->Timer = NULL;\r
+ }\r
+\r
+ if (IpSb->FasterTimer != NULL) {\r
+ gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);\r
+ gBS->CloseEvent (IpSb->FasterTimer);\r
+\r
+ IpSb->FasterTimer = NULL;\r
+ }\r
+ //\r
+ // Free the Neighbor Discovery resources\r
+ //\r
+ while (!IsListEmpty (&IpSb->NeighborTable)) {\r
+ NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link);\r
+ Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create a new IP6 driver service binding protocol.\r
+\r
+ @param[in] Controller The controller that has MNP service binding\r
+ installed.\r
+ @param[in] ImageHandle The IP6 driver's image handle.\r
+ @param[out] Service The variable to receive the newly created IP6\r
+ service.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.\r
+ @retval EFI_SUCCESS A new IP6 service binding private is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CreateService (\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE ImageHandle,\r
+ OUT IP6_SERVICE **Service\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ EFI_STATUS Status;\r
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;\r
+ EFI_MANAGED_NETWORK_CONFIG_DATA *Config;\r
+ IP6_CONFIG_DATA_ITEM *DataItem;\r
+\r
+ ASSERT (Service != NULL);\r
+\r
+ *Service = NULL;\r
+\r
+ //\r
+ // allocate a service private data then initialize all the filed to\r
+ // empty resources, so if any thing goes wrong when allocating\r
+ // resources, Ip6CleanService can be called to clean it up.\r
+ //\r
+ IpSb = AllocateZeroPool (sizeof (IP6_SERVICE));\r
+\r
+ if (IpSb == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ IpSb->Signature = IP6_SERVICE_SIGNATURE;\r
+ IpSb->ServiceBinding.CreateChild = Ip6ServiceBindingCreateChild;\r
+ IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild;\r
+ IpSb->State = IP6_SERVICE_UNSTARTED;\r
+ IpSb->InDestroy = FALSE;\r
+\r
+ IpSb->NumChildren = 0;\r
+ InitializeListHead (&IpSb->Children);\r
+\r
+ InitializeListHead (&IpSb->Interfaces);\r
+ IpSb->DefaultInterface = NULL;\r
+ IpSb->RouteTable = NULL;\r
+\r
+ IpSb->RecvRequest.Signature = IP6_LINK_RX_SIGNATURE;\r
+ IpSb->RecvRequest.CallBack = NULL;\r
+ IpSb->RecvRequest.Context = NULL;\r
+ MnpToken = &IpSb->RecvRequest.MnpToken;\r
+ MnpToken->Event = NULL;\r
+ MnpToken->Status = EFI_NOT_READY;\r
+ MnpToken->Packet.RxData = NULL;\r
+\r
+ Ip6CreateAssembleTable (&IpSb->Assemble);\r
+\r
+ IpSb->MldCtrl.Mldv1QuerySeen = 0;\r
+ InitializeListHead (&IpSb->MldCtrl.Groups);\r
+\r
+ ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS));\r
+ IpSb->LinkLocalOk = FALSE;\r
+ IpSb->LinkLocalDadFail = FALSE;\r
+ IpSb->Dhcp6NeedStart = FALSE;\r
+ IpSb->Dhcp6NeedInfoRequest = FALSE;\r
+\r
+ IpSb->CurHopLimit = IP6_HOP_LIMIT;\r
+ IpSb->LinkMTU = IP6_MIN_LINK_MTU;\r
+ IpSb->BaseReachableTime = IP6_REACHABLE_TIME;\r
+ Ip6UpdateReachableTime (IpSb);\r
+ //\r
+ // RFC4861 RETRANS_TIMER: 1,000 milliseconds\r
+ //\r
+ IpSb->RetransTimer = IP6_RETRANS_TIMER;\r
+\r
+ IpSb->RoundRobin = 0;\r
+\r
+ InitializeListHead (&IpSb->NeighborTable);\r
+ InitializeListHead (&IpSb->DefaultRouterList);\r
+ InitializeListHead (&IpSb->OnlinkPrefix);\r
+ InitializeListHead (&IpSb->AutonomousPrefix);\r
+\r
+ IpSb->InterfaceIdLen = IP6_IF_ID_LEN;\r
+ IpSb->InterfaceId = NULL;\r
+\r
+ IpSb->RouterAdvertiseReceived = FALSE;\r
+ IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS;\r
+ IpSb->Ticks = 0;\r
+\r
+ IpSb->Image = ImageHandle;\r
+ IpSb->Controller = Controller;\r
+\r
+ IpSb->MnpChildHandle = NULL;\r
+ IpSb->Mnp = NULL;\r
+\r
+ Config = &IpSb->MnpConfigData;\r
+ Config->ReceivedQueueTimeoutValue = 0;\r
+ Config->TransmitQueueTimeoutValue = 0;\r
+ Config->ProtocolTypeFilter = IP6_ETHER_PROTO;\r
+ Config->EnableUnicastReceive = TRUE;\r
+ Config->EnableMulticastReceive = TRUE;\r
+ Config->EnableBroadcastReceive = TRUE;\r
+ Config->EnablePromiscuousReceive = FALSE;\r
+ Config->FlushQueuesOnReset = TRUE;\r
+ Config->EnableReceiveTimestamps = FALSE;\r
+ Config->DisableBackgroundPolling = FALSE;\r
+\r
+ ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));\r
+\r
+ IpSb->Timer = NULL;\r
+ IpSb->FasterTimer = NULL;\r
+\r
+ ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE));\r
+\r
+ IpSb->MacString = NULL;\r
+\r
+ //\r
+ // Create various resources. First create the route table, timer\r
+ // event, MNP token event and MNP child.\r
+ //\r
+\r
+ IpSb->RouteTable = Ip6CreateRouteTable ();\r
+ if (IpSb->RouteTable == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ Ip6TimerTicking,\r
+ IpSb,\r
+ &IpSb->Timer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ Ip6NdFasterTimerTicking,\r
+ IpSb,\r
+ &IpSb->FasterTimer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = NetLibCreateServiceChild (\r
+ Controller,\r
+ ImageHandle,\r
+ &gEfiManagedNetworkServiceBindingProtocolGuid,\r
+ &IpSb->MnpChildHandle\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ IpSb->MnpChildHandle,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ (VOID **) (&IpSb->Mnp),\r
+ ImageHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = Ip6ServiceConfigMnp (IpSb, TRUE);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER);\r
+ if (NetLibGetVlanId (IpSb->Controller) != 0) {\r
+ //\r
+ // This is a VLAN device, reduce MTU by VLAN tag length\r
+ //\r
+ IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN;\r
+ }\r
+ IpSb->OldMaxPacketSize = IpSb->MaxPacketSize;\r
+\r
+ //\r
+ // Currently only ETHERNET is supported in IPv6 stack, since\r
+ // link local address requires an IEEE 802 48-bit MACs for\r
+ // EUI-64 format interface identifier mapping.\r
+ //\r
+ if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = Ip6InitMld (IpSb);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds.\r
+ //\r
+ Status = gBS->SetTimer (IpSb->FasterTimer, TimerPeriodic, TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds.\r
+ //\r
+ Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_MS * IP6_ONE_SECOND_IN_MS);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ Ip6OnFrameReceived,\r
+ &IpSb->RecvRequest,\r
+ &MnpToken->Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE);\r
+ if (IpSb->DefaultInterface == NULL) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // If there is any manual address, set it.\r
+ //\r
+ DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress];\r
+ if (DataItem->Data.Ptr != NULL) {\r
+ DataItem->SetData (\r
+ &IpSb->Ip6ConfigInstance,\r
+ DataItem->DataSize,\r
+ DataItem->Data.Ptr\r
+ );\r
+ }\r
+\r
+ InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);\r
+\r
+ *Service = IpSb;\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ Ip6CleanService (IpSb);\r
+ FreePool (IpSb);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCES This driver is added to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Test for the Ip6 service binding protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+\r
+ if (Status == EFI_SUCCESS) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ ASSERT (IpSb != NULL);\r
+\r
+ //\r
+ // Install the Ip6ServiceBinding Protocol onto ControlerHandle\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &ControllerHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ &IpSb->ServiceBinding,\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ &IpSb->Ip6ConfigInstance.Ip6Config,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ Ip6CleanService (IpSb);\r
+ FreePool (IpSb);\r
+ } else {\r
+ //\r
+ // Initialize the IP6 ID\r
+ //\r
+ mIp6Id = NET_RANDOM (NetRandomInitSeed ());\r
+\r
+ Ip6SetVariableData (IpSb);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Stop this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number\r
+ of children is zero, stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL\r
+ if NumberOfChildren is 0.\r
+\r
+ @retval EFI_SUCCESS The device was stopped.\r
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ )\r
+{\r
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_HANDLE NicHandle;\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsDhcp6;\r
+ EFI_TPL OldTpl;\r
+ INTN State;\r
+\r
+ IsDhcp6 = FALSE;\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);\r
+\r
+ if (NicHandle != NULL) {\r
+ //\r
+ // DriverBindingStop is triggered by the uninstallation of the EFI DHCPv6\r
+ // Protocol used by Ip6Config.\r
+ //\r
+ IsDhcp6 = TRUE;\r
+ } else {\r
+\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);\r
+\r
+ if (NicHandle == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ NicHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ (VOID **) &ServiceBinding,\r
+ This->DriverBindingHandle,\r
+ NicHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding);\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (IpSb->InDestroy) {\r
+ Status = EFI_SUCCESS;\r
+ goto Exit;\r
+ }\r
+\r
+ if (IsDhcp6) {\r
+\r
+ Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance);\r
+ gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event);\r
+ IpSb->Ip6ConfigInstance.Dhcp6Event = NULL;\r
+ } else if (NumberOfChildren == 0) {\r
+\r
+ IpSb->InDestroy = TRUE;\r
+ State = IpSb->State;\r
+ IpSb->State = IP6_SERVICE_DESTROY;\r
+\r
+ //\r
+ // Clear the variable data.\r
+ //\r
+ Ip6ClearVariableData (IpSb);\r
+\r
+ Status = Ip6CleanService (IpSb);\r
+ if (EFI_ERROR (Status)) {\r
+ IpSb->State = State;\r
+ goto Exit;\r
+ }\r
+\r
+ Status = gBS->UninstallMultipleProtocolInterfaces (\r
+ NicHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ ServiceBinding,\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ &IpSb->Ip6ConfigInstance.Ip6Config,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ FreePool (IpSb);\r
+ } else {\r
+ //\r
+ // NumberOfChildren is not zero, destroy all IP6 children instances.\r
+ //\r
+ while (!IsListEmpty (&IpSb->Children)) {\r
+ IpInstance = NET_LIST_HEAD (&IpSb->Children, IP6_PROTOCOL, Link);\r
+ ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle);\r
+ }\r
+\r
+ if (IpSb->NumChildren != 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+Exit:\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Creates a child handle with a set of I/O services.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ChildHandle Pointer to the handle of the child to create. If\r
+ it is NULL, then a new handle is created. If it\r
+ is not NULL, then the I/O services are added to\r
+ the existing child handle.\r
+\r
+ @retval EFI_SUCCES The child handle was created with the I/O services.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create\r
+ the child.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE *ChildHandle\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ VOID *Mnp;\r
+\r
+ if ((This == NULL) || (ChildHandle == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpSb = IP6_SERVICE_FROM_PROTOCOL (This);\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ IpInstance = AllocatePool (sizeof (IP6_PROTOCOL));\r
+\r
+ if (IpInstance == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Ip6InitProtocol (IpSb, IpInstance);\r
+\r
+ //\r
+ // Install Ip6 onto ChildHandle\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ &IpInstance->Ip6Proto,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ IpInstance->Handle = *ChildHandle;\r
+\r
+ //\r
+ // Open the Managed Network protocol BY_CHILD.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ IpSb->MnpChildHandle,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ (VOID **) &Mnp,\r
+ gIp6DriverBinding.DriverBindingHandle,\r
+ IpInstance->Handle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ &IpInstance->Ip6Proto,\r
+ NULL\r
+ );\r
+\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Insert it into the service binding instance.\r
+ //\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ InsertTailList (&IpSb->Children, &IpInstance->Link);\r
+ IpSb->NumChildren++;\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ON_ERROR:\r
+\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ Ip6CleanProtocol (IpInstance);\r
+\r
+ FreePool (IpInstance);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Destroys a child handle with a set of I/O services.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The I/O services were removed from the child\r
+ handle.\r
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services\r
+ that are being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.\r
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because\r
+ its I/O services are being used.\r
+ @retval other The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_IP6_PROTOCOL *Ip6;\r
+ EFI_TPL OldTpl;\r
+ INTN State;\r
+\r
+ if ((This == NULL) || (ChildHandle == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Retrieve the private context data structures\r
+ //\r
+ IpSb = IP6_SERVICE_FROM_PROTOCOL (This);\r
+\r
+ Status = gBS->OpenProtocol (\r
+ ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ (VOID **) &Ip6,\r
+ gIp6DriverBinding.DriverBindingHandle,\r
+ ChildHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6);\r
+\r
+ if (IpInstance->Service != IpSb) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // A child can be destroyed more than once. For example,\r
+ // Ip6DriverBindingStop will destory all of its children.\r
+ // when UDP driver is being stopped, it will destory all\r
+ // the IP child it opens.\r
+ //\r
+ if (IpInstance->State == IP6_STATE_DESTROY) {\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ State = IpInstance->State;\r
+ IpInstance->State = IP6_STATE_DESTROY;\r
+\r
+ //\r
+ // Close the Managed Network protocol.\r
+ //\r
+ gBS->CloseProtocol (\r
+ IpSb->MnpChildHandle,\r
+ &gEfiManagedNetworkProtocolGuid,\r
+ gIp6DriverBinding.DriverBindingHandle,\r
+ ChildHandle\r
+ );\r
+\r
+ //\r
+ // Uninstall the IP6 protocol first. Many thing happens during\r
+ // this:\r
+ // 1. The consumer of the IP6 protocol will be stopped if it\r
+ // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is\r
+ // stopped, IP driver's stop function will be called, and uninstall\r
+ // EFI_IP6_PROTOCOL will trigger the UDP's stop function. This\r
+ // makes it possible to create the network stack bottom up, and\r
+ // stop it top down.\r
+ // 2. the upper layer will recycle the received packet. The recycle\r
+ // event's TPL is higher than this function. The recycle events\r
+ // will be called back before preceeding. If any packets not recycled,\r
+ // that means there is a resource leak.\r
+ //\r
+ Status = gBS->UninstallProtocolInterface (\r
+ ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ &IpInstance->Ip6Proto\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = Ip6CleanProtocol (IpInstance);\r
+\r
+ Ip6SetVariableData (IpSb);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->InstallMultipleProtocolInterfaces (\r
+ &ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ Ip6,\r
+ NULL\r
+ );\r
+\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ RemoveEntryList (&IpInstance->Link);\r
+ ASSERT (IpSb->NumChildren > 0);\r
+ IpSb->NumChildren--;\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ FreePool (IpInstance);\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ IpInstance->State = State;\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+ The driver binding and service binding protocol for IP6 driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_DRIVER_H__\r
+#define __EFI_IP6_DRIVER_H__\r
+\r
+extern EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding;\r
+extern EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2;\r
+\r
+/**\r
+ Clean up an IP6 service binding instance. It releases all\r
+ the resource allocated by the instance. The instance may be\r
+ partly initialized, or partly destroyed. If a resource is\r
+ destroyed, it is marked as that in case the destory failed and\r
+ being called again later.\r
+\r
+ @param[in] IpSb The IP6 service binding instance to clean up.\r
+\r
+ @retval EFI_SUCCESS The resource used by the instance are cleaned up.\r
+ @retval Others Failed to clean up some of the resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanService (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+//\r
+// Function prototype for the driver's entry point\r
+//\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including\r
+ both device drivers and bus drivers.\r
+\r
+ The entry point for IP6 driver which installs the driver\r
+ binding and component name protocol on its image.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ );\r
+\r
+//\r
+// Function prototypes for the Drivr Binding Protocol\r
+//\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to test.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCES This driver is added to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Stop this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number\r
+ of children is zero, stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL\r
+ if NumberOfChildren is 0.\r
+\r
+ @retval EFI_SUCCESS The device was stopped.\r
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ );\r
+\r
+//\r
+// Function ptototypes for the ServiceBinding Prococol\r
+//\r
+\r
+/**\r
+ Creates a child handle with a set of I/O services.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ChildHandle Pointer to the handle of the child to create. If\r
+ it is NULL, then a new handle is created. If it\r
+ is not NULL, then the I/O services are added to\r
+ the existing child handle.\r
+\r
+ @retval EFI_SUCCES The child handle was created with the I/O services.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create\r
+ the child.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE *ChildHandle\r
+ );\r
+\r
+/**\r
+ Destroys a child handle with a set of I/O services.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The I/O services were removed from the child\r
+ handle.\r
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services\r
+ that are being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.\r
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because\r
+ its I/O services are being used.\r
+ @retval other The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ );\r
+\r
+#endif\r
--- /dev/null
+## @file\r
+# Component description file for Ip6 module.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = Ip6Dxe\r
+ FILE_GUID = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C\r
+ MODULE_TYPE = UEFI_DRIVER\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = Ip6DriverEntryPoint\r
+ UNLOAD_IMAGE = NetLibDefaultUnload\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC\r
+#\r
+# DRIVER_BINDING = gIp6DriverBinding\r
+# COMPONENT_NAME = gIp6ComponentName\r
+# COMPONENT_NAME2 = gIp6ComponentName2\r
+#\r
+\r
+[Sources]\r
+ Ip6Output.h\r
+ Ip6Option.h\r
+ Ip6Input.h\r
+ Ip6Nd.h\r
+ Ip6Mld.h\r
+ Ip6Impl.c\r
+ Ip6Driver.c\r
+ ComponentName.c\r
+ Ip6Nd.c\r
+ Ip6Input.c\r
+ Ip6ConfigImpl.c\r
+ Ip6ConfigImpl.h\r
+ Ip6Impl.h\r
+ Ip6Option.c\r
+ Ip6If.h\r
+ Ip6Icmp.h\r
+ Ip6Mld.c\r
+ Ip6Common.c\r
+ Ip6Route.c\r
+ Ip6If.c\r
+ Ip6Driver.h\r
+ Ip6Output.c\r
+ Ip6Icmp.c\r
+ Ip6Common.h\r
+ Ip6Route.h\r
+ Ip6DxeStrings.uni\r
+ Ip6NvData.h\r
+ Ip6ConfigNv.c\r
+ Ip6ConfigNv.h\r
+ Ip6Config.vfr\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+ BaseLib\r
+ BaseMemoryLib\r
+ DevicePathLib\r
+ HiiLib\r
+ UefiHiiServicesLib\r
+ PrintLib\r
+ MemoryAllocationLib\r
+ UefiBootServicesTableLib\r
+ UefiDriverEntryPoint\r
+ UefiRuntimeServicesTableLib\r
+ UefiLib\r
+ DebugLib\r
+ NetLib\r
+ DpcLib\r
+\r
+[Protocols]\r
+ gEfiManagedNetworkServiceBindingProtocolGuid\r
+ gEfiManagedNetworkProtocolGuid\r
+ gEfiIp6ServiceBindingProtocolGuid\r
+ gEfiIp6ProtocolGuid\r
+ gEfiIp6ConfigProtocolGuid\r
+ gEfiDhcp6ServiceBindingProtocolGuid\r
+ gEfiDhcp6ProtocolGuid\r
+ gEfiIpSecProtocolGuid\r
+ gEfiHiiConfigAccessProtocolGuid\r
+\r
+[Guids]\r
+ gEfiIfrTianoGuid ## CONSUMES ## GUID\r
--- /dev/null
+/** @file\r
+ The ICMPv6 handle routines to process the ICMPv6 control messages.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = {\r
+\r
+ {\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_NO_ROUTE_TO_DEST\r
+ },\r
+ {\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_COMM_PROHIBITED\r
+ },\r
+ {\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_BEYOND_SCOPE\r
+ },\r
+ {\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_ADDR_UNREACHABLE\r
+ },\r
+ {\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_PORT_UNREACHABLE\r
+ },\r
+ {\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_SOURCE_ADDR_FAILED\r
+ },\r
+ {\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_ROUTE_REJECTED\r
+ },\r
+\r
+ {\r
+ ICMP_V6_PACKET_TOO_BIG,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+\r
+ {\r
+ ICMP_V6_TIME_EXCEEDED,\r
+ ICMP_V6_TIMEOUT_HOP_LIMIT\r
+ },\r
+ {\r
+ ICMP_V6_TIME_EXCEEDED,\r
+ ICMP_V6_TIMEOUT_REASSEMBLE\r
+ },\r
+\r
+ {\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ ICMP_V6_ERRONEOUS_HEADER\r
+ },\r
+ {\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ ICMP_V6_UNRECOGNIZE_NEXT_HDR\r
+ },\r
+ {\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ ICMP_V6_UNRECOGNIZE_OPTION\r
+ },\r
+\r
+ {\r
+ ICMP_V6_ECHO_REQUEST,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+ {\r
+ ICMP_V6_ECHO_REPLY,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+\r
+ {\r
+ ICMP_V6_LISTENER_QUERY,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+ {\r
+ ICMP_V6_LISTENER_REPORT,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+ {\r
+ ICMP_V6_LISTENER_REPORT_2,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+ {\r
+ ICMP_V6_LISTENER_DONE,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+\r
+ {\r
+ ICMP_V6_ROUTER_SOLICIT,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+ {\r
+ ICMP_V6_ROUTER_ADVERTISE,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+ {\r
+ ICMP_V6_NEIGHBOR_SOLICIT,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+ {\r
+ ICMP_V6_NEIGHBOR_ADVERTISE,\r
+ ICMP_V6_DEFAULT_CODE\r
+ },\r
+};\r
+\r
+/**\r
+ Reply an ICMPv6 echo request.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the ICMPv6 informational message.\r
+ @param[in] Packet The content of the ICMPv6 message with the IP head\r
+ removed.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+ @retval EFI_SUCCESS Successfully answered the ICMPv6 Echo request.\r
+ @retval Others Failed to answer the ICMPv6 Echo request.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IcmpReplyEcho (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_INFORMATION_HEAD *Icmp;\r
+ NET_BUF *Data;\r
+ EFI_STATUS Status;\r
+ EFI_IP6_HEADER ReplyHead;\r
+\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ //\r
+ // make a copy the packet, it is really a bad idea to\r
+ // send the MNP's buffer back to MNP.\r
+ //\r
+ Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN);\r
+ if (Data == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Change the ICMP type to echo reply, exchange the source\r
+ // and destination, then send it. The source is updated to\r
+ // use specific destination. See RFC1122. SRR/RR option\r
+ // update is omitted.\r
+ //\r
+ Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL);\r
+ if (Icmp == NULL) {\r
+ NetbufFree (Data);\r
+ goto Exit;\r
+ }\r
+\r
+ Icmp->Head.Type = ICMP_V6_ECHO_REPLY;\r
+ Icmp->Head.Checksum = 0;\r
+\r
+ //\r
+ // Generate the IPv6 basic header\r
+ // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address,\r
+ // the Source address of the Echo Reply must be the same address.\r
+ //\r
+ ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER));\r
+\r
+ ReplyHead.PayloadLength = HTONS ((UINT16) (Packet->TotalSize));\r
+ ReplyHead.NextHeader = IP6_ICMP;\r
+ ReplyHead.HopLimit = IpSb->CurHopLimit;\r
+ IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress);\r
+\r
+ if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {\r
+ IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress);\r
+ }\r
+\r
+ //\r
+ // If source is unspecified, Ip6Output will select a source for us\r
+ //\r
+ Status = Ip6Output (\r
+ IpSb,\r
+ NULL,\r
+ NULL,\r
+ Data,\r
+ &ReplyHead,\r
+ NULL,\r
+ 0,\r
+ Ip6SysPacketSent,\r
+ NULL\r
+ );\r
+\r
+Exit:\r
+ NetbufFree (Packet);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process Packet Too Big message sent by a router in response to a packet that\r
+ it cannot forward because the packet is larger than the MTU of outgoing link.\r
+ Since this driver already uses IPv6 minimum link MTU as the maximum packet size,\r
+ if Packet Too Big message is still received, do not reduce the packet size, but\r
+ rather include a Fragment header in the subsequent packets.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the ICMPv6 error packet.\r
+ @param[in] Packet The content of the ICMPv6 error with the IP head\r
+ removed.\r
+\r
+ @retval EFI_SUCCESS The ICMPv6 error processed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of\r
+ resource.\r
+ @retval EFI_NOT_FOUND The packet too big message is not sent to us.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessPacketTooBig (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_ERROR_HEAD Icmp;\r
+ UINT32 Mtu;\r
+ IP6_ROUTE_ENTRY *RouteEntry;\r
+ EFI_IPv6_ADDRESS *DestAddress;\r
+\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+ Mtu = NTOHL (Icmp.Fourth);\r
+ DestAddress = &Icmp.IpHead.DestinationAddress;\r
+\r
+ if (Mtu < IP6_MIN_LINK_MTU) {\r
+ //\r
+ // Normally the multicast address is considered to be on-link and not recorded\r
+ // in route table. Here it is added into the table since the MTU information\r
+ // need be recorded.\r
+ //\r
+ if (IP6_IS_MULTICAST (DestAddress)) {\r
+ RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL);\r
+ if (RouteEntry == NULL) {\r
+ NetbufFree (Packet);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG;\r
+ InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link);\r
+ IpSb->RouteTable->TotalNum++;\r
+ } else {\r
+ RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL);\r
+ if (RouteEntry == NULL) {\r
+ NetbufFree (Packet);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG;\r
+\r
+ Ip6FreeRouteEntry (RouteEntry);\r
+ }\r
+ }\r
+\r
+ NetbufFree (Packet);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Process the ICMPv6 error packet, and deliver the packet to upper layer.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the ICMPv6 error packet.\r
+ @param[in] Packet The content of the ICMPv6 error with the IP head\r
+ removed.\r
+\r
+ @retval EFI_SUCCESS The ICMPv6 error processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessIcmpError (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_ERROR_HEAD Icmp;\r
+\r
+ //\r
+ // Check the validity of the packet\r
+ //\r
+ if (Packet->TotalSize < sizeof (Icmp)) {\r
+ goto DROP;\r
+ }\r
+\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+ if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) {\r
+ return Ip6ProcessPacketTooBig (IpSb, Head, Packet);\r
+ }\r
+\r
+ //\r
+ // Notify the upper-layer process that an ICMPv6 eror message is received.\r
+ //\r
+ IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;\r
+ return Ip6Demultiplex (IpSb, Head, Packet);\r
+\r
+DROP:\r
+ NetbufFree (Packet);\r
+ Packet = NULL;\r
+ return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+ Process the ICMPv6 informational messages. If it is an ICMPv6 echo\r
+ request, answer it. If it is a MLD message, trigger MLD routines to\r
+ process it. If it is a ND message, trigger ND routines to process it.\r
+ Otherwise, deliver it to upper layer.\r
+\r
+ @param[in] IpSb The IP service that receivd the packet.\r
+ @param[in] Head The IP head of the ICMPv6 informational packet.\r
+ @param[in] Packet The content of the ICMPv6 informational packet\r
+ with IP head removed.\r
+\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval EFI_SUCCESS The ICMPv6 informational message processed.\r
+ @retval Others Failed to process ICMPv6 informational message.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessIcmpInformation (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_INFORMATION_HEAD Icmp;\r
+ EFI_STATUS Status;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);\r
+ ASSERT (Head != NULL);\r
+\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ switch (Icmp.Head.Type) {\r
+ case ICMP_V6_ECHO_REQUEST:\r
+ //\r
+ // If ICMPv6 echo, reply it\r
+ //\r
+ if (Icmp.Head.Code == 0) {\r
+ Status = Ip6IcmpReplyEcho (IpSb, Head, Packet);\r
+ }\r
+ break;\r
+ case ICMP_V6_LISTENER_QUERY:\r
+ Status = Ip6ProcessMldQuery (IpSb, Head, Packet);\r
+ break;\r
+ case ICMP_V6_LISTENER_REPORT:\r
+ case ICMP_V6_LISTENER_REPORT_2:\r
+ Status = Ip6ProcessMldReport (IpSb, Head, Packet);\r
+ break;\r
+ case ICMP_V6_NEIGHBOR_SOLICIT:\r
+ Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet);\r
+ break;\r
+ case ICMP_V6_NEIGHBOR_ADVERTISE:\r
+ Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet);\r
+ break;\r
+ case ICMP_V6_ROUTER_ADVERTISE:\r
+ Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet);\r
+ break;\r
+ case ICMP_V6_REDIRECT:\r
+ Status = Ip6ProcessRedirect (IpSb, Head, Packet);\r
+ break;\r
+ case ICMP_V6_ECHO_REPLY:\r
+ Status = Ip6Demultiplex (IpSb, Head, Packet);\r
+ break;\r
+ default:\r
+ Status = EFI_INVALID_PARAMETER;\r
+ break;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Handle the ICMPv6 packet. First validate the message format,\r
+ then, according to the message types, process it as an informational packet or\r
+ an error packet.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the ICMPv6 packet.\r
+ @param[in] Packet The content of the ICMPv6 packet with IP head\r
+ removed.\r
+\r
+ @retval EFI_INVALID_PARAMETER The packet is malformated.\r
+ @retval EFI_SUCCESS The ICMPv6 message successfully processed.\r
+ @retval Others Failed to handle the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IcmpHandle (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_HEAD Icmp;\r
+ UINT16 PseudoCheckSum;\r
+ UINT16 CheckSum;\r
+\r
+ //\r
+ // Check the validity of the incoming packet.\r
+ //\r
+ if (Packet->TotalSize < sizeof (Icmp)) {\r
+ goto DROP;\r
+ }\r
+\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+ //\r
+ // Make sure checksum is valid.\r
+ //\r
+ PseudoCheckSum = NetIp6PseudoHeadChecksum (\r
+ &Head->SourceAddress,\r
+ &Head->DestinationAddress,\r
+ IP6_ICMP,\r
+ Packet->TotalSize\r
+ );\r
+ CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet));\r
+ if (CheckSum != 0) {\r
+ goto DROP;\r
+ }\r
+\r
+ //\r
+ // According to the packet type, call corresponding process\r
+ //\r
+ if (Icmp.Type <= ICMP_V6_ERROR_MAX) {\r
+ return Ip6ProcessIcmpError (IpSb, Head, Packet);\r
+ } else {\r
+ return Ip6ProcessIcmpInformation (IpSb, Head, Packet);\r
+ }\r
+\r
+DROP:\r
+ NetbufFree (Packet);\r
+ return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+ Retrieve the Prefix address according to the PrefixLength by clear the useless\r
+ bits.\r
+\r
+ @param[in] PrefixLength The prefix length of the prefix.\r
+ @param[in, out] Prefix On input, points to the original prefix address\r
+ with dirty bits; on output, points to the updated\r
+ address with useless bit clear.\r
+\r
+**/\r
+VOID\r
+Ip6GetPrefix (\r
+ IN UINT8 PrefixLength,\r
+ IN OUT EFI_IPv6_ADDRESS *Prefix\r
+ )\r
+{\r
+ UINT8 Byte;\r
+ UINT8 Bit;\r
+ UINT8 Mask;\r
+ UINT8 Value;\r
+\r
+ ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_NUM));\r
+\r
+ if (PrefixLength == 0) {\r
+ ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));\r
+ return ;\r
+ }\r
+\r
+ if (PrefixLength == IP6_PREFIX_NUM - 1) {\r
+ return ;\r
+ }\r
+\r
+ Byte = (UINT8) (PrefixLength / 8);\r
+ Bit = (UINT8) (PrefixLength % 8);\r
+ Value = Prefix->Addr[Byte];\r
+\r
+ if ((Byte > 0) && (Byte < 16)) {\r
+ ZeroMem (Prefix->Addr + Byte, 16 - Byte);\r
+ }\r
+\r
+ if (Bit > 0) {\r
+ Mask = (UINT8) (0xFF << (8 - Bit));\r
+ Prefix->Addr[Byte] = (UINT8) (Value & Mask);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Check whether the DestinationAddress is an anycast address.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] DestinationAddress Points to the Destination Address of the packet.\r
+\r
+ @retval TRUE The DestinationAddress is anycast address.\r
+ @retval FALSE The DestinationAddress is not anycast address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsAnycast (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress\r
+ )\r
+{\r
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;\r
+ EFI_IPv6_ADDRESS Prefix;\r
+ BOOLEAN Flag;\r
+\r
+ ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ Flag = FALSE;\r
+\r
+ //\r
+ // If the address is known as on-link or autonomous prefix, record it as\r
+ // anycast address.\r
+ //\r
+ do {\r
+ PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress);\r
+ if (PrefixEntry != NULL) {\r
+ IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix);\r
+ Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix);\r
+ if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ Flag = (BOOLEAN) !Flag;\r
+ } while (Flag);\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Generate ICMPv6 error message and send it out to DestinationAddress. Currently\r
+ Destination Unreachable message, Time Exceeded message and Parameter Problem\r
+ message are supported.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Packet The packet which invoking ICMPv6 error.\r
+ @param[in] SourceAddress If not NULL, points to the SourceAddress.\r
+ Otherwise, the IP layer will select a source address\r
+ according to the DestinationAddress.\r
+ @param[in] DestinationAddress Points to the Destination Address of the ICMPv6\r
+ error message.\r
+ @param[in] Type The type of the ICMPv6 message.\r
+ @param[in] Code The additional level of the ICMPv6 message.\r
+ @param[in] Pointer If not NULL, identifies the octet offset within\r
+ the invoking packet where the error was detected.\r
+\r
+ @retval EFI_INVALID_PARAMETER The packet is malformated.\r
+ @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The ICMPv6 message was successfully sent out.\r
+ @retval Others Failed to generate the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendIcmpError (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress,\r
+ IN UINT8 Type,\r
+ IN UINT8 Code,\r
+ IN UINT32 *Pointer OPTIONAL\r
+ )\r
+{\r
+ UINT32 PacketLen;\r
+ NET_BUF *ErrorMsg;\r
+ UINT16 PayloadLen;\r
+ EFI_IP6_HEADER Head;\r
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+ UINT8 *ErrorBody;\r
+\r
+ if (DestinationAddress == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // An ICMPv6 error message must not be originated as a result of receiving\r
+ // a packet whose source address does not uniquely identify a single node --\r
+ // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address\r
+ // known by the ICMP message originator to be an IPv6 anycast address.\r
+ //\r
+ if (NetIp6IsUnspecifiedAddr (DestinationAddress) ||\r
+ IP6_IS_MULTICAST (DestinationAddress) ||\r
+ Ip6IsAnycast (IpSb, DestinationAddress)\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ switch (Type) {\r
+ case ICMP_V6_DEST_UNREACHABLE:\r
+ case ICMP_V6_TIME_EXCEEDED:\r
+ break;\r
+\r
+ case ICMP_V6_PARAMETER_PROBLEM:\r
+ if (Pointer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize;\r
+\r
+ if (PacketLen > IpSb->MaxPacketSize) {\r
+ PacketLen = IpSb->MaxPacketSize;\r
+ }\r
+\r
+ ErrorMsg = NetbufAlloc (PacketLen);\r
+ if (ErrorMsg == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Create the basic IPv6 header.\r
+ //\r
+ ZeroMem (&Head, sizeof (EFI_IP6_HEADER));\r
+\r
+ Head.PayloadLength = HTONS (PayloadLen);\r
+ Head.NextHeader = IP6_ICMP;\r
+ Head.HopLimit = IpSb->CurHopLimit;\r
+\r
+ if (SourceAddress != NULL) {\r
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+ } else {\r
+ ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+\r
+ NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Fill in the ICMP error message head\r
+ //\r
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+ if (IcmpHead == NULL) {\r
+ NetbufFree (ErrorMsg);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+ IcmpHead->Head.Type = Type;\r
+ IcmpHead->Head.Code = Code;\r
+\r
+ if (Pointer != NULL) {\r
+ IcmpHead->Fourth = HTONL (*Pointer);\r
+ }\r
+\r
+ //\r
+ // Fill in the ICMP error message body\r
+ //\r
+ PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD);\r
+ ErrorBody = NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE);\r
+ if (ErrorBody != NULL) {\r
+ ZeroMem (ErrorBody, PayloadLen);\r
+ NetbufCopy (Packet, 0, PayloadLen, ErrorBody);\r
+ }\r
+\r
+ //\r
+ // Transmit the packet\r
+ //\r
+ return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Header file for ICMPv6 protocol.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_ICMP_H__\r
+#define __EFI_IP6_ICMP_H__\r
+\r
+#define ICMP_V6_DEFAULT_CODE 0\r
+\r
+#define ICMP_V6_ERROR_MAX 127\r
+\r
+//\r
+// ICMPv6 message classes, each class of ICMPv6 message shares\r
+// a common message format. INVALID_MESSAGE is only a flag.\r
+//\r
+#define ICMP_V6_INVALID_MESSAGE 0\r
+#define ICMP_V6_ERROR_MESSAGE 1\r
+#define ICMP_V6_INFORMATION_MESSAGE 2\r
+\r
+\r
+extern EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[];\r
+\r
+/**\r
+ Handle the ICMPv6 packet. First validate the message format,\r
+ then, according to the message types, process it as an informational packet or\r
+ an error packet.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the ICMPv6 packet.\r
+ @param[in] Packet The content of the ICMPv6 packet with IP head\r
+ removed.\r
+\r
+ @retval EFI_INVALID_PARAMETER The packet is malformated.\r
+ @retval EFI_SUCCESS The ICMPv6 message successfully processed.\r
+ @retval Others Failed to handle the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IcmpHandle (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ Check whether the DestinationAddress is an anycast address.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] DestinationAddress Points to the Destination Address of the packet.\r
+\r
+ @retval TRUE The DestinationAddress is anycast address.\r
+ @retval FALSE The DestinationAddress is not anycast address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsAnycast (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress\r
+ );\r
+\r
+/**\r
+ Generate ICMPv6 error message and send it out to DestinationAddress. Currently\r
+ Destination Unreachable message, Time Exceeded message and Parameter Problem\r
+ message are supported.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Packet The packet which invoking ICMPv6 error.\r
+ @param[in] SourceAddress If not NULL, points to the SourceAddress.\r
+ Otherwise, the IP layer will select a source address\r
+ according to the DestinationAddress.\r
+ @param[in] DestinationAddress Points to the Destination Address of the ICMPv6\r
+ error message.\r
+ @param[in] Type The type of the ICMPv6 message.\r
+ @param[in] Code The additional level of the ICMPv6 message.\r
+ @param[in] Pointer If not NULL, identifies the octet offset within\r
+ the invoking packet where the error was detected.\r
+\r
+ @retval EFI_INVALID_PARAMETER The packet is malformated.\r
+ @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The ICMPv6 message was successfully sent out.\r
+ @retval Others Failed to generate the ICMPv6 packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendIcmpError (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress,\r
+ IN UINT8 Type,\r
+ IN UINT8 Code,\r
+ IN UINT32 *Pointer OPTIONAL\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ Implement IP6 pesudo interface.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+ Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.\r
+\r
+ @param[in] Event The transmit token's event.\r
+ @param[in] Context The Context which is pointed to the token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameSent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Fileter function to cancel all the frame related to an IP instance.\r
+\r
+ @param[in] Frame The transmit request to test whether to cancel.\r
+ @param[in] Context The context which is the Ip instance that issued\r
+ the transmit.\r
+\r
+ @retval TRUE The frame belongs to this instance and is to be\r
+ removed.\r
+ @retval FALSE The frame doesn't belong to this instance.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6CancelInstanceFrame (\r
+ IN IP6_LINK_TX_TOKEN *Frame,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ if (Frame->IpInstance == (IP6_PROTOCOL *) Context) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Set the interface's address. This will trigger the DAD process for the\r
+ address to set. To set an already set address, the lifetimes wil be\r
+ updated to the new value passed in.\r
+\r
+ @param[in] Interface The interface to set the address.\r
+ @param[in] Ip6Addr The interface's to be assigned IPv6 address.\r
+ @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast.\r
+ Otherwise, it is not anycast.\r
+ @param[in] PrefixLength The prefix length of the Ip6Addr.\r
+ @param[in] ValidLifetime The valid lifetime for this address.\r
+ @param[in] PreferredLifetime The preferred lifetime for this address.\r
+ @param[in] DadCallback The caller's callback to trigger when DAD finishes.\r
+ This is an optional parameter that may be NULL.\r
+ @param[in] Context The context that will be passed to DadCallback.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The interface is scheduled to be configured with\r
+ the specified address.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to\r
+ lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetAddress (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN EFI_IPv6_ADDRESS *Ip6Addr,\r
+ IN BOOLEAN IsAnycast,\r
+ IN UINT8 PrefixLength,\r
+ IN UINT32 ValidLifetime,\r
+ IN UINT32 PreferredLifetime,\r
+ IN IP6_DAD_CALLBACK DadCallback OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_ADDRESS_INFO *AddressInfo;\r
+ LIST_ENTRY *Entry;\r
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;\r
+ UINT64 Delay;\r
+ IP6_DELAY_JOIN_LIST *DelayNode;\r
+\r
+ NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);\r
+\r
+ IpSb = Interface->Service;\r
+\r
+ if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) {\r
+ ASSERT (AddressInfo != NULL);\r
+ //\r
+ // Update the lifetime.\r
+ //\r
+ AddressInfo->ValidLifetime = ValidLifetime;\r
+ AddressInfo->PreferredLifetime = PreferredLifetime;\r
+\r
+ if (DadCallback != NULL) {\r
+ DadCallback (TRUE, Ip6Addr, Context);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO));\r
+ if (AddressInfo == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ AddressInfo->Signature = IP6_ADDR_INFO_SIGNATURE;\r
+ IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr);\r
+ AddressInfo->IsAnycast = IsAnycast;\r
+ AddressInfo->PrefixLength = PrefixLength;\r
+ AddressInfo->ValidLifetime = ValidLifetime;\r
+ AddressInfo->PreferredLifetime = PreferredLifetime;\r
+\r
+ if (AddressInfo->PrefixLength == 0) {\r
+ //\r
+ // Find an appropriate prefix from on-link prefixes and update the prefixlength.\r
+ // Longest prefix match is used here.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {\r
+ PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+\r
+ if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {\r
+ AddressInfo->PrefixLength = PrefixEntry->PrefixLength;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (AddressInfo->PrefixLength == 0) {\r
+ //\r
+ // If the prefix length is still zero, try the autonomous prefixes.\r
+ // Longest prefix match is used here.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {\r
+ PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+\r
+ if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {\r
+ AddressInfo->PrefixLength = PrefixEntry->PrefixLength;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (AddressInfo->PrefixLength == 0) {\r
+ //\r
+ // BUGBUG: Stil fail, use 64 as the default prefix length.\r
+ //\r
+ AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+ }\r
+\r
+\r
+ //\r
+ // Node should delay joining the solicited-node mulitcast address by a random delay\r
+ // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second).\r
+ // Thus queue the address to be processed in Duplicate Address Detection module\r
+ // after the delay time (in milliseconds).\r
+ //\r
+ Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ());\r
+ Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS);\r
+ Delay = RShiftU64 (Delay, 32);\r
+\r
+ DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST));\r
+ if (DelayNode == NULL) {\r
+ FreePool (AddressInfo);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ DelayNode->DelayTime = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS));\r
+ DelayNode->Interface = Interface;\r
+ DelayNode->AddressInfo = AddressInfo;\r
+ DelayNode->DadCallback = DadCallback;\r
+ DelayNode->Context = Context;\r
+\r
+ InsertTailList (&Interface->DelayJoinList, &DelayNode->Link);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create an IP6_INTERFACE.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] LinkLocal If TRUE, the instance is created for link-local address.\r
+ Otherwise, it is not for a link-local address.\r
+\r
+ @return Point to the created IP6_INTERFACE, otherwise NULL.\r
+\r
+**/\r
+IP6_INTERFACE *\r
+Ip6CreateInterface (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN LinkLocal\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP6_INTERFACE *Interface;\r
+ EFI_IPv6_ADDRESS *Ip6Addr;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ Interface = AllocatePool (sizeof (IP6_INTERFACE));\r
+ if (Interface == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ Interface->Signature = IP6_INTERFACE_SIGNATURE;\r
+ Interface->RefCnt = 1;\r
+\r
+ InitializeListHead (&Interface->AddressList);\r
+ Interface->AddressCount = 0;\r
+ Interface->Configured = FALSE;\r
+\r
+ Interface->Service = IpSb;\r
+ Interface->Controller = IpSb->Controller;\r
+ Interface->Image = IpSb->Image;\r
+\r
+ InitializeListHead (&Interface->ArpQues);\r
+ InitializeListHead (&Interface->SentFrames);\r
+\r
+ Interface->DupAddrDetect = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits;\r
+ InitializeListHead (&Interface->DupAddrDetectList);\r
+\r
+ InitializeListHead (&Interface->DelayJoinList);\r
+\r
+ InitializeListHead (&Interface->IpInstances);\r
+ Interface->PromiscRecv = FALSE;\r
+\r
+ if (!LinkLocal) {\r
+ return Interface;\r
+ }\r
+\r
+ //\r
+ // Get the link local addr\r
+ //\r
+ Ip6Addr = Ip6CreateLinkLocalAddr (IpSb);\r
+ if (Ip6Addr == NULL) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Perform DAD - Duplicate Address Detection.\r
+ //\r
+ Status = Ip6SetAddress (\r
+ Interface,\r
+ Ip6Addr,\r
+ FALSE,\r
+ IP6_LINK_LOCAL_PREFIX_LENGTH,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ NULL,\r
+ NULL\r
+ );\r
+\r
+ FreePool (Ip6Addr);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ return Interface;\r
+\r
+ON_ERROR:\r
+\r
+ FreePool (Interface);\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Free the interface used by IpInstance. All the IP instance with\r
+ the same Ip/prefix pair share the same interface. It is reference\r
+ counted. All the frames that haven't been sent will be cancelled.\r
+ Because the IpInstance is optional, the caller must remove\r
+ IpInstance from the interface's instance list.\r
+\r
+ @param[in] Interface The interface used by the IpInstance.\r
+ @param[in] IpInstance The IP instance that free the interface. NULL if\r
+ the IP driver is releasing the default interface.\r
+\r
+**/\r
+VOID\r
+Ip6CleanInterface (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN IP6_PROTOCOL *IpInstance OPTIONAL\r
+ )\r
+{\r
+ IP6_DAD_ENTRY *Duplicate;\r
+ IP6_DELAY_JOIN_LIST *Delay;\r
+\r
+ NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);\r
+ ASSERT (Interface->RefCnt > 0);\r
+\r
+ //\r
+ // Remove all the pending transmit token related to this IP instance.\r
+ //\r
+ Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance);\r
+\r
+ if (--Interface->RefCnt > 0) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Destory the interface if this is the last IP instance.\r
+ // Remove all the system transmitted packets\r
+ // from this interface, cancel the receive request if exists.\r
+ //\r
+ Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL);\r
+\r
+ ASSERT (IsListEmpty (&Interface->IpInstances));\r
+ ASSERT (IsListEmpty (&Interface->ArpQues));\r
+ ASSERT (IsListEmpty (&Interface->SentFrames));\r
+\r
+ while (!IsListEmpty (&Interface->DupAddrDetectList)) {\r
+ Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link);\r
+ NetListRemoveHead (&Interface->DupAddrDetectList);\r
+ FreePool (Duplicate);\r
+ }\r
+\r
+ while (!IsListEmpty (&Interface->DelayJoinList)) {\r
+ Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link);\r
+ NetListRemoveHead (&Interface->DelayJoinList);\r
+ FreePool (Delay);\r
+ }\r
+\r
+ Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0);\r
+\r
+ RemoveEntryList (&Interface->Link);\r
+ FreePool (Interface);\r
+}\r
+\r
+/**\r
+ Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN.\r
+\r
+ @param[in] Interface The interface to send out from.\r
+ @param[in] IpInstance The IpInstance that transmit the packet. NULL if\r
+ the packet is sent by the IP6 driver itself.\r
+ @param[in] Packet The packet to transmit\r
+ @param[in] CallBack Call back function to execute if transmission\r
+ finished.\r
+ @param[in] Context Opaque parameter to the callback.\r
+\r
+ @return The wrapped token if succeed or NULL.\r
+\r
+**/\r
+IP6_LINK_TX_TOKEN *\r
+Ip6CreateLinkTxToken (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,\r
+ IN NET_BUF *Packet,\r
+ IN IP6_FRAME_CALLBACK CallBack,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;\r
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData;\r
+ IP6_LINK_TX_TOKEN *Token;\r
+ EFI_STATUS Status;\r
+ UINT32 Count;\r
+\r
+ Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));\r
+\r
+ if (Token == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ Token->Signature = IP6_LINK_TX_SIGNATURE;\r
+ InitializeListHead (&Token->Link);\r
+\r
+ Token->IpInstance = IpInstance;\r
+ Token->CallBack = CallBack;\r
+ Token->Packet = Packet;\r
+ Token->Context = Context;\r
+ ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS));\r
+ IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress);\r
+\r
+ MnpToken = &(Token->MnpToken);\r
+ MnpToken->Status = EFI_NOT_READY;\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ Ip6OnFrameSent,\r
+ Token,\r
+ &MnpToken->Event\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Token);\r
+ return NULL;\r
+ }\r
+\r
+ MnpTxData = &Token->MnpTxData;\r
+ MnpToken->Packet.TxData = MnpTxData;\r
+\r
+ MnpTxData->DestinationAddress = &Token->DstMac;\r
+ MnpTxData->SourceAddress = &Token->SrcMac;\r
+ MnpTxData->ProtocolType = IP6_ETHER_PROTO;\r
+ MnpTxData->DataLength = Packet->TotalSize;\r
+ MnpTxData->HeaderLength = 0;\r
+\r
+ Count = Packet->BlockOpNum;\r
+\r
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);\r
+ MnpTxData->FragmentCount = (UINT16)Count;\r
+\r
+ return Token;\r
+}\r
+\r
+/**\r
+ Free the link layer transmit token. It will close the event,\r
+ then free the memory used.\r
+\r
+ @param[in] Token Token to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeLinkTxToken (\r
+ IN IP6_LINK_TX_TOKEN *Token\r
+ )\r
+{\r
+ NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);\r
+\r
+ gBS->CloseEvent (Token->MnpToken.Event);\r
+ FreePool (Token);\r
+}\r
+\r
+/**\r
+ Callback function when the received packet is freed.\r
+ Check Ip6OnFrameReceived for information.\r
+\r
+ @param[in] Context Points to EFI_MANAGED_NETWORK_RECEIVE_DATA.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6RecycleFrame (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData;\r
+\r
+ RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context;\r
+\r
+ gBS->SignalEvent (RxData->RecycleEvent);\r
+}\r
+\r
+/**\r
+ Received a frame from MNP. Wrap it in net buffer then deliver\r
+ it to IP's input function. The ownship of the packet also\r
+ is transferred to IP. When Ip is finished with this packet, it\r
+ will call NetbufFree to release the packet, NetbufFree will\r
+ again call the Ip6RecycleFrame to signal MNP's event and free\r
+ the token used.\r
+\r
+ @param[in] Context Context for the callback.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameReceivedDpc (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;\r
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData;\r
+ IP6_LINK_RX_TOKEN *Token;\r
+ NET_FRAGMENT Netfrag;\r
+ NET_BUF *Packet;\r
+ UINT32 Flag;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ Token = (IP6_LINK_RX_TOKEN *) Context;\r
+ NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE);\r
+\r
+ //\r
+ // First clear the interface's receive request in case the\r
+ // caller wants to call Ip6ReceiveFrame in the callback.\r
+ //\r
+ IpSb = (IP6_SERVICE *) Token->Context;\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+\r
+ MnpToken = &Token->MnpToken;\r
+ MnpRxData = MnpToken->Packet.RxData;\r
+\r
+ if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {\r
+ Token->CallBack (NULL, MnpToken->Status, 0, Token->Context);\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // Wrap the frame in a net buffer then deliever it to IP input.\r
+ // IP will reassemble the packet, and deliver it to upper layer\r
+ //\r
+ Netfrag.Len = MnpRxData->DataLength;\r
+ Netfrag.Bulk = MnpRxData->PacketData;\r
+\r
+ Packet = NetbufFromExt (&Netfrag, 1, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData);\r
+\r
+ if (Packet == NULL) {\r
+ gBS->SignalEvent (MnpRxData->RecycleEvent);\r
+\r
+ Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);\r
+\r
+ return ;\r
+ }\r
+\r
+ Flag = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0);\r
+ Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0);\r
+ Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0);\r
+\r
+ Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context);\r
+}\r
+\r
+/**\r
+ Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK.\r
+\r
+ @param Event The receive event delivered to MNP for receive.\r
+ @param Context Context for the callback.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameReceived (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ //\r
+ // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK\r
+ //\r
+ QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context);\r
+}\r
+\r
+/**\r
+ Request to receive the packet from the interface.\r
+\r
+ @param[in] CallBack Function to call when receive finished.\r
+ @param[in] IpSb Points to IP6 service binding instance.\r
+\r
+ @retval EFI_ALREADY_STARTED There is already a pending receive request.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive.\r
+ @retval EFI_SUCCESS The recieve request has been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ReceiveFrame (\r
+ IN IP6_FRAME_CALLBACK CallBack,\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP6_LINK_RX_TOKEN *Token;\r
+\r
+ if (IpSb->InDestroy) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ Token = &IpSb->RecvRequest;\r
+ Token->CallBack = CallBack;\r
+ Token->Context = (VOID *) IpSb;\r
+\r
+ Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Callback funtion when frame transmission is finished. It will\r
+ call the frame owner's callback function to tell it the result.\r
+\r
+ @param[in] Context Context which points to the token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameSentDpc (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_LINK_TX_TOKEN *Token;\r
+\r
+ Token = (IP6_LINK_TX_TOKEN *) Context;\r
+ NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);\r
+\r
+ RemoveEntryList (&Token->Link);\r
+\r
+ Token->CallBack (\r
+ Token->Packet,\r
+ Token->MnpToken.Status,\r
+ 0,\r
+ Token->Context\r
+ );\r
+\r
+ Ip6FreeLinkTxToken (Token);\r
+}\r
+\r
+/**\r
+ Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.\r
+\r
+ @param[in] Event The transmit token's event.\r
+ @param[in] Context Context which points to the token.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameSent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ //\r
+ // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK\r
+ //\r
+ QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context);\r
+}\r
+\r
+/**\r
+ Send a frame from the interface. If the next hop is a multicast address,\r
+ it is transmitted immediately. If the next hop is a unicast,\r
+ and the NextHop's MAC is not known, it will perform address resolution.\r
+ If an error occurred, the CallBack won't be called. So, the caller\r
+ must test the return value, and take action when there is an error.\r
+\r
+ @param[in] Interface The interface to send the frame from\r
+ @param[in] IpInstance The IP child that request the transmission.\r
+ NULL if it is the IP6 driver itself.\r
+ @param[in] Packet The packet to transmit.\r
+ @param[in] NextHop The immediate destination to transmit the packet to.\r
+ @param[in] CallBack Function to call back when transmit finished.\r
+ @param[in] Context Opaque parameter to the callback.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame.\r
+ @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop.\r
+ @retval EFI_SUCCESS The packet successfully transmitted.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendFrame (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_IPv6_ADDRESS *NextHop,\r
+ IN IP6_FRAME_CALLBACK CallBack,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_LINK_TX_TOKEN *Token;\r
+ EFI_STATUS Status;\r
+ IP6_NEIGHBOR_ENTRY *NeighborCache;\r
+ LIST_ENTRY *Entry;\r
+ IP6_NEIGHBOR_ENTRY *ArpQue;\r
+\r
+ IpSb = Interface->Service;\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ //\r
+ // Only when link local address is performing DAD, the interface could be used in unconfigured.\r
+ //\r
+ if (IpSb->LinkLocalOk) {\r
+ ASSERT (Interface->Configured);\r
+ }\r
+\r
+ Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);\r
+\r
+ if (Token == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ if (IP6_IS_MULTICAST (NextHop)) {\r
+ Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ goto SendNow;\r
+ }\r
+\r
+ //\r
+ // If send to itself, directly send out\r
+ //\r
+ if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) {\r
+ IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress);\r
+ goto SendNow;\r
+ }\r
+\r
+ //\r
+ // If unicast, check the neighbor state.\r
+ //\r
+\r
+ NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop);\r
+ ASSERT (NeighborCache != NULL);\r
+\r
+ if (NeighborCache->Interface == NULL) {\r
+ NeighborCache->Interface = Interface;\r
+ }\r
+\r
+ switch (NeighborCache->State) {\r
+ case EfiNeighborStale:\r
+ NeighborCache->State = EfiNeighborDelay;\r
+ NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);\r
+ //\r
+ // Fall through\r
+ //\r
+ case EfiNeighborReachable:\r
+ case EfiNeighborDelay:\r
+ case EfiNeighborProbe:\r
+ IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress);\r
+ goto SendNow;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Have to do asynchronous ARP resolution. First check whether there is\r
+ // already a pending request.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {\r
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);\r
+ if (ArpQue == NeighborCache) {\r
+ InsertTailList (&NeighborCache->Frames, &Token->Link);\r
+ NeighborCache->ArpFree = TRUE;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ //\r
+ // First frame requires ARP.\r
+ //\r
+ InsertTailList (&NeighborCache->Frames, &Token->Link);\r
+ InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList);\r
+\r
+ NeighborCache->ArpFree = TRUE;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+SendNow:\r
+ //\r
+ // Insert the tx token into the SentFrames list before calling Mnp->Transmit.\r
+ // Remove it if the returned status is not EFI_SUCCESS.\r
+ //\r
+ InsertTailList (&Interface->SentFrames, &Token->Link);\r
+ Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);\r
+ if (EFI_ERROR (Status)) {\r
+ RemoveEntryList (&Token->Link);\r
+ goto Error;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Error:\r
+ Ip6FreeLinkTxToken (Token);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The heartbeat timer of IP6 service instance. It times out\r
+ all of its IP6 children's received-but-not-delivered and\r
+ transmitted-but-not-recycle packets.\r
+\r
+ @param[in] Event The IP6 service instance's heartbeat timer.\r
+ @param[in] Context The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6TimerTicking (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+\r
+ IpSb = (IP6_SERVICE *) Context;\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ Ip6PacketTimerTicking (IpSb);\r
+ Ip6NdTimerTicking (IpSb);\r
+ Ip6MldTimerTicking (IpSb);\r
+}\r
--- /dev/null
+/** @file\r
+ Definition for IP6 pesudo interface structure.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_IF_H__\r
+#define __EFI_IP6_IF_H__\r
+\r
+#define IP6_LINK_RX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'R')\r
+#define IP6_LINK_TX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'T')\r
+#define IP6_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'I')\r
+#define IP6_ADDR_INFO_SIGNATURE SIGNATURE_32 ('I', 'P', 'A', 'I')\r
+\r
+//\r
+// This prototype is used by both receive and transmission.\r
+// When receiving Netbuf is allocated by IP6_INTERFACE, and\r
+// released by IP6. Flag shows whether the frame is received\r
+// as unicast/multicast/anycast...\r
+//\r
+// When transmitting, the Netbuf is from IP6, and provided\r
+// to the callback as a reference. Flag isn't used.\r
+//\r
+// IpInstance can be NULL which means that it is the IP6 driver\r
+// itself sending the packets. IP6 driver may send packets that\r
+// don't belong to any instance, such as ICMP errors, ICMP\r
+// informational packets. IpInstance is used as a tag in\r
+// this module.\r
+//\r
+typedef\r
+VOID\r
+(*IP6_FRAME_CALLBACK) (\r
+ NET_BUF *Packet,\r
+ EFI_STATUS IoStatus,\r
+ UINT32 LinkFlag,\r
+ VOID *Context\r
+ );\r
+\r
+//\r
+// Each receive request is wrapped in an IP6_LINK_RX_TOKEN.\r
+// Upon completion, the Callback will be called. Only one\r
+// receive request is send to MNP. IpInstance is always NULL.\r
+// Reference MNP's spec for information.\r
+//\r
+typedef struct {\r
+ UINT32 Signature;\r
+ IP6_FRAME_CALLBACK CallBack;\r
+ VOID *Context;\r
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;\r
+} IP6_LINK_RX_TOKEN;\r
+\r
+//\r
+// Each transmit request is wrapped in an IP6_LINK_TX_TOKEN.\r
+// Upon completion, the Callback will be called.\r
+//\r
+typedef struct {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_FRAME_CALLBACK CallBack;\r
+ NET_BUF *Packet;\r
+ VOID *Context;\r
+\r
+ EFI_MAC_ADDRESS DstMac;\r
+ EFI_MAC_ADDRESS SrcMac;\r
+\r
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;\r
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData;\r
+} IP6_LINK_TX_TOKEN;\r
+\r
+struct _IP6_ADDRESS_INFO {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+ EFI_IPv6_ADDRESS Address;\r
+ BOOLEAN IsAnycast;\r
+ UINT8 PrefixLength;\r
+ UINT32 ValidLifetime;\r
+ UINT32 PreferredLifetime;\r
+};\r
+\r
+//\r
+// Callback to select which frame to cancel. Caller can cancel a\r
+// single frame, or all the frame from an IP instance.\r
+//\r
+typedef\r
+BOOLEAN\r
+(*IP6_FRAME_TO_CANCEL) (\r
+ IP6_LINK_TX_TOKEN *Frame,\r
+ VOID *Context\r
+ );\r
+\r
+struct _IP6_INTERFACE {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+ INTN RefCnt;\r
+\r
+ //\r
+ // IP address and prefix length of the interface. The fileds\r
+ // are invalid if (Configured == FALSE)\r
+ //\r
+ LIST_ENTRY AddressList;\r
+ UINT32 AddressCount;\r
+ BOOLEAN Configured;\r
+\r
+ IP6_SERVICE *Service;\r
+\r
+ EFI_HANDLE Controller;\r
+ EFI_HANDLE Image;\r
+\r
+\r
+ //\r
+ // Queues to keep the frames sent and waiting ARP request.\r
+ //\r
+ LIST_ENTRY ArpQues;\r
+ LIST_ENTRY SentFrames;\r
+\r
+\r
+ //\r
+ // The interface's configuration variables\r
+ //\r
+ UINT32 DupAddrDetect;\r
+ LIST_ENTRY DupAddrDetectList;\r
+ LIST_ENTRY DelayJoinList;\r
+\r
+ //\r
+ // All the IP instances that have the same IP/SubnetMask are linked\r
+ // together through IpInstances. If any of the instance enables\r
+ // promiscuous receive, PromiscRecv is true.\r
+ //\r
+ LIST_ENTRY IpInstances;\r
+ BOOLEAN PromiscRecv;\r
+};\r
+\r
+/**\r
+ Create an IP6_INTERFACE.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] LinkLocal If TRUE, the instance is created for link-local address.\r
+ Otherwise, it is not for a link-local address.\r
+\r
+ @return Point to the created IP6_INTERFACE, otherwise NULL.\r
+\r
+**/\r
+IP6_INTERFACE *\r
+Ip6CreateInterface (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN LinkLocal\r
+ );\r
+\r
+/**\r
+ Free the interface used by IpInstance. All the IP instance with\r
+ the same Ip/prefix pair share the same interface. It is reference\r
+ counted. All the frames that haven't been sent will be cancelled.\r
+ Because the IpInstance is optional, the caller must remove\r
+ IpInstance from the interface's instance list.\r
+\r
+ @param[in] Interface The interface used by the IpInstance.\r
+ @param[in] IpInstance The IP instance that free the interface. NULL if\r
+ the IP driver is releasing the default interface.\r
+\r
+**/\r
+VOID\r
+Ip6CleanInterface (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN IP6_PROTOCOL *IpInstance OPTIONAL\r
+ );\r
+\r
+/**\r
+ Free the link layer transmit token. It will close the event\r
+ then free the memory used.\r
+\r
+ @param[in] Token Token to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeLinkTxToken (\r
+ IN IP6_LINK_TX_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK\r
+\r
+ @param Event The receive event delivered to MNP for receive.\r
+ @param Context Context for the callback.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFrameReceived (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Request to receive the packet from the interface.\r
+\r
+ @param[in] CallBack Function to call when the receive finished.\r
+ @param[in] IpSb Points to the IP6 service binding instance.\r
+\r
+ @retval EFI_ALREADY_STARTED There is already a pending receive request.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to receive.\r
+ @retval EFI_SUCCESS The recieve request has been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ReceiveFrame (\r
+ IN IP6_FRAME_CALLBACK CallBack,\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ Send a frame from the interface. If the next hop is multicast address,\r
+ it is transmitted immediately. If the next hop is a unicast,\r
+ and the NextHop's MAC is not known, it will perform address resolution.\r
+ If some error happened, the CallBack won't be called. So, the caller\r
+ must test the return value, and take action when there is an error.\r
+\r
+ @param[in] Interface The interface to send the frame from\r
+ @param[in] IpInstance The IP child that request the transmission.\r
+ NULL if it is the IP6 driver itself.\r
+ @param[in] Packet The packet to transmit.\r
+ @param[in] NextHop The immediate destination to transmit the packet to.\r
+ @param[in] CallBack Function to call back when transmit finished.\r
+ @param[in] Context Opaque parameter to the call back.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame.\r
+ @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop.\r
+ @retval EFI_SUCCESS The packet successfully transmitted.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendFrame (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_IPv6_ADDRESS *NextHop,\r
+ IN IP6_FRAME_CALLBACK CallBack,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ The heartbeat timer of IP6 service instance. It times out\r
+ all of its IP6 children's received-but-not-delivered and\r
+ transmitted-but-not-recycle packets.\r
+\r
+ @param[in] Event The IP6 service instance's heart beat timer.\r
+ @param[in] Context The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6TimerTicking (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Implementation of EFI_IP6_PROTOCOL protocol interfaces.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_IPSEC_PROTOCOL *mIpSec = NULL;\r
+\r
+EFI_IP6_PROTOCOL mEfiIp6ProtocolTemplete = {\r
+ EfiIp6GetModeData,\r
+ EfiIp6Configure,\r
+ EfiIp6Groups,\r
+ EfiIp6Routes,\r
+ EfiIp6Neighbors,\r
+ EfiIp6Transmit,\r
+ EfiIp6Receive,\r
+ EfiIp6Cancel,\r
+ EfiIp6Poll\r
+};\r
+\r
+/**\r
+ Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.\r
+\r
+ The GetModeData() function returns the current operational mode data for this driver instance.\r
+ The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to\r
+ retrieve the operational mode data of underlying networks or drivers.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[out] Ip6ModeData Pointer to the EFI IPv6 Protocol mode data structure.\r
+ @param[out] MnpConfigData Pointer to the managed network configuration data structure.\r
+ @param[out] SnpModeData Pointer to the simple network mode data structure.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6GetModeData (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_INTERFACE *IpIf;\r
+ EFI_IP6_CONFIG_DATA *Config;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+ IpIf = IpInstance->Interface;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Ip6ModeData != NULL) {\r
+ //\r
+ // IsStarted is "whether the EfiIp6Configure has been called".\r
+ // IsConfigured is "whether the station address has been configured"\r
+ //\r
+ Ip6ModeData->IsStarted = (BOOLEAN) (IpInstance->State == IP6_STATE_CONFIGED);\r
+ Ip6ModeData->MaxPacketSize = IpSb->MaxPacketSize;\r
+ CopyMem (&Ip6ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP6_CONFIG_DATA));\r
+ Ip6ModeData->IsConfigured = FALSE;\r
+\r
+ Ip6ModeData->AddressCount = 0;\r
+ Ip6ModeData->AddressList = NULL;\r
+\r
+ Ip6ModeData->GroupCount = IpInstance->GroupCount;\r
+ Ip6ModeData->GroupTable = NULL;\r
+\r
+ Ip6ModeData->RouteCount = 0;\r
+ Ip6ModeData->RouteTable = NULL;\r
+\r
+ Ip6ModeData->NeighborCount = 0;\r
+ Ip6ModeData->NeighborCache = NULL;\r
+\r
+ Ip6ModeData->PrefixCount = 0;\r
+ Ip6ModeData->PrefixTable = NULL;\r
+\r
+ Ip6ModeData->IcmpTypeCount = 23;\r
+ Ip6ModeData->IcmpTypeList = AllocateCopyPool (\r
+ Ip6ModeData->IcmpTypeCount * sizeof (EFI_IP6_ICMP_TYPE),\r
+ mIp6SupportedIcmp\r
+ );\r
+ if (Ip6ModeData->IcmpTypeList == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Return the currently configured IPv6 addresses and corresponding prefix lengths.\r
+ //\r
+ Status = Ip6BuildEfiAddressList (\r
+ IpSb,\r
+ &Ip6ModeData->AddressCount,\r
+ &Ip6ModeData->AddressList\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Return the current station address for this IP child.\r
+ // If UseAnyStationAddress is set to TRUE, IP6 driver will\r
+ // select a source address from its address list. Otherwise use the\r
+ // StationAddress in config data.\r
+ //\r
+ if (Ip6ModeData->IsStarted) {\r
+ Config = &Ip6ModeData->ConfigData;\r
+\r
+ if (IpIf->Configured || NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {\r
+ Ip6ModeData->IsConfigured = TRUE;\r
+ } else {\r
+ Ip6ModeData->IsConfigured = FALSE;\r
+ }\r
+\r
+ //\r
+ // Build a EFI route table for user from the internal route table.\r
+ //\r
+ Status = Ip6BuildEfiRouteTable (\r
+ IpSb->RouteTable,\r
+ &Ip6ModeData->RouteCount,\r
+ &Ip6ModeData->RouteTable\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ if (Ip6ModeData->IsConfigured) {\r
+ //\r
+ // Return the joined multicast group addresses.\r
+ //\r
+ if (IpInstance->GroupCount != 0) {\r
+ Ip6ModeData->GroupTable = AllocateCopyPool (\r
+ IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS),\r
+ IpInstance->GroupList\r
+ );\r
+ if (Ip6ModeData->GroupTable == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ }\r
+ //\r
+ // Return the neighbor cache entries\r
+ //\r
+ Status = Ip6BuildEfiNeighborCache (\r
+ IpInstance,\r
+ &Ip6ModeData->NeighborCount,\r
+ &Ip6ModeData->NeighborCache\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Return the prefix table entries\r
+ //\r
+ Status = Ip6BuildPrefixTable (\r
+ IpInstance,\r
+ &Ip6ModeData->PrefixCount,\r
+ &Ip6ModeData->PrefixTable\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get fresh mode data from MNP, since underlying media status may change\r
+ //\r
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData);\r
+\r
+ goto Exit;\r
+\r
+Error:\r
+ if (Ip6ModeData != NULL) {\r
+ if (Ip6ModeData->AddressList != NULL) {\r
+ FreePool (Ip6ModeData->AddressList);\r
+ }\r
+\r
+ if (Ip6ModeData->GroupTable != NULL) {\r
+ FreePool (Ip6ModeData->GroupTable);\r
+ }\r
+\r
+ if (Ip6ModeData->RouteTable != NULL) {\r
+ FreePool (Ip6ModeData->RouteTable);\r
+ }\r
+\r
+ if (Ip6ModeData->NeighborCache != NULL) {\r
+ FreePool (Ip6ModeData->NeighborCache);\r
+ }\r
+\r
+ if (Ip6ModeData->PrefixTable != NULL) {\r
+ FreePool (Ip6ModeData->PrefixTable);\r
+ }\r
+\r
+ if (Ip6ModeData->IcmpTypeList != NULL) {\r
+ FreePool (Ip6ModeData->IcmpTypeList);\r
+ }\r
+ }\r
+\r
+Exit:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Validate that Ipv6 address is OK to be used as station address or next hop address/ neighbor.\r
+\r
+ @param[in] IpSb The IP6 service instance.\r
+ @param[in] Ip The IPv6 address to validate.\r
+ @param[in] Flag If TRUE, validate if the address is OK to be used\r
+ as station address. If FALSE, validate if the\r
+ address is OK to be used as the next hop address/\r
+ neighbor.\r
+\r
+ @retval TRUE The Ip address is valid and could be used.\r
+ @retval FALSE Invalid Ip address.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidAddress (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Ip,\r
+ IN BOOLEAN Flag\r
+ )\r
+{\r
+ if (!NetIp6IsUnspecifiedAddr (Ip)) {\r
+ if (!NetIp6IsValidUnicast(Ip)) {\r
+ return FALSE;\r
+ }\r
+ if (Ip6IsOneOfSetAddress (IpSb, Ip, NULL, NULL)) {\r
+ return Flag;\r
+ }\r
+ } else {\r
+ return Flag;\r
+ }\r
+\r
+ return (BOOLEAN) !Flag;\r
+}\r
+\r
+/**\r
+ Validate whether the value of protocol is illegal or not. Protocol is the 'Next Header' field\r
+ in the last IPv6 extension header, or basic IPv6 header is there's no extension header.\r
+\r
+ @param[in] Protocol Default value of 'Next Header'\r
+\r
+ @retval TRUE The protocol is illegal.\r
+ @retval FALSE The protocol is legal.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsIllegalProtocol (\r
+ IN UINT8 Protocol\r
+ )\r
+{\r
+ if (Protocol == IP6_HOP_BY_HOP || Protocol == EFI_IP_PROTO_ICMP || Protocol == IP4_PROTO_IGMP) {\r
+ return TRUE;\r
+ }\r
+\r
+ if (Protocol == 41 || Protocol == 43 || Protocol == 44 || Protocol == 59 || Protocol == 60 || Protocol == 124) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Intiialize the IP6_PROTOCOL structure to the unconfigured states.\r
+\r
+ @param[in] IpSb The IP6 service instance.\r
+ @param[in, out] IpInstance The IP6 child instance.\r
+\r
+**/\r
+VOID\r
+Ip6InitProtocol (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN OUT IP6_PROTOCOL *IpInstance\r
+ )\r
+{\r
+ ASSERT ((IpSb != NULL) && (IpInstance != NULL));\r
+\r
+ ZeroMem (IpInstance, sizeof (IP6_PROTOCOL));\r
+\r
+ IpInstance->Signature = IP6_PROTOCOL_SIGNATURE;\r
+ IpInstance->State = IP6_STATE_UNCONFIGED;\r
+ IpInstance->Service = IpSb;\r
+ IpInstance->GroupList = NULL;\r
+ CopyMem (&IpInstance->Ip6Proto, &mEfiIp6ProtocolTemplete, sizeof (EFI_IP6_PROTOCOL));\r
+\r
+ NetMapInit (&IpInstance->RxTokens);\r
+ NetMapInit (&IpInstance->TxTokens);\r
+ InitializeListHead (&IpInstance->Received);\r
+ InitializeListHead (&IpInstance->Delivered);\r
+\r
+ EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY);\r
+}\r
+\r
+/**\r
+ Configure the IP6 child. If the child is already configured,\r
+ change the configuration parameter. Otherwise, configure it\r
+ for the first time. The caller should validate the configuration\r
+ before deliver them to it. It also don't do configure NULL.\r
+\r
+ @param[in, out] IpInstance The IP6 child to configure.\r
+ @param[in] Config The configure data.\r
+\r
+ @retval EFI_SUCCESS The IP6 child is successfully configured.\r
+ @retval EFI_DEVICE_ERROR Failed to free the pending transive or to\r
+ configure underlying MNP, or other errors.\r
+ @retval EFI_NO_MAPPING The IP6 child is configured to use the default\r
+ address, but the default address hasn't been\r
+ configured. The IP6 child doesn't need to be\r
+ reconfigured when the default address is configured.\r
+ @retval EFI_OUT_OF_RESOURCES No more memory space is available.\r
+ @retval other Other error occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ConfigProtocol (\r
+ IN OUT IP6_PROTOCOL *IpInstance,\r
+ IN EFI_IP6_CONFIG_DATA *Config\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_INTERFACE *IpIf;\r
+ EFI_STATUS Status;\r
+ EFI_IP6_CONFIG_DATA *Current;\r
+ IP6_ADDRESS_INFO *AddressInfo;\r
+ BOOLEAN StationZero;\r
+ BOOLEAN DestZero;\r
+ EFI_IPv6_ADDRESS Source;\r
+ BOOLEAN AddrOk;\r
+\r
+ IpSb = IpInstance->Service;\r
+ Current = &IpInstance->ConfigData;\r
+\r
+ //\r
+ // User is changing packet filters. It must be stopped\r
+ // before the station address can be changed.\r
+ //\r
+ if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+ //\r
+ // Cancel all the pending transmit/receive from upper layer\r
+ //\r
+ Status = Ip6Cancel (IpInstance, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Set up the interface.\r
+ //\r
+ StationZero = NetIp6IsUnspecifiedAddr (&Config->StationAddress);\r
+ DestZero = NetIp6IsUnspecifiedAddr (&Config->DestinationAddress);\r
+\r
+ if (StationZero && DestZero) {\r
+ //\r
+ // StationAddress is still zero.\r
+ //\r
+\r
+ NET_GET_REF (IpSb->DefaultInterface);\r
+ IpInstance->Interface = IpSb->DefaultInterface;\r
+ InsertTailList (&IpSb->DefaultInterface->IpInstances, &IpInstance->AddrLink);\r
+\r
+ CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+ IpInstance->State = IP6_STATE_CONFIGED;\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (StationZero && !DestZero) {\r
+ Status = Ip6SelectSourceAddress (IpSb, &Config->DestinationAddress, &Source);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else {\r
+ IP6_COPY_ADDRESS (&Source, &Config->StationAddress);\r
+ }\r
+\r
+ AddrOk = Ip6IsOneOfSetAddress (IpSb, &Source, &IpIf, &AddressInfo);\r
+ if (AddrOk) {\r
+ if (AddressInfo != NULL) {\r
+ IpInstance->PrefixLength = AddressInfo->PrefixLength;\r
+ } else {\r
+ IpInstance->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+ }\r
+ } else {\r
+ //\r
+ // The specified source address is not one of the addresses IPv6 maintains.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+\r
+ NET_GET_REF (IpIf);\r
+ IpInstance->Interface = IpIf;\r
+ InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink);\r
+\r
+ CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));\r
+ IP6_COPY_ADDRESS (&Current->StationAddress, &Source);\r
+ IpInstance->State = IP6_STATE_CONFIGED;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Clean up the IP6 child, and release all the resources used by it.\r
+\r
+ @param[in, out] IpInstance The IP6 child to clean up.\r
+\r
+ @retval EFI_SUCCESS The IP6 child is cleaned up.\r
+ @retval EFI_DEVICE_ERROR Some resources failed to be released.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanProtocol (\r
+ IN OUT IP6_PROTOCOL *IpInstance\r
+ )\r
+{\r
+ if (EFI_ERROR (Ip6Cancel (IpInstance, NULL))) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (EFI_ERROR (Ip6Groups (IpInstance, FALSE, NULL))) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Some packets haven't been recycled. It is because either the\r
+ // user forgets to recycle the packets, or because the callback\r
+ // hasn't been called. Just leave it alone.\r
+ //\r
+ if (!IsListEmpty (&IpInstance->Delivered)) {\r
+ ;\r
+ }\r
+\r
+ if (IpInstance->Interface != NULL) {\r
+ RemoveEntryList (&IpInstance->AddrLink);\r
+ Ip6CleanInterface (IpInstance->Interface, IpInstance);\r
+ IpInstance->Interface = NULL;\r
+ }\r
+\r
+ if (IpInstance->GroupList != NULL) {\r
+ FreePool (IpInstance->GroupList);\r
+ IpInstance->GroupList = NULL;\r
+ IpInstance->GroupCount = 0;\r
+ }\r
+\r
+ NetMapClean (&IpInstance->TxTokens);\r
+\r
+ NetMapClean (&IpInstance->RxTokens);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Configure the MNP parameter used by IP. The IP driver uses one MNP\r
+ child to transmit/receive frames. By default, it configures MNP\r
+ to receive unicast/multicast/broadcast. Also, it will enable/disable\r
+ the promiscuous receive according to whether there is IP child\r
+ enable that or not. If Force is FALSE, it will iterate through\r
+ all the IP children to check whether the promiscuous receive\r
+ setting has been changed. If it hasn't been changed, it won't\r
+ reconfigure the MNP. If Force is TRUE, the MNP is configured\r
+ whether that is changed or not.\r
+\r
+ @param[in] IpSb The IP6 service instance that is to be changed.\r
+ @param[in] Force Force the configuration or not.\r
+\r
+ @retval EFI_SUCCESS The MNP successfully configured/reconfigured.\r
+ @retval Others Configuration failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ServiceConfigMnp (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN Force\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *ProtoEntry;\r
+ IP6_INTERFACE *IpIf;\r
+ IP6_PROTOCOL *IpInstance;\r
+ BOOLEAN Reconfig;\r
+ BOOLEAN PromiscReceive;\r
+ EFI_STATUS Status;\r
+\r
+ Reconfig = FALSE;\r
+ PromiscReceive = FALSE;\r
+\r
+ if (!Force) {\r
+ //\r
+ // Iterate through the IP children to check whether promiscuous\r
+ // receive setting has been changed. Update the interface's receive\r
+ // filter also.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+\r
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+ IpIf->PromiscRecv = FALSE;\r
+\r
+ NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) {\r
+ IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP6_PROTOCOL, AddrLink);\r
+\r
+ if (IpInstance->ConfigData.AcceptPromiscuous) {\r
+ IpIf->PromiscRecv = TRUE;\r
+ PromiscReceive = TRUE;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // If promiscuous receive isn't changed, it isn't necessary to reconfigure.\r
+ //\r
+ if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Reconfig = TRUE;\r
+ IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive;\r
+ }\r
+\r
+ Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData);\r
+\r
+ //\r
+ // recover the original configuration if failed to set the configure.\r
+ //\r
+ if (EFI_ERROR (Status) && Reconfig) {\r
+ IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.\r
+\r
+ The Configure() function is used to set, change, or reset the operational parameters and filter\r
+ settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic\r
+ can be sent or received by this instance. Once the parameters have been reset (by calling this\r
+ function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these\r
+ parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped\r
+ independently of each other by enabling or disabling their receive filter settings with the\r
+ Configure() function.\r
+\r
+ If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required\r
+ to be one of the currently configured IPv6 addresses listed in the EFI IPv6 drivers, or else\r
+ EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is\r
+ unspecified, the IPv6 driver will bind a source address according to the source address selection\r
+ algorithm. Clients could frequently call GetModeData() to check get currently configured IPv6\r
+ address list in the EFI IPv6 driver. If both Ip6ConfigData.StationAddress and\r
+ Ip6ConfigData.Destination are unspecified, when transmitting the packet afterwards, the\r
+ source address filled in each outgoing IPv6 packet is decided based on the destination of this packet.\r
+\r
+ If operational parameters are reset or changed, any pending transmit and receive requests will be\r
+ cancelled. Their completion token status will be set to EFI_ABORTED and their events will be\r
+ signaled.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Ip6ConfigData Pointer to the EFI IPv6 Protocol configuration data structure.\r
+ If NULL, reset the configuration data.\r
+\r
+ @retval EFI_SUCCESS The driver instance was successfully opened.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Ip6ConfigData.StationAddress is neither zero nor\r
+ a unicast IPv6 address.\r
+ - Ip6ConfigData.StationAddress is neither zero nor\r
+ one of the configured IP addresses in the EFI IPv6 driver.\r
+ - Ip6ConfigData.DefaultProtocol is illegal.\r
+ @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated.\r
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for\r
+ this instance, but no source address was available for use.\r
+ @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6\r
+ address or prefix length can be changed.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6\r
+ Protocol driver instance was not opened.\r
+ @retval EFI_UNSUPPORTED Default protocol specified through\r
+ Ip6ConfigData.DefaulProtocol isn't supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Configure (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_IP6_CONFIG_DATA *Current;\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ //\r
+ // First, validate the parameters\r
+ //\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ //\r
+ // Validate the configuration first.\r
+ //\r
+ if (Ip6ConfigData != NULL) {\r
+ //\r
+ // Check whether the station address is valid.\r
+ //\r
+ if (!Ip6IsValidAddress (IpSb, &Ip6ConfigData->StationAddress, TRUE)) {\r
+ goto Exit;\r
+ }\r
+ //\r
+ // Check whether the default protocol is valid.\r
+ //\r
+ if (Ip6IsIllegalProtocol (Ip6ConfigData->DefaultProtocol)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // User can only update packet filters when already configured.\r
+ // If it wants to change the station address, it must configure(NULL)\r
+ // the instance firstly.\r
+ //\r
+ if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+ Current = &IpInstance->ConfigData;\r
+\r
+ if (!EFI_IP6_EQUAL (&Current->StationAddress, &Ip6ConfigData->StationAddress)) {\r
+ Status = EFI_ALREADY_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&Current->StationAddress) && IP6_NO_MAPPING (IpInstance)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Configure the instance or clean it up.\r
+ //\r
+ if (Ip6ConfigData != NULL) {\r
+ Status = Ip6ConfigProtocol (IpInstance, Ip6ConfigData);\r
+ } else {\r
+ Status = Ip6CleanProtocol (IpInstance);\r
+\r
+ //\r
+ // Don't change the state if it is DESTORY, consider the following\r
+ // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped,\r
+ // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED,\r
+ // the unload fails miserably.\r
+ //\r
+ if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+ IpInstance->State = IP6_STATE_UNCONFIGED;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Update the MNP's configure data. Ip6ServiceConfigMnp will check\r
+ // whether it is necessary to reconfigure the MNP.\r
+ //\r
+ Ip6ServiceConfigMnp (IpInstance->Service, FALSE);\r
+\r
+ //\r
+ // Update the variable data.\r
+ //\r
+ Ip6SetVariableData (IpInstance->Service);\r
+\r
+Exit:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Joins and leaves multicast groups.\r
+\r
+ The Groups() function is used to join and leave multicast group sessions. Joining a group will\r
+ enable reception of matching multicast packets. Leaving a group will disable reception of matching\r
+ multicast packets. Source-Specific Multicast isn't required to be supported.\r
+\r
+ If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] JoinFlag Set to TRUE to join the multicast group session, and FALSE to leave.\r
+ @param[in] GroupAddress Pointer to the IPv6 multicast address.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:\r
+ - This is NULL.\r
+ - JoinFlag is TRUE and GroupAddress is NULL.\r
+ - GroupAddress is not NULL and *GroupAddress is\r
+ not a multicast IPv6 address.\r
+ - GroupAddress is not NULL and *GroupAddress is in the\r
+ range of SSM destination address.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_OUT_OF_RESOURCES System resources could not be allocated.\r
+ @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups.\r
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when\r
+ JoinFlag is TRUE).\r
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Groups (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN BOOLEAN JoinFlag,\r
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ if ((This == NULL) || (JoinFlag && GroupAddress == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (GroupAddress != NULL && !IP6_IS_MULTICAST (GroupAddress)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Ip6Groups (IpInstance, JoinFlag, GroupAddress);\r
+\r
+ON_EXIT:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Adds and deletes routing table entries.\r
+\r
+ The Routes() function adds a route to, or deletes a route from, the routing table.\r
+\r
+ Routes are determined by comparing the leftmost PrefixLength bits of Destination with\r
+ the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the\r
+ configured station address.\r
+\r
+ The default route is added with Destination and PrefixLegth both set to all zeros. The\r
+ default route matches all destination IPv6 addresses that do not match any other routes.\r
+\r
+ All EFI IPv6 Protocol instances share a routing table.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to\r
+ FALSE to add this route to the routing table. Destination,\r
+ PrefixLength and Gateway are used as the key to each\r
+ route entry.\r
+ @param[in] Destination The address prefix of the subnet that needs to be routed.\r
+ This is an optional parameter that may be NULL.\r
+ @param[in] PrefixLength The prefix length of Destination. Ignored if Destination\r
+ is NULL.\r
+ @param[in] GatewayAddress The unicast gateway IPv6 address for this route.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The driver instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - When DeleteRoute is TRUE, both Destination and\r
+ GatewayAddress are NULL.\r
+ - When DeleteRoute is FALSE, either Destination or\r
+ GatewayAddress is NULL.\r
+ - *GatewayAddress is not a valid unicast IPv6 address.\r
+ - *GatewayAddress is one of the local configured IPv6\r
+ addresses.\r
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.\r
+ @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE).\r
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when\r
+ DeleteRoute is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Routes (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN BOOLEAN DeleteRoute,\r
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ if ((This == NULL) || (PrefixLength >= IP6_PREFIX_NUM)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (DeleteRoute && (Destination == NULL) && (GatewayAddress == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!DeleteRoute && (Destination == NULL || GatewayAddress == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (GatewayAddress != NULL) {\r
+ if (!Ip6IsValidAddress (IpSb, GatewayAddress, FALSE)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!NetIp6IsUnspecifiedAddr (GatewayAddress) &&\r
+ !NetIp6IsNetEqual (GatewayAddress, &IpInstance->ConfigData.StationAddress, PrefixLength)\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Update the route table\r
+ //\r
+ if (DeleteRoute) {\r
+ Status = Ip6DelRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);\r
+ } else {\r
+ Status = Ip6AddRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Add or delete Neighbor cache entries.\r
+\r
+ The Neighbors() function is used to add, update, or delete an entry from neighbor cache.\r
+ IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as\r
+ network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network\r
+ traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not\r
+ timeout) or dynamic (will timeout).\r
+\r
+ The implementation should follow the neighbor cache timeout mechanism which is defined in\r
+ RFC4861. The default neighbor cache timeout value should be tuned for the expected network\r
+ environment\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] DeleteFlag Set to TRUE to delete the specified cache entry, set to FALSE to\r
+ add (or update, if it already exists and Override is TRUE) the\r
+ specified cache entry. TargetIp6Address is used as the key\r
+ to find the requested cache entry.\r
+ @param[in] TargetIp6Address Pointer to the Target IPv6 address.\r
+ @param[in] TargetLinkAddress Pointer to the link-layer address of the target. Ignored if NULL.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor\r
+ cache, it will be deleted after Timeout. A value of zero means that\r
+ the entry is permanent. A non-zero value means that the entry is\r
+ dynamic.\r
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will\r
+ be overridden and updated; if FALSE, EFI_ACCESS_DENIED\r
+ will be returned if a corresponding cache entry already existed.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - TargetIpAddress is NULL.\r
+ - *TargetLinkAddress is invalid when not NULL.\r
+ - *TargetIpAddress is not a valid unicast IPv6 address.\r
+ - *TargetIpAddress is one of the local configured IPv6\r
+ addresses.\r
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache.\r
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is\r
+ TRUE or when DeleteFlag is FALSE while\r
+ TargetLinkAddress is NULL.).\r
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,\r
+ and that entry is tagged as un-overridden (when Override\r
+ is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Neighbors (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN BOOLEAN DeleteFlag,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,\r
+ IN UINT32 Timeout,\r
+ IN BOOLEAN Override\r
+ )\r
+{\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ if (This == NULL || TargetIp6Address == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (NetIp6IsUnspecifiedAddr (TargetIp6Address)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (!Ip6IsValidAddress (IpSb, TargetIp6Address, FALSE)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (TargetLinkAddress != NULL) {\r
+ if (!Ip6IsValidLinkAddress (IpSb, TargetLinkAddress)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if (Ip6IsOneOfSetAddress (IpSb, TargetIp6Address, NULL, NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ if (DeleteFlag) {\r
+ Status = Ip6DelNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);\r
+ } else {\r
+ Status = Ip6AddNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);\r
+ }\r
+\r
+Exit:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check whether the user's token or event has already\r
+ been enqueue on IP6's list.\r
+\r
+ @param[in] Map The container of either user's transmit or receive\r
+ token.\r
+ @param[in] Item Current item to check against.\r
+ @param[in] Context The Token to check againist.\r
+\r
+ @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP\r
+ @retval EFI_SUCCESS The current item isn't the same token/event as the\r
+ context.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6TokenExist (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_IP6_COMPLETION_TOKEN *Token;\r
+ EFI_IP6_COMPLETION_TOKEN *TokenInItem;\r
+\r
+ Token = (EFI_IP6_COMPLETION_TOKEN *) Context;\r
+ TokenInItem = (EFI_IP6_COMPLETION_TOKEN *) Item->Key;\r
+\r
+ if (Token == TokenInItem || Token->Event == TokenInItem->Event) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Validate the user's token against the current station address.\r
+\r
+ @param[in] Token User's token to validate.\r
+\r
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid.\r
+ @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long.\r
+ @retval EFI_SUCCESS The token is OK.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6TxTokenValid (\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token\r
+ )\r
+{\r
+ EFI_IP6_TRANSMIT_DATA *TxData;\r
+ UINT32 Index;\r
+ UINT32 DataLength;\r
+\r
+ if (Token == NULL || Token->Event == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TxData = Token->Packet.TxData;\r
+\r
+ if (TxData == NULL || (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (TxData->FragmentCount == 0 || TxData->DataLength == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ for (DataLength = 0, Index = 0; Index < TxData->FragmentCount; Index++) {\r
+ if (TxData->FragmentTable[Index].FragmentLength == 0 || TxData->FragmentTable[Index].FragmentBuffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DataLength += TxData->FragmentTable[Index].FragmentLength;\r
+ }\r
+\r
+ if (TxData->DataLength != DataLength) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // TODO: Token.Packet.TxData.DataLength is too short to transmit.\r
+ // return EFI_BUFFER_TOO_SMALL;\r
+ //\r
+\r
+ //\r
+ // If Token.Packet.TxData.DataLength is beyond the maximum that which can be\r
+ // described through the Fragment Offset field in Fragment header when performing\r
+ // fragmentation.\r
+ //\r
+ if (TxData->DataLength > 64 * 1024) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The callback function for the net buffer which wraps the user's\r
+ transmit token. Although this function seems simple, there\r
+ are some subtle aspects.\r
+ When user requests the IP to transmit a packet by passing it a\r
+ token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data\r
+ is wrapped in an net buffer. The net buffer's Free function is\r
+ set to Ip6FreeTxToken. The Token and token wrap are added to the\r
+ IP child's TxToken map. Then the buffer is passed to Ip6Output for\r
+ transmission. If an error happened before that, the buffer\r
+ is freed, which in turn frees the token wrap. The wrap may\r
+ have been added to the TxToken map or not, and the user's event\r
+ shouldn't be fired because we are still in the EfiIp6Transmit. If\r
+ the buffer has been sent by Ip6Output, it should be removed from\r
+ the TxToken map and user's event signaled. The token wrap and buffer\r
+ are bound together. Check the comments in Ip6Output for information\r
+ about IP fragmentation.\r
+\r
+ @param[in] Context The token's wrap.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6FreeTxToken (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_TXTOKEN_WRAP *Wrap;\r
+ NET_MAP_ITEM *Item;\r
+\r
+ Wrap = (IP6_TXTOKEN_WRAP *) Context;\r
+\r
+ //\r
+ // Signal IpSecRecycleEvent to inform IPsec free the memory\r
+ //\r
+ if (Wrap->IpSecRecycleSignal != NULL) {\r
+ gBS->SignalEvent (Wrap->IpSecRecycleSignal);\r
+ }\r
+\r
+ //\r
+ // Find the token in the instance's map. EfiIp6Transmit put the\r
+ // token to the map. If that failed, NetMapFindKey will return NULL.\r
+ //\r
+ Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token);\r
+\r
+ if (Item != NULL) {\r
+ NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL);\r
+ }\r
+\r
+ if (Wrap->Sent) {\r
+ gBS->SignalEvent (Wrap->Token->Event);\r
+\r
+ //\r
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.\r
+ //\r
+ DispatchDpc ();\r
+ }\r
+\r
+ FreePool (Wrap);\r
+}\r
+\r
+\r
+/**\r
+ The callback function to Ip6Output to update the transmit status.\r
+\r
+ @param[in] Packet The user's transmit packet.\r
+ @param[in] IoStatus The result of the transmission.\r
+ @param[in] Flag Not used during transmission.\r
+ @param[in] Context The token's wrap.\r
+\r
+**/\r
+VOID\r
+Ip6OnPacketSent (\r
+ IN NET_BUF *Packet,\r
+ IN EFI_STATUS IoStatus,\r
+ IN UINT32 Flag,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_TXTOKEN_WRAP *Wrap;\r
+\r
+ //\r
+ // This is the transmission request from upper layer,\r
+ // not the IP6 driver itself.\r
+ //\r
+ Wrap = (IP6_TXTOKEN_WRAP *) Context;\r
+ Wrap->Token->Status = IoStatus;\r
+\r
+ NetbufFree (Wrap->Packet);\r
+}\r
+\r
+/**\r
+ Places outgoing data packets into the transmit queue.\r
+\r
+ The Transmit() function places a sending request in the transmit queue of this\r
+ EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some\r
+ errors occur, the event in the token will be signaled, and the status is updated.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the transmit token.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing\r
+ a source address for this transmission,\r
+ but no source address was available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Event is NULL.\r
+ - Token.Packet.TxData is NULL.\r
+ - Token.Packet.ExtHdrsLength is not zero and\r
+ Token.Packet.ExtHdrs is NULL.\r
+ - Token.Packet.FragmentCount is zero.\r
+ - One or more of the Token.Packet.TxData.\r
+ FragmentTable[].FragmentLength fields is zero.\r
+ - One or more of the Token.Packet.TxData.\r
+ FragmentTable[].FragmentBuffer fields is NULL.\r
+ - Token.Packet.TxData.DataLength is zero or not\r
+ equal to the sum of fragment lengths.\r
+ - Token.Packet.TxData.DestinationAddress is non\r
+ zero when DestinationAddress is configured as\r
+ non-zero when doing Configure() for this\r
+ EFI IPv6 protocol instance.\r
+ - Token.Packet.TxData.DestinationAddress is\r
+ unspecified when DestinationAddress is unspecified\r
+ when doing Configure() for this EFI IPv6 protocol\r
+ instance.\r
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.\r
+ Event was already in the transmit queue.\r
+ @retval EFI_NOT_READY The completion token could not be queued because\r
+ the transmit queue is full.\r
+ @retval EFI_NOT_FOUND Not route is found to destination address.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.\r
+ @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too\r
+ short to transmit.\r
+ @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the\r
+ maximum that which can be described through the\r
+ Fragment Offset field in Fragment header when\r
+ performing fragmentation.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Transmit (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_IP6_CONFIG_DATA *Config;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ EFI_IP6_HEADER Head;\r
+ EFI_IP6_TRANSMIT_DATA *TxData;\r
+ EFI_IP6_OVERRIDE_DATA *Override;\r
+ IP6_TXTOKEN_WRAP *Wrap;\r
+ UINT8 *ExtHdrs;\r
+\r
+ //\r
+ // Check input parameters.\r
+ //\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ExtHdrs = NULL;\r
+\r
+ Status = Ip6TxTokenValid (Token);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ Config = &IpInstance->ConfigData;\r
+\r
+ //\r
+ // Check whether the token or signal already existed.\r
+ //\r
+ if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip6TokenExist, Token))) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Build the IP header, fill in the information from ConfigData or OverrideData\r
+ //\r
+ ZeroMem (&Head, sizeof(EFI_IP6_HEADER));\r
+ TxData = Token->Packet.TxData;\r
+ IP6_COPY_ADDRESS (&Head.SourceAddress, &Config->StationAddress);\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, &Config->DestinationAddress);\r
+\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&TxData->DestinationAddress)) {\r
+ if (NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {\r
+ goto Exit;\r
+ }\r
+\r
+ ASSERT (!NetIp6IsUnspecifiedAddr (&Config->StationAddress));\r
+\r
+ } else {\r
+ //\r
+ // StationAddress is unspecified only when ConfigData.Dest is unspecified.\r
+ // Use TxData.Dest to override the DestinationAddress.\r
+ //\r
+ if (!NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&Config->StationAddress)) {\r
+ Status = Ip6SelectSourceAddress (\r
+ IpSb,\r
+ &TxData->DestinationAddress,\r
+ &Head.SourceAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, &TxData->DestinationAddress);\r
+ }\r
+\r
+ //\r
+ // Fill in Head infos.\r
+ //\r
+ Head.NextHeader = Config->DefaultProtocol;\r
+ if (TxData->ExtHdrsLength != 0) {\r
+ Head.NextHeader = TxData->NextHeader;\r
+ }\r
+\r
+ if (TxData->OverrideData != NULL) {\r
+ Override = TxData->OverrideData;\r
+ Head.NextHeader = Override->Protocol;\r
+ Head.HopLimit = Override->HopLimit;\r
+ Head.FlowLabelL = HTONS ((UINT16) Override->FlowLabel);\r
+ Head.FlowLabelH = (UINT8) ((Override->FlowLabel >> 16) & 0x0F);\r
+ } else {\r
+ Head.HopLimit = Config->HopLimit;\r
+ Head.FlowLabelL = HTONS ((UINT16) Config->FlowLabel);\r
+ Head.FlowLabelH = (UINT8) ((Config->FlowLabel >> 16) & 0x0F);\r
+ }\r
+\r
+ Head.PayloadLength = HTONS ((UINT16) (TxData->ExtHdrsLength + TxData->DataLength));\r
+\r
+ //\r
+ // OK, it survives all the validation check. Wrap the token in\r
+ // a IP6_TXTOKEN_WRAP and the data in a netbuf\r
+ //\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ Wrap = AllocateZeroPool (sizeof (IP6_TXTOKEN_WRAP));\r
+ if (Wrap == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ Wrap->IpInstance = IpInstance;\r
+ Wrap->Token = Token;\r
+ Wrap->Sent = FALSE;\r
+ Wrap->Life = IP6_US_TO_SEC (Config->TransmitTimeout);\r
+ Wrap->Packet = NetbufFromExt (\r
+ (NET_FRAGMENT *) TxData->FragmentTable,\r
+ TxData->FragmentCount,\r
+ IP6_MAX_HEADLEN,\r
+ 0,\r
+ Ip6FreeTxToken,\r
+ Wrap\r
+ );\r
+\r
+ if (Wrap->Packet == NULL) {\r
+ FreePool (Wrap);\r
+ goto Exit;\r
+ }\r
+\r
+ Token->Status = EFI_NOT_READY;\r
+\r
+ Status = NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // NetbufFree will call Ip6FreeTxToken, which in turn will\r
+ // free the IP6_TXTOKEN_WRAP. Now, the token wrap hasn't been\r
+ // enqueued.\r
+ //\r
+ NetbufFree (Wrap->Packet);\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Allocate a new buffer to store IPv6 extension headers to avoid updating\r
+ // the original data in EFI_IP6_COMPLETION_TOKEN.\r
+ //\r
+ if (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs != NULL) {\r
+ ExtHdrs = (UINT8 *) AllocateCopyPool (TxData->ExtHdrsLength, TxData->ExtHdrs);\r
+ if (ExtHdrs == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Mark the packet sent before output it. Mark it not sent again if the\r
+ // returned status is not EFI_SUCCESS;\r
+ //\r
+ Wrap->Sent = TRUE;\r
+\r
+ Status = Ip6Output (\r
+ IpSb,\r
+ NULL,\r
+ IpInstance,\r
+ Wrap->Packet,\r
+ &Head,\r
+ ExtHdrs,\r
+ TxData->ExtHdrsLength,\r
+ Ip6OnPacketSent,\r
+ Wrap\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Wrap->Sent = FALSE;\r
+ NetbufFree (Wrap->Packet);\r
+ }\r
+\r
+Exit:\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ if (ExtHdrs != NULL) {\r
+ FreePool (ExtHdrs);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Places a receiving request into the receiving queue.\r
+\r
+ The Receive() function places a completion token into the receive packet queue.\r
+ This function is always asynchronous.\r
+\r
+ The Token.Event field in the completion token must be filled in by the caller\r
+ and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol\r
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event\r
+ is signaled.\r
+\r
+ Current Udp implementation creates an IP child for each Udp child.\r
+ It initates a asynchronous receive immediately no matter whether\r
+ there is no mapping or not. Therefore, disable the returning EFI_NO_MAPPING for now.\r
+ To enable it, the following check must be performed:\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that is associated with the receive data descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token was cached.\r
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.\r
+ @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance,\r
+ while no source address is available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system\r
+ resources (usually memory).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ The EFI IPv6 Protocol instance has been reset to startup defaults.\r
+ @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already\r
+ in the receive queue.\r
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Receive (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ IP6_SERVICE *IpSb;\r
+\r
+ if (This == NULL || Token == NULL || Token->Event == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Check whether the toke is already on the receive queue.\r
+ //\r
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip6TokenExist, Token);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Queue the token then check whether there is pending received packet.\r
+ //\r
+ Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Ip6InstanceDeliverPacket (IpInstance);\r
+\r
+ //\r
+ // Dispatch the DPC queued by the NotifyFunction of this instane's receive\r
+ // event.\r
+ //\r
+ DispatchDpc ();\r
+\r
+Exit:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Cancel the transmitted but not recycled packet. If a matching\r
+ token is found, it will call Ip6CancelPacket to cancel the\r
+ packet. Ip6CancelPacket cancels all the fragments of the\r
+ packet. When all the fragments are freed, the IP6_TXTOKEN_WRAP\r
+ is deleted from the Map, and user's event is signalled.\r
+ Because Ip6CancelPacket and other functions are all called in\r
+ line, after Ip6CancelPacket returns, the Item has been freed.\r
+\r
+ @param[in] Map The IP6 child's transmit queue.\r
+ @param[in] Item The current transmitted packet to test.\r
+ @param[in] Context The user's token to cancel.\r
+\r
+ @retval EFI_SUCCESS Continue to check the next Item.\r
+ @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6CancelTxTokens (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_IP6_COMPLETION_TOKEN *Token;\r
+ IP6_TXTOKEN_WRAP *Wrap;\r
+\r
+ Token = (EFI_IP6_COMPLETION_TOKEN *) Context;\r
+\r
+ //\r
+ // Return EFI_SUCCESS to check the next item in the map if\r
+ // this one doesn't match.\r
+ //\r
+ if ((Token != NULL) && (Token != Item->Key)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;\r
+ ASSERT (Wrap != NULL);\r
+\r
+ //\r
+ // Don't access the Item, Wrap and Token's members after this point.\r
+ // Item and wrap has been freed. And we no longer own the Token.\r
+ //\r
+ Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);\r
+\r
+ //\r
+ // If only one item is to be cancel, return EFI_ABORTED to stop\r
+ // iterating the map any more.\r
+ //\r
+ if (Token != NULL) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Cancel the receive request. This is simple, because\r
+ it is only enqueued in our local receive map.\r
+\r
+ @param[in] Map The IP6 child's receive queue.\r
+ @param[in] Item Current receive request to cancel.\r
+ @param[in] Context The user's token to cancel.\r
+\r
+\r
+ @retval EFI_SUCCESS Continue to check the next receive request on the\r
+ queue.\r
+ @retval EFI_ABORTED The user's token (token != NULL) has been\r
+ cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6CancelRxTokens (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_IP6_COMPLETION_TOKEN *Token;\r
+ EFI_IP6_COMPLETION_TOKEN *This;\r
+\r
+ Token = (EFI_IP6_COMPLETION_TOKEN *) Context;\r
+ This = Item->Key;\r
+\r
+ if ((Token != NULL) && (Token != This)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ NetMapRemoveItem (Map, Item, NULL);\r
+\r
+ This->Status = EFI_ABORTED;\r
+ This->Packet.RxData = NULL;\r
+ gBS->SignalEvent (This->Event);\r
+\r
+ if (Token != NULL) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Cancel the user's receive/transmit request. It is the worker function of\r
+ EfiIp6Cancel API.\r
+\r
+ @param[in] IpInstance The IP6 child.\r
+ @param[in] Token The token to cancel. If NULL, all token will be\r
+ cancelled.\r
+\r
+ @retval EFI_SUCCESS The token is cancelled.\r
+ @retval EFI_NOT_FOUND The token isn't found on either the\r
+ transmit/receive queue.\r
+ @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Cancel (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // First check the transmitted packet. Ip6CancelTxTokens returns\r
+ // EFI_ABORTED to mean that the token has been cancelled when\r
+ // token != NULL. So, return EFI_SUCCESS for this condition.\r
+ //\r
+ Status = NetMapIterate (&IpInstance->TxTokens, Ip6CancelTxTokens, Token);\r
+ if (EFI_ERROR (Status)) {\r
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the receive queue. Ip6CancelRxTokens also returns EFI_ABORT\r
+ // for Token!=NULL and it is cancelled.\r
+ //\r
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip6CancelRxTokens, Token);\r
+ //\r
+ // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's\r
+ // events.\r
+ //\r
+ DispatchDpc ();\r
+ if (EFI_ERROR (Status)) {\r
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // OK, if the Token is found when Token != NULL, the NetMapIterate\r
+ // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS.\r
+ //\r
+ if (Token != NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // If Token == NULL, cancel all the tokens. return error if not\r
+ // all of them are cancelled.\r
+ //\r
+ if (!NetMapIsEmpty (&IpInstance->TxTokens) || !NetMapIsEmpty (&IpInstance->RxTokens)) {\r
+\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Abort an asynchronous transmit or receive request.\r
+\r
+ The Cancel() function is used to abort a pending transmit or receive request.\r
+ If the token is in the transmit or receive request queues, after calling this\r
+ function, Token->Status will be set to EFI_ABORTED, and then Token->Event will\r
+ be signaled. If the token is not in one of the queues, which usually means the\r
+ asynchronous operation has completed, this function will not signal the token,\r
+ and EFI_NOT_FOUND is returned.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that has been issued by\r
+ EFI_IP6_PROTOCOL.Transmit() or\r
+ EFI_IP6_PROTOCOL.Receive(). If NULL, all pending\r
+ tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is\r
+ defined in EFI_IP6_PROTOCOL.Transmit().\r
+\r
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and\r
+ Token->Event was signaled. When Token is NULL, all\r
+ pending requests were aborted, and their events were signaled.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was\r
+ not found in the transmit or receive queue. It has either completed\r
+ or was not issued by Transmit() and Receive().\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Cancel (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_SERVICE *IpSb;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Ip6Cancel (IpInstance, Token);\r
+\r
+Exit:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Polls for incoming data packets, and processes outgoing data packets.\r
+\r
+ The Poll() function polls for incoming data packets and processes outgoing data\r
+ packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()\r
+ function to increase the rate that data packets are moved between the communications\r
+ device and the transmit and receive queues.\r
+\r
+ In some systems the periodic timer event may not poll the underlying communications\r
+ device fast enough to transmit and/or receive all data packets without missing\r
+ incoming packets or dropping outgoing packets. Drivers and applications that are\r
+ experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function\r
+ more often.\r
+\r
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred.\r
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.\r
+ Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Poll (\r
+ IN EFI_IP6_PROTOCOL *This\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_SERVICE *IpSb;\r
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (IpSb->LinkLocalDadFail) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (IpInstance->State == IP6_STATE_UNCONFIGED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ Mnp = IpInstance->Service->Mnp;\r
+\r
+ //\r
+ // Don't lock the Poll function to enable the deliver of\r
+ // the packet polled up.\r
+ //\r
+ return Mnp->Poll (Mnp);\r
+\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_IMPL_H__\r
+#define __EFI_IP6_IMPL_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/ManagedNetwork.h>\r
+#include <Protocol/IpSec.h>\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+#include <Protocol/Dhcp6.h>\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/HiiConfigRouting.h>\r
+#include <Protocol/HiiConfigAccess.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DpcLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/UefiHiiServicesLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PrintLib.h>\r
+\r
+#include <Guid/MdeModuleHii.h>\r
+\r
+#include "Ip6Common.h"\r
+#include "Ip6Driver.h"\r
+#include "Ip6Icmp.h"\r
+#include "Ip6If.h"\r
+#include "Ip6Input.h"\r
+#include "Ip6Mld.h"\r
+#include "Ip6Nd.h"\r
+#include "Ip6Option.h"\r
+#include "Ip6Output.h"\r
+#include "Ip6Route.h"\r
+#include "Ip6ConfigNv.h"\r
+#include "Ip6ConfigImpl.h"\r
+\r
+#define IP6_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'P')\r
+#define IP6_SERVICE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'S')\r
+\r
+//\r
+// The state of IP6 protocol. It starts from UNCONFIGED. if it is\r
+// successfully configured, it goes to CONFIGED. if configure NULL\r
+// is called, it becomes UNCONFIGED again. If (partly) destroyed, it\r
+// becomes DESTROY.\r
+//\r
+#define IP6_STATE_UNCONFIGED 0\r
+#define IP6_STATE_CONFIGED 1\r
+#define IP6_STATE_DESTROY 2\r
+\r
+//\r
+// The state of IP6 service. It starts from UNSTARTED. It transits\r
+// to STARTED if autoconfigure is started. If default address is\r
+// configured, it becomes CONFIGED. and if partly destroyed, it goes\r
+// to DESTROY.\r
+//\r
+#define IP6_SERVICE_UNSTARTED 0\r
+#define IP6_SERVICE_STARTED 1\r
+#define IP6_SERVICE_CONFIGED 2\r
+#define IP6_SERVICE_DESTROY 3\r
+\r
+#define IP6_INSTANCE_FROM_PROTOCOL(Ip6) \\r
+ CR ((Ip6), IP6_PROTOCOL, Ip6Proto, IP6_PROTOCOL_SIGNATURE)\r
+\r
+#define IP6_SERVICE_FROM_PROTOCOL(Sb) \\r
+ CR ((Sb), IP6_SERVICE, ServiceBinding, IP6_SERVICE_SIGNATURE)\r
+\r
+#define IP6_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured)\r
+\r
+extern EFI_IPSEC_PROTOCOL *mIpSec;\r
+\r
+//\r
+// IP6_TXTOKEN_WRAP wraps the upper layer's transmit token.\r
+// The user's data is kept in the Packet. When fragment is\r
+// needed, each fragment of the Packet has a reference to the\r
+// Packet, no data is actually copied. The Packet will be\r
+// released when all the fragments of it have been recycled by\r
+// MNP. Upon then, the IP6_TXTOKEN_WRAP will be released, and\r
+// user's event signalled.\r
+//\r
+typedef struct {\r
+ IP6_PROTOCOL *IpInstance;\r
+ EFI_IP6_COMPLETION_TOKEN *Token;\r
+ EFI_EVENT IpSecRecycleSignal;\r
+ NET_BUF *Packet;\r
+ BOOLEAN Sent;\r
+ INTN Life;\r
+} IP6_TXTOKEN_WRAP;\r
+\r
+typedef struct {\r
+ EFI_EVENT IpSecRecycleSignal;\r
+ NET_BUF *Packet;\r
+} IP6_IPSEC_WRAP;\r
+\r
+//\r
+// IP6_RXDATA_WRAP wraps the data IP6 child delivers to the\r
+// upper layers. The received packet is kept in the Packet.\r
+// The Packet itself may be constructured from some fragments.\r
+// All the fragments of the Packet is organized by a\r
+// IP6_ASSEMBLE_ENTRY structure. If the Packet is recycled by\r
+// the upper layer, the assemble entry and its associated\r
+// fragments will be freed at last.\r
+//\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ IP6_PROTOCOL *IpInstance;\r
+ NET_BUF *Packet;\r
+ EFI_IP6_RECEIVE_DATA RxData;\r
+} IP6_RXDATA_WRAP;\r
+\r
+struct _IP6_PROTOCOL {\r
+ UINT32 Signature;\r
+\r
+ EFI_IP6_PROTOCOL Ip6Proto;\r
+ EFI_HANDLE Handle;\r
+ INTN State;\r
+\r
+ IP6_SERVICE *Service;\r
+ LIST_ENTRY Link; // Link to all the IP protocol from the service\r
+\r
+ UINT8 PrefixLength; // PrefixLength of the configured station address.\r
+ //\r
+ // User's transmit/receive tokens, and received/deliverd packets\r
+ //\r
+ NET_MAP RxTokens;\r
+ NET_MAP TxTokens; // map between (User's Token, IP6_TXTOKE_WRAP)\r
+ LIST_ENTRY Received; // Received but not delivered packet\r
+ LIST_ENTRY Delivered; // Delivered and to be recycled packets\r
+ EFI_LOCK RecycleLock;\r
+\r
+ IP6_INTERFACE *Interface;\r
+ LIST_ENTRY AddrLink; // Ip instances with the same IP address.\r
+\r
+ EFI_IPv6_ADDRESS *GroupList; // stored in network order.\r
+ UINT32 GroupCount;\r
+\r
+ EFI_IP6_CONFIG_DATA ConfigData;\r
+};\r
+\r
+struct _IP6_SERVICE {\r
+ UINT32 Signature;\r
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;\r
+ INTN State;\r
+ BOOLEAN InDestroy;\r
+\r
+ //\r
+ // List of all the IP instances and interfaces, and default\r
+ // interface and route table and caches.\r
+ //\r
+ UINTN NumChildren;\r
+ LIST_ENTRY Children;\r
+\r
+ LIST_ENTRY Interfaces;\r
+\r
+ IP6_INTERFACE *DefaultInterface;\r
+ IP6_ROUTE_TABLE *RouteTable;\r
+\r
+ IP6_LINK_RX_TOKEN RecvRequest;\r
+\r
+ //\r
+ // Ip reassemble utilities and MLD data\r
+ //\r
+ IP6_ASSEMBLE_TABLE Assemble;\r
+ IP6_MLD_SERVICE_DATA MldCtrl;\r
+\r
+ EFI_IPv6_ADDRESS LinkLocalAddr;\r
+ BOOLEAN LinkLocalOk;\r
+ BOOLEAN LinkLocalDadFail;\r
+ BOOLEAN Dhcp6NeedStart;\r
+ BOOLEAN Dhcp6NeedInfoRequest;\r
+\r
+ //\r
+ // ND data\r
+ //\r
+ UINT8 CurHopLimit;\r
+ UINT32 LinkMTU;\r
+ UINT32 BaseReachableTime;\r
+ UINT32 ReachableTime;\r
+ UINT32 RetransTimer;\r
+ LIST_ENTRY NeighborTable;\r
+\r
+ LIST_ENTRY OnlinkPrefix;\r
+ LIST_ENTRY AutonomousPrefix;\r
+\r
+ LIST_ENTRY DefaultRouterList;\r
+ UINT32 RoundRobin;\r
+\r
+ UINT8 InterfaceIdLen;\r
+ UINT8 *InterfaceId;\r
+\r
+ BOOLEAN RouterAdvertiseReceived;\r
+ UINT8 SolicitTimer;\r
+ UINT32 Ticks;\r
+\r
+ //\r
+ // Low level protocol used by this service instance\r
+ //\r
+ EFI_HANDLE Image;\r
+ EFI_HANDLE Controller;\r
+\r
+ EFI_HANDLE MnpChildHandle;\r
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
+\r
+ EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;\r
+ EFI_SIMPLE_NETWORK_MODE SnpMode;\r
+\r
+ EFI_EVENT Timer;\r
+ EFI_EVENT FasterTimer;\r
+\r
+ //\r
+ // IPv6 Configuration Protocol instance\r
+ //\r
+ IP6_CONFIG_INSTANCE Ip6ConfigInstance;\r
+\r
+ //\r
+ // The string representation of the current mac address of the\r
+ // NIC this IP6_SERVICE works on.\r
+ //\r
+ CHAR16 *MacString;\r
+ UINT32 MaxPacketSize;\r
+ UINT32 OldMaxPacketSize;\r
+};\r
+\r
+/**\r
+ The callback function for the net buffer which wraps the user's\r
+ transmit token. Although this function seems simple,\r
+ there are some subtle aspects.\r
+ When a user requests the IP to transmit a packet by passing it a\r
+ token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data\r
+ is wrapped in a net buffer. The net buffer's Free function is\r
+ set to Ip6FreeTxToken. The Token and token wrap are added to the\r
+ IP child's TxToken map. Then the buffer is passed to Ip6Output for\r
+ transmission. If an error occurs before that, the buffer\r
+ is freed, which in turn frees the token wrap. The wrap may\r
+ have been added to the TxToken map or not, and the user's event\r
+ shouldn't be signaled because we are still in the EfiIp6Transmit. If\r
+ the buffer has been sent by Ip6Output, it should be removed from\r
+ the TxToken map and the user's event signaled. The token wrap and buffer\r
+ are bound together. Refer to the comments in Ip6Output for information\r
+ about IP fragmentation.\r
+\r
+ @param[in] Context The token's wrap.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6FreeTxToken (\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Config the MNP parameter used by IP. The IP driver use one MNP\r
+ child to transmit/receive frames. By default, it configures MNP\r
+ to receive unicast/multicast/broadcast. And it will enable/disable\r
+ the promiscuous receive according to whether there is IP child\r
+ enable that or not. If Force is FALSE, it will iterate through\r
+ all the IP children to check whether the promiscuous receive\r
+ setting has been changed. If it hasn't been changed, it won't\r
+ reconfigure the MNP. If Force is TRUE, the MNP is configured\r
+ whether that is changed or not.\r
+\r
+ @param[in] IpSb The IP6 service instance that is to be changed.\r
+ @param[in] Force Force the configuration or not.\r
+\r
+ @retval EFI_SUCCESS The MNP successfully configured/reconfigured.\r
+ @retval Others The configuration failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ServiceConfigMnp (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN Force\r
+ );\r
+\r
+/**\r
+ Cancel the user's receive/transmit request. It is the worker function of\r
+ EfiIp6Cancel API.\r
+\r
+ @param[in] IpInstance The IP6 child.\r
+ @param[in] Token The token to cancel. If NULL, all tokens will be\r
+ cancelled.\r
+\r
+ @retval EFI_SUCCESS The token was cancelled.\r
+ @retval EFI_NOT_FOUND The token isn't found on either the\r
+ transmit or receive queue.\r
+ @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Cancel (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ );\r
+\r
+/**\r
+ Initialize the IP6_PROTOCOL structure to the unconfigured states.\r
+\r
+ @param[in] IpSb The IP6 service instance.\r
+ @param[in, out] IpInstance The IP6 child instance.\r
+\r
+**/\r
+VOID\r
+Ip6InitProtocol (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN OUT IP6_PROTOCOL *IpInstance\r
+ );\r
+\r
+/**\r
+ Clean up the IP6 child, release all the resources used by it.\r
+\r
+ @param[in, out] IpInstance The IP6 child to clean up.\r
+\r
+ @retval EFI_SUCCESS The IP6 child was cleaned up\r
+ @retval EFI_DEVICE_ERROR Some resources failed to be released.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CleanProtocol (\r
+ IN OUT IP6_PROTOCOL *IpInstance\r
+ );\r
+\r
+//\r
+// EFI_IP6_PROTOCOL interface prototypes\r
+//\r
+\r
+/**\r
+ Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.\r
+\r
+ The GetModeData() function returns the current operational mode data for this driver instance.\r
+ The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to\r
+ retrieve the operational mode data of underlying networks or drivers.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[out] Ip6ModeData The pointer to the EFI IPv6 Protocol mode data structure.\r
+ @param[out] MnpConfigData The pointer to the managed network configuration data structure.\r
+ @param[out] SnpModeData The pointer to the simple network mode data structure.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6GetModeData (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.\r
+\r
+ The Configure() function is used to set, change, or reset the operational parameters and filter\r
+ settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic\r
+ can be sent or received by this instance. Once the parameters have been reset (by calling this\r
+ function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these\r
+ parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped\r
+ independently of each other by enabling or disabling their receive filter settings with the\r
+ Configure() function.\r
+\r
+ If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required\r
+ to be one of the currently configured IPv6 addresses list in the EFI IPv6 drivers, or else\r
+ EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is\r
+ unspecified, the IPv6 driver will bind a source address according to the source address selection\r
+ algorithm. Clients could frequently call GetModeData() to check get a currently configured IPv6.\r
+ If both Ip6ConfigData.StationAddress and Ip6ConfigData.Destination are unspecified, when\r
+ transmitting the packet afterwards, the source address filled in each outgoing IPv6 packet\r
+ is decided based on the destination of this packet.\r
+\r
+ If operational parameters are reset or changed, any pending transmit and receive requests will be\r
+ cancelled. Their completion token status will be set to EFI_ABORTED, and their events will be\r
+ signaled.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Ip6ConfigData The pointer to the EFI IPv6 Protocol configuration data structure.\r
+ If NULL, reset the configuration data.\r
+\r
+ @retval EFI_SUCCESS The driver instance was successfully opened.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Ip6ConfigData.StationAddress is neither zero nor\r
+ a unicast IPv6 address.\r
+ - Ip6ConfigData.StationAddress is neither zero nor\r
+ one of the configured IP addresses in the EFI IPv6 driver.\r
+ - Ip6ConfigData.DefaultProtocol is illegal.\r
+ @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated.\r
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for\r
+ this instance, but no source address was available for use.\r
+ @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6\r
+ address or prefix length can be changed.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6\r
+ Protocol driver instance was not opened.\r
+ @retval EFI_UNSUPPORTED Default protocol specified through\r
+ Ip6ConfigData.DefaulProtocol isn't supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Configure (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Joins and leaves multicast groups.\r
+\r
+ The Groups() function is used to join and leave multicast group sessions. Joining a group will\r
+ enable reception of matching multicast packets. Leaving a group will disable reception of matching\r
+ multicast packets. Source-Specific Multicast isn't required to be supported.\r
+\r
+ If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave.\r
+ @param[in] GroupAddress The pointer to the IPv6 multicast address.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:\r
+ - This is NULL.\r
+ - JoinFlag is TRUE and GroupAddress is NULL.\r
+ - GroupAddress is not NULL and *GroupAddress is\r
+ not a multicast IPv6 address.\r
+ - GroupAddress is not NULL and *GroupAddress is in the\r
+ range of SSM destination address.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_OUT_OF_RESOURCES System resources could not be allocated.\r
+ @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups.\r
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when\r
+ JoinFlag is TRUE).\r
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Groups (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN BOOLEAN JoinFlag,\r
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ Adds and deletes routing table entries.\r
+\r
+ The Routes() function adds a route to or deletes a route from the routing table.\r
+\r
+ Routes are determined by comparing the leftmost PrefixLength bits of Destination with\r
+ the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the\r
+ configured station address.\r
+\r
+ The default route is added with Destination and PrefixLegth both set to all zeros. The\r
+ default route matches all destination IPv6 addresses that do not match any other routes.\r
+\r
+ All EFI IPv6 Protocol instances share a routing table.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to\r
+ FALSE to add this route to the routing table. Destination,\r
+ PrefixLength and Gateway are used as the key to each\r
+ route entry.\r
+ @param[in] Destination The address prefix of the subnet that needs to be routed.\r
+ This is an optional parameter that may be NULL.\r
+ @param[in] PrefixLength The prefix length of Destination. Ignored if Destination\r
+ is NULL.\r
+ @param[in] GatewayAddress The unicast gateway IPv6 address for this route.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The driver instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - When DeleteRoute is TRUE, both Destination and\r
+ GatewayAddress are NULL.\r
+ - When DeleteRoute is FALSE, either Destination or\r
+ GatewayAddress is NULL.\r
+ - *GatewayAddress is not a valid unicast IPv6 address.\r
+ - *GatewayAddress is one of the local configured IPv6\r
+ addresses.\r
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.\r
+ @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE).\r
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when\r
+ DeleteRoute is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Routes (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN BOOLEAN DeleteRoute,\r
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ Add or delete Neighbor cache entries.\r
+\r
+ The Neighbors() function is used to add, update, or delete an entry from a neighbor cache.\r
+ IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as\r
+ network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network\r
+ traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not\r
+ timeout) or dynamic (will timeout).\r
+\r
+ The implementation should follow the neighbor cache timeout mechanism defined in\r
+ RFC4861. The default neighbor cache timeout value should be tuned for the expected network\r
+ environment.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] DeleteFlag Set to TRUE to delete the specified cache entry. Set to FALSE to\r
+ add (or update, if it already exists and Override is TRUE) the\r
+ specified cache entry. TargetIp6Address is used as the key\r
+ to find the requested cache entry.\r
+ @param[in] TargetIp6Address The pointer to the Target IPv6 address.\r
+ @param[in] TargetLinkAddress The pointer to link-layer address of the target. Ignored if NULL.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor\r
+ cache, it will be deleted after Timeout. A value of zero means that\r
+ the entry is permanent. A non-zero value means that the entry is\r
+ dynamic.\r
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will\r
+ be overridden and updated; if FALSE, EFI_ACCESS_DENIED\r
+ will be returned if a corresponding cache entry already exists.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - TargetIpAddress is NULL.\r
+ - *TargetLinkAddress is invalid when not NULL.\r
+ - *TargetIpAddress is not a valid unicast IPv6 address.\r
+ - *TargetIpAddress is one of the local configured IPv6\r
+ addresses.\r
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache.\r
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is\r
+ TRUE or when DeleteFlag is FALSE while\r
+ TargetLinkAddress is NULL.).\r
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,\r
+ and that entry is tagged as un-overridden (when Override\r
+ is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Neighbors (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN BOOLEAN DeleteFlag,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,\r
+ IN UINT32 Timeout,\r
+ IN BOOLEAN Override\r
+ );\r
+\r
+/**\r
+ Places outgoing data packets into the transmit queue.\r
+\r
+ The Transmit() function places a sending request in the transmit queue of this\r
+ EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some\r
+ errors occur, the event in the token will be signaled and the status is updated.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Token The pointer to the transmit token.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing\r
+ a source address for this transmission,\r
+ but no source address was available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Event is NULL.\r
+ - Token.Packet.TxData is NULL.\r
+ - Token.Packet.ExtHdrsLength is not zero and\r
+ Token.Packet.ExtHdrs is NULL.\r
+ - Token.Packet.FragmentCount is zero.\r
+ - One or more of the Token.Packet.TxData.\r
+ FragmentTable[].FragmentLength fields is zero.\r
+ - One or more of the Token.Packet.TxData.\r
+ FragmentTable[].FragmentBuffer fields is NULL.\r
+ - Token.Packet.TxData.DataLength is zero or not\r
+ equal to the sum of fragment lengths.\r
+ - Token.Packet.TxData.DestinationAddress is non-\r
+ zero when DestinationAddress is configured as\r
+ non-zero when doing Configure() for this\r
+ EFI IPv6 protocol instance.\r
+ - Token.Packet.TxData.DestinationAddress is\r
+ unspecified when DestinationAddress is unspecified\r
+ when doing Configure() for this EFI IPv6 protocol\r
+ instance.\r
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.\r
+ The event was already in the transmit queue.\r
+ @retval EFI_NOT_READY The completion token could not be queued because\r
+ the transmit queue is full.\r
+ @retval EFI_NOT_FOUND Not route is found to the destination address.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.\r
+ @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too\r
+ short to transmit.\r
+ @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the\r
+ maximum that which can be described through the\r
+ Fragment Offset field in Fragment header when\r
+ performing fragmentation.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Transmit (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Places a receiving request into the receiving queue.\r
+\r
+ The Receive() function places a completion token into the receive packet queue.\r
+ This function is always asynchronous.\r
+\r
+ The Token.Event field in the completion token must be filled in by the caller\r
+ and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol\r
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event\r
+ is signaled.\r
+\r
+ Current Udp implementation creates an IP child for each Udp child.\r
+ It initates a asynchronous receive immediately whether or not\r
+ there is no mapping. Therefore, disable the returning EFI_NO_MAPPING for now.\r
+ To enable it, the following check must be performed:\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Token The pointer to a token that is associated with the\r
+ receive data descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token was cached.\r
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.\r
+ @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance,\r
+ while no source address is available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system\r
+ resources (usually memory).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ The EFI IPv6 Protocol instance has been reset to startup defaults.\r
+ @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already\r
+ in the receive queue.\r
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Receive (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Abort an asynchronous transmit or receive request.\r
+\r
+ The Cancel() function is used to abort a pending transmit or receive request.\r
+ If the token is in the transmit or receive request queues, after calling this\r
+ function, Token->Status will be set to EFI_ABORTED, and then Token->Event will\r
+ be signaled. If the token is not in one of the queues, which usually means the\r
+ asynchronous operation has completed, this function will not signal the token,\r
+ and EFI_NOT_FOUND is returned.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+ @param[in] Token The pointer to a token that has been issued by\r
+ EFI_IP6_PROTOCOL.Transmit() or\r
+ EFI_IP6_PROTOCOL.Receive(). If NULL, all pending\r
+ tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is\r
+ defined in EFI_IP6_PROTOCOL.Transmit().\r
+\r
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and\r
+ Token->Event was signaled. When Token is NULL, all\r
+ pending requests were aborted, and their events were signaled.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was\r
+ not found in the transmit or receive queue. It has either completed\r
+ or was not issued by Transmit() and Receive().\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Cancel (\r
+ IN EFI_IP6_PROTOCOL *This,\r
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ );\r
+\r
+/**\r
+ Polls for incoming data packets and processes outgoing data packets.\r
+\r
+ The Poll() function polls for incoming data packets and processes outgoing data\r
+ packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()\r
+ function to increase the rate that data packets are moved between the communications\r
+ device and the transmit and receive queues.\r
+\r
+ In some systems the periodic timer event may not poll the underlying communications\r
+ device fast enough to transmit and/or receive all data packets without missing\r
+ incoming packets or dropping outgoing packets. Drivers and applications that are\r
+ experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function\r
+ more often.\r
+\r
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.\r
+ Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIp6Poll (\r
+ IN EFI_IP6_PROTOCOL *This\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ IP6 internal functions to process the incoming packets.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+ Create an empty assemble entry for the packet identified by\r
+ (Dst, Src, Id). The default life for the packet is 60 seconds.\r
+\r
+ @param[in] Dst The destination address.\r
+ @param[in] Src The source address.\r
+ @param[in] Id The ID field in the IP header.\r
+\r
+ @return NULL if failed to allocate memory for the entry. Otherwise,\r
+ the pointer to the just created reassemble entry.\r
+\r
+**/\r
+IP6_ASSEMBLE_ENTRY *\r
+Ip6CreateAssembleEntry (\r
+ IN EFI_IPv6_ADDRESS *Dst,\r
+ IN EFI_IPv6_ADDRESS *Src,\r
+ IN UINT32 Id\r
+ )\r
+{\r
+ IP6_ASSEMBLE_ENTRY *Assemble;\r
+\r
+ Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY));\r
+ if (Assemble == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&Assemble->Dst, Dst);\r
+ IP6_COPY_ADDRESS (&Assemble->Src, Src);\r
+ InitializeListHead (&Assemble->Fragments);\r
+\r
+ Assemble->Id = Id;\r
+ Assemble->Life = IP6_FRAGMENT_LIFE + 1;\r
+\r
+ Assemble->TotalLen = 0;\r
+ Assemble->CurLen = 0;\r
+ Assemble->Head = NULL;\r
+ Assemble->Info = NULL;\r
+ Assemble->Packet = NULL;\r
+\r
+ return Assemble;\r
+}\r
+\r
+/**\r
+ Release all the fragments of a packet, then free the assemble entry.\r
+\r
+ @param[in] Assemble The assemble entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeAssembleEntry (\r
+ IN IP6_ASSEMBLE_ENTRY *Assemble\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ NET_BUF *Fragment;\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {\r
+ Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+ RemoveEntryList (Entry);\r
+ NetbufFree (Fragment);\r
+ }\r
+\r
+ if (Assemble->Packet != NULL) {\r
+ NetbufFree (Assemble->Packet);\r
+ }\r
+\r
+ FreePool (Assemble);\r
+}\r
+\r
+/**\r
+ Release all the fragments of the packet. This is the callback for\r
+ the assembled packet's OnFree. It will free the assemble entry,\r
+ which in turn frees all the fragments of the packet.\r
+\r
+ @param[in] Arg The assemble entry to free.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnFreeFragments (\r
+ IN VOID *Arg\r
+ )\r
+{\r
+ Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg);\r
+}\r
+\r
+/**\r
+ Trim the packet to fit in [Start, End), and update per the\r
+ packet information.\r
+\r
+ @param[in, out] Packet Packet to trim.\r
+ @param[in] Start The sequence of the first byte to fit in.\r
+ @param[in] End One beyond the sequence of last byte to fit in.\r
+\r
+**/\r
+VOID\r
+Ip6TrimPacket (\r
+ IN OUT NET_BUF *Packet,\r
+ IN INTN Start,\r
+ IN INTN End\r
+ )\r
+{\r
+ IP6_CLIP_INFO *Info;\r
+ INTN Len;\r
+\r
+ Info = IP6_GET_CLIP_INFO (Packet);\r
+\r
+ ASSERT (Info->Start + Info->Length == Info->End);\r
+ ASSERT ((Info->Start < End) && (Start < Info->End));\r
+\r
+ if (Info->Start < Start) {\r
+ Len = Start - Info->Start;\r
+\r
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);\r
+ Info->Start = (UINT32) Start;\r
+ Info->Length -= (UINT32) Len;\r
+ }\r
+\r
+ if (End < Info->End) {\r
+ Len = End - Info->End;\r
+\r
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);\r
+ Info->End = (UINT32) End;\r
+ Info->Length -= (UINT32) Len;\r
+ }\r
+}\r
+\r
+/**\r
+ Reassemble the IP fragments. If all the fragments of the packet\r
+ have been received, it will wrap the packet in a net buffer then\r
+ return it to caller. If the packet can't be assembled, NULL is\r
+ returned.\r
+\r
+ @param[in, out] Table The assemble table used. A new assemble entry will be created\r
+ if the Packet is from a new chain of fragments.\r
+ @param[in] Packet The fragment to assemble. It might be freed if the fragment\r
+ can't be re-assembled.\r
+\r
+ @return NULL if the packet can't be reassembled. The pointer to the just assembled\r
+ packet if all the fragments of the packet have arrived.\r
+\r
+**/\r
+NET_BUF *\r
+Ip6Reassemble (\r
+ IN OUT IP6_ASSEMBLE_TABLE *Table,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ EFI_IP6_HEADER *Head;\r
+ IP6_CLIP_INFO *This;\r
+ IP6_CLIP_INFO *Node;\r
+ IP6_ASSEMBLE_ENTRY *Assemble;\r
+ IP6_ASSEMBLE_ENTRY *Entry;\r
+ LIST_ENTRY *ListHead;\r
+ LIST_ENTRY *Prev;\r
+ LIST_ENTRY *Cur;\r
+ NET_BUF *Fragment;\r
+ NET_BUF *TmpPacket;\r
+ NET_BUF *NewPacket;\r
+ NET_BUF *Duplicate;\r
+ UINT8 *DupHead;\r
+ INTN Index;\r
+ UINT16 UnFragmentLen;\r
+ UINT8 *NextHeader;\r
+\r
+ Head = Packet->Ip.Ip6;\r
+ This = IP6_GET_CLIP_INFO (Packet);\r
+\r
+ ASSERT (Head != NULL);\r
+\r
+ //\r
+ // Find the corresponding assemble entry by (Dst, Src, Id)\r
+ //\r
+ Assemble = NULL;\r
+ Index = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id);\r
+\r
+ NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {\r
+ Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link);\r
+\r
+ if (Entry->Id == This->Id &&\r
+ EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) &&\r
+ EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress)\r
+ ) {\r
+ Assemble = Entry;\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Create a new entry if can not find an existing one, insert it to assemble table\r
+ //\r
+ if (Assemble == NULL) {\r
+ Assemble = Ip6CreateAssembleEntry (\r
+ &Head->DestinationAddress,\r
+ &Head->SourceAddress,\r
+ This->Id\r
+ );\r
+\r
+ if (Assemble == NULL) {\r
+ goto Error;\r
+ }\r
+\r
+ InsertHeadList (&Table->Bucket[Index], &Assemble->Link);\r
+ }\r
+\r
+ //\r
+ // Find the point to insert the packet: before the first\r
+ // fragment with THIS.Start < CUR.Start. the previous one\r
+ // has PREV.Start <= THIS.Start < CUR.Start.\r
+ //\r
+ ListHead = &Assemble->Fragments;\r
+\r
+ NET_LIST_FOR_EACH (Cur, ListHead) {\r
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+\r
+ if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check whether the current fragment overlaps with the previous one.\r
+ // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to\r
+ // check whether THIS.Start < PREV.End for overlap. If two fragments\r
+ // overlaps, trim the overlapped part off THIS fragment.\r
+ //\r
+ if ((Cur != ListHead) && ((Prev = Cur->BackLink) != ListHead)) {\r
+ Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
+ Node = IP6_GET_CLIP_INFO (Fragment);\r
+\r
+ if (This->Start < Node->End) {\r
+ if (This->End <= Node->End) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Trim the previous fragment from tail.\r
+ //\r
+ Ip6TrimPacket (Fragment, Node->Start, This->Start);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Insert the fragment into the packet. The fragment may be removed\r
+ // from the list by the following checks.\r
+ //\r
+ NetListInsertBefore (Cur, &Packet->List);\r
+\r
+ //\r
+ // Check the packets after the insert point. It holds that:\r
+ // THIS.Start <= NODE.Start < NODE.End. The equality holds\r
+ // if PREV and NEXT are continuous. THIS fragment may fill\r
+ // several holes. Remove the completely overlapped fragments\r
+ //\r
+ while (Cur != ListHead) {\r
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+ Node = IP6_GET_CLIP_INFO (Fragment);\r
+\r
+ //\r
+ // Remove fragments completely overlapped by this fragment\r
+ //\r
+ if (Node->End <= This->End) {\r
+ Cur = Cur->ForwardLink;\r
+\r
+ RemoveEntryList (&Fragment->List);\r
+ Assemble->CurLen -= Node->Length;\r
+\r
+ NetbufFree (Fragment);\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // The conditions are: THIS.Start <= NODE.Start, and THIS.End <\r
+ // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.\r
+ // If two fragments start at the same offset, remove THIS fragment\r
+ // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).\r
+ //\r
+ if (Node->Start < This->End) {\r
+ if (This->Start == Node->Start) {\r
+ RemoveEntryList (&Packet->List);\r
+ goto Error;\r
+ }\r
+\r
+ Ip6TrimPacket (Packet, This->Start, Node->Start);\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Update the assemble info: increase the current length. If it is\r
+ // the frist fragment, update the packet's IP head and per packet\r
+ // info. If it is the last fragment, update the total length.\r
+ //\r
+ Assemble->CurLen += This->Length;\r
+\r
+ if (This->Start == 0) {\r
+ //\r
+ // Once the first fragment is enqueued, it can't be removed\r
+ // from the fragment list. So, Assemble->Head always point\r
+ // to valid memory area.\r
+ //\r
+ if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Backup the first fragment in case the reasembly of that packet fail.\r
+ //\r
+ Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));\r
+ if (Duplicate == NULL) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // Revert IP head to network order.\r
+ //\r
+ DupHead = NetbufGetByte (Duplicate, 0, NULL);\r
+ ASSERT (DupHead != NULL);\r
+ Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead);\r
+ Assemble->Packet = Duplicate;\r
+\r
+ //\r
+ // Adjust the unfragmentable part in first fragment\r
+ //\r
+ UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER));\r
+ if (UnFragmentLen == 0) {\r
+ //\r
+ // There is not any unfragmentable extension header.\r
+ //\r
+ ASSERT (Head->NextHeader == IP6_FRAGMENT);\r
+ Head->NextHeader = This->NextHeader;\r
+ } else {\r
+ NextHeader = NetbufGetByte (\r
+ Packet,\r
+ This->FormerNextHeader + sizeof (EFI_IP6_HEADER),\r
+ 0\r
+ );\r
+ if (NextHeader == NULL) {\r
+ goto Error;\r
+ }\r
+\r
+ *NextHeader = This->NextHeader;\r
+ }\r
+\r
+ Assemble->Head = Head;\r
+ Assemble->Info = IP6_GET_CLIP_INFO (Packet);\r
+ }\r
+\r
+ //\r
+ // Don't update the length more than once.\r
+ //\r
+ if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) {\r
+ Assemble->TotalLen = This->End;\r
+ }\r
+\r
+ //\r
+ // Deliver the whole packet if all the fragments received.\r
+ // All fragments received if:\r
+ // 1. received the last one, so, the totoal length is know\r
+ // 2. received all the data. If the last fragment on the\r
+ // queue ends at the total length, all data is received.\r
+ //\r
+ if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {\r
+\r
+ RemoveEntryList (&Assemble->Link);\r
+\r
+ //\r
+ // If the packet is properly formated, the last fragment's End\r
+ // equals to the packet's total length. Otherwise, the packet\r
+ // is a fake, drop it now.\r
+ //\r
+ Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List);\r
+ if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) {\r
+ Ip6FreeAssembleEntry (Assemble);\r
+ goto Error;\r
+ }\r
+\r
+ Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List);\r
+ This = Assemble->Info;\r
+\r
+ //\r
+ // This TmpPacket is used to hold the unfragmentable part, i.e.,\r
+ // the IPv6 header and the unfragmentable extension headers. Be noted that\r
+ // the Fragment Header is exluded.\r
+ //\r
+ TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0);\r
+ ASSERT (TmpPacket != NULL);\r
+\r
+ NET_LIST_FOR_EACH (Cur, ListHead) {\r
+ //\r
+ // Trim off the unfragment part plus the fragment header from all fragments.\r
+ //\r
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+ NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE);\r
+ }\r
+\r
+ InsertHeadList (ListHead, &TmpPacket->List);\r
+\r
+ //\r
+ // Wrap the packet in a net buffer then deliver it up\r
+ //\r
+ NewPacket = NetbufFromBufList (\r
+ &Assemble->Fragments,\r
+ 0,\r
+ 0,\r
+ Ip6OnFreeFragments,\r
+ Assemble\r
+ );\r
+\r
+ if (NewPacket == NULL) {\r
+ Ip6FreeAssembleEntry (Assemble);\r
+ goto Error;\r
+ }\r
+\r
+ NewPacket->Ip.Ip6 = Assemble->Head;\r
+\r
+ CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO));\r
+\r
+ return NewPacket;\r
+ }\r
+\r
+ return NULL;\r
+\r
+Error:\r
+ NetbufFree (Packet);\r
+ return NULL;\r
+}\r
+\r
+\r
+/**\r
+ The callback function for the net buffer that wraps the packet processed by\r
+ IPsec. It releases the wrap packet and also signals IPsec to free the resources.\r
+\r
+ @param[in] Arg The wrap context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6IpSecFree (\r
+ IN VOID *Arg\r
+ )\r
+{\r
+ IP6_IPSEC_WRAP *Wrap;\r
+\r
+ Wrap = (IP6_IPSEC_WRAP *) Arg;\r
+\r
+ if (Wrap->IpSecRecycleSignal != NULL) {\r
+ gBS->SignalEvent (Wrap->IpSecRecycleSignal);\r
+ }\r
+\r
+ NetbufFree (Wrap->Packet);\r
+\r
+ FreePool (Wrap);\r
+\r
+ return;\r
+}\r
+\r
+/**\r
+ The work function to locate the IPsec protocol to process the inbound or\r
+ outbound IP packets. The process routine handles the packet with the following\r
+ actions: bypass the packet, discard the packet, or protect the packet.\r
+\r
+ @param[in] IpSb The IP6 service instance.\r
+ @param[in] Head The caller-supplied IP6 header.\r
+ @param[in, out] LastHead The next header field of last IP header.\r
+ @param[in, out] Netbuf The IP6 packet to be processed by IPsec.\r
+ @param[in] ExtHdrs The caller-supplied options.\r
+ @param[in] ExtHdrsLen The length of the option.\r
+ @param[in] Direction The directionality in an SPD entry,\r
+ EfiIPsecInBound, or EfiIPsecOutBound.\r
+ @param[in] Context The token's wrap.\r
+\r
+ @retval EFI_SUCCESS The IPsec protocol is not available or disabled.\r
+ @retval EFI_SUCCESS The packet was bypassed, and all buffers remain the same.\r
+ @retval EFI_SUCCESS The packet was protected.\r
+ @retval EFI_ACCESS_DENIED The packet was discarded.\r
+ @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation.\r
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the\r
+ number of input data blocks when building a fragment table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IpSecProcessPacket (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN OUT UINT8 *LastHead,\r
+ IN OUT NET_BUF **Netbuf,\r
+ IN VOID *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ NET_FRAGMENT *FragmentTable;\r
+ UINT32 FragmentCount;\r
+ EFI_EVENT RecycleEvent;\r
+ NET_BUF *Packet;\r
+ IP6_TXTOKEN_WRAP *TxWrap;\r
+ IP6_IPSEC_WRAP *IpSecWrap;\r
+ EFI_STATUS Status;\r
+ EFI_IP6_HEADER *PacketHead;\r
+ UINT8 *Buf;\r
+\r
+ Status = EFI_SUCCESS;\r
+ Packet = *Netbuf;\r
+ RecycleEvent = NULL;\r
+ IpSecWrap = NULL;\r
+ FragmentTable = NULL;\r
+ PacketHead = NULL;\r
+ Buf = NULL;\r
+ TxWrap = (IP6_TXTOKEN_WRAP *) Context;\r
+ FragmentCount = Packet->BlockOpNum;\r
+\r
+ if (mIpSec == NULL) {\r
+ gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &mIpSec);\r
+\r
+ //\r
+ // Check whether the ipsec protocol is available.\r
+ //\r
+ if (mIpSec == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check whether the ipsec enable variable is set.\r
+ //\r
+ if (mIpSec->DisabledFlag) {\r
+ //\r
+ // If IPsec is disabled, restore the original MTU\r
+ //\r
+ IpSb->MaxPacketSize = IpSb->OldMaxPacketSize;\r
+ goto ON_EXIT;\r
+ } else {\r
+ //\r
+ // If IPsec is enabled, use the MTU which reduce the IPsec header length.\r
+ //\r
+ IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN;\r
+ }\r
+\r
+\r
+ //\r
+ // Bypass all multicast inbound or outbound traffic.\r
+ //\r
+ if (IP6_IS_MULTICAST (&Head->DestinationAddress) || IP6_IS_MULTICAST (&Head->SourceAddress)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Rebuild fragment table from netbuf to ease ipsec process.\r
+ //\r
+ FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT));\r
+\r
+ if (FragmentTable == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ FreePool (FragmentTable);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Convert host byte order to network byte order\r
+ //\r
+ Ip6NtohHead (Head);\r
+\r
+ Status = mIpSec->Process (\r
+ mIpSec,\r
+ IpSb->Controller,\r
+ IP_VERSION_6,\r
+ (VOID *) Head,\r
+ LastHead,\r
+ NULL,\r
+ 0,\r
+ (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable),\r
+ &FragmentCount,\r
+ Direction,\r
+ &RecycleEvent\r
+ );\r
+ //\r
+ // Convert back to host byte order\r
+ //\r
+ Ip6NtohHead (Head);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (Direction == EfiIPsecOutBound && TxWrap != NULL) {\r
+\r
+ TxWrap->IpSecRecycleSignal = RecycleEvent;\r
+ TxWrap->Packet = NetbufFromExt (\r
+ FragmentTable,\r
+ FragmentCount,\r
+ IP6_MAX_HEADLEN,\r
+ 0,\r
+ Ip6FreeTxToken,\r
+ TxWrap\r
+ );\r
+ if (TxWrap->Packet == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ *Netbuf = TxWrap->Packet;\r
+\r
+ } else {\r
+\r
+ IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP));\r
+\r
+ if (IpSecWrap == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ IpSecWrap->IpSecRecycleSignal = RecycleEvent;\r
+ IpSecWrap->Packet = Packet;\r
+ Packet = NetbufFromExt (\r
+ FragmentTable,\r
+ FragmentCount,\r
+ IP6_MAX_HEADLEN,\r
+ 0,\r
+ Ip6IpSecFree,\r
+ IpSecWrap\r
+ );\r
+\r
+ if (Packet == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (Direction == EfiIPsecInBound) {\r
+\r
+ PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (\r
+ Packet,\r
+ sizeof (EFI_IP6_HEADER) + ExtHdrsLen,\r
+ NET_BUF_HEAD\r
+ );\r
+ if (PacketHead == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));\r
+ Packet->Ip.Ip6 = PacketHead;\r
+\r
+ if (ExtHdrs != NULL) {\r
+ Buf = (UINT8 *) (PacketHead + 1);\r
+ CopyMem (Buf, ExtHdrs, ExtHdrsLen);\r
+ }\r
+\r
+ NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);\r
+ CopyMem (\r
+ IP6_GET_CLIP_INFO (Packet),\r
+ IP6_GET_CLIP_INFO (IpSecWrap->Packet),\r
+ sizeof (IP6_CLIP_INFO)\r
+ );\r
+ }\r
+\r
+ *Netbuf = Packet;\r
+ }\r
+\r
+ON_EXIT:\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The IP6 input routine. It is called by the IP6_INTERFACE when an\r
+ IP6 fragment is received from MNP.\r
+\r
+ @param[in] Packet The IP6 packet received.\r
+ @param[in] IoStatus The return status of receive request.\r
+ @param[in] Flag The link layer flag for the packet received, such\r
+ as multicast.\r
+ @param[in] Context The IP6 service instance that owns the MNP.\r
+\r
+**/\r
+VOID\r
+Ip6AcceptFrame (\r
+ IN NET_BUF *Packet,\r
+ IN EFI_STATUS IoStatus,\r
+ IN UINT32 Flag,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_CLIP_INFO *Info;\r
+ EFI_IP6_HEADER *Head;\r
+ UINT16 PayloadLen;\r
+ UINT8 *Payload;\r
+ UINT16 TotalLen;\r
+ UINT8 *LastHead;\r
+ UINT32 FormerHeadOffset;\r
+ UINT32 UnFragmentLen;\r
+ UINT32 ExtHdrsLen;\r
+ UINT32 HeadLen;\r
+ BOOLEAN Fragmented;\r
+ IP6_FRAGMENT_HEADER *FragmentHead;\r
+ UINT16 FragmentOffset;\r
+ EFI_STATUS Status;\r
+ EFI_IPv6_ADDRESS Loopback;\r
+\r
+ IpSb = (IP6_SERVICE *) Context;\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ Payload = NULL;\r
+\r
+ //\r
+ // Check input parameters\r
+ //\r
+ if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) {\r
+ goto Drop;\r
+ }\r
+\r
+ //\r
+ // Check whether the input packet is a valid packet\r
+ //\r
+ if (Packet->TotalSize < IP6_MIN_HEADLEN) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // Get header information of the packet.\r
+ //\r
+ Head = (EFI_IP6_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+ if (Head == NULL) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // Multicast addresses must not be used as source addresses in IPv6 packets.\r
+ //\r
+ if ((Head->Version != 6) || (IP6_IS_MULTICAST (&Head->SourceAddress))) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // A packet with a destination address of loopback ::1/128 or unspecified must be dropped.\r
+ //\r
+ ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS));\r
+ Loopback.Addr[15] = 0x1;\r
+ if ((CompareMem (&Loopback, &Head->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) ||\r
+ (NetIp6IsUnspecifiedAddr (&Head->DestinationAddress))) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // Convert the IP header to host byte order.\r
+ //\r
+ Packet->Ip.Ip6 = Ip6NtohHead (Head);\r
+\r
+ //\r
+ // Get the per packet info.\r
+ //\r
+ Info = IP6_GET_CLIP_INFO (Packet);\r
+ Info->LinkFlag = Flag;\r
+ Info->CastType = 0;\r
+\r
+ if (IpSb->MnpConfigData.EnablePromiscuousReceive) {\r
+ Info->CastType = Ip6Promiscuous;\r
+ }\r
+\r
+ if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {\r
+ Info->CastType = Ip6Unicast;\r
+ } else if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+ if (Ip6FindMldEntry (IpSb, &Head->DestinationAddress) != NULL) {\r
+ Info->CastType = Ip6Multicast;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Drop the packet that is not delivered to us.\r
+ //\r
+ if (Info->CastType == 0) {\r
+ goto Restart;\r
+ }\r
+\r
+\r
+ PayloadLen = Head->PayloadLength;\r
+\r
+ Info->Start = 0;\r
+ Info->Length = PayloadLen;\r
+ Info->End = Info->Start + Info->Length;\r
+ Info->HeadLen = (UINT16) sizeof (EFI_IP6_HEADER);\r
+ Info->Status = EFI_SUCCESS;\r
+ Info->LastFrag = FALSE;\r
+\r
+ TotalLen = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Mnp may deliver frame trailer sequence up, trim it off.\r
+ //\r
+ if (TotalLen < Packet->TotalSize) {\r
+ NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);\r
+ }\r
+\r
+ if (TotalLen != Packet->TotalSize) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // Check the extension headers, if exist validate them\r
+ //\r
+ if (PayloadLen != 0) {\r
+ Payload = AllocatePool ((UINTN) PayloadLen);\r
+ if (Payload == NULL) {\r
+ goto Restart;\r
+ }\r
+\r
+ NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);\r
+ }\r
+\r
+ LastHead = NULL;\r
+ if (!Ip6IsExtsValid (\r
+ IpSb,\r
+ Packet,\r
+ &Head->NextHeader,\r
+ Payload,\r
+ (UINT32) PayloadLen,\r
+ TRUE,\r
+ &FormerHeadOffset,\r
+ &LastHead,\r
+ &ExtHdrsLen,\r
+ &UnFragmentLen,\r
+ &Fragmented\r
+ )) {\r
+ goto Restart;\r
+ }\r
+\r
+ HeadLen = sizeof (EFI_IP6_HEADER) + UnFragmentLen;\r
+\r
+ if (Fragmented) {\r
+ //\r
+ // Get the fragment offset from the Fragment header\r
+ //\r
+ FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (Packet, HeadLen, NULL);\r
+ if (FragmentHead == NULL) {\r
+ goto Restart;\r
+ }\r
+\r
+ FragmentOffset = NTOHS (FragmentHead->FragmentOffset);\r
+\r
+ if ((FragmentOffset & 0x1) == 0) {\r
+ Info->LastFrag = TRUE;\r
+ }\r
+\r
+ FragmentOffset &= (~0x1);\r
+\r
+ //\r
+ // This is the first fragment of the packet\r
+ //\r
+ if (FragmentOffset == 0) {\r
+ Info->NextHeader = FragmentHead->NextHeader;\r
+ }\r
+\r
+ Info->HeadLen = (UINT16) HeadLen;\r
+ HeadLen += sizeof (IP6_FRAGMENT_HEADER);\r
+ Info->Start = FragmentOffset;\r
+ Info->Length = TotalLen - (UINT16) HeadLen;\r
+ Info->End = Info->Start + Info->Length;\r
+ Info->Id = FragmentHead->Identification;\r
+ Info->FormerNextHeader = FormerHeadOffset;\r
+\r
+ //\r
+ // Fragments should in the unit of 8 octets long except the last one.\r
+ //\r
+ if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // Reassemble the packet.\r
+ //\r
+ Packet = Ip6Reassemble (&IpSb->Assemble, Packet);\r
+ if (Packet == NULL) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // Re-check the assembled packet to get the right values.\r
+ //\r
+ Head = Packet->Ip.Ip6;\r
+ PayloadLen = Head->PayloadLength;\r
+ if (PayloadLen != 0) {\r
+ if (Payload != NULL) {\r
+ FreePool (Payload);\r
+ }\r
+\r
+ Payload = AllocatePool ((UINTN) PayloadLen);\r
+ if (Payload == NULL) {\r
+ goto Restart;\r
+ }\r
+\r
+ NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);\r
+ }\r
+\r
+ if (!Ip6IsExtsValid (\r
+ IpSb,\r
+ Packet,\r
+ &Head->NextHeader,\r
+ Payload,\r
+ (UINT32) PayloadLen,\r
+ TRUE,\r
+ NULL,\r
+ &LastHead,\r
+ &ExtHdrsLen,\r
+ &UnFragmentLen,\r
+ &Fragmented\r
+ )) {\r
+ goto Restart;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Trim the head off, after this point, the packet is headless.\r
+ // and Packet->TotalLen == Info->Length.\r
+ //\r
+ NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);\r
+\r
+ //\r
+ // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer,\r
+ // and no need consider any other ahead ext headers.\r
+ //\r
+ Status = Ip6IpSecProcessPacket (\r
+ IpSb,\r
+ Head,\r
+ LastHead, // need get the lasthead value for input\r
+ &Packet,\r
+ NULL,\r
+ 0,\r
+ EfiIPsecInBound,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ goto Restart;\r
+ }\r
+\r
+ //\r
+ // TODO: may check the last head again, the same as the output routine\r
+ //\r
+\r
+ //\r
+ // Packet may have been changed. The ownership of the packet\r
+ // is transfered to the packet process logic.\r
+ //\r
+ Head = Packet->Ip.Ip6;\r
+ IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;\r
+\r
+ switch (*LastHead) {\r
+ case IP6_ICMP:\r
+ Ip6IcmpHandle (IpSb, Head, Packet);\r
+ break;\r
+ default:\r
+ Ip6Demultiplex (IpSb, Head, Packet);\r
+ }\r
+\r
+ Packet = NULL;\r
+\r
+ //\r
+ // Dispatch the DPCs queued by the NotifyFunction of the rx token's events\r
+ // which are signaled with received data.\r
+ //\r
+ DispatchDpc ();\r
+\r
+Restart:\r
+ if (Payload != NULL) {\r
+ FreePool (Payload);\r
+ }\r
+\r
+ Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);\r
+\r
+Drop:\r
+ if (Packet != NULL) {\r
+ NetbufFree (Packet);\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+/**\r
+ Initialize an already allocated assemble table. This is generally\r
+ the assemble table embedded in the IP6 service instance.\r
+\r
+ @param[in, out] Table The assemble table to initialize.\r
+\r
+**/\r
+VOID\r
+Ip6CreateAssembleTable (\r
+ IN OUT IP6_ASSEMBLE_TABLE *Table\r
+ )\r
+{\r
+ UINT32 Index;\r
+\r
+ for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {\r
+ InitializeListHead (&Table->Bucket[Index]);\r
+ }\r
+}\r
+\r
+/**\r
+ Clean up the assemble table by removing all of the fragments\r
+ and assemble entries.\r
+\r
+ @param[in, out] Table The assemble table to clean up.\r
+\r
+**/\r
+VOID\r
+Ip6CleanAssembleTable (\r
+ IN OUT IP6_ASSEMBLE_TABLE *Table\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_ASSEMBLE_ENTRY *Assemble;\r
+ UINT32 Index;\r
+\r
+ for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {\r
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);\r
+\r
+ RemoveEntryList (Entry);\r
+ Ip6FreeAssembleEntry (Assemble);\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ The signal handle of IP6's recycle event. It is called back\r
+ when the upper layer releases the packet.\r
+\r
+ @param[in] Event The IP6's recycle event.\r
+ @param[in] Context The context of the handle, which is a IP6_RXDATA_WRAP.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6OnRecyclePacket (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_RXDATA_WRAP *Wrap;\r
+\r
+ Wrap = (IP6_RXDATA_WRAP *) Context;\r
+\r
+ EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);\r
+ RemoveEntryList (&Wrap->Link);\r
+ EfiReleaseLock (&Wrap->IpInstance->RecycleLock);\r
+\r
+ ASSERT (!NET_BUF_SHARED (Wrap->Packet));\r
+ NetbufFree (Wrap->Packet);\r
+\r
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);\r
+ FreePool (Wrap);\r
+}\r
+\r
+/**\r
+ Wrap the received packet to a IP6_RXDATA_WRAP, which will be\r
+ delivered to the upper layer. Each IP6 child that accepts the\r
+ packet will get a not-shared copy of the packet which is wrapped\r
+ in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed\r
+ to the upper layer. The upper layer will signal the recycle event in\r
+ it when it is done with the packet.\r
+\r
+ @param[in] IpInstance The IP6 child to receive the packet.\r
+ @param[in] Packet The packet to deliver up.\r
+\r
+ @return NULL if it failed to wrap the packet; otherwise, the wrapper.\r
+\r
+**/\r
+IP6_RXDATA_WRAP *\r
+Ip6WrapRxData (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_RXDATA_WRAP *Wrap;\r
+ EFI_IP6_RECEIVE_DATA *RxData;\r
+ EFI_STATUS Status;\r
+\r
+ Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum));\r
+\r
+ if (Wrap == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ InitializeListHead (&Wrap->Link);\r
+\r
+ Wrap->IpInstance = IpInstance;\r
+ Wrap->Packet = Packet;\r
+ RxData = &Wrap->RxData;\r
+\r
+ ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ Ip6OnRecyclePacket,\r
+ Wrap,\r
+ &RxData->RecycleSignal\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Wrap);\r
+ return NULL;\r
+ }\r
+\r
+ ASSERT (Packet->Ip.Ip6 != NULL);\r
+\r
+ //\r
+ // The application expects a network byte order header.\r
+ //\r
+ RxData->HeaderLength = sizeof (EFI_IP6_HEADER);\r
+ RxData->Header = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6);\r
+ RxData->DataLength = Packet->TotalSize;\r
+\r
+ //\r
+ // Build the fragment table to be delivered up.\r
+ //\r
+ RxData->FragmentCount = Packet->BlockOpNum;\r
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);\r
+\r
+ return Wrap;\r
+}\r
+\r
+/**\r
+ Check whether this IP child accepts the packet.\r
+\r
+ @param[in] IpInstance The IP child to check.\r
+ @param[in] Head The IP header of the packet.\r
+ @param[in] Packet The data of the packet.\r
+\r
+ @retval TRUE The child wants to receive the packet.\r
+ @retval FALSE The child does not want to receive the packet.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6InstanceFrameAcceptable (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_ERROR_HEAD Icmp;\r
+ EFI_IP6_CONFIG_DATA *Config;\r
+ IP6_CLIP_INFO *Info;\r
+ UINT8 *Proto;\r
+ UINT32 Index;\r
+ UINT8 *ExtHdrs;\r
+ UINT16 ErrMsgPayloadLen;\r
+ UINT8 *ErrMsgPayload;\r
+\r
+ Config = &IpInstance->ConfigData;\r
+ Proto = NULL;\r
+\r
+ //\r
+ // Dirty trick for the Tiano UEFI network stack implmentation. If\r
+ // ReceiveTimeout == -1, the receive of the packet for this instance\r
+ // is disabled. The UEFI spec don't have such captibility. We add\r
+ // this to improve the performance because IP will make a copy of\r
+ // the received packet for each accepting instance. Some IP instances\r
+ // used by UDP/TCP only send packets, they don't wants to receive.\r
+ //\r
+ if (Config->ReceiveTimeout == (UINT32)(-1)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (Config->AcceptPromiscuous) {\r
+ return TRUE;\r
+ }\r
+\r
+ //\r
+ // Check whether the protocol is acceptable.\r
+ //\r
+ ExtHdrs = NetbufGetByte (Packet, 0, NULL);\r
+\r
+ if (!Ip6IsExtsValid (\r
+ IpInstance->Service,\r
+ Packet,\r
+ &Head->NextHeader,\r
+ ExtHdrs,\r
+ (UINT32) Head->PayloadLength,\r
+ TRUE,\r
+ NULL,\r
+ &Proto,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+ )) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // The upper layer driver may want to receive the ICMPv6 error packet\r
+ // invoked by its packet, like UDP.\r
+ //\r
+ if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) {\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+ if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) {\r
+ if (!Config->AcceptIcmpErrors) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Get the protocol of the invoking packet of ICMPv6 error packet.\r
+ //\r
+ ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength);\r
+ ErrMsgPayload = NetbufGetByte (Packet, sizeof (Icmp), NULL);\r
+\r
+ if (!Ip6IsExtsValid (\r
+ NULL,\r
+ NULL,\r
+ &Icmp.IpHead.NextHeader,\r
+ ErrMsgPayload,\r
+ ErrMsgPayloadLen,\r
+ TRUE,\r
+ NULL,\r
+ &Proto,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+ )) {\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Match the protocol\r
+ //\r
+ if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check for broadcast, the caller has computed the packet's\r
+ // cast type for this child's interface.\r
+ //\r
+ Info = IP6_GET_CLIP_INFO (Packet);\r
+\r
+ //\r
+ // If it is a multicast packet, check whether we are in the group.\r
+ //\r
+ if (Info->CastType == Ip6Multicast) {\r
+ //\r
+ // Receive the multicast if the instance wants to receive all packets.\r
+ //\r
+ if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) {\r
+ return TRUE;\r
+ }\r
+\r
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
+ if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ return (BOOLEAN)(Index < IpInstance->GroupCount);\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Enqueue a shared copy of the packet to the IP6 child if the\r
+ packet is acceptable to it. Here the data of the packet is\r
+ shared, but the net buffer isn't.\r
+\r
+ @param IpInstance The IP6 child to enqueue the packet to.\r
+ @param Head The IP header of the received packet.\r
+ @param Packet The data of the received packet.\r
+\r
+ @retval EFI_NOT_STARTED The IP child hasn't been configured.\r
+ @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources\r
+ @retval EFI_SUCCESS A shared copy the packet is enqueued to the child.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InstanceEnquePacket (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_CLIP_INFO *Info;\r
+ NET_BUF *Clone;\r
+\r
+ //\r
+ // Check whether the packet is acceptable to this instance.\r
+ //\r
+ if (IpInstance->State != IP6_STATE_CONFIGED) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Enque a shared copy of the packet.\r
+ //\r
+ Clone = NetbufClone (Packet);\r
+\r
+ if (Clone == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Set the receive time out for the assembled packet. If it expires,\r
+ // packet will be removed from the queue.\r
+ //\r
+ Info = IP6_GET_CLIP_INFO (Clone);\r
+ Info->Life = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);\r
+\r
+ InsertTailList (&IpInstance->Received, &Clone->List);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Deliver the received packets to the upper layer if there are both received\r
+ requests and enqueued packets. If the enqueued packet is shared, it will\r
+ duplicate it to a non-shared packet, release the shared packet, then\r
+ deliver the non-shared packet up.\r
+\r
+ @param[in] IpInstance The IP child to deliver the packet up.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the\r
+ packets.\r
+ @retval EFI_SUCCESS All the enqueued packets that can be delivered\r
+ are delivered up.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InstanceDeliverPacket (\r
+ IN IP6_PROTOCOL *IpInstance\r
+ )\r
+{\r
+ EFI_IP6_COMPLETION_TOKEN *Token;\r
+ IP6_RXDATA_WRAP *Wrap;\r
+ NET_BUF *Packet;\r
+ NET_BUF *Dup;\r
+ UINT8 *Head;\r
+\r
+ //\r
+ // Deliver a packet if there are both a packet and a receive token.\r
+ //\r
+ while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) {\r
+\r
+ Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);\r
+\r
+ if (!NET_BUF_SHARED (Packet)) {\r
+ //\r
+ // If this is the only instance that wants the packet, wrap it up.\r
+ //\r
+ Wrap = Ip6WrapRxData (IpInstance, Packet);\r
+\r
+ if (Wrap == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ RemoveEntryList (&Packet->List);\r
+\r
+ } else {\r
+ //\r
+ // Create a duplicated packet if this packet is shared\r
+ //\r
+ Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));\r
+\r
+ if (Dup == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Copy the IP head over. The packet to deliver up is\r
+ // headless. Trim the head off after copy. The IP head\r
+ // may be not continuous before the data.\r
+ //\r
+ Head = NetbufAllocSpace (Dup, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD);\r
+ ASSERT (Head != NULL);\r
+ Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head;\r
+\r
+ CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER));\r
+ NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE);\r
+\r
+ Wrap = Ip6WrapRxData (IpInstance, Dup);\r
+\r
+ if (Wrap == NULL) {\r
+ NetbufFree (Dup);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ RemoveEntryList (&Packet->List);\r
+ NetbufFree (Packet);\r
+\r
+ Packet = Dup;\r
+ }\r
+\r
+ //\r
+ // Insert it into the delivered packet, then get a user's\r
+ // receive token, pass the wrapped packet up.\r
+ //\r
+ EfiAcquireLockOrFail (&IpInstance->RecycleLock);\r
+ InsertHeadList (&IpInstance->Delivered, &Wrap->Link);\r
+ EfiReleaseLock (&IpInstance->RecycleLock);\r
+\r
+ Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL);\r
+ Token->Status = IP6_GET_CLIP_INFO (Packet)->Status;\r
+ Token->Packet.RxData = &Wrap->RxData;\r
+\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Enqueue a received packet to all the IP children that share\r
+ the same interface.\r
+\r
+ @param[in] IpSb The IP6 service instance that receive the packet.\r
+ @param[in] Head The header of the received packet.\r
+ @param[in] Packet The data of the received packet.\r
+ @param[in] IpIf The interface to enqueue the packet to.\r
+\r
+ @return The number of the IP6 children that accepts the packet.\r
+\r
+**/\r
+INTN\r
+Ip6InterfaceEnquePacket (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet,\r
+ IN IP6_INTERFACE *IpIf\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_CLIP_INFO *Info;\r
+ LIST_ENTRY *Entry;\r
+ INTN Enqueued;\r
+ INTN LocalType;\r
+ INTN SavedType;\r
+\r
+ //\r
+ // First, check that the packet is acceptable to this interface\r
+ // and find the local cast type for the interface.\r
+ //\r
+ LocalType = 0;\r
+ Info = IP6_GET_CLIP_INFO (Packet);\r
+\r
+ if (IpIf->PromiscRecv) {\r
+ LocalType = Ip6Promiscuous;\r
+ } else {\r
+ LocalType = Info->CastType;\r
+ }\r
+\r
+ //\r
+ // Iterate through the ip instances on the interface, enqueue\r
+ // the packet if filter passed. Save the original cast type,\r
+ // and pass the local cast type to the IP children on the\r
+ // interface. The global cast type will be restored later.\r
+ //\r
+ SavedType = Info->CastType;\r
+ Info->CastType = (UINT32) LocalType;\r
+\r
+ Enqueued = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
+ IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);\r
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+\r
+ if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {\r
+ Enqueued++;\r
+ }\r
+ }\r
+\r
+ Info->CastType = (UINT32) SavedType;\r
+ return Enqueued;\r
+}\r
+\r
+/**\r
+ Deliver the packet for each IP6 child on the interface.\r
+\r
+ @param[in] IpSb The IP6 service instance that received the packet.\r
+ @param[in] IpIf The IP6 interface to deliver the packet.\r
+\r
+**/\r
+VOID\r
+Ip6InterfaceDeliverPacket (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *IpIf\r
+ )\r
+{\r
+ IP6_PROTOCOL *IpInstance;\r
+ LIST_ENTRY *Entry;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
+ IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);\r
+ Ip6InstanceDeliverPacket (IpInstance);\r
+ }\r
+}\r
+\r
+/**\r
+ De-multiplex the packet. the packet delivery is processed in two\r
+ passes. The first pass will enqueue a shared copy of the packet\r
+ to each IP6 child that accepts the packet. The second pass will\r
+ deliver a non-shared copy of the packet to each IP6 child that\r
+ has pending receive requests. Data is copied if more than one\r
+ child wants to consume the packet, because each IP child needs\r
+ its own copy of the packet to make changes.\r
+\r
+ @param[in] IpSb The IP6 service instance that received the packet.\r
+ @param[in] Head The header of the received packet.\r
+ @param[in] Packet The data of the received packet.\r
+\r
+ @retval EFI_NOT_FOUND No IP child accepts the packet.\r
+ @retval EFI_SUCCESS The packet is enqueued or delivered to some IP\r
+ children.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Demultiplex (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+\r
+ LIST_ENTRY *Entry;\r
+ IP6_INTERFACE *IpIf;\r
+ INTN Enqueued;\r
+\r
+ //\r
+ // Two pass delivery: first, enque a shared copy of the packet\r
+ // to each instance that accept the packet.\r
+ //\r
+ Enqueued = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+ if (IpIf->Configured) {\r
+ Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Second: deliver a duplicate of the packet to each instance.\r
+ // Release the local reference first, so that the last instance\r
+ // getting the packet will not copy the data.\r
+ //\r
+ NetbufFree (Packet);\r
+ Packet = NULL;\r
+\r
+ if (Enqueued == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+ if (IpIf->Configured) {\r
+ Ip6InterfaceDeliverPacket (IpSb, IpIf);\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Decrease the life of the transmitted packets. If it is\r
+ decreased to zero, cancel the packet. This function is\r
+ called by Ip6packetTimerTicking that provides timeout for both the\r
+ received-but-not-delivered and transmitted-but-not-recycle\r
+ packets.\r
+\r
+ @param[in] Map The IP6 child's transmit map.\r
+ @param[in] Item Current transmitted packet.\r
+ @param[in] Context Not used.\r
+\r
+ @retval EFI_SUCCESS Always returns EFI_SUCCESS.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Ip6SentPacketTicking (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_TXTOKEN_WRAP *Wrap;\r
+\r
+ Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;\r
+ ASSERT (Wrap != NULL);\r
+\r
+ if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {\r
+ Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Timeout the fragments, and the enqueued, and transmitted packets.\r
+\r
+ @param[in] IpSb The IP6 service instance to timeout.\r
+\r
+**/\r
+VOID\r
+Ip6PacketTimerTicking (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ LIST_ENTRY *InstanceEntry;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_PROTOCOL *IpInstance;\r
+ IP6_ASSEMBLE_ENTRY *Assemble;\r
+ NET_BUF *Packet;\r
+ IP6_CLIP_INFO *Info;\r
+ UINT32 Index;\r
+\r
+ //\r
+ // First, time out the fragments. The packet's life is counting down\r
+ // once the first-arriving fragment of that packet was received.\r
+ //\r
+ for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) {\r
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);\r
+\r
+ if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {\r
+ //\r
+ // If the first fragment (the one with a Fragment Offset of zero)\r
+ // has been received, an ICMP Time Exceeded - Fragment Reassembly\r
+ // Time Exceeded message should be sent to the source of that fragment.\r
+ //\r
+ if ((Assemble->Packet != NULL) &&\r
+ !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) {\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ Assemble->Packet,\r
+ NULL,\r
+ &Assemble->Head->SourceAddress,\r
+ ICMP_V6_TIME_EXCEEDED,\r
+ ICMP_V6_TIMEOUT_REASSEMBLE,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ //\r
+ // If reassembly of a packet is not completed within 60 seconds of\r
+ // the reception of the first-arriving fragment of that packet, the\r
+ // reassembly must be abandoned and all the fragments that have been\r
+ // received for that packet must be discarded.\r
+ //\r
+ RemoveEntryList (Entry);\r
+ Ip6FreeAssembleEntry (Assemble);\r
+ }\r
+ }\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {\r
+ IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link);\r
+\r
+ //\r
+ // Second, time out the assembled packets enqueued on each IP child.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {\r
+ Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+ Info = IP6_GET_CLIP_INFO (Packet);\r
+\r
+ if ((Info->Life > 0) && (--Info->Life == 0)) {\r
+ RemoveEntryList (Entry);\r
+ NetbufFree (Packet);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Third: time out the transmitted packets.\r
+ //\r
+ NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL);\r
+ }\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ IP6 internal functions and definitions to process the incoming packets.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_INPUT_H__\r
+#define __EFI_IP6_INPUT_H__\r
+\r
+#define IP6_MIN_HEADLEN 40\r
+#define IP6_MAX_HEADLEN 120\r
+///\r
+/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54\r
+///\r
+#define IP6_MAX_IPSEC_HEADLEN 54\r
+\r
+\r
+#define IP6_ASSEMLE_HASH_SIZE 127\r
+///\r
+/// Lift time in seconds.\r
+///\r
+#define IP6_FRAGMENT_LIFE 60\r
+#define IP6_MAX_PACKET_SIZE 65535\r
+\r
+\r
+#define IP6_GET_CLIP_INFO(Packet) ((IP6_CLIP_INFO *) ((Packet)->ProtoData))\r
+\r
+#define IP6_ASSEMBLE_HASH(Dst, Src, Id) \\r
+ ((*((UINT32 *) (Dst)) + *((UINT32 *) (Src)) + (Id)) % IP6_ASSEMLE_HASH_SIZE)\r
+\r
+#define IP6_RXDATA_WRAP_SIZE(NumFrag) \\r
+ (sizeof (IP6_RXDATA_WRAP) + sizeof (EFI_IP6_FRAGMENT_DATA) * ((NumFrag) - 1))\r
+\r
+//\r
+// Per packet information for input process. LinkFlag specifies whether\r
+// the packet is received as Link layer unicast, multicast or broadcast.\r
+// The CastType is the IP layer cast type, such as IP multicast or unicast.\r
+// Start, End and Length are staffs used to assemble the packets. Start\r
+// is the sequence number of the first byte of data in the packet. Length\r
+// is the number of bytes of data. End = Start + Length, that is, the\r
+// sequence number of last byte + 1. Each assembled packet has a count down\r
+// life. If it isn't consumed before Life reaches zero, the packet is released.\r
+//\r
+typedef struct {\r
+ UINT32 LinkFlag;\r
+ INT32 CastType;\r
+ INT32 Start;\r
+ INT32 End;\r
+ INT32 Length;\r
+ UINT32 Life;\r
+ EFI_STATUS Status;\r
+ UINT32 Id;\r
+ UINT16 HeadLen;\r
+ UINT8 NextHeader;\r
+ UINT8 LastFrag;\r
+ UINT32 FormerNextHeader;\r
+} IP6_CLIP_INFO;\r
+\r
+//\r
+// Structure used to assemble IP packets.\r
+//\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ LIST_ENTRY Fragments; // List of all the fragments of this packet\r
+\r
+ //\r
+ // Identity of one IP6 packet. Each fragment of a packet has\r
+ // the same (Dst, Src, Id).\r
+ //\r
+ EFI_IPv6_ADDRESS Dst;\r
+ EFI_IPv6_ADDRESS Src;\r
+ UINT32 Id;\r
+\r
+ UINT32 TotalLen;\r
+ UINT32 CurLen;\r
+ UINT32 Life; // Count down life for the packet.\r
+\r
+ EFI_IP6_HEADER *Head; // IP head of the first fragment\r
+ IP6_CLIP_INFO *Info; // Per packet information of the first fragment\r
+ NET_BUF *Packet; // The first fragment of the packet\r
+} IP6_ASSEMBLE_ENTRY;\r
+\r
+//\r
+// Each Ip service instance has an assemble table to reassemble\r
+// the packets before delivery to its children. It is organized\r
+// as hash table.\r
+//\r
+typedef struct {\r
+ LIST_ENTRY Bucket[IP6_ASSEMLE_HASH_SIZE];\r
+} IP6_ASSEMBLE_TABLE;\r
+\r
+/**\r
+ The IP6 input routine. It is called by the IP6_INTERFACE when an\r
+ IP6 fragment is received from MNP.\r
+\r
+ @param[in] Packet The IP6 packet received.\r
+ @param[in] IoStatus The return status of receive request.\r
+ @param[in] Flag The link layer flag for the packet received, such\r
+ as multicast.\r
+ @param[in] Context The IP6 service instance that own the MNP.\r
+\r
+**/\r
+VOID\r
+Ip6AcceptFrame (\r
+ IN NET_BUF *Packet,\r
+ IN EFI_STATUS IoStatus,\r
+ IN UINT32 Flag,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Deliver the received packets to upper layer if there are both received\r
+ requests and enqueued packets. If the enqueued packet is shared, it will\r
+ duplicate it to a non-shared packet, release the shared packet, then\r
+ deliver the non-shared packet up.\r
+\r
+ @param[in] IpInstance The IP child to deliver the packet up.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the\r
+ packets.\r
+ @retval EFI_SUCCESS All the enqueued packets that can be delivered\r
+ are delivered up.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InstanceDeliverPacket (\r
+ IN IP6_PROTOCOL *IpInstance\r
+ );\r
+\r
+/**\r
+ The work function to locate IPsec protocol to process the inbound or\r
+ outbound IP packets. The process routine handls the packet with the following\r
+ actions: bypass the packet, discard the packet, or protect the packet.\r
+\r
+ @param[in] IpSb The IP6 service instance.\r
+ @param[in] Head The caller supplied IP6 header.\r
+ @param[in, out] LastHead The next header field of last IP header.\r
+ @param[in, out] Netbuf The IP6 packet to be processed by IPsec.\r
+ @param[in] ExtHdrs The caller supplied options.\r
+ @param[in] ExtHdrsLen The length of the option.\r
+ @param[in] Direction The directionality in an SPD entry,\r
+ EfiIPsecInBound or EfiIPsecOutBound.\r
+ @param[in] Context The token's wrap.\r
+\r
+ @retval EFI_SUCCESS The IPsec protocol is not available or disabled.\r
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.\r
+ @retval EFI_SUCCESS The packet was protected.\r
+ @retval EFI_ACCESS_DENIED The packet was discarded.\r
+ @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation.\r
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the\r
+ number of input data blocks when building a fragment table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6IpSecProcessPacket (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN OUT UINT8 *LastHead,\r
+ IN OUT NET_BUF **Netbuf,\r
+ IN VOID *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Initialize an already allocated assemble table. This is generally\r
+ the assemble table embedded in the IP6 service instance.\r
+\r
+ @param[in, out] Table The assemble table to initialize.\r
+\r
+**/\r
+VOID\r
+Ip6CreateAssembleTable (\r
+ IN OUT IP6_ASSEMBLE_TABLE *Table\r
+ );\r
+\r
+/**\r
+ Clean up the assemble table: remove all the fragments\r
+ and assemble entries.\r
+\r
+ @param[in, out] Table The assemble table to clean up.\r
+\r
+**/\r
+VOID\r
+Ip6CleanAssembleTable (\r
+ IN OUT IP6_ASSEMBLE_TABLE *Table\r
+ );\r
+\r
+/**\r
+ Demultiple the packet. the packet delivery is processed in two\r
+ passes. The first pass will enque a shared copy of the packet\r
+ to each IP6 child that accepts the packet. The second pass will\r
+ deliver a non-shared copy of the packet to each IP6 child that\r
+ has pending receive requests. Data is copied if more than one\r
+ child wants to consume the packet bacause each IP child need\r
+ its own copy of the packet to make changes.\r
+\r
+ @param[in] IpSb The IP6 service instance that received the packet.\r
+ @param[in] Head The header of the received packet.\r
+ @param[in] Packet The data of the received packet.\r
+\r
+ @retval EFI_NOT_FOUND No IP child accepts the packet.\r
+ @retval EFI_SUCCESS The packet is enqueued or delivered to some IP\r
+ children.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Demultiplex (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ Timeout the fragmented, enqueued, and transmitted packets.\r
+\r
+ @param[in] IpSb The IP6 service instance to timeout.\r
+\r
+**/\r
+VOID\r
+Ip6PacketTimerTicking (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Multicast Listener Discovery support routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+ Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data.\r
+\r
+ @param[in, out] IpSb Points to IP6 service binding instance.\r
+ @param[in] MulticastAddr The IPv6 multicast address to be recorded.\r
+ @param[in] DelayTimer The maximum allowed delay before sending a responding\r
+ report, in units of milliseconds.\r
+ @return The created IP6_ML_GROUP list entry or NULL.\r
+\r
+**/\r
+IP6_MLD_GROUP *\r
+Ip6CreateMldEntry (\r
+ IN OUT IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddr,\r
+ IN UINT32 DelayTimer\r
+ )\r
+{\r
+ IP6_MLD_GROUP *Entry;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+ Entry = AllocatePool (sizeof (IP6_MLD_GROUP));\r
+ if (Entry != NULL) {\r
+ Entry->RefCnt = 1;\r
+ Entry->DelayTimer = DelayTimer;\r
+ Entry->SendByUs = FALSE;\r
+ IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr);\r
+ InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link);\r
+ }\r
+\r
+ return Entry;\r
+}\r
+\r
+/**\r
+ Search a IP6_MLD_GROUP list entry node from a list array.\r
+\r
+ @param[in] IpSb Points to IP6 service binding instance.\r
+ @param[in] MulticastAddr The IPv6 multicast address to be searched.\r
+\r
+ @return The found IP6_ML_GROUP list entry or NULL.\r
+\r
+**/\r
+IP6_MLD_GROUP *\r
+Ip6FindMldEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddr\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_MLD_GROUP *Group;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+ if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) {\r
+ return Group;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Count the number of IP6 multicast groups that are mapped to the\r
+ same MAC address. Several IP6 multicast address may be mapped to\r
+ the same MAC address.\r
+\r
+ @param[in] MldCtrl The MLD control block to search in.\r
+ @param[in] Mac The MAC address to search.\r
+\r
+ @return The number of the IP6 multicast group that mapped to the same\r
+ multicast group Mac.\r
+\r
+**/\r
+INTN\r
+Ip6FindMac (\r
+ IN IP6_MLD_SERVICE_DATA *MldCtrl,\r
+ IN EFI_MAC_ADDRESS *Mac\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_MLD_GROUP *Group;\r
+ INTN Count;\r
+\r
+ Count = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) {\r
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+\r
+ if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {\r
+ Count++;\r
+ }\r
+ }\r
+\r
+ return Count;\r
+}\r
+\r
+/**\r
+ Generate MLD report message and send it out to MulticastAddr.\r
+\r
+ @param[in] IpSb The IP service to send the packet.\r
+ @param[in] Interface The IP interface to send the packet.\r
+ If NULL, a system interface will be selected.\r
+ @param[in] MulticastAddr The specific IPv6 multicast address to which\r
+ the message sender is listening.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The MLD report message was successfully sent out.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendMldReport (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *Interface OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddr\r
+ )\r
+{\r
+ IP6_MLD_HEAD *MldHead;\r
+ NET_BUF *Packet;\r
+ EFI_IP6_HEADER Head;\r
+ UINT16 PayloadLen;\r
+ UINTN OptionLen;\r
+ UINT8 *Options;\r
+ EFI_STATUS Status;\r
+ UINT16 HeadChecksum;\r
+ UINT16 PseudoChecksum;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+ //\r
+ // Generate the packet to be sent\r
+ // IPv6 basic header + Hop by Hop option + MLD message\r
+ //\r
+\r
+ OptionLen = 0;\r
+ Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);\r
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+ PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));\r
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Create the basic IPv6 header.\r
+ // RFC3590: Use link-local address as source address if it is available,\r
+ // otherwise use the unspecified address.\r
+ //\r
+ Head.FlowLabelL = 0;\r
+ Head.FlowLabelH = 0;\r
+ Head.PayloadLength = HTONS (PayloadLen);\r
+ Head.NextHeader = IP6_HOP_BY_HOP;\r
+ Head.HopLimit = 1;\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr);\r
+\r
+ //\r
+ // If Link-Local address is not ready, we use unspecified address.\r
+ //\r
+ IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);\r
+\r
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header\r
+ //\r
+ Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);\r
+ ASSERT (Options != NULL);\r
+ Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);\r
+ if (EFI_ERROR (Status)) {\r
+ NetbufFree (Packet);\r
+ Packet = NULL;\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Fill in MLD message - Report\r
+ //\r
+ MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);\r
+ ASSERT (MldHead != NULL);\r
+ ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));\r
+ MldHead->Head.Type = ICMP_V6_LISTENER_REPORT;\r
+ MldHead->Head.Code = 0;\r
+ IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);\r
+\r
+ HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));\r
+ PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+ &Head.SourceAddress,\r
+ &Head.DestinationAddress,\r
+ IP6_ICMP,\r
+ sizeof (IP6_MLD_HEAD)\r
+ );\r
+\r
+ MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);\r
+\r
+ //\r
+ // Transmit the packet\r
+ //\r
+ return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+ Generate MLD Done message and send it out to MulticastAddr.\r
+\r
+ @param[in] IpSb The IP service to send the packet.\r
+ @param[in] MulticastAddr The specific IPv6 multicast address to which\r
+ the message sender is ceasing to listen.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The MLD report message was successfully sent out.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendMldDone (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddr\r
+ )\r
+{\r
+ IP6_MLD_HEAD *MldHead;\r
+ NET_BUF *Packet;\r
+ EFI_IP6_HEADER Head;\r
+ UINT16 PayloadLen;\r
+ UINTN OptionLen;\r
+ UINT8 *Options;\r
+ EFI_STATUS Status;\r
+ EFI_IPv6_ADDRESS Destination;\r
+ UINT16 HeadChecksum;\r
+ UINT16 PseudoChecksum;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
+\r
+ //\r
+ // Generate the packet to be sent\r
+ // IPv6 basic header + Hop by Hop option + MLD message\r
+ //\r
+\r
+ OptionLen = 0;\r
+ Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);\r
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+ PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));\r
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Create the basic IPv6 header.\r
+ //\r
+ Head.FlowLabelL = 0;\r
+ Head.FlowLabelH = 0;\r
+ Head.PayloadLength = HTONS (PayloadLen);\r
+ Head.NextHeader = IP6_HOP_BY_HOP;\r
+ Head.HopLimit = 1;\r
+\r
+ //\r
+ // If Link-Local address is not ready, we use unspecified address.\r
+ //\r
+ IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);\r
+\r
+ Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination);\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination);\r
+\r
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header\r
+ //\r
+ Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);\r
+ ASSERT (Options != NULL);\r
+ Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);\r
+ if (EFI_ERROR (Status)) {\r
+ NetbufFree (Packet);\r
+ Packet = NULL;\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Fill in MLD message - Done\r
+ //\r
+ MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);\r
+ ASSERT (MldHead != NULL);\r
+ ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));\r
+ MldHead->Head.Type = ICMP_V6_LISTENER_DONE;\r
+ MldHead->Head.Code = 0;\r
+ IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);\r
+\r
+ HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));\r
+ PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+ &Head.SourceAddress,\r
+ &Head.DestinationAddress,\r
+ IP6_ICMP,\r
+ sizeof (IP6_MLD_HEAD)\r
+ );\r
+\r
+ MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);\r
+\r
+ //\r
+ // Transmit the packet\r
+ //\r
+ return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+ Init the MLD data of the IP6 service instance. Configure\r
+ MNP to receive ALL SYSTEM multicast.\r
+\r
+ @param[in] IpSb The IP6 service whose MLD is to be initialized.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The MLD module successfully initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitMld (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ EFI_IPv6_ADDRESS AllNodes;\r
+ IP6_MLD_GROUP *Group;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Join the link-scope all-nodes multicast address (FF02::1).\r
+ // This address is started in Idle Listener state and never transitions to\r
+ // another state, and never sends a Report or Done for that address.\r
+ //\r
+\r
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
+\r
+ Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME);\r
+ if (Group == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR;\r
+ }\r
+\r
+ //\r
+ // Configure MNP to receive all-nodes multicast\r
+ //\r
+ Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ERROR:\r
+ RemoveEntryList (&Group->Link);\r
+ FreePool (Group);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Add a group address to the array of group addresses.\r
+ The caller should make sure that no duplicated address\r
+ existed in the array.\r
+\r
+ @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.\r
+ @param[in] Group The IP6 multicast address to add.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete\r
+ the operation.\r
+ @retval EFI_SUCESS The address is added to the group address array.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CombineGroups (\r
+ IN OUT IP6_PROTOCOL *IpInstance,\r
+ IN EFI_IPv6_ADDRESS *Group\r
+ )\r
+{\r
+ EFI_IPv6_ADDRESS *GroupList;\r
+\r
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+ ASSERT (Group != NULL && IP6_IS_MULTICAST (Group));\r
+\r
+ IpInstance->GroupCount++;\r
+\r
+ GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS));\r
+ if (GroupList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ if (IpInstance->GroupCount > 1) {\r
+ ASSERT (IpInstance->GroupList != NULL);\r
+\r
+ CopyMem (\r
+ GroupList,\r
+ IpInstance->GroupList,\r
+ (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+\r
+ FreePool (IpInstance->GroupList);\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group);\r
+\r
+ IpInstance->GroupList = GroupList;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Remove a group address from the array of group addresses.\r
+ Although the function doesn't assume the byte order of Group,\r
+ the network byte order is used by the caller.\r
+\r
+ @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.\r
+ @param[in] Group The IP6 multicast address to remove.\r
+\r
+ @retval EFI_NOT_FOUND Cannot find the to be removed group address.\r
+ @retval EFI_SUCCESS The group address was successfully removed.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6RemoveGroup (\r
+ IN OUT IP6_PROTOCOL *IpInstance,\r
+ IN EFI_IPv6_ADDRESS *Group\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT32 Count;\r
+\r
+ Count = IpInstance->GroupCount;\r
+\r
+ for (Index = 0; Index < Count; Index++) {\r
+ if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Index == Count) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ while (Index < Count - 1) {\r
+ IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1);\r
+ Index++;\r
+ }\r
+\r
+ ASSERT (IpInstance->GroupCount > 0);\r
+ IpInstance->GroupCount--;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Join the multicast group on behalf of this IP6 service binding instance.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] Interface Points to an IP6_INTERFACE structure.\r
+ @param[in] Address The group address to join.\r
+\r
+ @retval EFI_SUCCESS Successfully join the multicast group.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+ @retval Others Failed to join the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6JoinGroup (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *Interface,\r
+ IN EFI_IPv6_ADDRESS *Address\r
+ )\r
+{\r
+ IP6_MLD_GROUP *Group;\r
+ EFI_STATUS Status;\r
+\r
+ Group = Ip6FindMldEntry (IpSb, Address);\r
+ if (Group != NULL) {\r
+ Group->RefCnt++;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s)\r
+ // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss.\r
+ //\r
+ Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL);\r
+ if (Group == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Group->SendByUs = TRUE;\r
+\r
+ Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);\r
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
+ goto ERROR;\r
+ }\r
+\r
+ //\r
+ // Send unsolicited report when a node starts listening to a multicast address\r
+ //\r
+ Status = Ip6SendMldReport (IpSb, Interface, Address);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ERROR:\r
+ RemoveEntryList (&Group->Link);\r
+ FreePool (Group);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Leave the IP6 multicast group.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] Address The group address to leave.\r
+\r
+ @retval EFI_NOT_FOUND The IP6 service instance isn't in the group.\r
+ @retval EFI_SUCCESS Successfully leave the multicast group..\r
+ @retval Others Failed to leave the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6LeaveGroup (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Address\r
+ )\r
+{\r
+ IP6_MLD_GROUP *Group;\r
+ EFI_STATUS Status;\r
+\r
+ Group = Ip6FindMldEntry (IpSb, Address);\r
+ if (Group == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // If more than one instance is in the group, decrease\r
+ // the RefCnt then return.\r
+ //\r
+ if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // If multiple IP6 group addresses are mapped to the same\r
+ // multicast MAC address, don't configure the MNP to leave\r
+ // the MAC.\r
+ //\r
+ if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) {\r
+ Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac);\r
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Send a leave report if we are the last node to report\r
+ //\r
+ if (Group->SendByUs) {\r
+ Status = Ip6SendMldDone (IpSb, Address);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ RemoveEntryList (&Group->Link);\r
+ FreePool (Group);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Worker function for EfiIp6Groups(). The caller\r
+ should make sure that the parameters are valid.\r
+\r
+ @param[in] IpInstance The IP6 child to change the setting.\r
+ @param[in] JoinFlag TRUE to join the group, otherwise leave it.\r
+ @param[in] GroupAddress The target group address. If NULL, leave all\r
+ the group addresses.\r
+\r
+ @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient resources.\r
+ @retval EFI_DEVICE_ERROR Failed to set the group configuraton.\r
+ @retval EFI_SUCCESS Successfully updated the group setting.\r
+ @retval EFI_NOT_FOUND Try to leave the group which it isn't a member.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Groups (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ IN BOOLEAN JoinFlag,\r
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP6_SERVICE *IpSb;\r
+ UINT32 Index;\r
+ EFI_IPv6_ADDRESS *Group;\r
+\r
+ IpSb = IpInstance->Service;\r
+\r
+ if (JoinFlag) {\r
+ ASSERT (GroupAddress != NULL);\r
+\r
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
+ if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+ }\r
+\r
+ Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress);\r
+ if (!EFI_ERROR (Status)) {\r
+ return Ip6CombineGroups (IpInstance, GroupAddress);\r
+ }\r
+\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Leave the group. Leave all the groups if GroupAddress is NULL.\r
+ //\r
+ for (Index = IpInstance->GroupCount; Index > 0; Index--) {\r
+ Group = IpInstance->GroupList + (Index - 1);\r
+\r
+ if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) {\r
+ Status = Ip6LeaveGroup (IpInstance->Service, Group);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Ip6RemoveGroup (IpInstance, Group);\r
+\r
+ if (IpInstance->GroupCount == 0) {\r
+ ASSERT (Index == 1);\r
+ FreePool (IpInstance->GroupList);\r
+ IpInstance->GroupList = NULL;\r
+ }\r
+\r
+ if (GroupAddress != NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ }\r
+\r
+ return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+ Set a random value of the delay timer for the multicast address from the range\r
+ [0, Maximum Response Delay]. If a timer for any address is already\r
+ running, it is reset to the new random value only if the requested\r
+ Maximum Response Delay is less than the remaining value of the\r
+ running timer. If the Query packet specifies a Maximum Response\r
+ Delay of zero, each timer is effectively set to zero, and the action\r
+ specified below for timer expiration is performed immediately.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds.\r
+ @param[in] MulticastAddr The multicast address.\r
+ @param[in, out] Group Points to a IP6_MLD_GROUP list entry node.\r
+\r
+ @retval EFI_SUCCESS The delay timer is successfully updated or\r
+ timer expiration is performed immediately.\r
+ @retval Others Failed to send out MLD report message.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6UpdateDelayTimer (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN UINT16 MaxRespDelay,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddr,\r
+ IN OUT IP6_MLD_GROUP *Group\r
+ )\r
+{\r
+ UINT32 Delay;\r
+\r
+ //\r
+ // If the Query packet specifies a Maximum Response Delay of zero, perform timer\r
+ // expiration immediately.\r
+ //\r
+ if (MaxRespDelay == 0) {\r
+ Group->DelayTimer = 0;\r
+ return Ip6SendMldReport (IpSb, NULL, MulticastAddr);\r
+ }\r
+\r
+ Delay = (UINT32) (MaxRespDelay / 1000);\r
+\r
+ //\r
+ // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay]\r
+ // If a timer is already running, resets it if the request Maximum Response Delay\r
+ // is less than the remaining value of the running timer.\r
+ //\r
+ if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) {\r
+ Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ());\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Process the Multicast Listener Query message.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the MLD query packet.\r
+ @param[in] Packet The content of the MLD query packet with IP head\r
+ removed.\r
+\r
+ @retval EFI_SUCCESS The MLD query packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldQuery (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ EFI_IPv6_ADDRESS AllNodes;\r
+ IP6_MLD_GROUP *Group;\r
+ IP6_MLD_HEAD MldPacket;\r
+ LIST_ENTRY *Entry;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ //\r
+ // Check the validity of the packet, generic query or specific query\r
+ //\r
+ if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // The Packet points to MLD report raw data without Hop-By-Hop option.\r
+ //\r
+ NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);\r
+ MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay);\r
+\r
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
+ if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) {\r
+ //\r
+ // Receives a Multicast-Address-Specific Query, check it firstly\r
+ //\r
+ if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {\r
+ goto Exit;\r
+ }\r
+ //\r
+ // The node is not listening but it receives the specific query. Just return.\r
+ //\r
+ Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);\r
+ if (Group == NULL) {\r
+ Status = EFI_SUCCESS;\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Ip6UpdateDelayTimer (\r
+ IpSb,\r
+ MldPacket.MaxRespDelay,\r
+ &MldPacket.Group,\r
+ Group\r
+ );\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Receives a General Query, sets a delay timer for each multicast address it is listening\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+ Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+ NetbufFree (Packet);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process the Multicast Listener Report message.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the MLD report packet.\r
+ @param[in] Packet The content of the MLD report packet with IP head\r
+ removed.\r
+\r
+ @retval EFI_SUCCESS The MLD report packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldReport (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_MLD_HEAD MldPacket;\r
+ IP6_MLD_GROUP *Group;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ //\r
+ // Validate the incoming message, if invalid, drop it.\r
+ //\r
+ if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // The Packet points to MLD report raw data without Hop-By-Hop option.\r
+ //\r
+ NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);\r
+ if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);\r
+ if (Group == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // The report is sent by another node, stop its own timer relates to the multicast address and clear\r
+ //\r
+\r
+ if (!Group->SendByUs) {\r
+ Group->DelayTimer = 0;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+ NetbufFree (Packet);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The heartbeat timer of MLD module. It sends out a solicited MLD report when\r
+ DelayTimer expires.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6MldTimerTicking (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ IP6_MLD_GROUP *Group;\r
+ LIST_ENTRY *Entry;\r
+\r
+ //\r
+ // Send solicited report when timer expires\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
+ if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) {\r
+ Ip6SendMldReport (IpSb, NULL, &Group->Address);\r
+ }\r
+ }\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Multicast Listener Discovery support routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_MLD_H__\r
+#define __EFI_IP6_MLD_H__\r
+\r
+#define IP6_UNSOLICITED_REPORT_INTERVAL 10\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+ IP6_ICMP_HEAD Head;\r
+ UINT16 MaxRespDelay;\r
+ UINT16 Reserved;\r
+ EFI_IPv6_ADDRESS Group;\r
+} IP6_MLD_HEAD;\r
+#pragma pack()\r
+\r
+//\r
+// The status of multicast group. It isn't necessary to maintain\r
+// explicit state of host state diagram. A group with finity\r
+// DelayTime (less than 0xffffffff) is in "delaying listener" state. otherwise, it is in\r
+// "idle listener" state.\r
+//\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ INTN RefCnt;\r
+ EFI_IPv6_ADDRESS Address;\r
+ UINT32 DelayTimer;\r
+ BOOLEAN SendByUs;\r
+ EFI_MAC_ADDRESS Mac;\r
+} IP6_MLD_GROUP;\r
+\r
+//\r
+// The MLD status. Each IP6 service instance has a MLD_SERVICE_DATA\r
+// attached. The Mldv1QuerySeen remember whether the server on this\r
+// connected network is v1 or v2.\r
+//\r
+typedef struct {\r
+ INTN Mldv1QuerySeen;\r
+ LIST_ENTRY Groups;\r
+} IP6_MLD_SERVICE_DATA;\r
+\r
+/**\r
+ Search a IP6_MLD_GROUP list entry node from a list array.\r
+\r
+ @param[in] IpSb Points to an IP6 service binding instance.\r
+ @param[in] MulticastAddr The IPv6 multicast address to be searched.\r
+\r
+ @return The found IP6_ML_GROUP list entry or NULL.\r
+\r
+**/\r
+IP6_MLD_GROUP *\r
+Ip6FindMldEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddr\r
+ );\r
+\r
+/**\r
+ Init the MLD data of the IP6 service instance, configure\r
+ MNP to receive ALL SYSTEM multicasts.\r
+\r
+ @param[in] IpSb The IP6 service whose MLD is to be initialized.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The MLD module successfully initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitMld (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ Join the multicast group on behalf of this IP6 service binding instance.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] Interface Points to an IP6_INTERFACE structure.\r
+ @param[in] Address The group address to join.\r
+\r
+ @retval EFI_SUCCESS Successfully joined the multicast group.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+ @retval Others Failed to join the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6JoinGroup (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *Interface,\r
+ IN EFI_IPv6_ADDRESS *Address\r
+ );\r
+\r
+/**\r
+ Leave the IP6 multicast group.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] Address The group address to leave.\r
+\r
+ @retval EFI_NOT_FOUND The IP6 service instance isn't in the group.\r
+ @retval EFI_SUCCESS Successfully left the multicast group.\r
+ @retval Others Failed to leave the multicast group.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6LeaveGroup (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Address\r
+ );\r
+\r
+/**\r
+ Worker function for EfiIp6Groups(). The caller\r
+ should verify that the parameters are valid.\r
+\r
+ @param[in] IpInstance The IP6 child to change the setting.\r
+ @param[in] JoinFlag TRUE to join the group, otherwise leave it.\r
+ @param[in] GroupAddress The target group address. If NULL, leave all\r
+ the group addresses.\r
+\r
+ @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.\r
+ @retval EFI_DEVICE_ERROR Failed to set the group configuraton.\r
+ @retval EFI_SUCCESS Successfully updated the group setting.\r
+ @retval EFI_NOT_FOUND Tried to leave a group of whom it isn't a member.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Groups (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ IN BOOLEAN JoinFlag,\r
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ Process the Multicast Listener Query message.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the MLD query packet.\r
+ @param[in] Packet The content of the MLD query packet with IP head\r
+ removed.\r
+\r
+ @retval EFI_SUCCESS The MLD query packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldQuery (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ Process the Multicast Listener Report message.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the MLD report packet.\r
+ @param[in] Packet The content of the MLD report packet with IP head\r
+ removed.\r
+\r
+ @retval EFI_SUCCESS The MLD report packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessMldReport (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+\r
+/**\r
+ The heartbeat timer of the MLD module. It sends out solicited MLD report when\r
+ DelayTimer expires.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6MldTimerTicking (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ Implementation of Neighbor Discovery support routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+EFI_MAC_ADDRESS mZeroMacAddress;\r
+\r
+/**\r
+ Update the ReachableTime in IP6 service binding instance data, in milliseconds.\r
+\r
+ @param[in, out] IpSb Points to the IP6_SERVICE.\r
+\r
+**/\r
+VOID\r
+Ip6UpdateReachableTime (\r
+ IN OUT IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ UINT32 Random;\r
+\r
+ Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE;\r
+ Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED;\r
+ IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE;\r
+}\r
+\r
+/**\r
+ Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number\r
+ of EFI_IP6_NEIGHBOR_CACHE is also returned.\r
+\r
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.\r
+ @param[out] NeighborCount The number of returned neighbor cache entries.\r
+ @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.\r
+\r
+ @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiNeighborCache (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ OUT UINT32 *NeighborCount,\r
+ OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache\r
+ )\r
+{\r
+ IP6_NEIGHBOR_ENTRY *Neighbor;\r
+ LIST_ENTRY *Entry;\r
+ IP6_SERVICE *IpSb;\r
+ UINT32 Count;\r
+ EFI_IP6_NEIGHBOR_CACHE *EfiNeighborCache;\r
+ EFI_IP6_NEIGHBOR_CACHE *NeighborCacheTmp;\r
+\r
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+ ASSERT (NeighborCount != NULL && NeighborCache != NULL);\r
+\r
+ IpSb = IpInstance->Service;\r
+ Count = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {\r
+ Count++;\r
+ }\r
+\r
+ if (Count == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE));\r
+ if (NeighborCacheTmp == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ *NeighborCount = Count;\r
+ Count = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {\r
+ Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);\r
+\r
+ EfiNeighborCache = NeighborCacheTmp + Count;\r
+\r
+ EfiNeighborCache->State = Neighbor->State;\r
+ IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor);\r
+ IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress);\r
+\r
+ Count++;\r
+ }\r
+\r
+ ASSERT (*NeighborCount == Count);\r
+ *NeighborCache = NeighborCacheTmp;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+ of prefix entries is also returned.\r
+\r
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.\r
+ @param[out] PrefixCount The number of returned prefix entries.\r
+ @param[out] PrefixTable The pointer to the array of PrefixTable.\r
+\r
+ @retval EFI_SUCCESS The prefix table successfully built.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildPrefixTable (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ OUT UINT32 *PrefixCount,\r
+ OUT EFI_IP6_ADDRESS_INFO **PrefixTable\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_SERVICE *IpSb;\r
+ UINT32 Count;\r
+ IP6_PREFIX_LIST_ENTRY *PrefixList;\r
+ EFI_IP6_ADDRESS_INFO *EfiPrefix;\r
+ EFI_IP6_ADDRESS_INFO *PrefixTableTmp;\r
+\r
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
+ ASSERT (PrefixCount != NULL && PrefixTable != NULL);\r
+\r
+ IpSb = IpInstance->Service;\r
+ Count = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {\r
+ Count++;\r
+ }\r
+\r
+ if (Count == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO));\r
+ if (PrefixTableTmp == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ *PrefixCount = Count;\r
+ Count = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {\r
+ PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+ EfiPrefix = PrefixTableTmp + Count;\r
+ IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix);\r
+ EfiPrefix->PrefixLength = PrefixList->PrefixLength;\r
+\r
+ Count++;\r
+ }\r
+\r
+ ASSERT (*PrefixCount == Count);\r
+ *PrefixTable = PrefixTableTmp;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate and initialize a IP6 prefix list entry.\r
+\r
+ @param[in] IpSb The pointer to IP6_SERVICE instance.\r
+ @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list.\r
+ Otherwise, it is created for the autoconfiguration prefix list.\r
+ @param[in] ValidLifetime The length of time in seconds that the prefix\r
+ is valid for the purpose of on-link determination.\r
+ @param[in] PreferredLifetime The length of time in seconds that addresses\r
+ generated from the prefix via stateless address\r
+ autoconfiguration remain preferred.\r
+ @param[in] PrefixLength The prefix length of the Prefix.\r
+ @param[in] Prefix The prefix address.\r
+\r
+ @return NULL if it failed to allocate memory for the prefix node. Otherwise, point\r
+ to the created or existing prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6CreatePrefixListEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN OnLinkOrAuto,\r
+ IN UINT32 ValidLifetime,\r
+ IN UINT32 PreferredLifetime,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *Prefix\r
+ )\r
+{\r
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;\r
+ IP6_ROUTE_ENTRY *RtEntry;\r
+ LIST_ENTRY *ListHead;\r
+ LIST_ENTRY *Entry;\r
+ IP6_PREFIX_LIST_ENTRY *TmpPrefixEntry;\r
+\r
+ if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength >= IP6_PREFIX_NUM) {\r
+ return NULL;\r
+ }\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ PrefixEntry = Ip6FindPrefixListEntry (\r
+ IpSb,\r
+ OnLinkOrAuto,\r
+ PrefixLength,\r
+ Prefix\r
+ );\r
+ if (PrefixEntry != NULL) {\r
+ PrefixEntry->RefCnt ++;\r
+ return PrefixEntry;\r
+ }\r
+\r
+ PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY));\r
+ if (PrefixEntry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ PrefixEntry->RefCnt = 1;\r
+ PrefixEntry->ValidLifetime = ValidLifetime;\r
+ PrefixEntry->PreferredLifetime = PreferredLifetime;\r
+ PrefixEntry->PrefixLength = PrefixLength;\r
+ IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix);\r
+\r
+ ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix;\r
+\r
+ //\r
+ // Create a direct route entry for on-link prefix and insert to route area.\r
+ //\r
+ if (OnLinkOrAuto) {\r
+ RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL);\r
+ if (RtEntry == NULL) {\r
+ FreePool (PrefixEntry);\r
+ return NULL;\r
+ }\r
+\r
+ RtEntry->Flag = IP6_DIRECT_ROUTE;\r
+ InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link);\r
+ IpSb->RouteTable->TotalNum++;\r
+ }\r
+\r
+ //\r
+ // Insert the prefix entry in the order that a prefix with longer prefix length\r
+ // is put ahead in the list.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, ListHead) {\r
+ TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+\r
+ if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ NetListInsertBefore (Entry, &PrefixEntry->Link);\r
+\r
+ return PrefixEntry;\r
+}\r
+\r
+/**\r
+ Destory a IP6 prefix list entry.\r
+\r
+ @param[in] IpSb The pointer to IP6_SERVICE instance.\r
+ @param[in] PrefixEntry The to be destroyed prefix list entry.\r
+ @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list.\r
+ Otherwise remove from autoconfiguration prefix list.\r
+ @param[in] ImmediateDelete If TRUE, remove the entry directly.\r
+ Otherwise, check the reference count to see whether\r
+ it should be removed.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyPrefixListEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_PREFIX_LIST_ENTRY *PrefixEntry,\r
+ IN BOOLEAN OnLinkOrAuto,\r
+ IN BOOLEAN ImmediateDelete\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_INTERFACE *IpIf;\r
+ EFI_STATUS Status;\r
+\r
+ if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) {\r
+ return ;\r
+ }\r
+\r
+ if (OnLinkOrAuto) {\r
+ //\r
+ // Remove the direct route for onlink prefix from route table.\r
+ //\r
+ do {\r
+ Status = Ip6DelRoute (\r
+ IpSb->RouteTable,\r
+ &PrefixEntry->Prefix,\r
+ PrefixEntry->PrefixLength,\r
+ NULL\r
+ );\r
+ } while (Status != EFI_NOT_FOUND);\r
+ } else {\r
+ //\r
+ // Remove the corresponding addresses generated from this autonomous prefix.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);\r
+\r
+ Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength);\r
+ }\r
+ }\r
+\r
+ RemoveEntryList (&PrefixEntry->Link);\r
+ FreePool (PrefixEntry);\r
+}\r
+\r
+/**\r
+ Search the list array to find an IP6 prefix list entry.\r
+\r
+ @param[in] IpSb The pointer to IP6_SERVICE instance.\r
+ @param[in] OnLinkOrAuto If TRUE, the search the link prefix list,\r
+ Otherwise search the autoconfiguration prefix list.\r
+ @param[in] PrefixLength The prefix length of the Prefix\r
+ @param[in] Prefix The prefix address.\r
+\r
+ @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the\r
+ pointer to the IP6 prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6FindPrefixListEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN OnLinkOrAuto,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *Prefix\r
+ )\r
+{\r
+ IP6_PREFIX_LIST_ENTRY *PrefixList;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *ListHead;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (Prefix != NULL);\r
+\r
+ if (OnLinkOrAuto) {\r
+ ListHead = &IpSb->OnlinkPrefix;\r
+ } else {\r
+ ListHead = &IpSb->AutonomousPrefix;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, ListHead) {\r
+ PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+ if (PrefixLength != 255) {\r
+ //\r
+ // Perform exactly prefix match.\r
+ //\r
+ if (PrefixList->PrefixLength == PrefixLength &&\r
+ NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) {\r
+ return PrefixList;\r
+ }\r
+ } else {\r
+ //\r
+ // Perform the longest prefix match. The list is already sorted with\r
+ // the longest length prefix put at the head of the list.\r
+ //\r
+ if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) {\r
+ return PrefixList;\r
+ }\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Release the resource in the prefix list table, and destroy the list entry and\r
+ corresponding addresses or route entries.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] ListHead The list entry head of the prefix list table.\r
+\r
+**/\r
+VOID\r
+Ip6CleanPrefixListTable (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN LIST_ENTRY *ListHead\r
+ )\r
+{\r
+ IP6_PREFIX_LIST_ENTRY *PrefixList;\r
+ BOOLEAN OnLink;\r
+\r
+ OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix);\r
+\r
+ while (!IsListEmpty (ListHead)) {\r
+ PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link);\r
+ Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);\r
+ }\r
+}\r
+\r
+/**\r
+ Callback function when address resolution is finished. It will cancel\r
+ all the queued frames if the address resolution failed, or transmit them\r
+ if the request succeeded.\r
+\r
+ @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.\r
+\r
+**/\r
+VOID\r
+Ip6OnArpResolved (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_NEIGHBOR_ENTRY *ArpQue;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_LINK_TX_TOKEN *Token;\r
+ EFI_STATUS Status;\r
+ BOOLEAN Sent;\r
+\r
+ ArpQue = (IP6_NEIGHBOR_ENTRY *) Context;\r
+ if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) {\r
+ return ;\r
+ }\r
+\r
+ IpSb = ArpQue->Interface->Service;\r
+ if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) {\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // ARP resolve failed for some reason. Release all the frame\r
+ // and ARP queue itself. Ip6FreeArpQue will call the frame's\r
+ // owner back.\r
+ //\r
+ if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) {\r
+ Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL);\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // ARP resolve succeeded, Transmit all the frame.\r
+ //\r
+ Sent = FALSE;\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {\r
+ RemoveEntryList (Entry);\r
+\r
+ Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);\r
+ IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress);\r
+\r
+ //\r
+ // Insert the tx token before transmitting it via MNP as the FrameSentDpc\r
+ // may be called before Mnp->Transmit returns which will remove this tx\r
+ // token from the SentFrames list. Remove it from the list if the returned\r
+ // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the\r
+ // FrameSentDpc won't be queued.\r
+ //\r
+ InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link);\r
+\r
+ Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);\r
+ if (EFI_ERROR (Status)) {\r
+ RemoveEntryList (&Token->Link);\r
+ Token->CallBack (Token->Packet, Status, 0, Token->Context);\r
+\r
+ Ip6FreeLinkTxToken (Token);\r
+ continue;\r
+ } else {\r
+ Sent = TRUE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free the ArpQue only but not the whole neighbor entry.\r
+ //\r
+ Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL);\r
+\r
+ if (Sent && (ArpQue->State == EfiNeighborStale)) {\r
+ ArpQue->State = EfiNeighborDelay;\r
+ ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);\r
+ }\r
+}\r
+\r
+/**\r
+ Allocate and initialize an IP6 neighbor cache entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] CallBack The callback function to be called when\r
+ address resolution is finished.\r
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.\r
+ @param[in] LinkAddress Points to the MAC address of the neighbor.\r
+ Ignored if NULL.\r
+\r
+ @return NULL if failed to allocate memory for the neighbor cache entry.\r
+ Otherwise, point to the created neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6CreateNeighborEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_ARP_CALLBACK CallBack,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address,\r
+ IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL\r
+ )\r
+{\r
+ IP6_NEIGHBOR_ENTRY *Entry;\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (Ip6Address!= NULL);\r
+\r
+ Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY));\r
+ if (Entry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ Entry->RefCnt = 1;\r
+ Entry->IsRouter = FALSE;\r
+ Entry->ArpFree = FALSE;\r
+ Entry->Dynamic = FALSE;\r
+ Entry->State = EfiNeighborInComplete;\r
+ Entry->Transmit = IP6_MAX_MULTICAST_SOLICIT + 1;\r
+ Entry->CallBack = CallBack;\r
+ Entry->Interface = NULL;\r
+\r
+ InitializeListHead (&Entry->Frames);\r
+\r
+ IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address);\r
+\r
+ if (LinkAddress != NULL) {\r
+ IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress);\r
+ } else {\r
+ IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress);\r
+ }\r
+\r
+ InsertHeadList (&IpSb->NeighborTable, &Entry->Link);\r
+\r
+ //\r
+ // If corresponding default router entry exists, establish the relationship.\r
+ //\r
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address);\r
+ if (DefaultRouter != NULL) {\r
+ DefaultRouter->NeighborCache = Entry;\r
+ }\r
+\r
+ return Entry;\r
+}\r
+\r
+/**\r
+ Search a IP6 neighbor cache entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.\r
+\r
+ @return NULL if it failed to find the matching neighbor cache entry.\r
+ Otherwise, point to the found neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6FindNeighborEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_NEIGHBOR_ENTRY *Neighbor;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (Ip6Address != NULL);\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {\r
+ Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);\r
+ if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) {\r
+ RemoveEntryList (Entry);\r
+ InsertHeadList (&IpSb->NeighborTable, Entry);\r
+\r
+ return Neighbor;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Free a IP6 neighbor cache entry and remove all the frames on the address\r
+ resolution queue that pass the FrameToCancel. That is, either FrameToCancel\r
+ is NULL, or it returns true for the frame.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] NeighborCache The to be free neighbor cache entry.\r
+ @param[in] SendIcmpError If TRUE, send out ICMP error.\r
+ @param[in] FullFree If TRUE, remove the neighbor cache entry.\r
+ Otherwise remove the pending frames.\r
+ @param[in] IoStatus The status returned to the cancelled frames'\r
+ callback function.\r
+ @param[in] FrameToCancel Function to select which frame to cancel.\r
+ This is an optional parameter that may be NULL.\r
+ @param[in] Context Opaque parameter to the FrameToCancel.\r
+ Ignored if FrameToCancel is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FreeNeighborEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_NEIGHBOR_ENTRY *NeighborCache,\r
+ IN BOOLEAN SendIcmpError,\r
+ IN BOOLEAN FullFree,\r
+ IN EFI_STATUS IoStatus,\r
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ IP6_LINK_TX_TOKEN *TxToken;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+\r
+ //\r
+ // If FrameToCancel fails, the token will not be released.\r
+ // To avoid the memory leak, stop this usage model.\r
+ //\r
+ if (FullFree && FrameToCancel != NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) {\r
+ TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);\r
+\r
+ if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) {\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ TxToken->Packet,\r
+ NULL,\r
+ &TxToken->Packet->Ip.Ip6->SourceAddress,\r
+ ICMP_V6_DEST_UNREACHABLE,\r
+ ICMP_V6_ADDR_UNREACHABLE,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) {\r
+ RemoveEntryList (Entry);\r
+ TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context);\r
+ Ip6FreeLinkTxToken (TxToken);\r
+ }\r
+ }\r
+\r
+ if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) {\r
+ RemoveEntryList (&NeighborCache->ArpList);\r
+ NeighborCache->ArpFree = FALSE;\r
+ }\r
+\r
+ if (FullFree) {\r
+ if (NeighborCache->IsRouter) {\r
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor);\r
+ if (DefaultRouter != NULL) {\r
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+ }\r
+ }\r
+\r
+ RemoveEntryList (&NeighborCache->Link);\r
+ FreePool (NeighborCache);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate and initialize an IP6 default router entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Ip6Address The IPv6 address of the default router.\r
+ @param[in] RouterLifetime The lifetime associated with the default\r
+ router, in units of seconds.\r
+\r
+ @return NULL if it failed to allocate memory for the default router node.\r
+ Otherwise, point to the created default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6CreateDefaultRouter (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address,\r
+ IN UINT16 RouterLifetime\r
+ )\r
+{\r
+ IP6_DEFAULT_ROUTER *Entry;\r
+ IP6_ROUTE_ENTRY *RtEntry;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (Ip6Address != NULL);\r
+\r
+ Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER));\r
+ if (Entry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ Entry->RefCnt = 1;\r
+ Entry->Lifetime = RouterLifetime;\r
+ Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address);\r
+ IP6_COPY_ADDRESS (&Entry->Router, Ip6Address);\r
+\r
+ //\r
+ // Add a default route into route table with both Destination and PrefixLength set to zero.\r
+ //\r
+ RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address);\r
+ if (RtEntry == NULL) {\r
+ FreePool (Entry);\r
+ return NULL;\r
+ }\r
+\r
+ InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link);\r
+ IpSb->RouteTable->TotalNum++;\r
+\r
+ InsertTailList (&IpSb->DefaultRouterList, &Entry->Link);\r
+\r
+ return Entry;\r
+}\r
+\r
+/**\r
+ Destroy an IP6 default router entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyDefaultRouter (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_DEFAULT_ROUTER *DefaultRouter\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ RemoveEntryList (&DefaultRouter->Link);\r
+\r
+ //\r
+ // Update the Destination Cache - all entries using the time-out router as next-hop\r
+ // should perform next-hop determination again.\r
+ //\r
+ do {\r
+ Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router);\r
+ } while (Status != EFI_NOT_FOUND);\r
+\r
+ FreePool (DefaultRouter);\r
+}\r
+\r
+/**\r
+ Clean an IP6 default router list.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6CleanDefaultRouterList (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+\r
+ while (!IsListEmpty (&IpSb->DefaultRouterList)) {\r
+ DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link);\r
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+ }\r
+}\r
+\r
+/**\r
+ Search a default router node from an IP6 default router list.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Ip6Address The IPv6 address of the to be searched default router node.\r
+\r
+ @return NULL if it failed to find the matching default router node.\r
+ Otherwise, point to the found default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6FindDefaultRouter (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (Ip6Address != NULL);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) {\r
+ DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);\r
+ if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) {\r
+ return DefaultRouter;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ The function to be called after DAD (Duplicate Address Detection) is performed.\r
+\r
+ @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.\r
+ @param[in] IpIf Points to the IP6_INTERFACE.\r
+ @param[in] DadEntry The DAD entry which already performed DAD.\r
+\r
+**/\r
+VOID\r
+Ip6OnDADFinished (\r
+ IN BOOLEAN IsDadPassed,\r
+ IN IP6_INTERFACE *IpIf,\r
+ IN IP6_DAD_ENTRY *DadEntry\r
+ )\r
+{\r
+ IP6_SERVICE *IpSb;\r
+ IP6_ADDRESS_INFO *AddrInfo;\r
+ EFI_DHCP6_PROTOCOL *Dhcp6;\r
+ UINT16 OptBuf[4];\r
+ EFI_DHCP6_PACKET_OPTION *Oro;\r
+ EFI_DHCP6_RETRANSMISSION InfoReqReXmit;\r
+\r
+ IpSb = IpIf->Service;\r
+ AddrInfo = DadEntry->AddressInfo;\r
+\r
+ if (IsDadPassed) {\r
+ //\r
+ // DAD succeed.\r
+ //\r
+ if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {\r
+ ASSERT (!IpSb->LinkLocalOk);\r
+\r
+ IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address);\r
+ IpSb->LinkLocalOk = TRUE;\r
+ IpIf->Configured = TRUE;\r
+\r
+ //\r
+ // Check whether DHCP6 need to be started.\r
+ //\r
+ Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6;\r
+\r
+ if (IpSb->Dhcp6NeedStart) {\r
+ Dhcp6->Start (Dhcp6);\r
+ IpSb->Dhcp6NeedStart = FALSE;\r
+ }\r
+\r
+ if (IpSb->Dhcp6NeedInfoRequest) {\r
+ //\r
+ // Set the exta options to send. Here we only want the option request option\r
+ // with DNS SERVERS.\r
+ //\r
+ Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf;\r
+ Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);\r
+ Oro->OpLen = HTONS (2);\r
+ *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);\r
+\r
+ InfoReqReXmit.Irt = 4;\r
+ InfoReqReXmit.Mrc = 64;\r
+ InfoReqReXmit.Mrt = 60;\r
+ InfoReqReXmit.Mrd = 0;\r
+\r
+ Dhcp6->InfoRequest (\r
+ Dhcp6,\r
+ TRUE,\r
+ Oro,\r
+ 0,\r
+ NULL,\r
+ &InfoReqReXmit,\r
+ IpSb->Ip6ConfigInstance.Dhcp6Event,\r
+ Ip6ConfigOnDhcp6Reply,\r
+ &IpSb->Ip6ConfigInstance\r
+ );\r
+ }\r
+\r
+ //\r
+ // Add an on-link prefix for link-local address.\r
+ //\r
+ Ip6CreatePrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ (UINT32) IP6_INFINIT_LIFETIME,\r
+ IP6_LINK_LOCAL_PREFIX_LENGTH,\r
+ &IpSb->LinkLocalAddr\r
+ );\r
+\r
+ } else {\r
+ //\r
+ // Global scope unicast address.\r
+ //\r
+ Ip6AddAddr (IpIf, AddrInfo);\r
+\r
+ //\r
+ // Add an on-link prefix for this address.\r
+ //\r
+ Ip6CreatePrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ AddrInfo->ValidLifetime,\r
+ AddrInfo->PreferredLifetime,\r
+ AddrInfo->PrefixLength,\r
+ &AddrInfo->Address\r
+ );\r
+\r
+ IpIf->Configured = TRUE;\r
+ }\r
+ } else {\r
+ //\r
+ // Leave the group we joined before.\r
+ //\r
+ Ip6LeaveGroup (IpSb, &DadEntry->Destination);\r
+ }\r
+\r
+ if (DadEntry->Callback != NULL) {\r
+ DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context);\r
+ }\r
+\r
+ if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {\r
+ FreePool (AddrInfo);\r
+ RemoveEntryList (&DadEntry->Link);\r
+ FreePool (DadEntry);\r
+ //\r
+ // Disable IP operation since link-local address is a duplicate address.\r
+ //\r
+ IpSb->LinkLocalDadFail = TRUE;\r
+ IpSb->Mnp->Configure (IpSb->Mnp, NULL);\r
+ gBS->SetTimer (IpSb->Timer, TimerCancel, 0);\r
+ gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);\r
+ return ;\r
+ }\r
+\r
+ if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {\r
+ //\r
+ // Free the AddressInfo we hold if DAD fails or it is a link-local address.\r
+ //\r
+ FreePool (AddrInfo);\r
+ }\r
+\r
+ RemoveEntryList (&DadEntry->Link);\r
+ FreePool (DadEntry);\r
+}\r
+\r
+/**\r
+ Create a DAD (Duplicate Address Detection) entry and queue it to be performed.\r
+\r
+ @param[in] IpIf Points to the IP6_INTERFACE.\r
+ @param[in] AddressInfo The address information which needs DAD performed.\r
+ @param[in] Callback The callback routine that will be called after DAD\r
+ is performed. This is an optional parameter that\r
+ may be NULL.\r
+ @param[in] Context The opaque parameter for a DAD callback routine.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The DAD entry was created and queued.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the\r
+ operation.\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitDADProcess (\r
+ IN IP6_INTERFACE *IpIf,\r
+ IN IP6_ADDRESS_INFO *AddressInfo,\r
+ IN IP6_DAD_CALLBACK Callback OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ IP6_DAD_ENTRY *Entry;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits;\r
+ IP6_SERVICE *IpSb;\r
+ EFI_STATUS Status;\r
+ UINT32 MaxDelayTick;\r
+\r
+ NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE);\r
+ ASSERT (AddressInfo != NULL);\r
+\r
+ Status = EFI_SUCCESS;\r
+ IpSb = IpIf->Service;\r
+ DadXmits = &IpSb->Ip6ConfigInstance.DadXmits;\r
+\r
+ //\r
+ // Allocate the resources and insert info\r
+ //\r
+ Entry = AllocatePool (sizeof (IP6_DAD_ENTRY));\r
+ if (Entry == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Map the incoming unicast address to solicited-node multicast address\r
+ //\r
+ Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination);\r
+\r
+ //\r
+ // Join in the solicited-node multicast address.\r
+ //\r
+ Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Entry);\r
+ return Status;\r
+ }\r
+\r
+ Entry->Signature = IP6_DAD_ENTRY_SIGNATURE;\r
+ Entry->MaxTransmit = DadXmits->DupAddrDetectTransmits;\r
+ Entry->Transmit = 0;\r
+ Entry->Receive = 0;\r
+ MaxDelayTick = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS;\r
+ Entry->RetransTick = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5;\r
+ Entry->AddressInfo = AddressInfo;\r
+ Entry->Callback = Callback;\r
+ Entry->Context = Context;\r
+ InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link);\r
+\r
+ if (Entry->MaxTransmit == 0) {\r
+ //\r
+ // DAD is disabled on this interface, immediately mark this DAD successful.\r
+ //\r
+ Ip6OnDADFinished (TRUE, IpIf, Entry);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Search IP6_DAD_ENTRY from the Duplicate Address Detection List.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Target The address information which needs DAD performed .\r
+ @param[out] Interface If not NULL, output the IP6 interface that configures\r
+ the tentative address.\r
+\r
+ @return NULL if failed to find the matching DAD entry.\r
+ Otherwise, point to the found DAD entry.\r
+\r
+**/\r
+IP6_DAD_ENTRY *\r
+Ip6FindDADEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Target,\r
+ OUT IP6_INTERFACE **Interface OPTIONAL\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Entry2;\r
+ IP6_INTERFACE *IpIf;\r
+ IP6_DAD_ENTRY *DupAddrDetect;\r
+ IP6_ADDRESS_INFO *AddrInfo;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+ NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) {\r
+ DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);\r
+ AddrInfo = DupAddrDetect->AddressInfo;\r
+ if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) {\r
+ if (Interface != NULL) {\r
+ *Interface = IpIf;\r
+ }\r
+ return DupAddrDetect;\r
+ }\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Generate router solicit message and send it out to Destination Address or\r
+ All Router Link Local scope multicast address.\r
+\r
+ @param[in] IpSb The IP service to send the packet.\r
+ @param[in] Interface If not NULL, points to the IP6 interface to send\r
+ the packet.\r
+ @param[in] SourceAddress If not NULL, the source address of the message.\r
+ @param[in] DestinationAddress If not NULL, the destination address of the message.\r
+ @param[in] SourceLinkAddress If not NULL, the MAC address of the source.\r
+ A source link-layer address option will be appended\r
+ to the message.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The router solicit message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendRouterSolicit (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *Interface OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL,\r
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL\r
+ )\r
+{\r
+ NET_BUF *Packet;\r
+ EFI_IP6_HEADER Head;\r
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;\r
+ UINT16 PayloadLen;\r
+ IP6_INTERFACE *IpIf;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ IpIf = Interface;\r
+ if (IpIf == NULL && IpSb->DefaultInterface != NULL) {\r
+ IpIf = IpSb->DefaultInterface;\r
+ }\r
+\r
+ //\r
+ // Generate the packet to be sent\r
+ //\r
+\r
+ PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD);\r
+ if (SourceLinkAddress != NULL) {\r
+ PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION);\r
+ }\r
+\r
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Create the basic IPv6 header.\r
+ //\r
+ Head.FlowLabelL = 0;\r
+ Head.FlowLabelH = 0;\r
+ Head.PayloadLength = HTONS (PayloadLen);\r
+ Head.NextHeader = IP6_ICMP;\r
+ Head.HopLimit = IP6_HOP_LIMIT;\r
+\r
+ if (SourceAddress != NULL) {\r
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+ } else {\r
+ ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ }\r
+\r
+\r
+ if (DestinationAddress != NULL) {\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+ } else {\r
+ Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress);\r
+ }\r
+\r
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Fill in the ICMP header, and Source link-layer address if contained.\r
+ //\r
+\r
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+ ASSERT (IcmpHead != NULL);\r
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+ IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT;\r
+ IcmpHead->Head.Code = 0;\r
+\r
+ LinkLayerOption = NULL;\r
+ if (SourceLinkAddress != NULL) {\r
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (\r
+ Packet,\r
+ sizeof (IP6_ETHER_ADDR_OPTION),\r
+ FALSE\r
+ );\r
+ ASSERT (LinkLayerOption != NULL);\r
+ LinkLayerOption->Type = Ip6OptionEtherSource;\r
+ LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION);\r
+ CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);\r
+ }\r
+\r
+ //\r
+ // Transmit the packet\r
+ //\r
+ return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+ Generate a Neighbor Advertisement message and send it out to Destination Address.\r
+\r
+ @param[in] IpSb The IP service to send the packet.\r
+ @param[in] SourceAddress The source address of the message.\r
+ @param[in] DestinationAddress The destination address of the message.\r
+ @param[in] TargetIp6Address The target address field in the Neighbor Solicitation\r
+ message that prompted this advertisement.\r
+ @param[in] TargetLinkAddress The MAC address for the target, i.e. the sender\r
+ of the advertisement.\r
+ @param[in] IsRouter If TRUE, indicates the sender is a router.\r
+ @param[in] Override If TRUE, indicates the advertisement should override\r
+ an existing cache entry and update the MAC address.\r
+ @param[in] Solicited If TRUE, indicates the advertisement was sent\r
+ in response to a Neighbor Solicitation from\r
+ the Destination address.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendNeighborAdvertise (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *SourceAddress,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *TargetLinkAddress,\r
+ IN BOOLEAN IsRouter,\r
+ IN BOOLEAN Override,\r
+ IN BOOLEAN Solicited\r
+ )\r
+{\r
+ NET_BUF *Packet;\r
+ EFI_IP6_HEADER Head;\r
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;\r
+ EFI_IPv6_ADDRESS *Target;\r
+ UINT16 PayloadLen;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ //\r
+ // The Neighbor Advertisement message must include a Target link-layer address option\r
+ // when responding to multicast solicitation and should include such option when\r
+ // responding to unicast solicitation. It also must include such option as unsolicited\r
+ // advertisement.\r
+ //\r
+ ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL);\r
+\r
+ PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION));\r
+\r
+ //\r
+ // Generate the packet to be sent\r
+ //\r
+\r
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Create the basic IPv6 header.\r
+ //\r
+ Head.FlowLabelL = 0;\r
+ Head.FlowLabelH = 0;\r
+ Head.PayloadLength = HTONS (PayloadLen);\r
+ Head.NextHeader = IP6_ICMP;\r
+ Head.HopLimit = IP6_HOP_LIMIT;\r
+\r
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+\r
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Fill in the ICMP header, Target address, and Target link-layer address.\r
+ // Set the Router flag, Solicited flag and Override flag.\r
+ //\r
+\r
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+ ASSERT (IcmpHead != NULL);\r
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+ IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE;\r
+ IcmpHead->Head.Code = 0;\r
+\r
+ if (IsRouter) {\r
+ IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG;\r
+ }\r
+\r
+ if (Solicited) {\r
+ IcmpHead->Fourth |= IP6_SOLICITED_FLAG;\r
+ }\r
+\r
+ if (Override) {\r
+ IcmpHead->Fourth |= IP6_OVERRIDE_FLAG;\r
+ }\r
+\r
+ Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);\r
+ ASSERT (Target != NULL);\r
+ IP6_COPY_ADDRESS (Target, TargetIp6Address);\r
+\r
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (\r
+ Packet,\r
+ sizeof (IP6_ETHER_ADDR_OPTION),\r
+ FALSE\r
+ );\r
+ ASSERT (LinkLayerOption != NULL);\r
+ LinkLayerOption->Type = Ip6OptionEtherTarget;\r
+ LinkLayerOption->Length = 1;\r
+ CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6);\r
+\r
+ //\r
+ // Transmit the packet\r
+ //\r
+ return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+ Generate the Neighbor Solicitation message and send it to the Destination Address.\r
+\r
+ @param[in] IpSb The IP service to send the packet\r
+ @param[in] SourceAddress The source address of the message.\r
+ @param[in] DestinationAddress The destination address of the message.\r
+ @param[in] TargetIp6Address The IP address of the target of the solicitation.\r
+ It must not be a multicast address.\r
+ @param[in] SourceLinkAddress The MAC address for the sender. If not NULL,\r
+ a source link-layer address option will be appended\r
+ to the message.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendNeighborSolicit (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *SourceAddress,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL\r
+ )\r
+{\r
+ NET_BUF *Packet;\r
+ EFI_IP6_HEADER Head;\r
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;\r
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;\r
+ EFI_IPv6_ADDRESS *Target;\r
+ BOOLEAN IsDAD;\r
+ UINT16 PayloadLen;\r
+ IP6_NEIGHBOR_ENTRY *Neighbor;\r
+\r
+ //\r
+ // Check input parameters\r
+ //\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ if (DestinationAddress == NULL || TargetIp6Address == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IsDAD = FALSE;\r
+\r
+ if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) {\r
+ IsDAD = TRUE;\r
+ }\r
+\r
+ //\r
+ // The Neighbor Solicitation message should include a source link-layer address option\r
+ // if the solicitation is not sent by performing DAD - Duplicate Address Detection.\r
+ // Otherwise must not include it.\r
+ //\r
+ PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ if (!IsDAD) {\r
+ if (SourceLinkAddress == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION));\r
+ }\r
+\r
+ //\r
+ // Generate the packet to be sent\r
+ //\r
+\r
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Create the basic IPv6 header\r
+ //\r
+ Head.FlowLabelL = 0;\r
+ Head.FlowLabelH = 0;\r
+ Head.PayloadLength = HTONS (PayloadLen);\r
+ Head.NextHeader = IP6_ICMP;\r
+ Head.HopLimit = IP6_HOP_LIMIT;\r
+\r
+ if (SourceAddress != NULL) {\r
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);\r
+ } else {\r
+ ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);\r
+\r
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
+\r
+ //\r
+ // Fill in the ICMP header, Target address, and Source link-layer address.\r
+ //\r
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);\r
+ ASSERT (IcmpHead != NULL);\r
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));\r
+ IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT;\r
+ IcmpHead->Head.Code = 0;\r
+\r
+ Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);\r
+ ASSERT (Target != NULL);\r
+ IP6_COPY_ADDRESS (Target, TargetIp6Address);\r
+\r
+ LinkLayerOption = NULL;\r
+ if (!IsDAD) {\r
+\r
+ //\r
+ // Fill in the source link-layer address option\r
+ //\r
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (\r
+ Packet,\r
+ sizeof (IP6_ETHER_ADDR_OPTION),\r
+ FALSE\r
+ );\r
+ ASSERT (LinkLayerOption != NULL);\r
+ LinkLayerOption->Type = Ip6OptionEtherSource;\r
+ LinkLayerOption->Length = 1;\r
+ CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);\r
+ }\r
+\r
+ //\r
+ // Create a Neighbor Cache entry in the INCOMPLETE state when performing\r
+ // address resolution.\r
+ //\r
+ if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) {\r
+ Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);\r
+ if (Neighbor == NULL) {\r
+ Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL);\r
+ ASSERT (Neighbor != NULL);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Transmit the packet\r
+ //\r
+ return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
+}\r
+\r
+/**\r
+ Process the Neighbor Solicitation message. The message may be sent for Duplicate\r
+ Address Detection or Address Resolution.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the message.\r
+ @param[in] Packet The content of the message with IP head removed.\r
+\r
+ @retval EFI_SUCCESS The packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborSolicit (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_INFORMATION_HEAD Icmp;\r
+ EFI_IPv6_ADDRESS Target;\r
+ IP6_ETHER_ADDR_OPTION LinkLayerOption;\r
+ BOOLEAN IsDAD;\r
+ BOOLEAN IsUnicast;\r
+ BOOLEAN IsMaintained;\r
+ IP6_DAD_ENTRY *DupAddrDetect;\r
+ IP6_INTERFACE *IpIf;\r
+ IP6_NEIGHBOR_ENTRY *Neighbor;\r
+ BOOLEAN Solicited;\r
+ BOOLEAN UpdateCache;\r
+ EFI_IPv6_ADDRESS Dest;\r
+ UINT16 OptionLen;\r
+ UINT8 *Option;\r
+ BOOLEAN Provided;\r
+ EFI_STATUS Status;\r
+ VOID *MacAddress;\r
+\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+ NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);\r
+\r
+ //\r
+ // Perform Message Validation:\r
+ // The IP Hop Limit field has a value of 255, i.e., the packet\r
+ // could not possibly have been forwarded by a router.\r
+ // ICMP Code is 0.\r
+ // Target Address is not a multicast address.\r
+ //\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // ICMP length is 24 or more octets.\r
+ //\r
+ OptionLen = 0;\r
+ if (Head->PayloadLength < IP6_ND_LENGTH) {\r
+ goto Exit;\r
+ } else {\r
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);\r
+ Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);\r
+\r
+ //\r
+ // All included options should have a length that is greater than zero.\r
+ //\r
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ IsDAD = NetIp6IsUnspecifiedAddr (&Head->SourceAddress);\r
+ IsUnicast = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress);\r
+ IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL);\r
+\r
+ Provided = FALSE;\r
+ if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {\r
+ NetbufCopy (\r
+ Packet,\r
+ IP6_ND_LENGTH,\r
+ sizeof (IP6_ETHER_ADDR_OPTION),\r
+ (UINT8 *) &LinkLayerOption\r
+ );\r
+ //\r
+ // The solicitation for neighbor discovery should include a source link-layer\r
+ // address option. If the option is not recognized, silently ignore it.\r
+ //\r
+ if (LinkLayerOption.Type == Ip6OptionEtherSource) {\r
+ if (IsDAD) {\r
+ //\r
+ // If the IP source address is the unspecified address, the source\r
+ // link-layer address option must not be included in the message.\r
+ //\r
+ goto Exit;\r
+ }\r
+\r
+ Provided = TRUE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If the IP source address is the unspecified address, the IP\r
+ // destination address is a solicited-node multicast address.\r
+ //\r
+ if (IsDAD && IsUnicast) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // If the target address is tentative, and the source address is a unicast address,\r
+ // the solicitation's sender is performing address resolution on the target;\r
+ // the solicitation should be silently ignored.\r
+ //\r
+ if (!IsDAD && !IsMaintained) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // If received unicast neighbor solicitation but destination is not this node,\r
+ // drop the packet.\r
+ //\r
+ if (IsUnicast && !IsMaintained) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // In DAD, when target address is a tentative address,\r
+ // process the received neighbor solicitation message but not send out response.\r
+ //\r
+ if (IsDAD && !IsMaintained) {\r
+ DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);\r
+ if (DupAddrDetect != NULL) {\r
+ if (DupAddrDetect->Transmit == 0) {\r
+ //\r
+ // The NS is from another node to performing DAD on the same address since\r
+ // we haven't send out any NS yet. Fail DAD for the tentative address.\r
+ //\r
+ Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);\r
+ Status = EFI_ICMP_ERROR;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Check the MAC address of the incoming packet.\r
+ //\r
+ if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress;\r
+ if (MacAddress != NULL) {\r
+ if (CompareMem (\r
+ MacAddress,\r
+ &IpSb->SnpMode.CurrentAddress,\r
+ IpSb->SnpMode.HwAddressSize\r
+ ) != 0) {\r
+ //\r
+ // The NS is from another node to performing DAD on the same address.\r
+ // Fail DAD for the tentative address.\r
+ //\r
+ Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);\r
+ Status = EFI_ICMP_ERROR;\r
+ } else {\r
+ //\r
+ // The below layer loopback the NS we sent. Record it and wait for more.\r
+ //\r
+ DupAddrDetect->Receive++;\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ }\r
+ }\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // If the solicitation does not contain a link-layer address, DO NOT create or\r
+ // update the neighbor cache entries.\r
+ //\r
+ if (Provided) {\r
+ Neighbor = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);\r
+ UpdateCache = FALSE;\r
+\r
+ if (Neighbor == NULL) {\r
+ Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL);\r
+ if (Neighbor == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+ UpdateCache = TRUE;\r
+ } else {\r
+ if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) {\r
+ UpdateCache = TRUE;\r
+ }\r
+ }\r
+\r
+ if (UpdateCache) {\r
+ Neighbor->State = EfiNeighborStale;\r
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+ //\r
+ // Send queued packets if exist.\r
+ //\r
+ Neighbor->CallBack ((VOID *) Neighbor);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Sends a Neighbor Advertisement as response.\r
+ // Set the Router flag to zero since the node is a host.\r
+ // If the source address of the solicitation is unspeicifed, and target address\r
+ // is one of the maintained address, reply a unsolicited multicast advertisement.\r
+ //\r
+ if (IsDAD && IsMaintained) {\r
+ Solicited = FALSE;\r
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest);\r
+ } else {\r
+ Solicited = TRUE;\r
+ IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress);\r
+ }\r
+\r
+ Status = Ip6SendNeighborAdvertise (\r
+ IpSb,\r
+ &Target,\r
+ &Dest,\r
+ &Target,\r
+ &IpSb->SnpMode.CurrentAddress,\r
+ FALSE,\r
+ TRUE,\r
+ Solicited\r
+ );\r
+Exit:\r
+ NetbufFree (Packet);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process the Neighbor Advertisement message.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the message.\r
+ @param[in] Packet The content of the message with IP head removed.\r
+\r
+ @retval EFI_SUCCESS The packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborAdvertise (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_INFORMATION_HEAD Icmp;\r
+ EFI_IPv6_ADDRESS Target;\r
+ IP6_ETHER_ADDR_OPTION LinkLayerOption;\r
+ BOOLEAN Provided;\r
+ INTN Compare;\r
+ IP6_NEIGHBOR_ENTRY *Neighbor;\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+ BOOLEAN Solicited;\r
+ BOOLEAN IsRouter;\r
+ BOOLEAN Override;\r
+ IP6_DAD_ENTRY *DupAddrDetect;\r
+ IP6_INTERFACE *IpIf;\r
+ UINT16 OptionLen;\r
+ UINT8 *Option;\r
+ EFI_STATUS Status;\r
+\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+ NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);\r
+\r
+ //\r
+ // Validate the incoming Neighbor Advertisement\r
+ //\r
+ Status = EFI_INVALID_PARAMETER;\r
+ //\r
+ // The IP Hop Limit field has a value of 255, i.e., the packet\r
+ // could not possibly have been forwarded by a router.\r
+ // ICMP Code is 0.\r
+ // Target Address is not a multicast address.\r
+ //\r
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // ICMP length is 24 or more octets.\r
+ //\r
+ Provided = FALSE;\r
+ OptionLen = 0;\r
+ if (Head->PayloadLength < IP6_ND_LENGTH) {\r
+ goto Exit;\r
+ } else {\r
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);\r
+ Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);\r
+\r
+ //\r
+ // All included options should have a length that is greater than zero.\r
+ //\r
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If the IP destination address is a multicast address, Solicited Flag is ZERO.\r
+ //\r
+ Solicited = FALSE;\r
+ if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) {\r
+ Solicited = TRUE;\r
+ }\r
+ if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // DAD - Check whether the Target is one of our tentative address.\r
+ //\r
+ DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);\r
+ if (DupAddrDetect != NULL) {\r
+ //\r
+ // DAD fails, some other node is using this address.\r
+ //\r
+ NetbufFree (Packet);\r
+ Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);\r
+ return EFI_ICMP_ERROR;\r
+ }\r
+\r
+ //\r
+ // Search the Neighbor Cache for the target's entry. If no entry exists,\r
+ // the advertisement should be silently discarded.\r
+ //\r
+ Neighbor = Ip6FindNeighborEntry (IpSb, &Target);\r
+ if (Neighbor == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Get IsRouter Flag and Override Flag\r
+ //\r
+ IsRouter = FALSE;\r
+ Override = FALSE;\r
+ if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) {\r
+ IsRouter = TRUE;\r
+ }\r
+ if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) {\r
+ Override = TRUE;\r
+ }\r
+\r
+ //\r
+ // Check whether link layer option is included.\r
+ //\r
+ if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {\r
+ NetbufCopy (\r
+ Packet,\r
+ IP6_ND_LENGTH,\r
+ sizeof (IP6_ETHER_ADDR_OPTION),\r
+ (UINT8 *) &LinkLayerOption\r
+ );\r
+\r
+ if (LinkLayerOption.Type == Ip6OptionEtherTarget) {\r
+ Provided = TRUE;\r
+ }\r
+ }\r
+\r
+ Compare = 0;\r
+ if (Provided) {\r
+ Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+ }\r
+\r
+ if (!Neighbor->IsRouter && IsRouter) {\r
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);\r
+ if (DefaultRouter != NULL) {\r
+ DefaultRouter->NeighborCache = Neighbor;\r
+ }\r
+ }\r
+\r
+ if (Neighbor->State == EfiNeighborInComplete) {\r
+ //\r
+ // If the target's Neighbor Cache entry is in INCOMPLETE state and no\r
+ // Target Link-Layer address option is included while link layer has\r
+ // address, the message should be silently discarded.\r
+ //\r
+ if (!Provided) {\r
+ goto Exit;\r
+ }\r
+ //\r
+ // Update the Neighbor Cache\r
+ //\r
+ CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+ if (Solicited) {\r
+ Neighbor->State = EfiNeighborReachable;\r
+ Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);\r
+ } else {\r
+ Neighbor->State = EfiNeighborStale;\r
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ //\r
+ // Send any packets queued for the neighbor awaiting address resolution.\r
+ //\r
+ Neighbor->CallBack ((VOID *) Neighbor);\r
+ }\r
+\r
+ Neighbor->IsRouter = IsRouter;\r
+\r
+ } else {\r
+ if (!Override && Compare != 0) {\r
+ //\r
+ // When the Override Flag is clear and supplied link-layer address differs from\r
+ // that in the cache, if the state of the entry is not REACHABLE, ignore the\r
+ // message. Otherwise set it to STALE but do not update the entry in any\r
+ // other way.\r
+ //\r
+ if (Neighbor->State == EfiNeighborReachable) {\r
+ Neighbor->State = EfiNeighborStale;\r
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ }\r
+ } else {\r
+ if (Compare != 0) {\r
+ CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);\r
+ }\r
+ //\r
+ // Update the entry's state\r
+ //\r
+ if (Solicited) {\r
+ Neighbor->State = EfiNeighborReachable;\r
+ Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);\r
+ } else {\r
+ if (Compare != 0) {\r
+ Neighbor->State = EfiNeighborStale;\r
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ }\r
+ }\r
+\r
+ //\r
+ // When IsRouter is changed from TRUE to FALSE, remove the router from the\r
+ // Default Router List and remove the Destination Cache entries for all destinations\r
+ // using the neighbor as a router.\r
+ //\r
+ if (Neighbor->IsRouter && !IsRouter) {\r
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);\r
+ if (DefaultRouter != NULL) {\r
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+ }\r
+ }\r
+\r
+ Neighbor->IsRouter = IsRouter;\r
+ }\r
+ }\r
+\r
+ if (Neighbor->State == EfiNeighborReachable) {\r
+ Neighbor->CallBack ((VOID *) Neighbor);\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+ NetbufFree (Packet);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process the Router Advertisement message according to RFC4861.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the message.\r
+ @param[in] Packet The content of the message with the IP head removed.\r
+\r
+ @retval EFI_SUCCESS The packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the\r
+ operation.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRouterAdvertise (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_INFORMATION_HEAD Icmp;\r
+ UINT32 ReachableTime;\r
+ UINT32 RetransTimer;\r
+ UINT16 RouterLifetime;\r
+ UINT16 Offset;\r
+ UINT8 Type;\r
+ UINT8 Length;\r
+ IP6_ETHER_ADDR_OPTION LinkLayerOption;\r
+ UINT32 Fourth;\r
+ UINT8 CurHopLimit;\r
+ BOOLEAN Mflag;\r
+ BOOLEAN Oflag;\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+ IP6_NEIGHBOR_ENTRY *NeighborCache;\r
+ EFI_MAC_ADDRESS LinkLayerAddress;\r
+ IP6_MTU_OPTION MTUOption;\r
+ IP6_PREFIX_INFO_OPTION PrefixOption;\r
+ IP6_PREFIX_LIST_ENTRY *PrefixList;\r
+ BOOLEAN OnLink;\r
+ BOOLEAN Autonomous;\r
+ EFI_IPv6_ADDRESS StatelessAddress;\r
+ EFI_STATUS Status;\r
+ UINT16 OptionLen;\r
+ UINT8 *Option;\r
+ INTN Result;\r
+\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) {\r
+ //\r
+ // Skip the process below as it's not required under the current policy.\r
+ //\r
+ goto Exit;\r
+ }\r
+\r
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
+\r
+ //\r
+ // Validate the incoming Router Advertisement\r
+ //\r
+\r
+ //\r
+ // The IP source address must be a link-local address\r
+ //\r
+ if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+ goto Exit;\r
+ }\r
+ //\r
+ // The IP Hop Limit field has a value of 255, i.e. the packet\r
+ // could not possibly have been forwarded by a router.\r
+ // ICMP Code is 0.\r
+ // ICMP length (derived from the IP length) is 16 or more octets.\r
+ //\r
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 ||\r
+ Head->PayloadLength < IP6_RA_LENGTH) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // All included options have a length that is greater than zero.\r
+ //\r
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH);\r
+ Option = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL);\r
+\r
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Process Fourth field.\r
+ // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag,\r
+ // and Router Lifetime (16 bit).\r
+ //\r
+\r
+ Fourth = NTOHL (Icmp.Fourth);\r
+ CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16));\r
+\r
+ //\r
+ // If the source address already in the default router list, update it.\r
+ // Otherwise create a new entry.\r
+ // A Lifetime of zero indicates that the router is not a default router.\r
+ //\r
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress);\r
+ if (DefaultRouter == NULL) {\r
+ if (RouterLifetime != 0) {\r
+ DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime);\r
+ if (DefaultRouter == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+ }\r
+ } else {\r
+ if (RouterLifetime != 0) {\r
+ DefaultRouter->Lifetime = RouterLifetime;\r
+ //\r
+ // Check the corresponding neighbor cache entry here.\r
+ //\r
+ if (DefaultRouter->NeighborCache == NULL) {\r
+ DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);\r
+ }\r
+ } else {\r
+ //\r
+ // If the address is in the host's default router list and the router lifetime is zero,\r
+ // immediately time-out the entry.\r
+ //\r
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+ }\r
+ }\r
+\r
+ CurHopLimit = *((UINT8 *) &Fourth + 3);\r
+ if (CurHopLimit != 0) {\r
+ IpSb->CurHopLimit = CurHopLimit;\r
+ }\r
+\r
+ Mflag = FALSE;\r
+ Oflag = FALSE;\r
+ if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) {\r
+ Mflag = TRUE;\r
+ } else {\r
+ if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) {\r
+ Oflag = TRUE;\r
+ }\r
+ }\r
+\r
+ if (Mflag || Oflag) {\r
+ //\r
+ // Use Ip6Config to get available addresses or other configuration from DHCP.\r
+ //\r
+ Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag);\r
+ }\r
+\r
+ //\r
+ // Process Reachable Time and Retrans Timer fields.\r
+ //\r
+ NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime);\r
+ NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer);\r
+ ReachableTime = NTOHL (ReachableTime);\r
+ RetransTimer = NTOHL (RetransTimer);\r
+\r
+ if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) {\r
+ //\r
+ // If new value is not unspecified and differs from the previous one, record it\r
+ // in BaseReachableTime and recompute a ReachableTime.\r
+ //\r
+ IpSb->BaseReachableTime = ReachableTime;\r
+ Ip6UpdateReachableTime (IpSb);\r
+ }\r
+\r
+ if (RetransTimer != 0) {\r
+ IpSb->RetransTimer = RetransTimer;\r
+ }\r
+\r
+ //\r
+ // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists.\r
+ //\r
+ NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);\r
+ if (NeighborCache != NULL) {\r
+ NeighborCache->IsRouter = TRUE;\r
+ }\r
+\r
+ //\r
+ // If an valid router advertisment is received, stops router solicitation.\r
+ //\r
+ IpSb->RouterAdvertiseReceived = TRUE;\r
+\r
+ //\r
+ // The only defined options that may appear are the Source\r
+ // Link-Layer Address, Prefix information and MTU options.\r
+ // All included options have a length that is greater than zero.\r
+ //\r
+ Offset = 16;\r
+ while (Offset < Head->PayloadLength) {\r
+ NetbufCopy (Packet, Offset, sizeof (UINT8), &Type);\r
+ switch (Type) {\r
+ case Ip6OptionEtherSource:\r
+ //\r
+ // Update the neighbor cache\r
+ //\r
+ NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption);\r
+ if (LinkLayerOption.Length <= 0) {\r
+ goto Exit;\r
+ }\r
+\r
+ ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS));\r
+ CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6);\r
+\r
+ if (NeighborCache == NULL) {\r
+ NeighborCache = Ip6CreateNeighborEntry (\r
+ IpSb,\r
+ Ip6OnArpResolved,\r
+ &Head->SourceAddress,\r
+ &LinkLayerAddress\r
+ );\r
+ if (NeighborCache == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+ NeighborCache->IsRouter = TRUE;\r
+ NeighborCache->State = EfiNeighborStale;\r
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ } else {\r
+ Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6);\r
+\r
+ //\r
+ // If the link-local address is the same as that already in the cache,\r
+ // the cache entry's state remains unchanged. Otherwise update the\r
+ // reachability state to STALE.\r
+ //\r
+ if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {\r
+ CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6);\r
+\r
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+\r
+ if (NeighborCache->State == EfiNeighborInComplete) {\r
+ //\r
+ // Send queued packets if exist.\r
+ //\r
+ NeighborCache->State = EfiNeighborStale;\r
+ NeighborCache->CallBack ((VOID *) NeighborCache);\r
+ } else {\r
+ NeighborCache->State = EfiNeighborStale;\r
+ }\r
+ }\r
+ }\r
+\r
+ Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8);\r
+ break;\r
+ case Ip6OptionPrefixInfo:\r
+ NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption);\r
+ if (PrefixOption.Length != 4) {\r
+ goto Exit;\r
+ }\r
+ PrefixOption.ValidLifetime = NTOHL (PrefixOption.ValidLifetime);\r
+ PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime);\r
+\r
+ //\r
+ // Get L and A flag, recorded in the lower 2 bits of Reserved1\r
+ //\r
+ OnLink = FALSE;\r
+ if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) {\r
+ OnLink = TRUE;\r
+ }\r
+ Autonomous = FALSE;\r
+ if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) {\r
+ Autonomous = TRUE;\r
+ }\r
+\r
+ //\r
+ // If the prefix is the link-local prefix, silently ignore the prefix option.\r
+ //\r
+ if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH &&\r
+ NetIp6IsLinkLocalAddr (&PrefixOption.Prefix)\r
+ ) {\r
+ Offset += sizeof (IP6_PREFIX_INFO_OPTION);\r
+ break;\r
+ }\r
+ //\r
+ // Do following if on-link flag is set according to RFC4861.\r
+ //\r
+ if (OnLink) {\r
+ PrefixList = Ip6FindPrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ PrefixOption.PrefixLength,\r
+ &PrefixOption.Prefix\r
+ );\r
+ //\r
+ // Create a new entry for the prefix, if the ValidLifetime is zero,\r
+ // silently ignore the prefix option.\r
+ //\r
+ if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) {\r
+ PrefixList = Ip6CreatePrefixListEntry (\r
+ IpSb,\r
+ TRUE,\r
+ PrefixOption.ValidLifetime,\r
+ PrefixOption.PreferredLifetime,\r
+ PrefixOption.PrefixLength,\r
+ &PrefixOption.Prefix\r
+ );\r
+ if (PrefixList == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+ } else if (PrefixList != NULL) {\r
+ if (PrefixOption.ValidLifetime != 0) {\r
+ PrefixList->ValidLifetime = PrefixOption.ValidLifetime;\r
+ } else {\r
+ //\r
+ // If the prefix exists and incoming ValidLifetime is zero, immediately\r
+ // remove the prefix.\r
+ Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Do following if Autonomous flag is set according to RFC4862.\r
+ //\r
+ if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) {\r
+ PrefixList = Ip6FindPrefixListEntry (\r
+ IpSb,\r
+ FALSE,\r
+ PrefixOption.PrefixLength,\r
+ &PrefixOption.Prefix\r
+ );\r
+ //\r
+ // Create a new entry for the prefix, and form an address by prefix + interface id\r
+ // If the sum of the prefix length and interface identifier length\r
+ // does not equal 128 bits, the Prefix Information option MUST be ignored.\r
+ //\r
+ if (PrefixList == NULL &&\r
+ PrefixOption.ValidLifetime != 0 &&\r
+ PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128\r
+ ) {\r
+ //\r
+ // Form the address in network order.\r
+ //\r
+ CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64));\r
+ CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64));\r
+\r
+ //\r
+ // If the address is not yet in the assigned address list, adds it into.\r
+ //\r
+ if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) {\r
+ //\r
+ // And also not in the DAD process, check its uniqeness firstly.\r
+ //\r
+ if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) {\r
+ Status = Ip6SetAddress (\r
+ IpSb->DefaultInterface,\r
+ &StatelessAddress,\r
+ FALSE,\r
+ PrefixOption.PrefixLength,\r
+ PrefixOption.ValidLifetime,\r
+ PrefixOption.PreferredLifetime,\r
+ NULL,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Adds the prefix option to stateless prefix option list.\r
+ //\r
+ PrefixList = Ip6CreatePrefixListEntry (\r
+ IpSb,\r
+ FALSE,\r
+ PrefixOption.ValidLifetime,\r
+ PrefixOption.PreferredLifetime,\r
+ PrefixOption.PrefixLength,\r
+ &PrefixOption.Prefix\r
+ );\r
+ if (PrefixList == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+ } else if (PrefixList != NULL) {\r
+\r
+ //\r
+ // Reset the preferred lifetime of the address if the advertised prefix exists.\r
+ // Perform specific action to valid lifetime together.\r
+ //\r
+ PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime;\r
+ if ((PrefixOption.ValidLifetime > 7200) ||\r
+ (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) {\r
+ //\r
+ // If the received Valid Lifetime is greater than 2 hours or\r
+ // greater than RemainingLifetime, set the valid lifetime of the\r
+ // corresponding address to the advertised Valid Lifetime.\r
+ //\r
+ PrefixList->ValidLifetime = PrefixOption.ValidLifetime;\r
+\r
+ } else if (PrefixList->ValidLifetime <= 7200) {\r
+ //\r
+ // If RemainingLifetime is less than or equls to 2 hours, ignore the\r
+ // Prefix Information option with regards to the valid lifetime.\r
+ // TODO: If this option has been authenticated, set the valid lifetime.\r
+ //\r
+ } else {\r
+ //\r
+ // Otherwise, reset the valid lifetime of the corresponding\r
+ // address to 2 hours.\r
+ //\r
+ PrefixList->ValidLifetime = 7200;\r
+ }\r
+ }\r
+ }\r
+\r
+ Offset += sizeof (IP6_PREFIX_INFO_OPTION);\r
+ break;\r
+ case Ip6OptionMtu:\r
+ NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption);\r
+ if (MTUOption.Length != 1) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order\r
+ // to omit implementation of Path MTU Discovery. Thus ignore the MTU option\r
+ // in Router Advertisement.\r
+ //\r
+\r
+ Offset += sizeof (IP6_MTU_OPTION);\r
+ break;\r
+ default:\r
+ //\r
+ // Silently ignore unrecognized options\r
+ //\r
+ NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length);\r
+ if (Length <= 0) {\r
+ goto Exit;\r
+ }\r
+\r
+ Offset = (UINT16) (Offset + (UINT16) Length * 8);\r
+ break;\r
+ }\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+ NetbufFree (Packet);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process the ICMPv6 redirect message. Find the instance, then update\r
+ its route cache.\r
+\r
+ @param[in] IpSb The IP6 service binding instance that received\r
+ the packet.\r
+ @param[in] Head The IP head of the received ICMPv6 packet.\r
+ @param[in] Packet The content of the ICMPv6 redirect packet with\r
+ the IP head removed.\r
+\r
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS Successfully updated the route caches.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRedirect (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ IP6_ICMP_INFORMATION_HEAD *Icmp;\r
+ EFI_IPv6_ADDRESS *Target;\r
+ EFI_IPv6_ADDRESS *IcmpDest;\r
+ UINT8 *Option;\r
+ UINT16 OptionLen;\r
+ IP6_ROUTE_ENTRY *RouteEntry;\r
+ IP6_ROUTE_CACHE_ENTRY *RouteCache;\r
+ IP6_NEIGHBOR_ENTRY *NeighborCache;\r
+ INT32 Length;\r
+ UINT8 OptLen;\r
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;\r
+ EFI_MAC_ADDRESS Mac;\r
+ UINT32 Index;\r
+ BOOLEAN IsRouter;\r
+ EFI_STATUS Status;\r
+ INTN Result;\r
+\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+ if (Icmp == NULL) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Validate the incoming Redirect message\r
+ //\r
+\r
+ //\r
+ // The IP Hop Limit field has a value of 255, i.e. the packet\r
+ // could not possibly have been forwarded by a router.\r
+ // ICMP Code is 0.\r
+ // ICMP length (derived from the IP length) is 40 or more octets.\r
+ //\r
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 ||\r
+ Head->PayloadLength < IP6_REDITECT_LENGTH) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // The IP source address must be a link-local address\r
+ //\r
+ if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // The dest of this ICMP redirect message is not us.\r
+ //\r
+ if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // All included options have a length that is greater than zero.\r
+ //\r
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH);\r
+ Option = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL);\r
+\r
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Target = (EFI_IPv6_ADDRESS *) (Icmp + 1);\r
+ IcmpDest = Target + 1;\r
+\r
+ //\r
+ // The ICMP Destination Address field in the redirect message does not contain\r
+ // a multicast address.\r
+ //\r
+ if (IP6_IS_MULTICAST (IcmpDest)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // The ICMP Target Address is either a link-local address (when redirected to\r
+ // a router) or the same as the ICMP Destination Address (when redirected to\r
+ // the on-link destination).\r
+ //\r
+ IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest);\r
+ if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Check the options. The only interested option here is the target-link layer\r
+ // address option.\r
+ //\r
+ Length = Packet->TotalSize - 40;\r
+ Option = (UINT8 *) (IcmpDest + 1);\r
+ LinkLayerOption = NULL;\r
+ while (Length > 0) {\r
+ switch (*Option) {\r
+ case Ip6OptionEtherTarget:\r
+\r
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option;\r
+ OptLen = LinkLayerOption->Length;\r
+ if (OptLen != 1) {\r
+ //\r
+ // For ethernet, the length must be 1.\r
+ //\r
+ goto Exit;\r
+ }\r
+ break;\r
+\r
+ default:\r
+\r
+ OptLen = *(Option + 1);\r
+ if (OptLen == 0) {\r
+ //\r
+ // A length of 0 is invalid.\r
+ //\r
+ goto Exit;\r
+ }\r
+ break;\r
+ }\r
+\r
+ Length -= 8 * OptLen;\r
+ Option += 8 * OptLen;\r
+ }\r
+\r
+ if (Length != 0) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // The IP source address of the Redirect is the same as the current\r
+ // first-hop router for the specified ICMP Destination Address.\r
+ //\r
+ RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress);\r
+ if (RouteCache != NULL) {\r
+ if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) {\r
+ //\r
+ // The source of this Redirect message must match the NextHop of the\r
+ // corresponding route cache entry.\r
+ //\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Update the NextHop.\r
+ //\r
+ IP6_COPY_ADDRESS (&RouteCache->NextHop, Target);\r
+\r
+ if (!IsRouter) {\r
+ RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag;\r
+ RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE;\r
+ }\r
+\r
+ } else {\r
+ //\r
+ // Get the Route Entry.\r
+ //\r
+ RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL);\r
+ if (RouteEntry == NULL) {\r
+ RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL);\r
+ if (RouteEntry == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ if (!IsRouter) {\r
+ RouteEntry->Flag = IP6_DIRECT_ROUTE;\r
+ }\r
+\r
+ //\r
+ // Create a route cache for this.\r
+ //\r
+ RouteCache = Ip6CreateRouteCacheEntry (\r
+ IcmpDest,\r
+ &Head->DestinationAddress,\r
+ Target,\r
+ (UINTN) RouteEntry\r
+ );\r
+ if (RouteCache == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Insert the newly created route cache entry.\r
+ //\r
+ Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress);\r
+ InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link);\r
+ }\r
+\r
+ //\r
+ // Try to locate the neighbor cache for the Target.\r
+ //\r
+ NeighborCache = Ip6FindNeighborEntry (IpSb, Target);\r
+\r
+ if (LinkLayerOption != NULL) {\r
+ if (NeighborCache == NULL) {\r
+ //\r
+ // Create a neighbor cache for the Target.\r
+ //\r
+ ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS));\r
+ CopyMem (&Mac, LinkLayerOption->EtherAddr, 6);\r
+ NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac);\r
+ if (NeighborCache == NULL) {\r
+ //\r
+ // Just report a success here. The neighbor cache can be created in\r
+ // some other place.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ goto Exit;\r
+ }\r
+\r
+ NeighborCache->State = EfiNeighborStale;\r
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ } else {\r
+ Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6);\r
+\r
+ //\r
+ // If the link-local address is the same as that already in the cache,\r
+ // the cache entry's state remains unchanged. Otherwise update the\r
+ // reachability state to STALE.\r
+ //\r
+ if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {\r
+ CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6);\r
+\r
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+\r
+ if (NeighborCache->State == EfiNeighborInComplete) {\r
+ //\r
+ // Send queued packets if exist.\r
+ //\r
+ NeighborCache->State = EfiNeighborStale;\r
+ NeighborCache->CallBack ((VOID *) NeighborCache);\r
+ } else {\r
+ NeighborCache->State = EfiNeighborStale;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if (NeighborCache != NULL && IsRouter) {\r
+ //\r
+ // The Target is a router, set IsRouter to TRUE.\r
+ //\r
+ NeighborCache->IsRouter = TRUE;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+ NetbufFree (Packet);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.\r
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor\r
+ cache. It will be deleted after Timeout. A value of zero means that\r
+ the entry is permanent. A non-zero value means that the entry is\r
+ dynamic.\r
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will\r
+ be overridden and updated; if FALSE, and if a\r
+ corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+ will be returned.\r
+\r
+ @retval EFI_SUCCESS The neighbor cache entry has been added.\r
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache\r
+ due to insufficient resources.\r
+ @retval EFI_NOT_FOUND TargetLinkAddress is NULL.\r
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,\r
+ and that entry is tagged as un-overridden (when DeleteFlag\r
+ is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddNeighbor (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,\r
+ IN UINT32 Timeout,\r
+ IN BOOLEAN Override\r
+ )\r
+{\r
+ IP6_NEIGHBOR_ENTRY *Neighbor;\r
+\r
+ Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);\r
+ if (Neighbor != NULL) {\r
+ if (!Override) {\r
+ return EFI_ACCESS_DENIED;\r
+ } else {\r
+ if (TargetLinkAddress != NULL) {\r
+ IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress);\r
+ }\r
+ }\r
+ } else {\r
+ if (TargetLinkAddress == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress);\r
+ if (Neighbor == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ Neighbor->State = EfiNeighborReachable;\r
+\r
+ if (Timeout != 0) {\r
+ Neighbor->Ticks = IP6_GET_TICKS (Timeout / TICKS_PER_MS);\r
+ Neighbor->Dynamic = TRUE;\r
+ } else {\r
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.\r
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor\r
+ cache. It will be deleted after Timeout. A value of zero means that\r
+ the entry is permanent. A non-zero value means that the entry is\r
+ dynamic.\r
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will\r
+ be overridden and updated; if FALSE, and if a\r
+ corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+ will be returned.\r
+\r
+ @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted.\r
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelNeighbor (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,\r
+ IN UINT32 Timeout,\r
+ IN BOOLEAN Override\r
+ )\r
+{\r
+ IP6_NEIGHBOR_ENTRY *Neighbor;\r
+\r
+ Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);\r
+ if (Neighbor == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ RemoveEntryList (&Neighbor->Link);\r
+ FreePool (Neighbor);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.\r
+ This time routine handles DAD module and neighbor state transition.\r
+ It is also responsible for sending out router solicitations.\r
+\r
+ @param[in] Event The IP6 service instance's heartbeat timer.\r
+ @param[in] Context The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6NdFasterTimerTicking (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ LIST_ENTRY *Entry2;\r
+ IP6_INTERFACE *IpIf;\r
+ IP6_DELAY_JOIN_LIST *DelayNode;\r
+ EFI_IPv6_ADDRESS Source;\r
+ IP6_DAD_ENTRY *DupAddrDetect;\r
+ EFI_STATUS Status;\r
+ IP6_NEIGHBOR_ENTRY *NeighborCache;\r
+ EFI_IPv6_ADDRESS Destination;\r
+ IP6_SERVICE *IpSb;\r
+ BOOLEAN Flag;\r
+\r
+ IpSb = (IP6_SERVICE *) Context;\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ //\r
+ // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router\r
+ // Solicitation messages, each separated by at least\r
+ // RTR_SOLICITATION_INTERVAL (4) seconds.\r
+ //\r
+ if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) &&\r
+ !IpSb->RouterAdvertiseReceived &&\r
+ IpSb->SolicitTimer > 0\r
+ ) {\r
+ if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) {\r
+ Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL);\r
+ if (!EFI_ERROR (Status)) {\r
+ IpSb->SolicitTimer--;\r
+ IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL);\r
+ }\r
+ }\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+ //\r
+ // Process the delay list to join the solicited-node multicast address.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) {\r
+ DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link);\r
+ if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) {\r
+ //\r
+ // The timer expires, init the duplicate address detection.\r
+ //\r
+ Ip6InitDADProcess (\r
+ DelayNode->Interface,\r
+ DelayNode->AddressInfo,\r
+ DelayNode->DadCallback,\r
+ DelayNode->Context\r
+ );\r
+\r
+ //\r
+ // Remove the delay node\r
+ //\r
+ RemoveEntryList (&DelayNode->Link);\r
+ FreePool (DelayNode);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Process the duplicate address detection list.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {\r
+ DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link);\r
+\r
+ if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) {\r
+ //\r
+ // The timer expires, check the remaining transmit counts.\r
+ //\r
+ if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) {\r
+ //\r
+ // Send the Neighbor Solicitation message with\r
+ // Source - unspecified address, destination - solicited-node multicast address\r
+ // Target - the address to be validated\r
+ //\r
+ Status = Ip6SendNeighborSolicit (\r
+ IpSb,\r
+ NULL,\r
+ &DupAddrDetect->Destination,\r
+ &DupAddrDetect->AddressInfo->Address,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ DupAddrDetect->Transmit++;\r
+ DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer);\r
+ } else {\r
+ //\r
+ // All required solicitation has been sent out, and the RetransTime after the last\r
+ // Neighbor Solicit is elapsed, finish the DAD process.\r
+ //\r
+ Flag = FALSE;\r
+ if ((DupAddrDetect->Receive == 0) ||\r
+ (DupAddrDetect->Transmit == DupAddrDetect->Receive)) {\r
+ Flag = TRUE;\r
+ }\r
+\r
+ Ip6OnDADFinished (Flag, IpIf, DupAddrDetect);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Polling the state of Neighbor cache\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {\r
+ NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);\r
+\r
+ switch (NeighborCache->State) {\r
+ case EfiNeighborInComplete:\r
+ if (NeighborCache->Ticks > 0) {\r
+ --NeighborCache->Ticks;\r
+ }\r
+\r
+ //\r
+ // Retransmit Neighbor Solicitation messages approximately every\r
+ // RetransTimer milliseconds while awaiting a response.\r
+ //\r
+ if (NeighborCache->Ticks == 0) {\r
+ if (NeighborCache->Transmit > 1) {\r
+ //\r
+ // Send out multicast neighbor solicitation for address resolution.\r
+ // After last neighbor solicitation message has been sent out, wait\r
+ // for RetransTimer and then remove entry if no response is received.\r
+ //\r
+ Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);\r
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ Status = Ip6SendNeighborSolicit (\r
+ IpSb,\r
+ &Source,\r
+ &Destination,\r
+ &NeighborCache->Neighbor,\r
+ &IpSb->SnpMode.CurrentAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Update the retransmit times.\r
+ //\r
+ if (NeighborCache->Transmit > 0) {\r
+ --NeighborCache->Transmit;\r
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);\r
+ }\r
+ }\r
+\r
+ if (NeighborCache->Transmit == 0) {\r
+ //\r
+ // Timeout, send ICMP destination unreachable packet and then remove entry\r
+ //\r
+ Status = Ip6FreeNeighborEntry (\r
+ IpSb,\r
+ NeighborCache,\r
+ TRUE,\r
+ TRUE,\r
+ EFI_ICMP_ERROR,\r
+ NULL,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ case EfiNeighborReachable:\r
+ //\r
+ // This entry is inserted by EfiIp6Neighbors() as static entry\r
+ // and will not timeout.\r
+ //\r
+ if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) {\r
+ break;\r
+ }\r
+\r
+ if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {\r
+ if (NeighborCache->Dynamic) {\r
+ //\r
+ // This entry is inserted by EfiIp6Neighbors() as dynamic entry\r
+ // and will be deleted after timeout.\r
+ //\r
+ Status = Ip6FreeNeighborEntry (\r
+ IpSb,\r
+ NeighborCache,\r
+ FALSE,\r
+ TRUE,\r
+ EFI_TIMEOUT,\r
+ NULL,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+ } else {\r
+ NeighborCache->State = EfiNeighborStale;\r
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ case EfiNeighborDelay:\r
+ if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {\r
+\r
+ NeighborCache->State = EfiNeighborProbe;\r
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);\r
+ NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1;\r
+ //\r
+ // Send out unicast neighbor solicitation for Neighbor Unreachability Detection\r
+ //\r
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ Status = Ip6SendNeighborSolicit (\r
+ IpSb,\r
+ &Source,\r
+ &NeighborCache->Neighbor,\r
+ &NeighborCache->Neighbor,\r
+ &IpSb->SnpMode.CurrentAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ NeighborCache->Transmit--;\r
+ }\r
+\r
+ break;\r
+\r
+ case EfiNeighborProbe:\r
+ if (NeighborCache->Ticks > 0) {\r
+ --NeighborCache->Ticks;\r
+ }\r
+\r
+ //\r
+ // Retransmit Neighbor Solicitation messages approximately every\r
+ // RetransTimer milliseconds while awaiting a response.\r
+ //\r
+ if (NeighborCache->Ticks == 0) {\r
+ if (NeighborCache->Transmit > 1) {\r
+ //\r
+ // Send out unicast neighbor solicitation for Neighbor Unreachability\r
+ // Detection. After last neighbor solicitation message has been sent out,\r
+ // wait for RetransTimer and then remove entry if no response is received.\r
+ //\r
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ Status = Ip6SendNeighborSolicit (\r
+ IpSb,\r
+ &Source,\r
+ &NeighborCache->Neighbor,\r
+ &NeighborCache->Neighbor,\r
+ &IpSb->SnpMode.CurrentAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Update the retransmit times.\r
+ //\r
+ if (NeighborCache->Transmit > 0) {\r
+ --NeighborCache->Transmit;\r
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);\r
+ }\r
+ }\r
+\r
+ if (NeighborCache->Transmit == 0) {\r
+ //\r
+ // Delete the neighbor entry.\r
+ //\r
+ Status = Ip6FreeNeighborEntry (\r
+ IpSb,\r
+ NeighborCache,\r
+ FALSE,\r
+ TRUE,\r
+ EFI_TIMEOUT,\r
+ NULL,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ The heartbeat timer of ND module in 1 second. This time routine handles following\r
+ things: 1) maitain default router list; 2) maintain prefix options;\r
+ 3) maintain route caches.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6NdTimerTicking (\r
+ IN IP6_SERVICE *IpSb\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_DEFAULT_ROUTER *DefaultRouter;\r
+ IP6_PREFIX_LIST_ENTRY *PrefixOption;\r
+ UINT8 Index;\r
+ IP6_ROUTE_CACHE_ENTRY *RouteCache;\r
+\r
+ //\r
+ // Decrease the lifetime of default router, if expires remove it from default router list.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) {\r
+ DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);\r
+ if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) {\r
+ if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) {\r
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) {\r
+ PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+ if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {\r
+ if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) {\r
+ if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) &&\r
+ (PrefixOption->PreferredLifetime > 0)\r
+ ) {\r
+ --PrefixOption->PreferredLifetime;\r
+ }\r
+ } else {\r
+ Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE);\r
+ }\r
+ }\r
+ }\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) {\r
+ PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+ if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {\r
+ if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) {\r
+ Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries.\r
+ // Remove the entries at the tail of the bucket. These entries\r
+ // are likely to be used least.\r
+ // Reclaim frequency is set to 1 second.\r
+ //\r
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+ while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) {\r
+ Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]);\r
+ if (Entry == NULL) {\r
+ break;\r
+ }\r
+\r
+ RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+ Ip6FreeRouteCacheEntry (RouteCache);\r
+ ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0);\r
+ IpSb->RouteTable->Cache.CacheNum[Index]--;\r
+ }\r
+ }\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Definition of Neighbor Discovery support routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_ND_H__\r
+#define __EFI_IP6_ND_H__\r
+\r
+#define IP6_GET_TICKS(Ms) (((Ms) + IP6_TIMER_INTERVAL_IN_MS - 1) / IP6_TIMER_INTERVAL_IN_MS)\r
+\r
+enum {\r
+ IP6_INF_ROUTER_LIFETIME = 0xFFFF,\r
+\r
+ IP6_MAX_RTR_SOLICITATION_DELAY = 1000, ///< 1000 milliseconds\r
+ IP6_MAX_RTR_SOLICITATIONS = 3,\r
+ IP6_RTR_SOLICITATION_INTERVAL = 4000,\r
+\r
+ IP6_MIN_RANDOM_FACTOR_SCALED = 1,\r
+ IP6_MAX_RANDOM_FACTOR_SCALED = 3,\r
+ IP6_RANDOM_FACTOR_SCALE = 2,\r
+\r
+ IP6_MAX_MULTICAST_SOLICIT = 3,\r
+ IP6_MAX_UNICAST_SOLICIT = 3,\r
+ IP6_MAX_ANYCAST_DELAY_TIME = 1,\r
+ IP6_MAX_NEIGHBOR_ADV = 3,\r
+ IP6_REACHABLE_TIME = 30000,\r
+ IP6_RETRANS_TIMER = 1000,\r
+ IP6_DELAY_FIRST_PROBE_TIME = 5000,\r
+\r
+ IP6_MIN_LINK_MTU = 1280,\r
+ IP6_MAX_LINK_MTU = 1500,\r
+\r
+ IP6_IS_ROUTER_FLAG = 0x80,\r
+ IP6_SOLICITED_FLAG = 0x40,\r
+ IP6_OVERRIDE_FLAG = 0x20,\r
+\r
+ IP6_M_ADDR_CONFIG_FLAG = 0x80,\r
+ IP6_O_CONFIG_FLAG = 0x40,\r
+\r
+ IP6_ON_LINK_FLAG = 0x80,\r
+ IP6_AUTO_CONFIG_FLAG = 0x40,\r
+\r
+ IP6_ND_LENGTH = 24,\r
+ IP6_RA_LENGTH = 16,\r
+ IP6_REDITECT_LENGTH = 40,\r
+ IP6_DAD_ENTRY_SIGNATURE = SIGNATURE_32 ('I', 'P', 'D', 'E')\r
+};\r
+\r
+typedef\r
+VOID\r
+(*IP6_ARP_CALLBACK) (\r
+ VOID *Context\r
+ );\r
+\r
+typedef struct _IP6_ETHE_ADDR_OPTION {\r
+ UINT8 Type;\r
+ UINT8 Length;\r
+ UINT8 EtherAddr[6];\r
+} IP6_ETHER_ADDR_OPTION;\r
+\r
+typedef struct _IP6_MTU_OPTION {\r
+ UINT8 Type;\r
+ UINT8 Length;\r
+ UINT16 Reserved;\r
+ UINT32 Mtu;\r
+} IP6_MTU_OPTION;\r
+\r
+typedef struct _IP6_PREFIX_INFO_OPTION {\r
+ UINT8 Type;\r
+ UINT8 Length;\r
+ UINT8 PrefixLength;\r
+ UINT8 Reserved1;\r
+ UINT32 ValidLifetime;\r
+ UINT32 PreferredLifetime;\r
+ UINT32 Reserved2;\r
+ EFI_IPv6_ADDRESS Prefix;\r
+} IP6_PREFIX_INFO_OPTION;\r
+\r
+typedef\r
+VOID\r
+(*IP6_DAD_CALLBACK) (\r
+ IN BOOLEAN IsDadPassed,\r
+ IN EFI_IPv6_ADDRESS *TargetAddress,\r
+ IN VOID *Context\r
+ );\r
+\r
+typedef struct _IP6_DAD_ENTRY {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+ UINT32 MaxTransmit;\r
+ UINT32 Transmit;\r
+ UINT32 Receive;\r
+ UINT32 RetransTick;\r
+ IP6_ADDRESS_INFO *AddressInfo;\r
+ EFI_IPv6_ADDRESS Destination;\r
+ IP6_DAD_CALLBACK Callback;\r
+ VOID *Context;\r
+} IP6_DAD_ENTRY;\r
+\r
+typedef struct _IP6_DELAY_JOIN_LIST {\r
+ LIST_ENTRY Link;\r
+ UINT32 DelayTime; ///< in tick per 50 milliseconds\r
+ IP6_INTERFACE *Interface;\r
+ IP6_ADDRESS_INFO *AddressInfo;\r
+ IP6_DAD_CALLBACK DadCallback;\r
+ VOID *Context;\r
+} IP6_DELAY_JOIN_LIST;\r
+\r
+typedef struct _IP6_NEIGHBOR_ENTRY {\r
+ LIST_ENTRY Link;\r
+ LIST_ENTRY ArpList;\r
+ INTN RefCnt;\r
+ BOOLEAN IsRouter;\r
+ BOOLEAN ArpFree;\r
+ BOOLEAN Dynamic;\r
+ EFI_IPv6_ADDRESS Neighbor;\r
+ EFI_MAC_ADDRESS LinkAddress;\r
+ EFI_IP6_NEIGHBOR_STATE State;\r
+ UINT32 Transmit;\r
+ UINT32 Ticks;\r
+\r
+ LIST_ENTRY Frames;\r
+ IP6_INTERFACE *Interface;\r
+ IP6_ARP_CALLBACK CallBack;\r
+} IP6_NEIGHBOR_ENTRY;\r
+\r
+typedef struct _IP6_DEFAULT_ROUTER {\r
+ LIST_ENTRY Link;\r
+ INTN RefCnt;\r
+ UINT16 Lifetime;\r
+ EFI_IPv6_ADDRESS Router;\r
+ IP6_NEIGHBOR_ENTRY *NeighborCache;\r
+} IP6_DEFAULT_ROUTER;\r
+\r
+typedef struct _IP6_PREFIX_LIST_ENTRY {\r
+ LIST_ENTRY Link;\r
+ INTN RefCnt;\r
+ UINT32 ValidLifetime;\r
+ UINT32 PreferredLifetime;\r
+ UINT8 PrefixLength;\r
+ EFI_IPv6_ADDRESS Prefix;\r
+} IP6_PREFIX_LIST_ENTRY;\r
+\r
+/**\r
+ Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number\r
+ of EFI_IP6_NEIGHBOR_CACHE is also returned.\r
+\r
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.\r
+ @param[out] NeighborCount The number of returned neighbor cache entries.\r
+ @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.\r
+\r
+ @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiNeighborCache (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ OUT UINT32 *NeighborCount,\r
+ OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache\r
+ );\r
+\r
+/**\r
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number\r
+ of prefix entries is also returned.\r
+\r
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.\r
+ @param[out] PrefixCount The number of returned prefix entries.\r
+ @param[out] PrefixTable The pointer to the array of PrefixTable.\r
+\r
+ @retval EFI_SUCCESS The prefix table successfully built.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildPrefixTable (\r
+ IN IP6_PROTOCOL *IpInstance,\r
+ OUT UINT32 *PrefixCount,\r
+ OUT EFI_IP6_ADDRESS_INFO **PrefixTable\r
+ );\r
+\r
+/**\r
+ Allocate and initialize an IP6 default router entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Ip6Address The IPv6 address of the default router.\r
+ @param[in] RouterLifetime The lifetime associated with the default\r
+ router, in units of seconds.\r
+\r
+ @return NULL if it failed to allocate memory for the default router node.\r
+ Otherwise, point to the created default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6CreateDefaultRouter (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address,\r
+ IN UINT16 RouterLifetime\r
+ );\r
+\r
+/**\r
+ Destroy an IP6 default router entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyDefaultRouter (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_DEFAULT_ROUTER *DefaultRouter\r
+ );\r
+\r
+/**\r
+ Clean an IP6 default router list.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.\r
+\r
+**/\r
+VOID\r
+Ip6CleanDefaultRouterList (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ Search a default router node from an IP6 default router list.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Ip6Address The IPv6 address of the to be searched default router node.\r
+\r
+ @return NULL if it failed to find the matching default router node.\r
+ Otherwise, point to the found default router node.\r
+\r
+**/\r
+IP6_DEFAULT_ROUTER *\r
+Ip6FindDefaultRouter (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address\r
+ );\r
+\r
+/**\r
+ The function to be called after DAD (Duplicate Address Detection) is performed.\r
+\r
+ @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.\r
+ @param[in] IpIf Points to the IP6_INTERFACE.\r
+ @param[in] DadEntry The DAD entry which already performed DAD.\r
+\r
+**/\r
+VOID\r
+Ip6OnDADFinished (\r
+ IN BOOLEAN IsDadPassed,\r
+ IN IP6_INTERFACE *IpIf,\r
+ IN IP6_DAD_ENTRY *DadEntry\r
+ );\r
+\r
+/**\r
+ Create a DAD (Duplicate Address Detection) entry and queue it to be performed.\r
+\r
+ @param[in] IpIf Points to the IP6_INTERFACE.\r
+ @param[in] AddressInfo The address information which needs DAD performed.\r
+ @param[in] Callback The callback routine that will be called after DAD\r
+ is performed. This is an optional parameter that\r
+ may be NULL.\r
+ @param[in] Context The opaque parameter for a DAD callback routine.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The DAD entry was created and queued.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the\r
+ operation.\r
+\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6InitDADProcess (\r
+ IN IP6_INTERFACE *IpIf,\r
+ IN IP6_ADDRESS_INFO *AddressInfo,\r
+ IN IP6_DAD_CALLBACK Callback OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ Search IP6_DAD_ENTRY from the Duplicate Address Detection List.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Target The address information which needs DAD performed .\r
+ @param[out] Interface If not NULL, output the IP6 interface that configures\r
+ the tentative address.\r
+\r
+ @return NULL if failed to find the matching DAD entry.\r
+ Otherwise, point to the found DAD entry.\r
+\r
+**/\r
+IP6_DAD_ENTRY *\r
+Ip6FindDADEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Target,\r
+ OUT IP6_INTERFACE **Interface OPTIONAL\r
+ );\r
+\r
+/**\r
+ Allocate and initialize a IP6 prefix list entry.\r
+\r
+ @param[in] IpSb The pointer to IP6_SERVICE instance.\r
+ @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list.\r
+ Otherwise, it is created for the autoconfiguration prefix list.\r
+ @param[in] ValidLifetime The length of time in seconds that the prefix\r
+ is valid for the purpose of on-link determination.\r
+ @param[in] PreferredLifetime The length of time in seconds that addresses\r
+ generated from the prefix via stateless address\r
+ autoconfiguration remain preferred.\r
+ @param[in] PrefixLength The prefix length of the Prefix.\r
+ @param[in] Prefix The prefix address.\r
+\r
+ @return NULL if it failed to allocate memory for the prefix node. Otherwise, point\r
+ to the created or existing prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6CreatePrefixListEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN OnLinkOrAuto,\r
+ IN UINT32 ValidLifetime,\r
+ IN UINT32 PreferredLifetime,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *Prefix\r
+ );\r
+\r
+/**\r
+ Destory a IP6 prefix list entry.\r
+\r
+ @param[in] IpSb The pointer to IP6_SERVICE instance.\r
+ @param[in] PrefixEntry The to be destroyed prefix list entry.\r
+ @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list.\r
+ Otherwise remove from autoconfiguration prefix list.\r
+ @param[in] ImmediateDelete If TRUE, remove the entry directly.\r
+ Otherwise, check the reference count to see whether\r
+ it should be removed.\r
+\r
+**/\r
+VOID\r
+Ip6DestroyPrefixListEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_PREFIX_LIST_ENTRY *PrefixEntry,\r
+ IN BOOLEAN OnLinkOrAuto,\r
+ IN BOOLEAN ImmediateDelete\r
+ );\r
+\r
+/**\r
+ Search the list array to find an IP6 prefix list entry.\r
+\r
+ @param[in] IpSb The pointer to IP6_SERVICE instance.\r
+ @param[in] OnLinkOrAuto If TRUE, the search the link prefix list,\r
+ Otherwise search the autoconfiguration prefix list.\r
+ @param[in] PrefixLength The prefix length of the Prefix\r
+ @param[in] Prefix The prefix address.\r
+\r
+ @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the\r
+ pointer to the IP6 prefix list entry.\r
+\r
+**/\r
+IP6_PREFIX_LIST_ENTRY *\r
+Ip6FindPrefixListEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN BOOLEAN OnLinkOrAuto,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *Prefix\r
+ );\r
+\r
+/**\r
+ Release the resource in prefix list table, and destroy the list entry and\r
+ corresponding addresses or route entries.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] ListHead The list entry head of the prefix list table.\r
+\r
+**/\r
+VOID\r
+Ip6CleanPrefixListTable (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN LIST_ENTRY *ListHead\r
+ );\r
+\r
+/**\r
+ Allocate and initialize an IP6 neighbor cache entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] CallBack The callback function to be called when\r
+ address resolution is finished.\r
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.\r
+ @param[in] LinkAddress Points to the MAC address of the neighbor.\r
+ Ignored if NULL.\r
+\r
+ @return NULL if failed to allocate memory for the neighbor cache entry.\r
+ Otherwise, point to the created neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6CreateNeighborEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_ARP_CALLBACK CallBack,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address,\r
+ IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ Search a IP6 neighbor cache entry.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.\r
+\r
+ @return NULL if it failed to find the matching neighbor cache entry.\r
+ Otherwise, point to the found neighbor cache entry.\r
+\r
+**/\r
+IP6_NEIGHBOR_ENTRY *\r
+Ip6FindNeighborEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Ip6Address\r
+ );\r
+\r
+/**\r
+ Free a IP6 neighbor cache entry and remove all the frames on the address\r
+ resolution queue that pass the FrameToCancel. That is, either FrameToCancel\r
+ is NULL, or it returns true for the frame.\r
+\r
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.\r
+ @param[in] NeighborCache The to be free neighbor cache entry.\r
+ @param[in] SendIcmpError If TRUE, send out ICMP error.\r
+ @param[in] FullFree If TRUE, remove the neighbor cache entry.\r
+ Otherwise remove the pending frames.\r
+ @param[in] IoStatus The status returned to the cancelled frames'\r
+ callback function.\r
+ @param[in] FrameToCancel Function to select which frame to cancel.\r
+ This is an optional parameter that may be NULL.\r
+ @param[in] Context Opaque parameter to the FrameToCancel.\r
+ Ignored if FrameToCancel is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.\r
+ @retval EFI_SUCCESS The operation finished successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FreeNeighborEntry (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_NEIGHBOR_ENTRY *NeighborCache,\r
+ IN BOOLEAN SendIcmpError,\r
+ IN BOOLEAN FullFree,\r
+ IN EFI_STATUS IoStatus,\r
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.\r
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor\r
+ cache. It will be deleted after Timeout. A value of zero means that\r
+ the entry is permanent. A non-zero value means that the entry is\r
+ dynamic.\r
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will\r
+ be overridden and updated; if FALSE, and if a\r
+ corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+ will be returned.\r
+\r
+ @retval EFI_SUCCESS The neighbor cache entry has been added.\r
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache\r
+ due to insufficient resources.\r
+ @retval EFI_NOT_FOUND TargetLinkAddress is NULL.\r
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,\r
+ and that entry is tagged as un-overridden (when DeleteFlag\r
+ is FALSE).\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddNeighbor (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,\r
+ IN UINT32 Timeout,\r
+ IN BOOLEAN Override\r
+ );\r
+\r
+/**\r
+ Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.\r
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor\r
+ cache. It will be deleted after Timeout. A value of zero means that\r
+ the entry is permanent. A non-zero value means that the entry is\r
+ dynamic.\r
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will\r
+ be overridden and updated; if FALSE, and if a\r
+ corresponding cache entry already existed, EFI_ACCESS_DENIED\r
+ will be returned.\r
+\r
+ @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted.\r
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelNeighbor (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,\r
+ IN UINT32 Timeout,\r
+ IN BOOLEAN Override\r
+ );\r
+\r
+/**\r
+ Process the Neighbor Solicitation message. The message may be sent for Duplicate\r
+ Address Detection or Address Resolution.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the message.\r
+ @param[in] Packet The content of the message with IP head removed.\r
+\r
+ @retval EFI_SUCCESS The packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborSolicit (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ Process the Neighbor Advertisement message.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the message.\r
+ @param[in] Packet The content of the message with IP head removed.\r
+\r
+ @retval EFI_SUCCESS The packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessNeighborAdvertise (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ Process the Router Advertisement message according to RFC4861.\r
+\r
+ @param[in] IpSb The IP service that received the packet.\r
+ @param[in] Head The IP head of the message.\r
+ @param[in] Packet The content of the message with the IP head removed.\r
+\r
+ @retval EFI_SUCCESS The packet processed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation.\r
+ @retval Others Failed to process the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRouterAdvertise (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ Process the ICMPv6 redirect message. Find the instance, then update\r
+ its route cache.\r
+\r
+ @param[in] IpSb The IP6 service binding instance that received\r
+ the packet.\r
+ @param[in] Head The IP head of the received ICMPv6 packet.\r
+ @param[in] Packet The content of the ICMPv6 redirect packet with\r
+ the IP head removed.\r
+\r
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS Successfully updated the route caches.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6ProcessRedirect (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ Generate router solicit message and send it out to Destination Address or\r
+ All Router Link Local scope multicast address.\r
+\r
+ @param[in] IpSb The IP service to send the packet.\r
+ @param[in] Interface If not NULL, points to the IP6 interface to send\r
+ the packet.\r
+ @param[in] SourceAddress If not NULL, the source address of the message.\r
+ @param[in] DestinationAddress If not NULL, the destination address of the message.\r
+ @param[in] SourceLinkAddress If not NULL, the MAC address of the source.\r
+ A source link-layer address option will be appended\r
+ to the message.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation.\r
+ @retval EFI_SUCCESS The router solicit message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendRouterSolicit (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *Interface OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL,\r
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ Generate the Neighbor Solicitation message and send it to the Destination Address.\r
+\r
+ @param[in] IpSb The IP service to send the packet\r
+ @param[in] SourceAddress The source address of the message.\r
+ @param[in] DestinationAddress The destination address of the message.\r
+ @param[in] TargetIp6Address The IP address of the target of the solicitation.\r
+ It must not be a multicast address.\r
+ @param[in] SourceLinkAddress The MAC address for the sender. If not NULL,\r
+ a source link-layer address option will be appended\r
+ to the message.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the\r
+ operation.\r
+ @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SendNeighborSolicit (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *SourceAddress,\r
+ IN EFI_IPv6_ADDRESS *DestinationAddress,\r
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,\r
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ Set the interface's address. This will trigger the DAD process for the\r
+ address to set. To set an already set address, the lifetimes wil be\r
+ updated to the new value passed in.\r
+\r
+ @param[in] Interface The interface to set the address.\r
+ @param[in] Ip6Addr The interface's to be assigned IPv6 address.\r
+ @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast.\r
+ Otherwise, it is not anycast.\r
+ @param[in] PrefixLength The prefix length of the Ip6Addr.\r
+ @param[in] ValidLifetime The valid lifetime for this address.\r
+ @param[in] PreferredLifetime The preferred lifetime for this address.\r
+ @param[in] DadCallback The caller's callback to trigger when DAD finishes.\r
+ This is an optional parameter that may be NULL.\r
+ @param[in] Context The context that will be passed to DadCallback.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The interface is scheduled to be configured with\r
+ the specified address.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to\r
+ lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SetAddress (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN EFI_IPv6_ADDRESS *Ip6Addr,\r
+ IN BOOLEAN IsAnycast,\r
+ IN UINT8 PrefixLength,\r
+ IN UINT32 ValidLifetime,\r
+ IN UINT32 PreferredLifetime,\r
+ IN IP6_DAD_CALLBACK DadCallback OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.\r
+ This time routine handles DAD module and neighbor state transition.\r
+ It is also responsible for sending out router solicitations.\r
+\r
+ @param[in] Event The IP6 service instance's heartbeat timer.\r
+ @param[in] Context The IP6 service instance.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Ip6NdFasterTimerTicking (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ The heartbeat timer of ND module in 1 second. This time routine handles following\r
+ things: 1) maitain default router list; 2) maintain prefix options;\r
+ 3) maintain route caches.\r
+\r
+ @param[in] IpSb The IP6 service binding instance.\r
+\r
+**/\r
+VOID\r
+Ip6NdTimerTicking (\r
+ IN IP6_SERVICE *IpSb\r
+ );\r
+\r
+/**\r
+ Callback function when address resolution is finished. It will cancel\r
+ all the queued frames if the address resolution failed, or transmit them\r
+ if the request succeeded.\r
+\r
+ @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.\r
+\r
+**/\r
+VOID\r
+Ip6OnArpResolved (\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Update the ReachableTime in IP6 service binding instance data, in milliseconds.\r
+\r
+ @param[in, out] IpSb Points to the IP6_SERVICE.\r
+\r
+**/\r
+VOID\r
+Ip6UpdateReachableTime (\r
+ IN OUT IP6_SERVICE *IpSb\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ NVData structure used by the IP6 configuration component.\r
+\r
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _IP6_NV_DATA_H_\r
+#define _IP6_NV_DATA_H_\r
+\r
+#define IP6_CONFIG_NVDATA_GUID \\r
+ { \\r
+ 0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99 } \\r
+ }\r
+\r
+#define FORMID_MAIN_FORM 1\r
+#define FORMID_MANUAL_CONFIG_FORM 2\r
+\r
+#define IP6_POLICY_AUTO 0\r
+#define IP6_POLICY_MANUAL 1\r
+#define DAD_MAX_TRANSMIT_COUNT 10\r
+\r
+#define KEY_INTERFACE_ID 0x101\r
+#define KEY_MANUAL_ADDRESS 0x102\r
+#define KEY_GATEWAY_ADDRESS 0x103\r
+#define KEY_DNS_ADDRESS 0x104\r
+#define KEY_SAVE_CHANGES 0x105\r
+#define KEY_SAVE_CONFIG_CHANGES 0x106\r
+#define KEY_IGNORE_CONFIG_CHANGES 0x107\r
+\r
+#define HOST_ADDRESS_LABEL 0x9000\r
+#define ROUTE_TABLE_LABEL 0xa000\r
+#define GATEWAY_ADDRESS_LABEL 0xb000\r
+#define DNS_ADDRESS_LABEL 0xc000\r
+#define LABEL_END 0xffff\r
+\r
+#define INTERFACE_ID_STR_MIN_SIZE 1\r
+#define INTERFACE_ID_STR_MAX_SIZE 23\r
+#define INTERFACE_ID_STR_STORAGE 24\r
+#define IP6_STR_MAX_SIZE 40\r
+#define ADDRESS_STR_MIN_SIZE 2\r
+#define ADDRESS_STR_MAX_SIZE 255\r
+\r
+///\r
+/// IP6_CONFIG_IFR_NVDATA contains the IP6 configure\r
+/// parameters for that NIC.\r
+///\r
+#pragma pack(1)\r
+typedef struct {\r
+ UINT8 IfType; ///< interface type\r
+ UINT8 Padding[3];\r
+ UINT32 Policy; ///< manual or automatic\r
+ UINT32 DadTransmitCount; ///< dad transmits count\r
+ CHAR16 InterfaceId[INTERFACE_ID_STR_STORAGE]; ///< alternative interface id\r
+ CHAR16 ManualAddress[ADDRESS_STR_MAX_SIZE]; ///< IP addresses\r
+ CHAR16 GatewayAddress[ADDRESS_STR_MAX_SIZE]; ///< Gateway address\r
+ CHAR16 DnsAddress[ADDRESS_STR_MAX_SIZE]; ///< DNS server address\r
+} IP6_CONFIG_IFR_NVDATA;\r
+#pragma pack()\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ IP6 option support functions and routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+ Validate the IP6 option format for both the packets we received\r
+ and that we will transmit. It will compute the ICMPv6 error message fields\r
+ if the option is malformated.\r
+\r
+ @param[in] IpSb The IP6 service data.\r
+ @param[in] Packet The to be validated packet.\r
+ @param[in] Option The first byte of the option.\r
+ @param[in] OptionLen The length of the whole option.\r
+ @param[in] Pointer Identifies the octet offset within\r
+ the invoking packet where the error was detected.\r
+\r
+\r
+ @retval TRUE The option is properly formatted.\r
+ @retval FALSE The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsOptionValid (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN NET_BUF *Packet,\r
+ IN UINT8 *Option,\r
+ IN UINT8 OptionLen,\r
+ IN UINT32 Pointer\r
+ )\r
+{\r
+ UINT8 Offset;\r
+ UINT8 OptionType;\r
+\r
+ Offset = 0;\r
+\r
+ while (Offset < OptionLen) {\r
+ OptionType = *(Option + Offset);\r
+\r
+ switch (OptionType) {\r
+ case Ip6OptionPad1:\r
+ //\r
+ // It is a Pad1 option\r
+ //\r
+ Offset++;\r
+ break;\r
+ case Ip6OptionPadN:\r
+ //\r
+ // It is a PadN option\r
+ //\r
+ Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2);\r
+ break;\r
+ case Ip6OptionRouterAlert:\r
+ //\r
+ // It is a Router Alert Option\r
+ //\r
+ Offset += 4;\r
+ break;\r
+ default:\r
+ //\r
+ // The highest-order two bits specify the action must be taken if\r
+ // the processing IPv6 node does not recognize the option type.\r
+ //\r
+ switch (OptionType & Ip6OptionMask) {\r
+ case Ip6OptionSkip:\r
+ Offset = (UINT8) (Offset + *(Option + Offset + 1));\r
+ break;\r
+ case Ip6OptionDiscard:\r
+ return FALSE;\r
+ case Ip6OptionParameterProblem:\r
+ Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ Packet,\r
+ NULL,\r
+ &Packet->Ip.Ip6->SourceAddress,\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ 2,\r
+ &Pointer\r
+ );\r
+ return FALSE;\r
+ case Ip6OptionMask:\r
+ if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+ Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ Packet,\r
+ NULL,\r
+ &Packet->Ip.Ip6->SourceAddress,\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ 2,\r
+ &Pointer\r
+ );\r
+ }\r
+\r
+ return FALSE;\r
+ break;\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Validate the IP6 option format for both the packets we received\r
+ and that we will transmit. It supports the defined options in Neighbor\r
+ Discovery messages.\r
+\r
+ @param[in] Option The first byte of the option.\r
+ @param[in] OptionLen The length of the whole option.\r
+\r
+ @retval TRUE The option is properly formatted.\r
+ @retval FALSE The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsNDOptionValid (\r
+ IN UINT8 *Option,\r
+ IN UINT16 OptionLen\r
+ )\r
+{\r
+ UINT16 Offset;\r
+ UINT8 OptionType;\r
+ UINT16 Length;\r
+\r
+ Offset = 0;\r
+\r
+ while (Offset < OptionLen) {\r
+ OptionType = *(Option + Offset);\r
+ Length = (UINT16) (*(Option + Offset + 1) * 8);\r
+\r
+ switch (OptionType) {\r
+ case Ip6OptionPrefixInfo:\r
+ if (Length != 32) {\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ case Ip6OptionMtu:\r
+ if (Length != 8) {\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and\r
+ // Ip6OptionRedirected here. For unrecognized options, silently ignore\r
+ // and continue processsing the message.\r
+ //\r
+ if (Length == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ Offset = (UINT16) (Offset + Length);\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Validate whether the NextHeader is a known valid protocol or one of the user configured\r
+ protocols from the upper layer.\r
+\r
+ @param[in] IpSb The IP6 service instance.\r
+ @param[in] NextHeader The next header field.\r
+\r
+ @retval TRUE The NextHeader is a known valid protocol or user configured.\r
+ @retval FALSE The NextHeader is not a known valid protocol.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsValidProtocol (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN UINT8 NextHeader\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_PROTOCOL *IpInstance;\r
+\r
+ if (NextHeader == EFI_IP_PROTO_TCP ||\r
+ NextHeader == EFI_IP_PROTO_UDP ||\r
+ NextHeader == IP6_ICMP ||\r
+ NextHeader == IP6_ESP\r
+ ) {\r
+ return TRUE;\r
+ }\r
+\r
+ if (IpSb == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (IpSb->Signature != IP6_SERVICE_SIGNATURE) {\r
+ return FALSE;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {\r
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);\r
+ if (IpInstance->State == IP6_STATE_CONFIGED) {\r
+ if (IpInstance->ConfigData.DefaultProtocol == NextHeader) {\r
+ return TRUE;\r
+ }\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Validate the IP6 extension header format for both the packets we received\r
+ and that we will transmit. It will compute the ICMPv6 error message fields\r
+ if the option is mal-formated.\r
+\r
+ @param[in] IpSb The IP6 service instance. This is an optional parameter.\r
+ @param[in] Packet The data of the packet. Ignored if NULL.\r
+ @param[in] NextHeader The next header field in IPv6 basic header.\r
+ @param[in] ExtHdrs The first byte of the option.\r
+ @param[in] ExtHdrsLen The length of the whole option.\r
+ @param[in] Rcvd The option is from the packet we received if TRUE,\r
+ otherwise, the option we want to transmit.\r
+ @param[out] FormerHeader The offset of NextHeader which points to Fragment\r
+ Header when we received, of the ExtHdrs.\r
+ Ignored if we transmit.\r
+ @param[out] LastHeader The pointer of NextHeader of the last extension\r
+ header processed by IP6.\r
+ @param[out] RealExtsLen The length of extension headers processed by IP6 layer.\r
+ This is an optional parameter that may be NULL.\r
+ @param[out] UnFragmentLen The length of unfragmented length of extension headers.\r
+ This is an optional parameter that may be NULL.\r
+ @param[out] Fragmented Indicate whether the packet is fragmented.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval TRUE The option is properly formated.\r
+ @retval FALSE The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsExtsValid (\r
+ IN IP6_SERVICE *IpSb OPTIONAL,\r
+ IN NET_BUF *Packet OPTIONAL,\r
+ IN UINT8 *NextHeader,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN BOOLEAN Rcvd,\r
+ OUT UINT32 *FormerHeader OPTIONAL,\r
+ OUT UINT8 **LastHeader,\r
+ OUT UINT32 *RealExtsLen OPTIONAL,\r
+ OUT UINT32 *UnFragmentLen OPTIONAL,\r
+ OUT BOOLEAN *Fragmented OPTIONAL\r
+ )\r
+{\r
+ UINT32 Pointer;\r
+ UINT32 Offset;\r
+ UINT8 *Option;\r
+ UINT8 OptionLen;\r
+ BOOLEAN Flag;\r
+ UINT8 CountD;\r
+ UINT8 CountA;\r
+ IP6_FRAGMENT_HEADER *FragmentHead;\r
+ UINT16 FragmentOffset;\r
+ IP6_ROUTING_HEADER *RoutingHead;\r
+\r
+ if (RealExtsLen != NULL) {\r
+ *RealExtsLen = 0;\r
+ }\r
+\r
+ if (UnFragmentLen != NULL) {\r
+ *UnFragmentLen = 0;\r
+ }\r
+\r
+ if (Fragmented != NULL) {\r
+ *Fragmented = FALSE;\r
+ }\r
+\r
+ *LastHeader = NextHeader;\r
+\r
+ if (ExtHdrs == NULL && ExtHdrsLen == 0) {\r
+ return TRUE;\r
+ }\r
+\r
+ if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {\r
+ return FALSE;\r
+ }\r
+\r
+ Pointer = 0;\r
+ Offset = 0;\r
+ Flag = FALSE;\r
+ CountD = 0;\r
+ CountA = 0;\r
+\r
+ while (Offset <= ExtHdrsLen) {\r
+\r
+ switch (*NextHeader) {\r
+ case IP6_HOP_BY_HOP:\r
+ if (Offset != 0) {\r
+ if (!Rcvd) {\r
+ return FALSE;\r
+ }\r
+ //\r
+ // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only.\r
+ // If not, generate a ICMP parameter problem message with code value of 1.\r
+ //\r
+ if (Pointer == 0) {\r
+ Pointer = sizeof (EFI_IP6_HEADER);\r
+ } else {\r
+ Pointer = Offset + sizeof (EFI_IP6_HEADER);\r
+ }\r
+\r
+ if ((IpSb != NULL) && (Packet != NULL) &&\r
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ Packet,\r
+ NULL,\r
+ &Packet->Ip.Ip6->SourceAddress,\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ 1,\r
+ &Pointer\r
+ );\r
+ }\r
+ return FALSE;\r
+ }\r
+\r
+ Flag = TRUE;\r
+\r
+ //\r
+ // Fall through\r
+ //\r
+ case IP6_DESTINATION:\r
+ if (*NextHeader == IP6_DESTINATION) {\r
+ CountD++;\r
+ }\r
+\r
+ if (CountD > 2) {\r
+ return FALSE;\r
+ }\r
+\r
+ NextHeader = ExtHdrs + Offset;\r
+ Pointer = Offset;\r
+\r
+ Offset++;\r
+ Option = ExtHdrs + Offset;\r
+ OptionLen = (UINT8) ((*Option + 1) * 8 - 2);\r
+ Option++;\r
+ Offset++;\r
+\r
+ if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) {\r
+ return FALSE;\r
+ }\r
+\r
+ Offset = Offset + OptionLen;\r
+\r
+ if (Flag) {\r
+ if (UnFragmentLen != NULL) {\r
+ *UnFragmentLen = Offset;\r
+ }\r
+\r
+ Flag = FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ case IP6_ROUTING:\r
+ NextHeader = ExtHdrs + Offset;\r
+ RoutingHead = (IP6_ROUTING_HEADER *) NextHeader;\r
+\r
+ //\r
+ // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095.\r
+ // Thus all routing types are processed as unrecognized.\r
+ //\r
+ if (RoutingHead->SegmentsLeft == 0) {\r
+ //\r
+ // Ignore the routing header and proceed to process the next header.\r
+ //\r
+ Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;\r
+\r
+ if (UnFragmentLen != NULL) {\r
+ *UnFragmentLen = Offset;\r
+ }\r
+\r
+ } else {\r
+ //\r
+ // Discard the packet and send an ICMP Parameter Problem, Code 0, message\r
+ // to the packet's source address, pointing to the unrecognized routing\r
+ // type.\r
+ //\r
+ Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);\r
+ if ((IpSb != NULL) && (Packet != NULL) &&\r
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ Packet,\r
+ NULL,\r
+ &Packet->Ip.Ip6->SourceAddress,\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ 0,\r
+ &Pointer\r
+ );\r
+ }\r
+\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ case IP6_FRAGMENT:\r
+\r
+ //\r
+ // RFC2402, AH header should after fragment header.\r
+ //\r
+ if (CountA > 1) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // RFC2460, ICMP Parameter Problem message with code 0 should be sent\r
+ // if the length of a fragment is not a multiple of 8 octects and the M\r
+ // flag of that fragment is 1, pointing to the Payload length field of the\r
+ // fragment packet.\r
+ //\r
+ if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) {\r
+ //\r
+ // Check whether it is the last fragment.\r
+ //\r
+ FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset);\r
+ if (FragmentHead == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ FragmentOffset = NTOHS (FragmentHead->FragmentOffset);\r
+\r
+ if (((FragmentOffset & 0x1) == 0x1) &&\r
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+ Pointer = sizeof (UINT32);\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ Packet,\r
+ NULL,\r
+ &Packet->Ip.Ip6->SourceAddress,\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ 0,\r
+ &Pointer\r
+ );\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ if (Fragmented != NULL) {\r
+ *Fragmented = TRUE;\r
+ }\r
+\r
+ if (Rcvd && FormerHeader != NULL) {\r
+ *FormerHeader = (UINT32) (NextHeader - ExtHdrs);\r
+ }\r
+\r
+ NextHeader = ExtHdrs + Offset;\r
+ Offset = Offset + 8;\r
+ break;\r
+\r
+ case IP6_AH:\r
+ if (++CountA > 1) {\r
+ return FALSE;\r
+ }\r
+\r
+ Option = ExtHdrs + Offset;\r
+ NextHeader = Option;\r
+ Option++;\r
+ //\r
+ // RFC2402, Payload length is specified in 32-bit words, minus "2".\r
+ //\r
+ OptionLen = (UINT8) ((*Option + 2) * 4);\r
+ Offset = Offset + OptionLen;\r
+ break;\r
+\r
+ case IP6_NO_NEXT_HEADER:\r
+ *LastHeader = NextHeader;\r
+ return FALSE;\r
+ break;\r
+\r
+ default:\r
+ if (Ip6IsValidProtocol (IpSb, *NextHeader)) {\r
+\r
+ *LastHeader = NextHeader;\r
+\r
+ if (RealExtsLen != NULL) {\r
+ *RealExtsLen = Offset;\r
+ }\r
+\r
+ return TRUE;\r
+ }\r
+\r
+ //\r
+ // The Next Header value is unrecognized by the node, discard the packet and\r
+ // send an ICMP parameter problem message with code value of 1.\r
+ //\r
+ if (Offset == 0) {\r
+ //\r
+ // The Next Header directly follows IPv6 basic header.\r
+ //\r
+ Pointer = 6;\r
+ } else {\r
+ if (Pointer == 0) {\r
+ Pointer = sizeof (EFI_IP6_HEADER);\r
+ } else {\r
+ Pointer = Offset + sizeof (EFI_IP6_HEADER);\r
+ }\r
+ }\r
+\r
+ if ((IpSb != NULL) && (Packet != NULL) &&\r
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {\r
+ Ip6SendIcmpError (\r
+ IpSb,\r
+ Packet,\r
+ NULL,\r
+ &Packet->Ip.Ip6->SourceAddress,\r
+ ICMP_V6_PARAMETER_PROBLEM,\r
+ 1,\r
+ &Pointer\r
+ );\r
+ }\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ *LastHeader = NextHeader;\r
+\r
+ if (RealExtsLen != NULL) {\r
+ *RealExtsLen = Offset;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Generate an IPv6 router alert option in network order and output it through Buffer.\r
+\r
+ @param[out] Buffer Points to a buffer to record the generated option.\r
+ @param[in, out] BufferLen The length of Buffer, in bytes.\r
+ @param[in] NextHeader The 8-bit selector indicates the type of header\r
+ immediately following the Hop-by-Hop Options header.\r
+\r
+ @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated\r
+ option. BufferLen is updated for the required size.\r
+\r
+ @retval EFI_SUCCESS The option is generated and filled in to Buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillHopByHop (\r
+ OUT UINT8 *Buffer,\r
+ IN OUT UINTN *BufferLen,\r
+ IN UINT8 NextHeader\r
+ )\r
+{\r
+ UINT8 BufferArray[8];\r
+\r
+ if (*BufferLen < 8) {\r
+ *BufferLen = 8;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ //\r
+ // Form the Hop-By-Hop option in network order.\r
+ // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN\r
+ // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets.\r
+ //\r
+ ZeroMem (BufferArray, sizeof (BufferArray));\r
+ BufferArray[0] = NextHeader;\r
+ BufferArray[2] = 0x5;\r
+ BufferArray[3] = 0x2;\r
+ BufferArray[6] = 1;\r
+\r
+ CopyMem (Buffer, BufferArray, sizeof (BufferArray));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.\r
+\r
+ @param[in] IpSb The IP6 service instance to transmit the packet.\r
+ @param[in] NextHeader The extension header type of first extension header.\r
+ @param[in] LastHeader The extension header type of last extension header.\r
+ @param[in] ExtHdrs The length of the original extension header.\r
+ @param[in] ExtHdrsLen The length of the extension headers.\r
+ @param[in] FragmentOffset The fragment offset of the data following the header.\r
+ @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted.\r
+ It's caller's responsiblity to free this buffer.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of\r
+ resource.\r
+ @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not\r
+ supported currently.\r
+ @retval EFI_SUCCESS The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillFragmentHeader (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN UINT8 NextHeader,\r
+ IN UINT8 LastHeader,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN UINT16 FragmentOffset,\r
+ OUT UINT8 **UpdatedExtHdrs\r
+ )\r
+{\r
+ UINT32 Length;\r
+ UINT8 *Buffer;\r
+ UINT32 FormerHeader;\r
+ UINT32 Offset;\r
+ UINT32 Part1Len;\r
+ UINT32 HeaderLen;\r
+ UINT8 Current;\r
+ IP6_FRAGMENT_HEADER FragmentHead;\r
+\r
+ if (UpdatedExtHdrs == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);\r
+ Buffer = AllocatePool (Length);\r
+ if (Buffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Offset = 0;\r
+ Part1Len = 0;\r
+ FormerHeader = 0;\r
+ Current = NextHeader;\r
+\r
+ while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) {\r
+ switch (NextHeader) {\r
+ case IP6_ROUTING:\r
+ case IP6_HOP_BY_HOP:\r
+ case IP6_DESTINATION:\r
+ Current = NextHeader;\r
+ NextHeader = *(ExtHdrs + Offset);\r
+\r
+ if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) {\r
+ //\r
+ // Destination Options header should occur at most twice, once before\r
+ // a Routing header and once before the upper-layer header. Here we\r
+ // find the one before the upper-layer header. Insert the Fragment\r
+ // Header before it.\r
+ //\r
+ CopyMem (Buffer, ExtHdrs, Part1Len);\r
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;\r
+ //\r
+ // Exit the loop.\r
+ //\r
+ Offset = ExtHdrsLen + 1;\r
+ break;\r
+ }\r
+\r
+\r
+ FormerHeader = Offset;\r
+ HeaderLen = (*(ExtHdrs + Offset + 1) + 1) * 8;\r
+ Part1Len = Part1Len + HeaderLen;\r
+ Offset = Offset + HeaderLen;\r
+ break;\r
+\r
+ case IP6_FRAGMENT:\r
+ Current = NextHeader;\r
+\r
+ if (Part1Len != 0) {\r
+ CopyMem (Buffer, ExtHdrs, Part1Len);\r
+ }\r
+\r
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;\r
+\r
+ //\r
+ // Exit the loop.\r
+ //\r
+ Offset = ExtHdrsLen + 1;\r
+ break;\r
+\r
+ case IP6_AH:\r
+ Current = NextHeader;\r
+ NextHeader = *(ExtHdrs + Offset);\r
+ //\r
+ // RFC2402, Payload length is specified in 32-bit words, minus "2".\r
+ //\r
+ HeaderLen = (*(ExtHdrs + Offset + 1) + 2) * 4;\r
+ Part1Len = Part1Len + HeaderLen;\r
+ Offset = Offset + HeaderLen;\r
+ break;\r
+\r
+ default:\r
+ if (Ip6IsValidProtocol (IpSb, NextHeader)) {\r
+ Current = NextHeader;\r
+ CopyMem (Buffer, ExtHdrs, Part1Len);\r
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;\r
+ //\r
+ // Exit the loop.\r
+ //\r
+ Offset = ExtHdrsLen + 1;\r
+ break;\r
+ }\r
+\r
+ FreePool (Buffer);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Append the Fragment header. If the fragment offset indicates the fragment\r
+ // is the first fragment.\r
+ //\r
+ if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {\r
+ FragmentHead.NextHeader = Current;\r
+ } else {\r
+ FragmentHead.NextHeader = LastHeader;\r
+ }\r
+\r
+ FragmentHead.Reserved = 0;\r
+ FragmentHead.FragmentOffset = HTONS (FragmentOffset);\r
+ FragmentHead.Identification = mIp6Id;\r
+\r
+ CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER));\r
+\r
+ if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) {\r
+ //\r
+ // Append the part2 (fragmentable part) of Extension headers\r
+ //\r
+ CopyMem (\r
+ Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER),\r
+ ExtHdrs + Part1Len,\r
+ ExtHdrsLen - Part1Len\r
+ );\r
+ }\r
+\r
+ *UpdatedExtHdrs = Buffer;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Definition of IP6 option process routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_OPTION_H__\r
+#define __EFI_IP6_OPTION_H__\r
+\r
+#define IP6_FRAGMENT_OFFSET_MASK (~0x3)\r
+\r
+typedef struct _IP6_FRAGMENT_HEADER {\r
+ UINT8 NextHeader;\r
+ UINT8 Reserved;\r
+ UINT16 FragmentOffset;\r
+ UINT32 Identification;\r
+} IP6_FRAGMENT_HEADER;\r
+\r
+typedef struct _IP6_ROUTING_HEADER {\r
+ UINT8 NextHeader;\r
+ UINT8 HeaderLen;\r
+ UINT8 RoutingType;\r
+ UINT8 SegmentsLeft;\r
+} IP6_ROUTING_HEADER;\r
+\r
+typedef enum {\r
+ Ip6OptionPad1 = 0,\r
+ Ip6OptionPadN = 1,\r
+ Ip6OptionRouterAlert = 5,\r
+ Ip6OptionSkip = 0,\r
+ Ip6OptionDiscard = 0x40,\r
+ Ip6OptionParameterProblem = 0x80,\r
+ Ip6OptionMask = 0xc0,\r
+\r
+ Ip6OptionEtherSource = 1,\r
+ Ip6OptionEtherTarget = 2,\r
+ Ip6OptionPrefixInfo = 3,\r
+ Ip6OptionRedirected = 4,\r
+ Ip6OptionMtu = 5\r
+} IP6_OPTION_TYPE;\r
+\r
+/**\r
+ Validate the IP6 extension header format for both the packets we received\r
+ and that we will transmit. It will compute the ICMPv6 error message fields\r
+ if the option is mal-formated.\r
+\r
+ @param[in] IpSb The IP6 service instance. This is an optional parameter.\r
+ @param[in] Packet The data of the packet. Ignored if NULL.\r
+ @param[in] NextHeader The next header field in IPv6 basic header.\r
+ @param[in] ExtHdrs The first byte of the option.\r
+ @param[in] ExtHdrsLen The length of the whole option.\r
+ @param[in] Rcvd The option is from the packet we received if TRUE,\r
+ otherwise, the option we want to transmit.\r
+ @param[out] FormerHeader The offset of NextHeader which points to Fragment\r
+ Header when we received, of the ExtHdrs.\r
+ Ignored if we transmit.\r
+ @param[out] LastHeader The pointer of NextHeader of the last extension\r
+ header processed by IP6.\r
+ @param[out] RealExtsLen The length of extension headers processed by IP6 layer.\r
+ This is an optional parameter that may be NULL.\r
+ @param[out] UnFragmentLen The length of unfragmented length of extension headers.\r
+ This is an optional parameter that may be NULL.\r
+ @param[out] Fragmented Indicate whether the packet is fragmented.\r
+ This is an optional parameter that may be NULL.\r
+\r
+ @retval TRUE The option is properly formated.\r
+ @retval FALSE The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsExtsValid (\r
+ IN IP6_SERVICE *IpSb OPTIONAL,\r
+ IN NET_BUF *Packet OPTIONAL,\r
+ IN UINT8 *NextHeader,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN BOOLEAN Rcvd,\r
+ OUT UINT32 *FormerHeader OPTIONAL,\r
+ OUT UINT8 **LastHeader,\r
+ OUT UINT32 *RealExtsLen OPTIONAL,\r
+ OUT UINT32 *UnFragmentLen OPTIONAL,\r
+ OUT BOOLEAN *Fragmented OPTIONAL\r
+ );\r
+\r
+/**\r
+ Generate an IPv6 router alert option in network order and output it through Buffer.\r
+\r
+ @param[out] Buffer Points to a buffer to record the generated option.\r
+ @param[in, out] BufferLen The length of Buffer, in bytes.\r
+ @param[in] NextHeader The 8-bit selector indicates the type of header\r
+ immediately following the Hop-by-Hop Options header.\r
+\r
+ @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated\r
+ option. BufferLen is updated for the required size.\r
+\r
+ @retval EFI_SUCCESS The option is generated and filled in to Buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillHopByHop (\r
+ OUT UINT8 *Buffer,\r
+ IN OUT UINTN *BufferLen,\r
+ IN UINT8 NextHeader\r
+ );\r
+\r
+/**\r
+ Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.\r
+\r
+ @param[in] IpSb The IP6 service instance to transmit the packet.\r
+ @param[in] NextHeader The extension header type of first extension header.\r
+ @param[in] LastHeader The extension header type of last extension header.\r
+ @param[in] ExtHdrs The length of the original extension header.\r
+ @param[in] ExtHdrsLen The length of the extension headers.\r
+ @param[in] FragmentOffset The fragment offset of the data following the header.\r
+ @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted.\r
+ It's caller's responsiblity to free this buffer.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of\r
+ resource.\r
+ @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not\r
+ supported currently.\r
+ @retval EFI_SUCCESS The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6FillFragmentHeader (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN UINT8 NextHeader,\r
+ IN UINT8 LastHeader,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN UINT16 FragmentOffset,\r
+ OUT UINT8 **UpdatedExtHdrs\r
+ );\r
+\r
+/**\r
+ Copy the extension headers from the original to buffer. A Fragment header is\r
+ appended to the end.\r
+\r
+ @param[in] NextHeader The 8-bit selector indicates the type of\r
+ the fragment header's next header.\r
+ @param[in] ExtHdrs The length of the original extension header.\r
+ @param[in] LastHeader The pointer of next header of last extension header.\r
+ @param[in] FragmentOffset The fragment offset of the data following the header.\r
+ @param[in] UnFragmentHdrLen The length of unfragmented length of extension headers.\r
+ @param[in, out] Buf The buffer to copy options to.\r
+ @param[in, out] BufLen The length of the buffer.\r
+\r
+ @retval EFI_SUCCESS The options are copied over.\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CopyExts (\r
+ IN UINT8 NextHeader,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT8 *LastHeader,\r
+ IN UINT16 FragmentOffset,\r
+ IN UINT32 UnFragmentHdrLen,\r
+ IN OUT UINT8 *Buf,\r
+ IN OUT UINT32 *BufLen\r
+ );\r
+\r
+/**\r
+ Validate the IP6 option format for both the packets we received\r
+ and that we will transmit. It supports the defined options in Neighbor\r
+ Discovery messages.\r
+\r
+ @param[in] Option The first byte of the option.\r
+ @param[in] OptionLen The length of the whole option.\r
+\r
+ @retval TRUE The option is properly formatted.\r
+ @retval FALSE The option is malformated.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6IsNDOptionValid (\r
+ IN UINT8 *Option,\r
+ IN UINT16 OptionLen\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The internal functions and routines to transmit the IP6 packet.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+UINT32 mIp6Id;\r
+\r
+/**\r
+ Output all the available source addresses to a list entry head SourceList. The\r
+ number of source addresses are also returned.\r
+\r
+ @param[in] IpSb Points to an IP6 service binding instance.\r
+ @param[out] SourceList The list entry head of all source addresses.\r
+ It is the caller's responsiblity to free the\r
+ resources.\r
+ @param[out] SourceCount The number of source addresses.\r
+\r
+ @retval EFI_SUCCESS The source addresses were copied to a list entry head\r
+ SourceList.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6CandidateSource (\r
+ IN IP6_SERVICE *IpSb,\r
+ OUT LIST_ENTRY *SourceList,\r
+ OUT UINT32 *SourceCount\r
+ )\r
+{\r
+ IP6_INTERFACE *IpIf;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Entry2;\r
+ IP6_ADDRESS_INFO *AddrInfo;\r
+ IP6_ADDRESS_INFO *Copy;\r
+\r
+ *SourceCount = 0;\r
+\r
+ if (IpSb->LinkLocalOk) {\r
+ Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO));\r
+ if (Copy == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Copy->Signature = IP6_ADDR_INFO_SIGNATURE;\r
+ IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr);\r
+ Copy->IsAnycast = FALSE;\r
+ Copy->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;\r
+ Copy->ValidLifetime = (UINT32) IP6_INFINIT_LIFETIME;\r
+ Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME;\r
+\r
+ InsertTailList (SourceList, &Copy->Link);\r
+ (*SourceCount)++;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);\r
+\r
+ NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {\r
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+ if (AddrInfo->IsAnycast) {\r
+ //\r
+ // Never use an anycast address.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo);\r
+ if (Copy == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ InsertTailList (SourceList, &Copy->Link);\r
+ (*SourceCount)++;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Caculate how many bits are the same between two IPv6 addresses.\r
+\r
+ @param[in] AddressA Points to an IPv6 address.\r
+ @param[in] AddressB Points to another IPv6 address.\r
+\r
+ @return The common bits of the AddressA and AddressB.\r
+\r
+**/\r
+UINT8\r
+Ip6CommonPrefixLen (\r
+ IN EFI_IPv6_ADDRESS *AddressA,\r
+ IN EFI_IPv6_ADDRESS *AddressB\r
+ )\r
+{\r
+ UINT8 Count;\r
+ UINT8 Index;\r
+ UINT8 ByteA;\r
+ UINT8 ByteB;\r
+ UINT8 NumBits;\r
+\r
+ Count = 0;\r
+ Index = 0;\r
+\r
+ while (Index < 16) {\r
+ ByteA = AddressA->Addr[Index];\r
+ ByteB = AddressB->Addr[Index];\r
+\r
+ if (ByteA == ByteB) {\r
+ Count += 8;\r
+ Index++;\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Check how many bits are common between the two bytes.\r
+ //\r
+ NumBits = 8;\r
+ ByteA = (UINT8) (ByteA ^ ByteB);\r
+\r
+ while (ByteA != 0) {\r
+ NumBits--;\r
+ ByteA = (UINT8) (ByteA >> 1);\r
+ }\r
+\r
+ return (UINT8) (Count + NumBits);\r
+ }\r
+\r
+ return Count;\r
+}\r
+\r
+/**\r
+ Output all the available source addresses to a list entry head SourceList. The\r
+ number of source addresses are also returned.\r
+\r
+ @param[in] IpSb Points to a IP6 service binding instance.\r
+ @param[in] Destination The IPv6 destination address.\r
+ @param[out] Source The selected IPv6 source address according to\r
+ the Destination.\r
+\r
+ @retval EFI_SUCCESS The source addresses were copied to a list entry\r
+ head SourceList.\r
+ @retval EFI_NO_MAPPING The IPv6 stack is not auto configured.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SelectSourceAddress (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Destination,\r
+ OUT EFI_IPv6_ADDRESS *Source\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY SourceList;\r
+ UINT32 SourceCount;\r
+ UINT8 ScopeD;\r
+ LIST_ENTRY *Entry;\r
+ IP6_ADDRESS_INFO *AddrInfo;\r
+ IP6_PREFIX_LIST_ENTRY *Prefix;\r
+ UINT8 LastCommonLength;\r
+ UINT8 CurrentCommonLength;\r
+ EFI_IPv6_ADDRESS *TmpAddress;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ Status = EFI_SUCCESS;\r
+ InitializeListHead (&SourceList);\r
+\r
+ if (!IpSb->LinkLocalOk) {\r
+ return EFI_NO_MAPPING;\r
+ }\r
+\r
+ //\r
+ // Rule 1: Prefer same address.\r
+ //\r
+ if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) {\r
+ IP6_COPY_ADDRESS (Source, Destination);\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Rule 2: Prefer appropriate scope.\r
+ //\r
+ if (IP6_IS_MULTICAST (Destination)) {\r
+ ScopeD = (UINT8) (Destination->Addr[1] >> 4);\r
+ } else if (NetIp6IsLinkLocalAddr (Destination)) {\r
+ ScopeD = 0x2;\r
+ } else {\r
+ ScopeD = 0xE;\r
+ }\r
+\r
+ if (ScopeD <= 0x2) {\r
+ //\r
+ // Return the link-local address if it exists\r
+ // One IP6_SERVICE only has one link-local address.\r
+ //\r
+ IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // All candidate source addresses are global unicast address.\r
+ //\r
+ Ip6CandidateSource (IpSb, &SourceList, &SourceCount);\r
+\r
+ if (SourceCount == 0) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);\r
+\r
+ if (SourceCount == 1) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Rule 3: Avoid deprecated addresses.\r
+ // TODO: check the "deprecated" state of the stateful configured address\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {\r
+ Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);\r
+ if (Prefix->PreferredLifetime == 0) {\r
+ Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength);\r
+\r
+ if (SourceCount == 1) {\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // TODO: Rule 4: Prefer home addresses.\r
+ // TODO: Rule 5: Prefer outgoing interface.\r
+ // TODO: Rule 6: Prefer matching label.\r
+ // TODO: Rule 7: Prefer public addresses.\r
+ //\r
+\r
+ //\r
+ // Rule 8: Use longest matching prefix.\r
+ //\r
+ LastCommonLength = Ip6CommonPrefixLen (Source, Destination);\r
+ TmpAddress = NULL;\r
+\r
+ for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) {\r
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);\r
+\r
+ CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination);\r
+ if (CurrentCommonLength > LastCommonLength) {\r
+ LastCommonLength = CurrentCommonLength;\r
+ TmpAddress = &AddrInfo->Address;\r
+ }\r
+ }\r
+\r
+ if (TmpAddress != NULL) {\r
+ IP6_COPY_ADDRESS (Source, TmpAddress);\r
+ }\r
+\r
+Exit:\r
+\r
+ Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Select an interface to send the packet generated in the IP6 driver\r
+ itself: that is, not by the requests of the IP6 child's consumer. Such\r
+ packets include the ICMPv6 echo replies and other ICMPv6 error packets.\r
+\r
+ @param[in] IpSb The IP4 service that wants to send the packets.\r
+ @param[in] Destination The destination of the packet.\r
+ @param[in, out] Source The source of the packet.\r
+\r
+ @return NULL if no proper interface is found, otherwise, the interface that\r
+ can be used to send the system packet from.\r
+\r
+**/\r
+IP6_INTERFACE *\r
+Ip6SelectInterface (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Destination,\r
+ IN OUT EFI_IPv6_ADDRESS *Source\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IPv6_ADDRESS SelectedSource;\r
+ IP6_INTERFACE *IpIf;\r
+ BOOLEAN Exist;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+ ASSERT (Destination != NULL && Source != NULL);\r
+\r
+ if (NetIp6IsUnspecifiedAddr (Destination)) {\r
+ return NULL;\r
+ }\r
+\r
+ if (!NetIp6IsUnspecifiedAddr (Source)) {\r
+ Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL);\r
+ ASSERT (Exist);\r
+\r
+ return IpIf;\r
+ }\r
+\r
+ //\r
+ // If source is unspecified, select a source according to the destination.\r
+ //\r
+ Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource);\r
+ if (EFI_ERROR (Status)) {\r
+ return IpSb->DefaultInterface;\r
+ }\r
+\r
+ Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL);\r
+ IP6_COPY_ADDRESS (Source, &SelectedSource);\r
+\r
+ return IpIf;\r
+}\r
+\r
+/**\r
+ The default callback function for the system generated packet.\r
+ It will free the packet.\r
+\r
+ @param[in] Packet The packet that transmitted.\r
+ @param[in] IoStatus The result of the transmission, succeeded or failed.\r
+ @param[in] LinkFlag Not used when transmitted. Check IP6_FRAME_CALLBACK\r
+ for reference.\r
+ @param[in] Context The context provided by us.\r
+\r
+**/\r
+VOID\r
+Ip6SysPacketSent (\r
+ NET_BUF *Packet,\r
+ EFI_STATUS IoStatus,\r
+ UINT32 LinkFlag,\r
+ VOID *Context\r
+ )\r
+{\r
+ NetbufFree (Packet);\r
+ Packet = NULL;\r
+}\r
+\r
+/**\r
+ Prefix an IP6 basic head and unfragmentable extension headers and a fragment header\r
+ to the Packet. Used for IP6 fragmentation.\r
+\r
+ @param[in] IpSb The IP6 service instance to transmit the packet.\r
+ @param[in] Packet The packet to prefix the IP6 header to.\r
+ @param[in] Head The caller supplied header.\r
+ @param[in] FragmentOffset The fragment offset of the data following the header.\r
+ @param[in] ExtHdrs The length of the original extension header.\r
+ @param[in] ExtHdrsLen The length of the extension headers.\r
+ @param[in] LastHeader The pointer of next header of last extension header.\r
+ @param[in] HeadLen The length of the unfragmented part of the IP6 header.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of\r
+ Packet.\r
+ @retval EFI_SUCCESS The operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6PrependHead (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN UINT16 FragmentOffset,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN UINT8 LastHeader,\r
+ IN UINT32 HeadLen\r
+ )\r
+{\r
+ UINT32 Len;\r
+ UINT32 UnFragExtHdrsLen;\r
+ EFI_IP6_HEADER *PacketHead;\r
+ UINT8 *UpdatedExtHdrs;\r
+ EFI_STATUS Status;\r
+ UINT8 NextHeader;\r
+\r
+ //\r
+ // HeadLen is the length of the fixed part of the sequences of fragments, i.e.\r
+ // the unfragment part.\r
+ //\r
+ PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);\r
+ if (PacketHead == NULL) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ //\r
+ // Set the head up, convert the host byte order to network byte order\r
+ //\r
+ CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));\r
+ PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER)));\r
+ Packet->Ip.Ip6 = PacketHead;\r
+\r
+ Len = HeadLen - sizeof (EFI_IP6_HEADER);\r
+ UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+ if (UnFragExtHdrsLen == 0) {\r
+ PacketHead->NextHeader = IP6_FRAGMENT;\r
+ }\r
+\r
+ //\r
+ // Append the extension headers: firstly copy the unfragmentable headers, then append\r
+ // fragmentation header.\r
+ //\r
+ if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {\r
+ NextHeader = Head->NextHeader;\r
+ } else {\r
+ NextHeader = PacketHead->NextHeader;\r
+ }\r
+\r
+ Status = Ip6FillFragmentHeader (\r
+ IpSb,\r
+ NextHeader,\r
+ LastHeader,\r
+ ExtHdrs,\r
+ ExtHdrsLen,\r
+ FragmentOffset,\r
+ &UpdatedExtHdrs\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ CopyMem (\r
+ (UINT8 *) (PacketHead + 1),\r
+ UpdatedExtHdrs,\r
+ UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER)\r
+ );\r
+\r
+ FreePool (UpdatedExtHdrs);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Transmit an IP6 packet. The packet comes either from the IP6\r
+ child's consumer (IpInstance != NULL) or the IP6 driver itself\r
+ (IpInstance == NULL). It will route the packet, fragment it,\r
+ then transmit all the fragments through an interface.\r
+\r
+ @param[in] IpSb The IP6 service instance to transmit the packet.\r
+ @param[in] Interface The IP6 interface to transmit the packet. Ignored\r
+ if NULL.\r
+ @param[in] IpInstance The IP6 child that issues the transmission. It is\r
+ NULL if the packet is from the system.\r
+ @param[in] Packet The user data to send, excluding the IP header.\r
+ @param[in] Head The caller supplied header. The caller should set\r
+ the following header fields: NextHeader, HopLimit,\r
+ Src, Dest, FlowLabel, PayloadLength. This function\r
+ will fill in the Ver, TrafficClass.\r
+ @param[in] ExtHdrs The extension headers to append to the IPv6 basic\r
+ header.\r
+ @param[in] ExtHdrsLen The length of the extension headers.\r
+ @param[in] Callback The callback function to issue when transmission\r
+ completed.\r
+ @param[in] Context The opaque context for the callback.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.\r
+ @retval EFI_NO_MAPPING There is no interface to the destination.\r
+ @retval EFI_NOT_FOUND There is no route to the destination.\r
+ @retval EFI_SUCCESS The packet successfully transmitted.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of\r
+ resources.\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Output (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *Interface OPTIONAL,\r
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN IP6_FRAME_CALLBACK Callback,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IP6_INTERFACE *IpIf;\r
+ EFI_IPv6_ADDRESS NextHop;\r
+ IP6_NEIGHBOR_ENTRY *NeighborCache;\r
+ IP6_ROUTE_CACHE_ENTRY *RouteCache;\r
+ EFI_STATUS Status;\r
+ UINT32 Mtu;\r
+ UINT32 HeadLen;\r
+ UINT16 FragmentOffset;\r
+ UINT8 *LastHeader;\r
+ UINT32 UnFragmentLen;\r
+ UINT32 UnFragmentHdrsLen;\r
+ UINT32 FragmentHdrsLen;\r
+ UINT16 *Checksum;\r
+ UINT16 PacketChecksum;\r
+ UINT16 PseudoChecksum;\r
+ UINT32 Index;\r
+ UINT32 PacketLen;\r
+ UINT32 RealExtLen;\r
+ UINT32 Offset;\r
+ NET_BUF *TmpPacket;\r
+ NET_BUF *Fragment;\r
+ UINT32 Num;\r
+ UINT8 *Buf;\r
+ EFI_IP6_HEADER *PacketHead;\r
+ IP6_ICMP_HEAD *IcmpHead;\r
+ IP6_TXTOKEN_WRAP *Wrap;\r
+ IP6_ROUTE_ENTRY *RouteEntry;\r
+ UINT8 *UpdatedExtHdrs;\r
+ UINT8 NextHeader;\r
+ UINT8 LastHeaderBackup;\r
+ BOOLEAN FragmentHeadInserted;\r
+ UINT8 *ExtHdrsBackup;\r
+ UINT8 NextHeaderBackup;\r
+ EFI_IPv6_ADDRESS Source;\r
+ EFI_IPv6_ADDRESS Destination;\r
+\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ //\r
+ // RFC2460: Each extension header is an integer multiple of 8 octets long,\r
+ // in order to retain 8-octet alignment for subsequent headers.\r
+ //\r
+ if ((ExtHdrsLen & 0x7) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ LastHeader = NULL;\r
+\r
+ Ip6IsExtsValid (\r
+ NULL,\r
+ NULL,\r
+ &Head->NextHeader,\r
+ ExtHdrs,\r
+ ExtHdrsLen,\r
+ FALSE,\r
+ NULL,\r
+ &LastHeader,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+ );\r
+\r
+ //\r
+ // Select an interface/source for system packet, application\r
+ // should select them itself.\r
+ //\r
+ IpIf = Interface;\r
+ if (IpIf == NULL) {\r
+ //\r
+ // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress\r
+ // and destinationaddress is unspecified.\r
+ //\r
+ if (IpInstance == NULL || IpInstance->Interface == NULL) {\r
+ IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress);\r
+ if (IpInstance != NULL) {\r
+ IpInstance->Interface = IpIf;\r
+ }\r
+ } else {\r
+ IpIf = IpInstance->Interface;\r
+ }\r
+ }\r
+\r
+ if (IpIf == NULL) {\r
+ return EFI_NO_MAPPING;\r
+ }\r
+\r
+ //\r
+ // Update the common field in Head here.\r
+ //\r
+ Head->Version = 6;\r
+ Head->TrafficClassL = 0;\r
+ Head->TrafficClassH = 0;\r
+\r
+ Checksum = NULL;\r
+ NextHeader = *LastHeader;\r
+\r
+ switch (NextHeader) {\r
+ case EFI_IP_PROTO_UDP:\r
+ Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+ ASSERT (Packet->Udp != NULL);\r
+ if (Packet->Udp->Checksum == 0) {\r
+ Checksum = &Packet->Udp->Checksum;\r
+ }\r
+ break;\r
+\r
+ case EFI_IP_PROTO_TCP:\r
+ Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+ ASSERT (Packet->Tcp != NULL);\r
+ if (Packet->Tcp->Checksum == 0) {\r
+ Checksum = &Packet->Tcp->Checksum;\r
+ }\r
+ break;\r
+\r
+ case IP6_ICMP:\r
+ //\r
+ // Don't send ICMP packet to an IPv6 anycast address.\r
+ //\r
+ if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
+ ASSERT (IcmpHead != NULL);\r
+ if (IcmpHead->Checksum == 0) {\r
+ Checksum = &IcmpHead->Checksum;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ if (Checksum != NULL) {\r
+ //\r
+ // Calculate the checksum for upper layer protocol if it is not calculated due to lack of\r
+ // IPv6 source address.\r
+ //\r
+ PacketChecksum = NetbufChecksum (Packet);\r
+ PseudoChecksum = NetIp6PseudoHeadChecksum (\r
+ &Head->SourceAddress,\r
+ &Head->DestinationAddress,\r
+ NextHeader,\r
+ Packet->TotalSize\r
+ );\r
+ *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum);\r
+ }\r
+\r
+ Status = Ip6IpSecProcessPacket (\r
+ IpSb,\r
+ Head,\r
+ LastHeader, // no need get the lasthead value for output\r
+ &Packet,\r
+ ExtHdrs,\r
+ ExtHdrsLen,\r
+ EfiIPsecOutBound,\r
+ Context\r
+ );\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ LastHeader = NULL;\r
+ //\r
+ // Check incoming parameters.\r
+ //\r
+ if (!Ip6IsExtsValid (\r
+ IpSb,\r
+ Packet,\r
+ &Head->NextHeader,\r
+ ExtHdrs,\r
+ ExtHdrsLen,\r
+ FALSE,\r
+ NULL,\r
+ &LastHeader,\r
+ &RealExtLen,\r
+ &UnFragmentHdrsLen,\r
+ NULL\r
+ )) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((RealExtLen & 0x7) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ LastHeaderBackup = *LastHeader;\r
+\r
+ //\r
+ // Perform next hop determination:\r
+ // For multicast packets, the next-hop is always the destination address and\r
+ // is considered to be on-link.\r
+ //\r
+ if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
+ IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);\r
+ } else {\r
+ //\r
+ // For unicast packets, use a combination of the Destination Cache, the Prefix List\r
+ // and the Default Router List to determine the IP address of the appropriate next hop.\r
+ //\r
+ RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress);\r
+ if (RouteCache == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop);\r
+ Ip6FreeRouteCacheEntry (RouteCache);\r
+ }\r
+\r
+ //\r
+ // Examines the Neighbor Cache for link-layer information about that neighbor.\r
+ // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error.\r
+ //\r
+ if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) {\r
+ NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop);\r
+ if (NeighborCache == NULL) {\r
+ NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL);\r
+\r
+ if (NeighborCache == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Send out multicast neighbor solicitation for address resolution immediatly.\r
+ //\r
+ Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);\r
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Ip6SendNeighborSolicit (\r
+ IpSb,\r
+ &Source,\r
+ &Destination,\r
+ &NeighborCache->Neighbor,\r
+ &IpSb->SnpMode.CurrentAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ --NeighborCache->Transmit;\r
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1;\r
+ }\r
+\r
+ NeighborCache->Interface = IpIf;\r
+ }\r
+\r
+ UpdatedExtHdrs = NULL;\r
+ ExtHdrsBackup = NULL;\r
+ NextHeaderBackup = 0;\r
+ FragmentHeadInserted = FALSE;\r
+\r
+ //\r
+ // Check whether we received Packet Too Big message for the packet sent to the\r
+ // Destination. If yes include a Fragment Header in the subsequent packets.\r
+ //\r
+ RouteEntry = Ip6FindRouteEntry (\r
+ IpSb->RouteTable,\r
+ &Head->DestinationAddress,\r
+ NULL\r
+ );\r
+ if (RouteEntry != NULL) {\r
+ if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) {\r
+\r
+ //\r
+ // FragmentHead is inserted after Hop-by-Hop Options header, Destination\r
+ // Options header (first occur), Routing header, and before Fragment header,\r
+ // Authentication header, Encapsulating Security Payload header, and\r
+ // Destination Options header (last occur), and upper-layer header.\r
+ //\r
+ Status = Ip6FillFragmentHeader (\r
+ IpSb,\r
+ Head->NextHeader,\r
+ LastHeaderBackup,\r
+ ExtHdrs,\r
+ ExtHdrsLen,\r
+ 0,\r
+ &UpdatedExtHdrs\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {\r
+ NextHeaderBackup = Head->NextHeader;\r
+ Head->NextHeader = IP6_FRAGMENT;\r
+ }\r
+\r
+ ExtHdrsBackup = ExtHdrs;\r
+ ExtHdrs = UpdatedExtHdrs;\r
+ ExtHdrsLen = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);\r
+ RealExtLen = RealExtLen + sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+ mIp6Id++;\r
+\r
+ FragmentHeadInserted = TRUE;\r
+ }\r
+\r
+ Ip6FreeRouteEntry (RouteEntry);\r
+ }\r
+\r
+ //\r
+ // OK, selected the source and route, fragment the packet then send\r
+ // them. Tag each fragment other than the first one as spawn from it.\r
+ // Each extension header is an integar multiple of 8 octets long, in\r
+ // order to retain 8-octet alignment for subsequent headers.\r
+ //\r
+ Mtu = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER);\r
+ HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen;\r
+\r
+ if (Packet->TotalSize + HeadLen > Mtu) {\r
+ //\r
+ // Remove the inserted Fragment Header since we need fragment the packet.\r
+ //\r
+ if (FragmentHeadInserted) {\r
+ ExtHdrs = ExtHdrsBackup;\r
+ ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+ if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {\r
+ Head->NextHeader = NextHeaderBackup;\r
+ }\r
+ }\r
+\r
+ FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen;\r
+\r
+ //\r
+ // The packet is beyond the maximum which can be described through the\r
+ // fragment offset field in Fragment header.\r
+ //\r
+ if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) {\r
+ Status = EFI_BAD_BUFFER_SIZE;\r
+ goto Error;\r
+ }\r
+\r
+ if (FragmentHdrsLen != 0) {\r
+ //\r
+ // Append the fragmentable extension hdrs before the upper layer payload\r
+ // to form a new NET_BUF. This NET_BUF contains all the buffer which will\r
+ // be fragmented below.\r
+ //\r
+ TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen);\r
+ ASSERT (TmpPacket != NULL);\r
+\r
+ //\r
+ // Allocate the space to contain the fragmentable hdrs and copy the data.\r
+ //\r
+ Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE);\r
+ ASSERT (Buf != NULL);\r
+ CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen);\r
+\r
+ //\r
+ // Free the old Packet.\r
+ //\r
+ NetbufFree (Packet);\r
+ Packet = TmpPacket;\r
+ }\r
+\r
+ //\r
+ // The unfragment part which appears in every fragmented IPv6 packet includes\r
+ // the IPv6 header, the unfragmentable extension hdrs and the fragment header.\r
+ //\r
+ UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER);\r
+\r
+ //\r
+ // Mtu now is the length of the fragment part in a full-length fragment.\r
+ //\r
+ Mtu = (Mtu - UnFragmentLen) & (~0x07);\r
+ Num = (Packet->TotalSize + Mtu - 1) / Mtu;\r
+\r
+ for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) {\r
+ //\r
+ // Get fragment from the Packet, append UnFragnmentLen spare buffer\r
+ // before the fragmented data, the corresponding data is filled in later.\r
+ //\r
+ Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen);\r
+ if (Fragment == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+\r
+ FragmentOffset = (UINT16) ((UINT16) Offset | 0x1);\r
+ if (Index == Num - 1){\r
+ //\r
+ // The last fragment, clear the M flag.\r
+ //\r
+ FragmentOffset &= (~0x1);\r
+ }\r
+\r
+ Status = Ip6PrependHead (\r
+ IpSb,\r
+ Fragment,\r
+ Head,\r
+ FragmentOffset,\r
+ ExtHdrs,\r
+ ExtHdrsLen,\r
+ LastHeaderBackup,\r
+ UnFragmentLen\r
+ );\r
+ ASSERT (Status == EFI_SUCCESS);\r
+\r
+ Status = Ip6SendFrame (\r
+ IpIf,\r
+ IpInstance,\r
+ Fragment,\r
+ &NextHop,\r
+ Ip6SysPacketSent,\r
+ Packet\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ //\r
+ // The last fragment of upper layer packet, update the IP6 token status.\r
+ //\r
+ if ((Index == Num -1) && (Context != NULL)) {\r
+ Wrap = (IP6_TXTOKEN_WRAP *) Context;\r
+ Wrap->Token->Status = Status;\r
+ }\r
+\r
+ Offset += PacketLen;\r
+ PacketLen = Packet->TotalSize - Offset;\r
+ if (PacketLen > Mtu) {\r
+ PacketLen = Mtu;\r
+ }\r
+ }\r
+\r
+ NetbufFree (Packet);\r
+ mIp6Id++;\r
+\r
+ if (UpdatedExtHdrs != NULL) {\r
+ FreePool (UpdatedExtHdrs);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Need not fragment the packet, send it in one frame.\r
+ //\r
+ PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);\r
+ if (PacketHead == NULL) {\r
+ Status = EFI_BAD_BUFFER_SIZE;\r
+ goto Error;\r
+ }\r
+\r
+ CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));\r
+ Packet->Ip.Ip6 = PacketHead;\r
+\r
+ if (ExtHdrs != NULL) {\r
+ Buf = (UINT8 *) (PacketHead + 1);\r
+ CopyMem (Buf, ExtHdrs, ExtHdrsLen);\r
+ }\r
+\r
+ if (UpdatedExtHdrs != NULL) {\r
+ //\r
+ // A Fragment Header is inserted to the packet, update the payload length.\r
+ //\r
+ PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) +\r
+ sizeof (IP6_FRAGMENT_HEADER));\r
+ PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength);\r
+ FreePool (UpdatedExtHdrs);\r
+ }\r
+\r
+ return Ip6SendFrame (\r
+ IpIf,\r
+ IpInstance,\r
+ Packet,\r
+ &NextHop,\r
+ Callback,\r
+ Context\r
+ );\r
+\r
+Error:\r
+ if (UpdatedExtHdrs != NULL) {\r
+ FreePool (UpdatedExtHdrs);\r
+ }\r
+ Ip6CancelPacket (IpIf, Packet, Status);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The filter function to find a packet and all its fragments.\r
+ The packet's fragments have their Context set to the packet.\r
+\r
+ @param[in] Frame The frames hold by the low level interface.\r
+ @param[in] Context Context to the function, which is the packet.\r
+\r
+ @retval TRUE This is the packet to cancel or its fragments.\r
+ @retval FALSE This is an unrelated packet.\r
+\r
+**/\r
+BOOLEAN\r
+Ip6CancelPacketFragments (\r
+ IN IP6_LINK_TX_TOKEN *Frame,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Remove all the frames on the interface that pass the FrameToCancel,\r
+ either queued on ARP queues or that have already been delivered to\r
+ MNP and not yet recycled.\r
+\r
+ @param[in] Interface Interface to remove the frames from.\r
+ @param[in] IoStatus The transmit status returned to the frames' callback.\r
+ @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all.\r
+ @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if\r
+ FrameToCancel is NULL.\r
+\r
+**/\r
+VOID\r
+Ip6CancelFrames (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN EFI_STATUS IoStatus,\r
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_LINK_TX_TOKEN *Token;\r
+ IP6_SERVICE *IpSb;\r
+ IP6_NEIGHBOR_ENTRY *ArpQue;\r
+ EFI_STATUS Status;\r
+\r
+ IpSb = Interface->Service;\r
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
+\r
+ //\r
+ // Cancel all the pending frames on ARP requests\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {\r
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);\r
+\r
+ Status = Ip6FreeNeighborEntry (\r
+ IpSb,\r
+ ArpQue,\r
+ FALSE,\r
+ FALSE,\r
+ IoStatus,\r
+ FrameToCancel,\r
+ Context\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ //\r
+ // Cancel all the frames that have been delivered to MNP\r
+ // but not yet recycled.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {\r
+ Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);\r
+\r
+ if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {\r
+ IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken);\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Cancel the Packet and all its fragments.\r
+\r
+ @param[in] IpIf The interface from which the Packet is sent.\r
+ @param[in] Packet The Packet to cancel.\r
+ @param[in] IoStatus The status returns to the sender.\r
+\r
+**/\r
+VOID\r
+Ip6CancelPacket (\r
+ IN IP6_INTERFACE *IpIf,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_STATUS IoStatus\r
+ )\r
+{\r
+ Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet);\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ The internal functions and routines to transmit the IP6 packet.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_OUTPUT_H__\r
+#define __EFI_IP6_OUTPUT_H__\r
+\r
+extern UINT32 mIp6Id;\r
+\r
+/**\r
+ Output all the available source addresses to the list entry head SourceList. The\r
+ number of source addresses are also returned.\r
+\r
+ @param[in] IpSb Points to a IP6 service binding instance.\r
+ @param[in] Destination The IPv6 destination address.\r
+ @param[out] Source The selected IPv6 source address according to\r
+ the Destination.\r
+\r
+ @retval EFI_SUCCESS The source addresses were copied to the list entry\r
+ head SourceList.\r
+ @retval EFI_NO_MAPPING The IPv6 stack is not auto configured.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6SelectSourceAddress (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Destination,\r
+ OUT EFI_IPv6_ADDRESS *Source\r
+ );\r
+\r
+/**\r
+ The default callback function for system generated packet.\r
+ It will free the packet.\r
+\r
+ @param[in] Packet The packet that transmitted.\r
+ @param[in] IoStatus The result of the transmission: succeeded or failed.\r
+ @param[in] LinkFlag Not used when transmission. Check IP6_FRAME_CALLBACK\r
+ for reference.\r
+ @param[in] Context The context provided by us.\r
+\r
+**/\r
+VOID\r
+Ip6SysPacketSent (\r
+ NET_BUF *Packet,\r
+ EFI_STATUS IoStatus,\r
+ UINT32 LinkFlag,\r
+ VOID *Context\r
+ );\r
+\r
+/**\r
+ Transmit an IP6 packet. The packet comes either from the IP6\r
+ child's consumer (IpInstance != NULL) or the IP6 driver itself\r
+ (IpInstance == NULL). It will route the packet, fragment it,\r
+ then transmit all the fragments through an interface.\r
+\r
+ @param[in] IpSb The IP6 service instance to transmit the packet.\r
+ @param[in] Interface The IP6 interface to transmit the packet. Ignored\r
+ if NULL.\r
+ @param[in] IpInstance The IP6 child that issues the transmission. It is\r
+ NULL if the packet is from the system.\r
+ @param[in] Packet The user data to send, excluding the IP header.\r
+ @param[in] Head The caller supplied header. The caller should set\r
+ the following header fields: NextHeader, HopLimit,\r
+ Src, Dest, FlowLabel, PayloadLength. This function\r
+ will fill in the Ver, TrafficClass.\r
+ @param[in] ExtHdrs The extension headers to append to the IPv6 basic\r
+ header.\r
+ @param[in] ExtHdrsLen The length of the extension headers.\r
+ @param[in] Callback The callback function to issue when transmission\r
+ completed.\r
+ @param[in] Context The opaque context for the callback.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.\r
+ @retval EFI_NO_MAPPING There is no interface to the destination.\r
+ @retval EFI_NOT_FOUND There is no route to the destination.\r
+ @retval EFI_SUCCESS The packet successfully transmitted.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of\r
+ resources.\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6Output (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN IP6_INTERFACE *Interface OPTIONAL,\r
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_IP6_HEADER *Head,\r
+ IN UINT8 *ExtHdrs,\r
+ IN UINT32 ExtHdrsLen,\r
+ IN IP6_FRAME_CALLBACK Callback,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Remove all the frames on the interface that pass the FrameToCancel,\r
+ either queued on ARP queues, or that have already been delivered to\r
+ MNP and not yet recycled.\r
+\r
+ @param[in] Interface Interface to remove the frames from.\r
+ @param[in] IoStatus The transmit status returned to the frames' callback.\r
+ @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all.\r
+ @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if\r
+ FrameToCancel is NULL.\r
+\r
+**/\r
+VOID\r
+Ip6CancelFrames (\r
+ IN IP6_INTERFACE *Interface,\r
+ IN EFI_STATUS IoStatus,\r
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ Cancel the Packet and all its fragments.\r
+\r
+ @param[in] IpIf The interface from which the Packet is sent.\r
+ @param[in] Packet The Packet to cancel.\r
+ @param[in] IoStatus The status returns to the sender.\r
+\r
+**/\r
+VOID\r
+Ip6CancelPacket (\r
+ IN IP6_INTERFACE *IpIf,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_STATUS IoStatus\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The functions and routines to handle the route caches and route table.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Ip6Impl.h"\r
+\r
+/**\r
+ This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value\r
+ as the index of the route cache bucket according to the prefix of two IPv6 addresses.\r
+\r
+ @param[in] Ip1 The IPv6 address.\r
+ @param[in] Ip2 The IPv6 address.\r
+\r
+ @return The hash value of the prefix of two IPv6 addresses.\r
+\r
+**/\r
+UINT32\r
+Ip6RouteCacheHash (\r
+ IN EFI_IPv6_ADDRESS *Ip1,\r
+ IN EFI_IPv6_ADDRESS *Ip2\r
+ )\r
+{\r
+ UINT32 Prefix1;\r
+ UINT32 Prefix2;\r
+\r
+ Prefix1 = *((UINT32 *) ((UINTN *) (Ip1)));\r
+ Prefix2 = *((UINT32 *) ((UINTN *) (Ip2)));\r
+\r
+ return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE);\r
+}\r
+\r
+/**\r
+ Allocate a route entry then initialize it with the Destination/PrefixLength\r
+ and Gateway.\r
+\r
+ @param[in] Destination The IPv6 destination address. This is an optional\r
+ parameter that may be NULL.\r
+ @param[in] PrefixLength The destination network's prefix length.\r
+ @param[in] GatewayAddress The next hop address. This is an optional parameter\r
+ that may be NULL.\r
+\r
+ @return NULL if failed to allocate memeory; otherwise, the newly created route entry.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6CreateRouteEntry (\r
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL\r
+ )\r
+{\r
+ IP6_ROUTE_ENTRY *RtEntry;\r
+\r
+ RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY));\r
+\r
+ if (RtEntry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ RtEntry->RefCnt = 1;\r
+ RtEntry->Flag = 0;\r
+ RtEntry->PrefixLength = PrefixLength;\r
+\r
+ if (Destination != NULL) {\r
+ IP6_COPY_ADDRESS (&RtEntry->Destination, Destination);\r
+ }\r
+\r
+ if (GatewayAddress != NULL) {\r
+ IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress);\r
+ }\r
+\r
+ return RtEntry;\r
+}\r
+\r
+/**\r
+ Free the route table entry. It is reference counted.\r
+\r
+ @param[in, out] RtEntry The route entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteEntry (\r
+ IN OUT IP6_ROUTE_ENTRY *RtEntry\r
+ )\r
+{\r
+ ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0));\r
+\r
+ if (--RtEntry->RefCnt == 0) {\r
+ FreePool (RtEntry);\r
+ }\r
+}\r
+\r
+/**\r
+ Search the route table for a most specific match to the Dst. It searches\r
+ from the longest route area (prefix length == 128) to the shortest route area\r
+ (default routes). In each route area, it will first search the instance's\r
+ route table, then the default route table. This is required per the following\r
+ requirements:\r
+ 1. IP search the route table for a most specific match.\r
+ 2. The local route entries have precedence over the default route entry.\r
+\r
+ @param[in] RtTable The route table to search from.\r
+ @param[in] Destination The destionation address to search. If NULL, search\r
+ the route table by NextHop.\r
+ @param[in] NextHop The next hop address. If NULL, search the route table\r
+ by Destination.\r
+\r
+ @return NULL if no route matches the Dst. Otherwise, the point to the\r
+ @return most specific route to the Dst.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6FindRouteEntry (\r
+ IN IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *NextHop OPTIONAL\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_ROUTE_ENTRY *RtEntry;\r
+ INTN Index;\r
+\r
+ ASSERT (Destination != NULL || NextHop != NULL);\r
+\r
+ RtEntry = NULL;\r
+\r
+ for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {\r
+ NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) {\r
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+ if (Destination != NULL) {\r
+ if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) {\r
+ NET_GET_REF (RtEntry);\r
+ return RtEntry;\r
+ }\r
+ } else if (NextHop != NULL) {\r
+ if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) {\r
+ NET_GET_REF (RtEntry);\r
+ return RtEntry;\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Allocate and initialize a IP6 route cache entry.\r
+\r
+ @param[in] Dst The destination address.\r
+ @param[in] Src The source address.\r
+ @param[in] GateWay The next hop address.\r
+ @param[in] Tag The tag from the caller. This marks all the cache entries\r
+ spawned from one route table entry.\r
+\r
+ @return NULL if failed to allocate memory for the cache. Otherwise, point\r
+ to the created route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6CreateRouteCacheEntry (\r
+ IN EFI_IPv6_ADDRESS *Dst,\r
+ IN EFI_IPv6_ADDRESS *Src,\r
+ IN EFI_IPv6_ADDRESS *GateWay,\r
+ IN UINTN Tag\r
+ )\r
+{\r
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;\r
+\r
+ RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY));\r
+\r
+ if (RtCacheEntry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ RtCacheEntry->RefCnt = 1;\r
+ RtCacheEntry->Tag = Tag;\r
+\r
+ IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst);\r
+ IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src);\r
+ IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay);\r
+\r
+ return RtCacheEntry;\r
+}\r
+\r
+/**\r
+ Free the route cache entry. It is reference counted.\r
+\r
+ @param[in, out] RtCacheEntry The route cache entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteCacheEntry (\r
+ IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry\r
+ )\r
+{\r
+ ASSERT (RtCacheEntry->RefCnt > 0);\r
+\r
+ if (--RtCacheEntry->RefCnt == 0) {\r
+ FreePool (RtCacheEntry);\r
+ }\r
+}\r
+\r
+/**\r
+ Find a route cache with the destination and source address. This is\r
+ used by the ICMPv6 redirect messasge process.\r
+\r
+ @param[in] RtTable The route table to search the cache for.\r
+ @param[in] Dest The destination address.\r
+ @param[in] Src The source address.\r
+\r
+ @return NULL if no route entry to the (Dest, Src). Otherwise, the pointer\r
+ to the correct route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6FindRouteCache (\r
+ IN IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Dest,\r
+ IN EFI_IPv6_ADDRESS *Src\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;\r
+ UINT32 Index;\r
+\r
+ Index = IP6_ROUTE_CACHE_HASH (Dest, Src);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {\r
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+\r
+ if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) {\r
+ NET_GET_REF (RtCacheEntry);\r
+ return RtCacheEntry;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number\r
+ of EFI_IP6_ROUTE_TABLE is also returned.\r
+\r
+ @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used.\r
+ @param[out] EfiRouteCount The number of returned route entries.\r
+ @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE.\r
+ If NULL, only the route entry count is returned.\r
+\r
+ @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiRouteTable (\r
+ IN IP6_ROUTE_TABLE *RouteTable,\r
+ OUT UINT32 *EfiRouteCount,\r
+ OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IP6_ROUTE_ENTRY *RtEntry;\r
+ EFI_IP6_ROUTE_TABLE *EfiTable;\r
+ UINT32 Count;\r
+ INT32 Index;\r
+\r
+ ASSERT (EfiRouteCount != NULL);\r
+\r
+ Count = RouteTable->TotalNum;\r
+ *EfiRouteCount = Count;\r
+\r
+ if ((EfiRouteTable == NULL) || (Count == 0)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (*EfiRouteTable == NULL) {\r
+ *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count);\r
+ if (*EfiRouteTable == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ EfiTable = *EfiRouteTable;\r
+\r
+ //\r
+ // Copy the route entry to EFI route table.\r
+ //\r
+ Count = 0;\r
+\r
+ for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {\r
+\r
+ NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) {\r
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+ Ip6CopyAddressByPrefix (\r
+ &EfiTable[Count].Destination,\r
+ &RtEntry->Destination,\r
+ RtEntry->PrefixLength\r
+ );\r
+\r
+ IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop);\r
+ EfiTable[Count].PrefixLength = RtEntry->PrefixLength;\r
+\r
+ Count++;\r
+ }\r
+ }\r
+\r
+ ASSERT (Count == RouteTable->TotalNum);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create an empty route table. This includes its internal route cache.\r
+\r
+ @return NULL if failed to allocate memory for the route table. Otherwise,\r
+ the point to newly created route table.\r
+\r
+**/\r
+IP6_ROUTE_TABLE *\r
+Ip6CreateRouteTable (\r
+ VOID\r
+ )\r
+{\r
+ IP6_ROUTE_TABLE *RtTable;\r
+ UINT32 Index;\r
+\r
+ RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE));\r
+ if (RtTable == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ RtTable->RefCnt = 1;\r
+ RtTable->TotalNum = 0;\r
+\r
+ for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {\r
+ InitializeListHead (&RtTable->RouteArea[Index]);\r
+ }\r
+\r
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+ InitializeListHead (&RtTable->Cache.CacheBucket[Index]);\r
+ RtTable->Cache.CacheNum[Index] = 0;\r
+ }\r
+\r
+ return RtTable;\r
+}\r
+\r
+/**\r
+ Free the route table and its associated route cache. Route\r
+ table is reference counted.\r
+\r
+ @param[in, out] RtTable The route table to free.\r
+\r
+**/\r
+VOID\r
+Ip6CleanRouteTable (\r
+ IN OUT IP6_ROUTE_TABLE *RtTable\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_ROUTE_ENTRY *RtEntry;\r
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;\r
+ UINT32 Index;\r
+\r
+ ASSERT (RtTable->RefCnt > 0);\r
+\r
+ if (--RtTable->RefCnt > 0) {\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // Free all the route table entry and its route cache.\r
+ //\r
+ for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) {\r
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+ RemoveEntryList (Entry);\r
+ Ip6FreeRouteEntry (RtEntry);\r
+ }\r
+ }\r
+\r
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) {\r
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+ RemoveEntryList (Entry);\r
+ Ip6FreeRouteCacheEntry (RtCacheEntry);\r
+ }\r
+ }\r
+\r
+ FreePool (RtTable);\r
+}\r
+\r
+/**\r
+ Remove all the cache entries bearing the Tag. When a route cache\r
+ entry is created, it is tagged with the address of route entry\r
+ from which it is spawned. When a route entry is deleted, the cache\r
+ entries spawned from it are also deleted.\r
+\r
+ @param[in] RtCache Route cache to remove the entries from.\r
+ @param[in] Tag The Tag of the entries to remove.\r
+\r
+**/\r
+VOID\r
+Ip6PurgeRouteCache (\r
+ IN IP6_ROUTE_CACHE *RtCache,\r
+ IN UINTN Tag\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;\r
+ UINT32 Index;\r
+\r
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {\r
+\r
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);\r
+\r
+ if (RtCacheEntry->Tag == Tag) {\r
+ RemoveEntryList (Entry);\r
+ Ip6FreeRouteCacheEntry (RtCacheEntry);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Add a route entry to the route table. It is the help function for EfiIp6Routes.\r
+\r
+ @param[in, out] RtTable Route table to add route to.\r
+ @param[in] Destination The destination of the network.\r
+ @param[in] PrefixLength The PrefixLength of the destination.\r
+ @param[in] GatewayAddress The next hop address.\r
+\r
+ @retval EFI_ACCESS_DENIED The same route already exists.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry.\r
+ @retval EFI_SUCCESS The route was added successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddRoute (\r
+ IN OUT IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Destination,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress\r
+ )\r
+{\r
+ LIST_ENTRY *ListHead;\r
+ LIST_ENTRY *Entry;\r
+ IP6_ROUTE_ENTRY *Route;\r
+\r
+ ListHead = &RtTable->RouteArea[PrefixLength];\r
+\r
+ //\r
+ // First check whether the route exists\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, ListHead) {\r
+ Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+ if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) &&\r
+ EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Create a route entry and insert it to the route area.\r
+ //\r
+ Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress);\r
+\r
+ if (Route == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ if (NetIp6IsUnspecifiedAddr (GatewayAddress)) {\r
+ Route->Flag = IP6_DIRECT_ROUTE;\r
+ }\r
+\r
+ InsertHeadList (ListHead, &Route->Link);\r
+ RtTable->TotalNum++;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Remove a route entry and all the route caches spawn from it.\r
+ It is the help function for EfiIp6Routes.\r
+\r
+ @param[in, out] RtTable The route table to remove the route from.\r
+ @param[in] Destination The destination network.\r
+ @param[in] PrefixLength The PrefixLength of the Destination.\r
+ @param[in] GatewayAddress The next hop address.\r
+\r
+ @retval EFI_SUCCESS The route entry was successfully removed.\r
+ @retval EFI_NOT_FOUND There is no route entry in the table with that\r
+ property.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelRoute (\r
+ IN OUT IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Destination,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress\r
+ )\r
+{\r
+ LIST_ENTRY *ListHead;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ IP6_ROUTE_ENTRY *Route;\r
+ UINT32 TotalNum;\r
+\r
+ ListHead = &RtTable->RouteArea[PrefixLength];\r
+ TotalNum = RtTable->TotalNum;\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) {\r
+ Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);\r
+\r
+ if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) {\r
+ continue;\r
+ }\r
+ if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {\r
+ continue;\r
+ }\r
+\r
+ Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route);\r
+ RemoveEntryList (Entry);\r
+ Ip6FreeRouteEntry (Route);\r
+\r
+ ASSERT (RtTable->TotalNum > 0);\r
+ RtTable->TotalNum--;\r
+ }\r
+\r
+ return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Search the route table to route the packet. Return/create a route\r
+ cache if there is a route to the destination.\r
+\r
+ @param[in] IpSb The IP6 service data.\r
+ @param[in] Dest The destination address to search for.\r
+ @param[in] Src The source address to search for.\r
+\r
+ @return NULL if it failed to route the packet. Otherwise, a route cache\r
+ entry that can be used to route packets.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6Route (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Dest,\r
+ IN EFI_IPv6_ADDRESS *Src\r
+ )\r
+{\r
+ IP6_ROUTE_TABLE *RtTable;\r
+ LIST_ENTRY *ListHead;\r
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;\r
+ IP6_ROUTE_ENTRY *RtEntry;\r
+ EFI_IPv6_ADDRESS NextHop;\r
+ UINT32 Index;\r
+\r
+ RtTable = IpSb->RouteTable;\r
+\r
+ ASSERT (RtTable != NULL);\r
+\r
+ //\r
+ // Search the destination cache in IP6_ROUTE_TABLE.\r
+ //\r
+ Index = IP6_ROUTE_CACHE_HASH (Dest, Src);\r
+ ListHead = &RtTable->Cache.CacheBucket[Index];\r
+\r
+ RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src);\r
+\r
+ //\r
+ // If found, promote the cache entry to the head of the hash bucket.\r
+ //\r
+ if (RtCacheEntry != NULL) {\r
+ RemoveEntryList (&RtCacheEntry->Link);\r
+ InsertHeadList (ListHead, &RtCacheEntry->Link);\r
+ return RtCacheEntry;\r
+ }\r
+\r
+ //\r
+ // Search the route table for the most specific route\r
+ //\r
+ RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL);\r
+ if (RtEntry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Found a route to the Dest, if it is a direct route, the packet\r
+ // will be send directly to the destination, such as for connected\r
+ // network. Otherwise, it is an indirect route, the packet will be\r
+ // send the next hop router.\r
+ //\r
+ if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) {\r
+ IP6_COPY_ADDRESS (&NextHop, Dest);\r
+ } else {\r
+ IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop);\r
+ }\r
+\r
+ Ip6FreeRouteEntry (RtEntry);\r
+\r
+ //\r
+ // Create a route cache entry, and tag it as spawned from this route entry\r
+ //\r
+ RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry);\r
+\r
+ if (RtCacheEntry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ InsertHeadList (ListHead, &RtCacheEntry->Link);\r
+ NET_GET_REF (RtCacheEntry);\r
+ RtTable->Cache.CacheNum[Index]++;\r
+\r
+ return RtCacheEntry;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ EFI IP6 route table and route cache table defintions.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_IP6_ROUTE_H__\r
+#define __EFI_IP6_ROUTE_H__\r
+\r
+#define IP6_DIRECT_ROUTE 0x00000001\r
+#define IP6_PACKET_TOO_BIG 0x00000010\r
+\r
+#define IP6_ROUTE_CACHE_HASH_SIZE 31\r
+///\r
+/// Max NO. of cache entry per hash bucket\r
+///\r
+#define IP6_ROUTE_CACHE_MAX 32\r
+\r
+#define IP6_ROUTE_CACHE_HASH(Ip1, Ip2) Ip6RouteCacheHash ((Ip1), (Ip2))\r
+\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ INTN RefCnt;\r
+ UINT32 Flag;\r
+ UINT8 PrefixLength;\r
+ EFI_IPv6_ADDRESS Destination;\r
+ EFI_IPv6_ADDRESS NextHop;\r
+} IP6_ROUTE_ENTRY;\r
+\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ INTN RefCnt;\r
+ UINTN Tag;\r
+ EFI_IPv6_ADDRESS Destination;\r
+ EFI_IPv6_ADDRESS Source;\r
+ EFI_IPv6_ADDRESS NextHop;\r
+} IP6_ROUTE_CACHE_ENTRY;\r
+\r
+typedef struct {\r
+ LIST_ENTRY CacheBucket[IP6_ROUTE_CACHE_HASH_SIZE];\r
+ UINT8 CacheNum[IP6_ROUTE_CACHE_HASH_SIZE];\r
+} IP6_ROUTE_CACHE;\r
+\r
+//\r
+// Each IP6 instance has its own route table. Each ServiceBinding\r
+// instance has a default route table and default address.\r
+//\r
+// All the route table entries with the same prefix length are linked\r
+// together in one route area. For example, RouteArea[0] contains\r
+// the default routes. A route table also contains a route cache.\r
+//\r
+\r
+typedef struct _IP6_ROUTE_TABLE {\r
+ INTN RefCnt;\r
+ UINT32 TotalNum;\r
+ LIST_ENTRY RouteArea[IP6_PREFIX_NUM];\r
+ IP6_ROUTE_CACHE Cache;\r
+} IP6_ROUTE_TABLE;\r
+\r
+/**\r
+ This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value\r
+ as the index of the route cache bucket according to the prefix of two IPv6 addresses.\r
+\r
+ @param[in] Ip1 The IPv6 address.\r
+ @param[in] Ip2 The IPv6 address.\r
+\r
+ @return The hash value of the prefix of two IPv6 addresses.\r
+\r
+**/\r
+UINT32\r
+Ip6RouteCacheHash (\r
+ IN EFI_IPv6_ADDRESS *Ip1,\r
+ IN EFI_IPv6_ADDRESS *Ip2\r
+ );\r
+\r
+/**\r
+ Allocate and initialize an IP6 route cache entry.\r
+\r
+ @param[in] Dst The destination address.\r
+ @param[in] Src The source address.\r
+ @param[in] GateWay The next hop address.\r
+ @param[in] Tag The tag from the caller. This marks all the cache entries\r
+ spawned from one route table entry.\r
+\r
+ @return NULL if it failed to allocate memory for the cache. Otherwise, point\r
+ to the created route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6CreateRouteCacheEntry (\r
+ IN EFI_IPv6_ADDRESS *Dst,\r
+ IN EFI_IPv6_ADDRESS *Src,\r
+ IN EFI_IPv6_ADDRESS *GateWay,\r
+ IN UINTN Tag\r
+ );\r
+\r
+/**\r
+ Free the route cache entry. It is reference counted.\r
+\r
+ @param[in, out] RtCacheEntry The route cache entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteCacheEntry (\r
+ IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry\r
+ );\r
+\r
+/**\r
+ Find a route cache with the destination and source address. This is\r
+ used by the ICMPv6 redirect messasge process.\r
+\r
+ @param[in] RtTable The route table to search the cache for.\r
+ @param[in] Dest The destination address.\r
+ @param[in] Src The source address.\r
+\r
+ @return NULL if no route entry to the (Dest, Src). Otherwise, point\r
+ to the correct route cache entry.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6FindRouteCache (\r
+ IN IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Dest,\r
+ IN EFI_IPv6_ADDRESS *Src\r
+ );\r
+\r
+/**\r
+ Build a array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number\r
+ of EFI_IP6_ROUTE_TABLE is also returned.\r
+\r
+ @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used.\r
+ @param[out] EfiRouteCount The number of returned route entries.\r
+ @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE.\r
+ If NULL, only the route entry count is returned.\r
+\r
+ @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6BuildEfiRouteTable (\r
+ IN IP6_ROUTE_TABLE *RouteTable,\r
+ OUT UINT32 *EfiRouteCount,\r
+ OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL\r
+ );\r
+\r
+/**\r
+ Create an empty route table, includes its internal route cache.\r
+\r
+ @return NULL if failed to allocate memory for the route table. Otherwise,\r
+ the point to newly created route table.\r
+\r
+**/\r
+IP6_ROUTE_TABLE *\r
+Ip6CreateRouteTable (\r
+ VOID\r
+ );\r
+\r
+/**\r
+ Free the route table and its associated route cache. Route\r
+ table is reference counted.\r
+\r
+ @param[in, out] RtTable The route table to free.\r
+\r
+**/\r
+VOID\r
+Ip6CleanRouteTable (\r
+ IN OUT IP6_ROUTE_TABLE *RtTable\r
+ );\r
+\r
+/**\r
+ Allocate a route entry then initialize it with the Destination/PrefixLength\r
+ and Gateway.\r
+\r
+ @param[in] Destination The IPv6 destination address. This is an optional\r
+ parameter that may be NULL.\r
+ @param[in] PrefixLength The destination network's prefix length.\r
+ @param[in] GatewayAddress The next hop address. This is optional parameter\r
+ that may be NULL.\r
+\r
+ @return NULL if it failed to allocate memeory. Otherwise, the newly created route entry.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6CreateRouteEntry (\r
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ Search the route table for a most specific match to the Dst. It searches\r
+ from the longest route area (prefix length == 128) to the shortest route area\r
+ (default routes). In each route area, it will first search the instance's\r
+ route table, then the default route table. This is required per the following\r
+ requirements:\r
+ 1. IP search the route table for a most specific match.\r
+ 2. The local route entries have precedence over the default route entry.\r
+\r
+ @param[in] RtTable The route table to search from.\r
+ @param[in] Destination The destionation address to search. If NULL, search\r
+ the route table by NextHop.\r
+ @param[in] NextHop The next hop address. If NULL, search the route table\r
+ by Destination.\r
+\r
+ @return NULL if no route matches the Dst. Otherwise the point to the\r
+ most specific route to the Dst.\r
+\r
+**/\r
+IP6_ROUTE_ENTRY *\r
+Ip6FindRouteEntry (\r
+ IN IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,\r
+ IN EFI_IPv6_ADDRESS *NextHop OPTIONAL\r
+ );\r
+\r
+/**\r
+ Free the route table entry. It is reference counted.\r
+\r
+ @param[in, out] RtEntry The route entry to free.\r
+\r
+**/\r
+VOID\r
+Ip6FreeRouteEntry (\r
+ IN OUT IP6_ROUTE_ENTRY *RtEntry\r
+ );\r
+\r
+/**\r
+ Add a route entry to the route table. It is the help function for EfiIp6Routes.\r
+\r
+ @param[in, out] RtTable Route table to add route to.\r
+ @param[in] Destination The destination of the network.\r
+ @param[in] PrefixLength The PrefixLength of the destination.\r
+ @param[in] GatewayAddress The next hop address.\r
+\r
+ @retval EFI_ACCESS_DENIED The same route already exists.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry.\r
+ @retval EFI_SUCCESS The route was added successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6AddRoute (\r
+ IN OUT IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Destination,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress\r
+ );\r
+\r
+/**\r
+ Remove a route entry and all the route caches spawn from it.\r
+ It is the help function for EfiIp6Routes.\r
+\r
+ @param[in, out] RtTable The route table to remove the route from.\r
+ @param[in] Destination The destination network.\r
+ @param[in] PrefixLength The PrefixLength of the Destination.\r
+ @param[in] GatewayAddress The next hop address.\r
+\r
+ @retval EFI_SUCCESS Successfully removed the route entry.\r
+ @retval EFI_NOT_FOUND There is no route entry in the table with that\r
+ properity.\r
+\r
+**/\r
+EFI_STATUS\r
+Ip6DelRoute (\r
+ IN OUT IP6_ROUTE_TABLE *RtTable,\r
+ IN EFI_IPv6_ADDRESS *Destination,\r
+ IN UINT8 PrefixLength,\r
+ IN EFI_IPv6_ADDRESS *GatewayAddress\r
+ );\r
+\r
+/**\r
+ Search the route table to route the packet. Return/create a route\r
+ cache if there is a route to the destination.\r
+\r
+ @param[in] IpSb The IP6 service data.\r
+ @param[in] Dest The destination address to search for.\r
+ @param[in] Src The source address to search for.\r
+\r
+ @return NULL if failed to route packet. Otherwise, a route cache\r
+ entry that can be used to route packet.\r
+\r
+**/\r
+IP6_ROUTE_CACHE_ENTRY *\r
+Ip6Route (\r
+ IN IP6_SERVICE *IpSb,\r
+ IN EFI_IPv6_ADDRESS *Dest,\r
+ IN EFI_IPv6_ADDRESS *Src\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ UEFI Component Name(2) protocol implementation for IPsec driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecImpl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle, OPTIONAL\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName = {\r
+ IpSecComponentNameGetDriverName,\r
+ IpSecComponentNameGetControllerName,\r
+ "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IpSecComponentNameGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IpSecComponentNameGetControllerName,\r
+ "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIpSecDriverNameTable[] = {\r
+ {\r
+ "eng;en",\r
+ L"IpSec Driver"\r
+ },\r
+ {\r
+ NULL,\r
+ NULL\r
+ }\r
+};\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This, and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mIpSecDriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN) (This == &gIpSecComponentName)\r
+ );\r
+}\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle, OPTIONAL\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
--- /dev/null
+/** @file\r
+ The implementation of IPSEC_CONFIG_PROTOCOL.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfigImpl.h"\r
+#include "IpSecDebug.h"\r
+\r
+LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];\r
+BOOLEAN mSetBySelf = FALSE;\r
+\r
+//\r
+// Common CompareSelector routine entry for spd/sad/pad.\r
+//\r
+IPSEC_COMPARE_SELECTOR mCompareSelector[] = {\r
+ (IPSEC_COMPARE_SELECTOR) CompareSpdSelector,\r
+ (IPSEC_COMPARE_SELECTOR) CompareSaId,\r
+ (IPSEC_COMPARE_SELECTOR) ComparePadId\r
+};\r
+\r
+//\r
+// Common IsZeroSelector routine entry for spd/sad/pad.\r
+//\r
+IPSEC_IS_ZERO_SELECTOR mIsZeroSelector[] = {\r
+ (IPSEC_IS_ZERO_SELECTOR) IsZeroSpdSelector,\r
+ (IPSEC_IS_ZERO_SELECTOR) IsZeroSaId,\r
+ (IPSEC_IS_ZERO_SELECTOR) IsZeroPadId\r
+};\r
+\r
+//\r
+// Common DuplicateSelector routine entry for spd/sad/pad.\r
+//\r
+IPSEC_DUPLICATE_SELECTOR mDuplicateSelector[] = {\r
+ (IPSEC_DUPLICATE_SELECTOR) DuplicateSpdSelector,\r
+ (IPSEC_DUPLICATE_SELECTOR) DuplicateSaId,\r
+ (IPSEC_DUPLICATE_SELECTOR) DuplicatePadId\r
+};\r
+\r
+//\r
+// Common FixPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_FIX_POLICY_ENTRY mFixPolicyEntry[] = {\r
+ (IPSEC_FIX_POLICY_ENTRY) FixSpdEntry,\r
+ (IPSEC_FIX_POLICY_ENTRY) FixSadEntry,\r
+ (IPSEC_FIX_POLICY_ENTRY) FixPadEntry\r
+};\r
+\r
+//\r
+// Common UnfixPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_FIX_POLICY_ENTRY mUnfixPolicyEntry[] = {\r
+ (IPSEC_FIX_POLICY_ENTRY) UnfixSpdEntry,\r
+ (IPSEC_FIX_POLICY_ENTRY) UnfixSadEntry,\r
+ (IPSEC_FIX_POLICY_ENTRY) UnfixPadEntry\r
+};\r
+\r
+//\r
+// Common SetPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_SET_POLICY_ENTRY mSetPolicyEntry[] = {\r
+ (IPSEC_SET_POLICY_ENTRY) SetSpdEntry,\r
+ (IPSEC_SET_POLICY_ENTRY) SetSadEntry,\r
+ (IPSEC_SET_POLICY_ENTRY) SetPadEntry\r
+};\r
+\r
+//\r
+// Common GetPolicyEntry routine entry for spd/sad/pad.\r
+//\r
+IPSEC_GET_POLICY_ENTRY mGetPolicyEntry[] = {\r
+ (IPSEC_GET_POLICY_ENTRY) GetSpdEntry,\r
+ (IPSEC_GET_POLICY_ENTRY) GetSadEntry,\r
+ (IPSEC_GET_POLICY_ENTRY) GetPadEntry\r
+};\r
+\r
+//\r
+// Routine entry for IpSecConfig protocol.\r
+//\r
+EFI_IPSEC_CONFIG_PROTOCOL mIpSecConfigInstance = {\r
+ EfiIpSecConfigSetData,\r
+ EfiIpSecConfigGetData,\r
+ EfiIpSecConfigGetNextSelector,\r
+ EfiIpSecConfigRegisterNotify,\r
+ EfiIpSecConfigUnregisterNotify\r
+};\r
+\r
+/**\r
+ Get the all IPSec configuration variables and store those variables\r
+ to the internal data structure.\r
+\r
+ This founction is called by IpSecConfigInitialize() that is to intialize the\r
+ IPsecConfiguration Protocol.\r
+\r
+ @param[in] Private Point to IPSEC_PRIVATE_DATA.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS Restore the IPsec Configuration successfully.\r
+ @retval others Other errors is found during the variable getting.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigRestore (\r
+ IN IPSEC_PRIVATE_DATA *Private\r
+ );\r
+\r
+/**\r
+ Check if the specified EFI_IP_ADDRESS_INFO is in EFI_IP_ADDRESS_INFO list.\r
+\r
+ @param[in] AddressInfo Pointer of IP_ADDRESS_INFO to be search in AddressInfo list.\r
+ @param[in] AddressInfoList A list that contains IP_ADDRESS_INFOs.\r
+ @param[in] AddressCount Point out how many IP_ADDRESS_INFO in the list.\r
+\r
+ @retval TRUE The specified AddressInfo is in the AddressInfoList.\r
+ @retval FALSE The specified AddressInfo is not in the AddressInfoList.\r
+\r
+**/\r
+BOOLEAN\r
+IsInAddressInfoList(\r
+ IN EFI_IP_ADDRESS_INFO *AddressInfo,\r
+ IN EFI_IP_ADDRESS_INFO *AddressInfoList,\r
+ IN UINT32 AddressCount\r
+ )\r
+{\r
+ UINT8 Index;\r
+\r
+ for (Index = 0; Index < AddressCount ; Index++) {\r
+ if (CompareMem (\r
+ AddressInfo,\r
+ &AddressInfoList[Index].Address,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ ) == 0 &&\r
+ AddressInfo->PrefixLength == AddressInfoList[Index].PrefixLength\r
+ ) {\r
+ return TRUE;\r
+ }\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Compare two SPD Selectors.\r
+\r
+ Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/\r
+ NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the\r
+ Local Addresses and remote Addresses.\r
+\r
+ @param[in] Selector1 Pointer of first SPD Selector.\r
+ @param[in] Selector2 Pointer of second SPD Selector.\r
+\r
+ @retval TRUE This two Selector have the same value in above fields.\r
+ @retval FALSE Not all above fields have the same value in these two Selectors.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSpdSelector (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ )\r
+{\r
+ EFI_IPSEC_SPD_SELECTOR *SpdSel1;\r
+ EFI_IPSEC_SPD_SELECTOR *SpdSel2;\r
+ BOOLEAN IsMatch;\r
+ UINTN Index;\r
+\r
+ SpdSel1 = &Selector1->SpdSelector;\r
+ SpdSel2 = &Selector2->SpdSelector;\r
+ IsMatch = TRUE;\r
+\r
+ //\r
+ // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/\r
+ // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the\r
+ // two Spdselectors. Since the SPD supports two directions, it needs to\r
+ // compare two directions.\r
+ //\r
+ if ((SpdSel1->LocalAddressCount != SpdSel2->LocalAddressCount &&\r
+ SpdSel1->LocalAddressCount != SpdSel2->RemoteAddressCount) ||\r
+ (SpdSel1->RemoteAddressCount != SpdSel2->RemoteAddressCount &&\r
+ SpdSel1->RemoteAddressCount != SpdSel2->LocalAddressCount) ||\r
+ SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol ||\r
+ SpdSel1->LocalPort != SpdSel2->LocalPort ||\r
+ SpdSel1->LocalPortRange != SpdSel2->LocalPortRange ||\r
+ SpdSel1->RemotePort != SpdSel2->RemotePort ||\r
+ SpdSel1->RemotePortRange != SpdSel2->RemotePortRange\r
+ ) {\r
+ IsMatch = FALSE;\r
+ return IsMatch;\r
+ }\r
+\r
+ //\r
+ // Compare the all LocalAddress fields in the two Spdselectors.\r
+ // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare\r
+ // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return\r
+ // TRUE.\r
+ //\r
+ for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel1->LocalAddress[Index],\r
+ SpdSel2->LocalAddress,\r
+ SpdSel2->LocalAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ if (IsMatch) {\r
+ for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel2->LocalAddress[Index],\r
+ SpdSel1->LocalAddress,\r
+ SpdSel1->LocalAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (IsMatch) {\r
+ for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel1->RemoteAddress[Index],\r
+ SpdSel2->RemoteAddress,\r
+ SpdSel2->RemoteAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (IsMatch) {\r
+ for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel2->RemoteAddress[Index],\r
+ SpdSel1->RemoteAddress,\r
+ SpdSel1->RemoteAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Finish the one direction compare. If it is matched, return; otherwise,\r
+ // compare the other direction.\r
+ //\r
+ if (IsMatch) {\r
+ return IsMatch;\r
+ }\r
+ //\r
+ // Secondly, the SpdSel1->LocalAddress doesn't equal to SpdSel2->LocalAddress and\r
+ // SpdSel1->RemoteAddress doesn't equal to SpdSel2->RemoteAddress. Try to compare\r
+ // the RemoteAddress to LocalAddress.\r
+ //\r
+ IsMatch = TRUE;\r
+ for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel1->RemoteAddress[Index],\r
+ SpdSel2->LocalAddress,\r
+ SpdSel2->LocalAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ if (IsMatch) {\r
+ for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel2->RemoteAddress[Index],\r
+ SpdSel1->LocalAddress,\r
+ SpdSel1->LocalAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (IsMatch) {\r
+ for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel1->LocalAddress[Index],\r
+ SpdSel2->RemoteAddress,\r
+ SpdSel2->RemoteAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (IsMatch) {\r
+ for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {\r
+ if (!IsInAddressInfoList (\r
+ &SpdSel2->LocalAddress[Index],\r
+ SpdSel1->RemoteAddress,\r
+ SpdSel1->RemoteAddressCount\r
+ )) {\r
+ IsMatch = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ return IsMatch;\r
+}\r
+\r
+/**\r
+ Compare two SA IDs.\r
+\r
+ @param[in] Selector1 Pointer of first SA ID.\r
+ @param[in] Selector2 Pointer of second SA ID.\r
+\r
+ @retval TRUE This two Selectors have the same SA ID.\r
+ @retval FALSE This two Selecotrs don't have the same SA ID.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSaId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ )\r
+{\r
+ EFI_IPSEC_SA_ID *SaId1;\r
+ EFI_IPSEC_SA_ID *SaId2;\r
+ BOOLEAN IsMatch;\r
+\r
+ SaId1 = &Selector1->SaId;\r
+ SaId2 = &Selector2->SaId;\r
+ IsMatch = TRUE;\r
+\r
+ if (CompareMem (SaId1, SaId2, sizeof (EFI_IPSEC_SA_ID)) != 0) {\r
+ IsMatch = FALSE;\r
+ }\r
+\r
+ return IsMatch;\r
+}\r
+\r
+/**\r
+ Compare two PAD IDs.\r
+\r
+ @param[in] Selector1 Pointer of first PAD ID.\r
+ @param[in] Selector2 Pointer of second PAD ID.\r
+\r
+ @retval TRUE This two Selectors have the same PAD ID.\r
+ @retval FALSE This two Selecotrs don't have the same PAD ID.\r
+\r
+**/\r
+BOOLEAN\r
+ComparePadId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ )\r
+{\r
+ EFI_IPSEC_PAD_ID *PadId1;\r
+ EFI_IPSEC_PAD_ID *PadId2;\r
+ BOOLEAN IsMatch;\r
+\r
+ PadId1 = &Selector1->PadId;\r
+ PadId2 = &Selector2->PadId;\r
+ IsMatch = TRUE;\r
+\r
+ //\r
+ // Compare the PeerIdValid fields in PadId.\r
+ //\r
+ if (PadId1->PeerIdValid != PadId2->PeerIdValid) {\r
+ IsMatch = FALSE;\r
+ }\r
+ //\r
+ // Compare the PeerId fields in PadId if PeerIdValid is true.\r
+ //\r
+ if (IsMatch &&\r
+ PadId1->PeerIdValid &&\r
+ AsciiStriCmp ((CONST CHAR8 *) PadId1->Id.PeerId, (CONST CHAR8 *) PadId2->Id.PeerId) != 0\r
+ ) {\r
+ IsMatch = FALSE;\r
+ }\r
+ //\r
+ // Compare the IpAddress fields in PadId if PeerIdValid is false.\r
+ //\r
+ if (IsMatch &&\r
+ !PadId1->PeerIdValid &&\r
+ (PadId1->Id.IpAddress.PrefixLength != PadId2->Id.IpAddress.PrefixLength ||\r
+ CompareMem (&PadId1->Id.IpAddress.Address, &PadId2->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0)\r
+ ) {\r
+ IsMatch = FALSE;\r
+ }\r
+\r
+ return IsMatch;\r
+}\r
+\r
+/**\r
+ Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount\r
+ fields.\r
+\r
+ @param[in] Selector Pointer of the SPD Selector.\r
+\r
+ @retval TRUE If the SPD Selector is Zero.\r
+ @retval FALSE If the SPD Selector is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSpdSelector (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ )\r
+{\r
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;\r
+ BOOLEAN IsZero;\r
+\r
+ SpdSel = &Selector->SpdSelector;\r
+ IsZero = FALSE;\r
+\r
+ if (SpdSel->LocalAddressCount == 0 && SpdSel->RemoteAddressCount == 0) {\r
+ IsZero = TRUE;\r
+ }\r
+\r
+ return IsZero;\r
+}\r
+\r
+/**\r
+ Check if the SA ID is Zero by its DestAddress.\r
+\r
+ @param[in] Selector Pointer of the SA ID.\r
+\r
+ @retval TRUE If the SA ID is Zero.\r
+ @retval FALSE If the SA ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSaId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ )\r
+{\r
+ EFI_IP_ADDRESS *DestAddr;\r
+ EFI_IP_ADDRESS ZeroAddr;\r
+ BOOLEAN IsZero;\r
+\r
+ DestAddr = &Selector->SaId.DestAddress;\r
+ IsZero = FALSE;\r
+\r
+ ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));\r
+\r
+ if (CompareMem (DestAddr, &ZeroAddr, sizeof (EFI_IP_ADDRESS)) == 0) {\r
+ IsZero = TRUE;\r
+ }\r
+\r
+ return IsZero;\r
+}\r
+\r
+/**\r
+ Check if the PAD ID is Zero.\r
+\r
+ @param[in] Selector Pointer of the PAD ID.\r
+\r
+ @retval TRUE If the PAD ID is Zero.\r
+ @retval FALSE If the PAD ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroPadId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ )\r
+{\r
+ EFI_IPSEC_PAD_ID *PadId;\r
+ EFI_IPSEC_PAD_ID ZeroId;\r
+ BOOLEAN IsZero;\r
+\r
+ PadId = &Selector->PadId;\r
+ IsZero = FALSE;\r
+\r
+ ZeroMem (&ZeroId, sizeof (EFI_IPSEC_PAD_ID));\r
+\r
+ if (CompareMem (PadId, &ZeroId, sizeof (EFI_IPSEC_PAD_ID)) == 0) {\r
+ IsZero = TRUE;\r
+ }\r
+\r
+ return IsZero;\r
+}\r
+\r
+/**\r
+ Copy Source SPD Selector to the Destination SPD Selector.\r
+\r
+ @param[in, out] DstSel Pointer of Destination SPD Selector.\r
+ @param[in] SrcSel Pointer of Source SPD Selector.\r
+ @param[in, out] Size The size of the Destination SPD Selector. If it\r
+ not NULL and its value less than the size of\r
+ Source SPD Selector, the value of Source SPD\r
+ Selector's size will be passed to caller by this\r
+ parameter.\r
+\r
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL\r
+ @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of the Source SPD Selector.\r
+ @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD\r
+ Selector successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSpdSelector (\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,\r
+ IN OUT UINTN *Size\r
+ )\r
+{\r
+ EFI_IPSEC_SPD_SELECTOR *Dst;\r
+ EFI_IPSEC_SPD_SELECTOR *Src;\r
+\r
+ Dst = &DstSel->SpdSelector;\r
+ Src = &SrcSel->SpdSelector;\r
+\r
+ if (Dst == NULL || Src == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Size != NULL && (*Size) < SIZE_OF_SPD_SELECTOR (Src)) {\r
+ *Size = SIZE_OF_SPD_SELECTOR (Src);\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ //\r
+ // Copy the base structure of spd selector.\r
+ //\r
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_SPD_SELECTOR));\r
+\r
+ //\r
+ // Copy the local address array of spd selector.\r
+ //\r
+ Dst->LocalAddress = (EFI_IP_ADDRESS_INFO *) (Dst + 1);\r
+ CopyMem (\r
+ Dst->LocalAddress,\r
+ Src->LocalAddress,\r
+ sizeof (EFI_IP_ADDRESS_INFO) * Dst->LocalAddressCount\r
+ );\r
+\r
+ //\r
+ // Copy the remote address array of spd selector.\r
+ //\r
+ Dst->RemoteAddress = Dst->LocalAddress + Dst->LocalAddressCount;\r
+ CopyMem (\r
+ Dst->RemoteAddress,\r
+ Src->RemoteAddress,\r
+ sizeof (EFI_IP_ADDRESS_INFO) * Dst->RemoteAddressCount\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Copy Source SA ID to the Destination SA ID.\r
+\r
+ @param[in, out] DstSel Pointer of Destination SA ID.\r
+ @param[in] SrcSel Pointer of Source SA ID.\r
+ @param[in, out] Size The size of the Destination SA ID. If it\r
+ not NULL and its value less than the size of\r
+ Source SA ID, the value of Source SA ID's size\r
+ will be passed to caller by this parameter.\r
+\r
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID.\r
+ @retval EFI_SUCCESS Copy Source SA ID to the Destination SA ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSaId (\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,\r
+ IN OUT UINTN *Size\r
+ )\r
+{\r
+ EFI_IPSEC_SA_ID *Dst;\r
+ EFI_IPSEC_SA_ID *Src;\r
+\r
+ Dst = &DstSel->SaId;\r
+ Src = &SrcSel->SaId;\r
+\r
+ if (Dst == NULL || Src == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Size != NULL && *Size < sizeof (EFI_IPSEC_SA_ID)) {\r
+ *Size = sizeof (EFI_IPSEC_SA_ID);\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_SA_ID));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Copy Source PAD ID to the Destination PAD ID.\r
+\r
+ @param[in, out] DstSel Pointer of Destination PAD ID.\r
+ @param[in] SrcSel Pointer of Source PAD ID.\r
+ @param[in, out] Size The size of the Destination PAD ID. If it\r
+ not NULL and its value less than the size of\r
+ Source PAD ID, the value of Source PAD ID's size\r
+ will be passed to caller by this parameter.\r
+\r
+ @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID .\r
+ @retval EFI_SUCCESS Copy Source PAD ID to the Destination PAD ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicatePadId (\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,\r
+ IN OUT UINTN *Size\r
+ )\r
+{\r
+ EFI_IPSEC_PAD_ID *Dst;\r
+ EFI_IPSEC_PAD_ID *Src;\r
+\r
+ Dst = &DstSel->PadId;\r
+ Src = &SrcSel->PadId;\r
+\r
+ if (Dst == NULL || Src == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Size != NULL && *Size < sizeof (EFI_IPSEC_PAD_ID)) {\r
+ *Size = sizeof (EFI_IPSEC_PAD_ID);\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_PAD_ID));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Fix the value of some members of SPD Selector.\r
+\r
+ This function is called by IpSecCopyPolicyEntry()which copy the Policy\r
+ Entry into the Variable. Since some members in SPD Selector are pointers,\r
+ a physical address to relative address convertion is required before copying\r
+ this SPD entry into the variable.\r
+\r
+ @param[in] Selector Pointer of SPD Selector.\r
+ @param[in, out] Data Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+FixSpdEntry (\r
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,\r
+ IN OUT EFI_IPSEC_SPD_DATA *Data\r
+ )\r
+{\r
+ //\r
+ // It assumes that all ref buffers in spd selector and data are\r
+ // stored in the continous memory and close to the base structure.\r
+ //\r
+ FIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);\r
+ FIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);\r
+\r
+ if (Data->ProcessingPolicy != NULL) {\r
+ if (Data->ProcessingPolicy->TunnelOption != NULL) {\r
+ FIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);\r
+ }\r
+\r
+ FIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Fix the value of some members of SA ID.\r
+\r
+ This function is called by IpSecCopyPolicyEntry()which copy the Policy\r
+ Entry into the Variable. Since some members in SA ID are pointers,\r
+ a physical address to relative address conversion is required before copying\r
+ this SAD into the variable.\r
+\r
+ @param[in] SaId Pointer of SA ID\r
+ @param[in, out] Data Pointer of SA Data.\r
+\r
+**/\r
+VOID\r
+FixSadEntry (\r
+ IN EFI_IPSEC_SA_ID *SaId,\r
+ IN OUT EFI_IPSEC_SA_DATA *Data\r
+ )\r
+{\r
+ //\r
+ // It assumes that all ref buffers in sad selector and data are\r
+ // stored in the continous memory and close to the base structure.\r
+ //\r
+ if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+ FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);\r
+ }\r
+\r
+ if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+ FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);\r
+ }\r
+\r
+ if (Data->SpdSelector != NULL) {\r
+ if (Data->SpdSelector->LocalAddress != NULL) {\r
+ FIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);\r
+ }\r
+\r
+ FIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);\r
+ FIX_REF_BUF_ADDR (Data->SpdSelector, Data);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Fix the value of some members of PAD ID.\r
+\r
+ This function is called by IpSecCopyPolicyEntry()which copy the Policy\r
+ Entry into the Variable. Since some members in PAD ID are pointers,\r
+ a physical address to relative address conversion is required before copying\r
+ this PAD into the variable.\r
+\r
+ @param[in] PadId Pointer of PAD ID.\r
+ @param[in, out] Data Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+FixPadEntry (\r
+ IN EFI_IPSEC_PAD_ID *PadId,\r
+ IN OUT EFI_IPSEC_PAD_DATA *Data\r
+ )\r
+{\r
+ //\r
+ // It assumes that all ref buffers in pad selector and data are\r
+ // stored in the continous memory and close to the base structure.\r
+ //\r
+ if (Data->AuthData != NULL) {\r
+ FIX_REF_BUF_ADDR (Data->AuthData, Data);\r
+ }\r
+\r
+ if (Data->RevocationData != NULL) {\r
+ FIX_REF_BUF_ADDR (Data->RevocationData, Data);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Recover the value of some members of SPD Selector.\r
+\r
+ This function is corresponding to FixSpdEntry(). It recovers the value of members\r
+ of SPD Selector that are fixed by FixSpdEntry().\r
+\r
+ @param[in, out] Selector Pointer of SPD Selector.\r
+ @param[in, out] Data Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSpdEntry (\r
+ IN OUT EFI_IPSEC_SPD_SELECTOR *Selector,\r
+ IN OUT EFI_IPSEC_SPD_DATA *Data\r
+ )\r
+{\r
+ //\r
+ // It assumes that all ref buffers in spd selector and data are\r
+ // stored in the continous memory and close to the base structure.\r
+ //\r
+ UNFIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);\r
+ UNFIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);\r
+\r
+ if (Data->ProcessingPolicy != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);\r
+ if (Data->ProcessingPolicy->TunnelOption != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);\r
+ }\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Recover the value of some members of SA ID.\r
+\r
+ This function is corresponding to FixSadEntry(). It recovers the value of members\r
+ of SAD ID that are fixed by FixSadEntry().\r
+\r
+ @param[in, out] SaId Pointer of SAD ID.\r
+ @param[in, out] Data Pointer of SAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSadEntry (\r
+ IN OUT EFI_IPSEC_SA_ID *SaId,\r
+ IN OUT EFI_IPSEC_SA_DATA *Data\r
+ )\r
+{\r
+ //\r
+ // It assumes that all ref buffers in sad selector and data are\r
+ // stored in the continous memory and close to the base structure.\r
+ //\r
+ if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);\r
+ }\r
+\r
+ if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);\r
+ }\r
+\r
+ if (Data->SpdSelector != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->SpdSelector, Data);\r
+ if (Data->SpdSelector->LocalAddress != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);\r
+ }\r
+\r
+ UNFIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Recover the value of some members of PAD ID.\r
+\r
+ This function is corresponding to FixPadEntry(). It recovers the value of members\r
+ of PAD ID that are fixed by FixPadEntry().\r
+\r
+ @param[in] PadId Pointer of PAD ID.\r
+ @param[in, out] Data Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixPadEntry (\r
+ IN EFI_IPSEC_PAD_ID *PadId,\r
+ IN OUT EFI_IPSEC_PAD_DATA *Data\r
+ )\r
+{\r
+ //\r
+ // It assumes that all ref buffers in pad selector and data are\r
+ // stored in the continous memory and close to the base structure.\r
+ //\r
+ if (Data->AuthData != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->AuthData, Data);\r
+ }\r
+\r
+ if (Data->RevocationData != NULL) {\r
+ UNFIX_REF_BUF_ADDR (Data->RevocationData, Data);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Set the security policy information for the EFI IPsec driver.\r
+\r
+ The IPsec configuration data has a unique selector/identifier separately to\r
+ identify a data entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector on operated\r
+ configuration data specified by DataType.\r
+ A NULL Selector causes the entire specified-type\r
+ configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure\r
+ of the data buffer should be EFI_IPSEC_SPD_DATA.\r
+ @param[in] Context Pointer to one entry selector that describes\r
+ the expected position the new data entry will\r
+ be added. If Context is NULL, the new entry will\r
+ be appended the end of database.\r
+\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - Selector is not NULL and its LocalAddress\r
+ is NULL or its RemoteAddress is NULL.\r
+ - Data is not NULL and its Action is Protected\r
+ and its plolicy is NULL.\r
+ - Data is not NULL, its Action is not protected,\r
+ and its policy is not NULL.\r
+ - The Action of Data is Protected, its policy\r
+ mode is Tunnel, and its tunnel option is NULL.\r
+ - The Action of Data is protected and its policy\r
+ mode is not Tunnel and it tunnel option is not NULL.\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSpdEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;\r
+ EFI_IPSEC_SPD_DATA *SpdData;\r
+ EFI_IPSEC_SPD_SELECTOR *InsertBefore;\r
+ LIST_ENTRY *SpdList;\r
+ LIST_ENTRY *SadList;\r
+ LIST_ENTRY *SpdSas;\r
+ LIST_ENTRY *EntryInsertBefore;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ LIST_ENTRY *Entry2;\r
+ IPSEC_SPD_ENTRY *SpdEntry;\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+ UINTN SpdEntrySize;\r
+ UINTN Index;\r
+\r
+ SpdSel = (Selector == NULL) ? NULL : &Selector->SpdSelector;\r
+ SpdData = (Data == NULL) ? NULL : (EFI_IPSEC_SPD_DATA *) Data;\r
+ InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SpdSelector;\r
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+ if (SpdSel != NULL) {\r
+ if (SpdSel->LocalAddress == NULL || SpdSel->RemoteAddress == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if (SpdData != NULL) {\r
+ if ((SpdData->Action == EfiIPsecActionProtect && SpdData->ProcessingPolicy == NULL) ||\r
+ (SpdData->Action != EfiIPsecActionProtect && SpdData->ProcessingPolicy != NULL)\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (SpdData->Action == EfiIPsecActionProtect) {\r
+ if ((SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption == NULL) ||\r
+ (SpdData->ProcessingPolicy->Mode != EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption != NULL)\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // The default behavior is to insert the node ahead of the header.\r
+ //\r
+ EntryInsertBefore = SpdList;\r
+\r
+ //\r
+ // Remove the existed spd entry.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SpdList) {\r
+\r
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+\r
+ if (SpdSel == NULL ||\r
+ CompareSpdSelector ((EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel)\r
+ ) {\r
+ //\r
+ // Record the existed entry position to keep the original order.\r
+ //\r
+ EntryInsertBefore = SpdEntry->List.ForwardLink;\r
+ RemoveEntryList (&SpdEntry->List);\r
+\r
+ //\r
+ // Update the reverse ref of sad entry in the spd.sas list.\r
+ //\r
+ SpdSas = &SpdEntry->Data->Sas;\r
+ NET_LIST_FOR_EACH (Entry2, SpdSas) {\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry2);\r
+ SadEntry->Data->SpdEntry = NULL;\r
+ }\r
+ //\r
+ // Free the existed spd entry\r
+ //\r
+ FreePool (SpdEntry);\r
+ }\r
+ }\r
+ //\r
+ // Return success here if only want to remove the spd entry.\r
+ //\r
+ if (SpdData == NULL || SpdSel == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ //\r
+ // Search the appointed entry position if InsertBefore is not NULL.\r
+ //\r
+ if (InsertBefore != NULL) {\r
+\r
+ NET_LIST_FOR_EACH (Entry, SpdList) {\r
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+\r
+ if (CompareSpdSelector (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore\r
+ )) {\r
+ EntryInsertBefore = Entry;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Do Padding for the different Arch.\r
+ //\r
+ SpdEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SPD_ENTRY));\r
+ SpdEntrySize = ALIGN_VARIABLE (SpdEntrySize + (UINTN)SIZE_OF_SPD_SELECTOR (SpdSel));\r
+ SpdEntrySize += IpSecGetSizeOfEfiSpdData (SpdData);\r
+\r
+ SpdEntry = AllocateZeroPool (SpdEntrySize);\r
+\r
+ if (SpdEntry == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Fix the address of Selector and Data buffer and copy them, which is\r
+ // continous memory and close to the base structure of spd entry.\r
+ //\r
+ SpdEntry->Selector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ((SpdEntry + 1), sizeof (UINTN));\r
+ SpdEntry->Data = (IPSEC_SPD_DATA *) ALIGN_POINTER (\r
+ ((UINT8 *) SpdEntry->Selector + SIZE_OF_SPD_SELECTOR (SpdSel)),\r
+ sizeof (UINTN)\r
+ );\r
+\r
+ DuplicateSpdSelector (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,\r
+ NULL\r
+ );\r
+\r
+ CopyMem (\r
+ SpdEntry->Data->Name,\r
+ SpdData->Name,\r
+ sizeof (SpdData->Name)\r
+ );\r
+ SpdEntry->Data->PackageFlag = SpdData->PackageFlag;\r
+ SpdEntry->Data->Action = SpdData->Action;\r
+\r
+ //\r
+ // Fix the address of ProcessingPolicy and copy it if need, which is continous\r
+ // memory and close to the base structure of sad data.\r
+ //\r
+ if (SpdData->Action != EfiIPsecActionProtect) {\r
+ SpdEntry->Data->ProcessingPolicy = NULL;\r
+ } else {\r
+ SpdEntry->Data->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (\r
+ SpdEntry->Data + 1,\r
+ sizeof (UINTN)\r
+ );\r
+ IpSecDuplicateProcessPolicy (SpdEntry->Data->ProcessingPolicy, SpdData->ProcessingPolicy);\r
+ }\r
+ //\r
+ // Update the sas list of the new spd entry.\r
+ //\r
+ InitializeListHead (&SpdEntry->Data->Sas);\r
+\r
+ SadList = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+ NET_LIST_FOR_EACH (Entry, SadList) {\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+ for (Index = 0; Index < SpdData->SaIdCount; Index++) {\r
+\r
+ if (CompareSaId (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) &SpdData->SaId[Index],\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id\r
+ )) {\r
+ InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);\r
+ SadEntry->Data->SpdEntry = SpdEntry;\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Insert the new spd entry.\r
+ //\r
+ InsertTailList (EntryInsertBefore, &SpdEntry->List);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Set the security association information for the EFI IPsec driver.\r
+\r
+ The IPsec configuration data has a unique selector/identifier separately to\r
+ identify a data entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector on operated\r
+ configuration data specified by DataType.\r
+ A NULL Selector causes the entire specified-type\r
+ configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure\r
+ of the data buffer should be EFI_IPSEC_SA_DATA.\r
+ @param[in] Context Pointer to one entry selector which describes\r
+ the expected position the new data entry will\r
+ be added. If Context is NULL,the new entry will\r
+ be appended the end of database.\r
+\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+ IPSEC_SPD_ENTRY *SpdEntry;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ LIST_ENTRY *SadList;\r
+ LIST_ENTRY *SpdList;\r
+ EFI_IPSEC_SA_ID *SaId;\r
+ EFI_IPSEC_SA_DATA *SaData;\r
+ EFI_IPSEC_SA_ID *InsertBefore;\r
+ LIST_ENTRY *EntryInsertBefore;\r
+ UINTN SadEntrySize;\r
+\r
+ SaId = (Selector == NULL) ? NULL : &Selector->SaId;\r
+ SaData = (Data == NULL) ? NULL : (EFI_IPSEC_SA_DATA *) Data;\r
+ InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SaId;\r
+ SadList = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+ //\r
+ // The default behavior is to insert the node ahead of the header.\r
+ //\r
+ EntryInsertBefore = SadList;\r
+\r
+ //\r
+ // Remove the existed sad entry.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SadList) {\r
+\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+ if (SaId == NULL ||\r
+ CompareSaId (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaId\r
+ )) {\r
+ //\r
+ // Record the existed entry position to keep the original order.\r
+ //\r
+ EntryInsertBefore = SadEntry->List.ForwardLink;\r
+\r
+ //\r
+ // Update the related sad.byspd field.\r
+ //\r
+ if (SadEntry->Data->SpdEntry != NULL) {\r
+ RemoveEntryList (&SadEntry->BySpd);\r
+ }\r
+\r
+ RemoveEntryList (&SadEntry->List);\r
+ FreePool (SadEntry);\r
+ }\r
+ }\r
+ //\r
+ // Return success here if only want to remove the sad entry\r
+ //\r
+ if (SaData == NULL || SaId == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ //\r
+ // Search the appointed entry position if InsertBefore is not NULL.\r
+ //\r
+ if (InsertBefore != NULL) {\r
+\r
+ NET_LIST_FOR_EACH (Entry, SadList) {\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+ if (CompareSaId (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore\r
+ )) {\r
+ EntryInsertBefore = Entry;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Do Padding for different Arch.\r
+ //\r
+ SadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SAD_ENTRY));\r
+ SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (EFI_IPSEC_SA_DATA));\r
+ SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (IPSEC_SAD_DATA));\r
+\r
+ if (SaId->Proto == EfiIPsecAH) {\r
+ SadEntrySize += SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength;\r
+ } else {\r
+ SadEntrySize = ALIGN_VARIABLE (SadEntrySize + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength);\r
+ SadEntrySize += SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+ }\r
+\r
+ SadEntry = AllocateZeroPool (SadEntrySize);\r
+\r
+ if (SadEntry == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Fix the address of Id and Data buffer and copy them, which is\r
+ // continous memory and close to the base structure of sad entry.\r
+ //\r
+ SadEntry->Id = (EFI_IPSEC_SA_ID *) ALIGN_POINTER ((SadEntry + 1), sizeof (UINTN));\r
+ SadEntry->Data = (IPSEC_SAD_DATA *) ALIGN_POINTER ((SadEntry->Id + 1), sizeof (UINTN));\r
+\r
+ CopyMem (SadEntry->Id, SaId, sizeof (EFI_IPSEC_SA_ID));\r
+\r
+ SadEntry->Data->Mode = SaData->Mode;\r
+ SadEntry->Data->SequenceNumber = SaData->SNCount;\r
+ SadEntry->Data->AntiReplayWindowSize = SaData->AntiReplayWindows;\r
+\r
+ ZeroMem (\r
+ &SadEntry->Data->AntiReplayBitmap,\r
+ sizeof (SadEntry->Data->AntiReplayBitmap)\r
+ );\r
+\r
+ ZeroMem (\r
+ &SadEntry->Data->AlgoInfo,\r
+ sizeof (EFI_IPSEC_ALGO_INFO)\r
+ );\r
+\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId = SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId;\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength = SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength;\r
+\r
+ if (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SadEntry->Data + 1), sizeof (UINTN));\r
+ CopyMem (\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+ SaData->AlgoInfo.EspAlgoInfo.AuthKey,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength\r
+ );\r
+ }\r
+\r
+ if (SaId->Proto == EfiIPsecESP) {\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId = SaData->AlgoInfo.EspAlgoInfo.EncAlgoId;\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength = SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+\r
+ if (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (\r
+ ((UINT8 *) (SadEntry->Data + 1) +\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength),\r
+ sizeof (UINTN)\r
+ );\r
+ CopyMem (\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
+ SaData->AlgoInfo.EspAlgoInfo.EncKey,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength\r
+ );\r
+ }\r
+ }\r
+\r
+ CopyMem (\r
+ &SadEntry->Data->SaLifetime,\r
+ &SaData->SaLifetime,\r
+ sizeof (EFI_IPSEC_SA_LIFETIME)\r
+ );\r
+\r
+ SadEntry->Data->PathMTU = SaData->PathMTU;\r
+ SadEntry->Data->SpdEntry = NULL;\r
+ SadEntry->Data->ESNEnabled = FALSE;\r
+ SadEntry->Data->ManualSet = SaData->ManualSet;\r
+\r
+ //\r
+ // Update the spd.sas list of the spd entry specified by sad.selector\r
+ //\r
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+ for (Entry = SpdList->ForwardLink; Entry != SpdList && SaData->SpdSelector != NULL; Entry = Entry->ForwardLink) {\r
+\r
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+ if (CompareSpdSelector (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector\r
+ ) && SpdEntry->Data->Action == EfiIPsecActionProtect) {\r
+ SadEntry->Data->SpdEntry = SpdEntry;\r
+ InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);\r
+ }\r
+ }\r
+ //\r
+ // Insert the new sad entry.\r
+ //\r
+ InsertTailList (EntryInsertBefore, &SadEntry->List);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Set the peer authorization configuration information for the EFI IPsec driver.\r
+\r
+ The IPsec configuration data has a unique selector/identifier separately to\r
+ identify a data entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector on operated\r
+ configuration data specified by DataType.\r
+ A NULL Selector causes the entire specified-type\r
+ configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure\r
+ of the data buffer should be EFI_IPSEC_PAD_DATA.\r
+ @param[in] Context Pointer to one entry selector that describes\r
+ the expected position the new data entry will\r
+ be added. If Context is NULL, the new entry will\r
+ be appended the end of database.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The required system resources could not be allocated.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetPadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ IPSEC_PAD_ENTRY *PadEntry;\r
+ EFI_IPSEC_PAD_ID *PadId;\r
+ EFI_IPSEC_PAD_DATA *PadData;\r
+ LIST_ENTRY *PadList;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ EFI_IPSEC_PAD_ID *InsertBefore;\r
+ LIST_ENTRY *EntryInsertBefore;\r
+ UINTN PadEntrySize;\r
+\r
+ PadId = (Selector == NULL) ? NULL : &Selector->PadId;\r
+ PadData = (Data == NULL) ? NULL : (EFI_IPSEC_PAD_DATA *) Data;\r
+ InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->PadId;\r
+ PadList = &mConfigData[IPsecConfigDataTypePad];\r
+\r
+ //\r
+ // The default behavior is to insert the node ahead of the header.\r
+ //\r
+ EntryInsertBefore = PadList;\r
+\r
+ //\r
+ // Remove the existed pad entry.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, PadList) {\r
+\r
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+\r
+ if (PadId == NULL ||\r
+ ComparePadId ((EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, (EFI_IPSEC_CONFIG_SELECTOR *) PadId)\r
+ ) {\r
+ //\r
+ // Record the existed entry position to keep the original order.\r
+ //\r
+ EntryInsertBefore = PadEntry->List.ForwardLink;\r
+ RemoveEntryList (&PadEntry->List);\r
+\r
+ FreePool (PadEntry);\r
+ }\r
+ }\r
+ //\r
+ // Return success here if only want to remove the pad entry\r
+ //\r
+ if (PadData == NULL || PadId == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ //\r
+ // Search the appointed entry position if InsertBefore is not NULL.\r
+ //\r
+ if (InsertBefore != NULL) {\r
+\r
+ NET_LIST_FOR_EACH (Entry, PadList) {\r
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+\r
+ if (ComparePadId (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore\r
+ )) {\r
+ EntryInsertBefore = Entry;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Do PADDING for different arch.\r
+ //\r
+ PadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_PAD_ENTRY));\r
+ PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_ID));\r
+ PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_DATA));\r
+ PadEntrySize = ALIGN_VARIABLE (PadEntrySize + (PadData->AuthData != NULL ? PadData->AuthDataSize : 0));\r
+ PadEntrySize += PadData->RevocationData != NULL ? PadData->RevocationDataSize : 0;\r
+\r
+ PadEntry = AllocateZeroPool (PadEntrySize);\r
+\r
+ if (PadEntry == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Fix the address of Id and Data buffer and copy them, which is\r
+ // continous memory and close to the base structure of pad entry.\r
+ //\r
+ PadEntry->Id = (EFI_IPSEC_PAD_ID *) ALIGN_POINTER ((PadEntry + 1), sizeof (UINTN));\r
+ PadEntry->Data = (EFI_IPSEC_PAD_DATA *) ALIGN_POINTER ((PadEntry->Id + 1), sizeof (UINTN));\r
+\r
+ CopyMem (PadEntry->Id, PadId, sizeof (EFI_IPSEC_PAD_ID));\r
+\r
+ PadEntry->Data->AuthProtocol = PadData->AuthProtocol;\r
+ PadEntry->Data->AuthMethod = PadData->AuthMethod;\r
+ PadEntry->Data->IkeIdFlag = PadData->IkeIdFlag;\r
+\r
+ if (PadData->AuthData != NULL) {\r
+ PadEntry->Data->AuthDataSize = PadData->AuthDataSize;\r
+ PadEntry->Data->AuthData = (VOID *) ALIGN_POINTER (PadEntry->Data + 1, sizeof (UINTN));\r
+ CopyMem (\r
+ PadEntry->Data->AuthData,\r
+ PadData->AuthData,\r
+ PadData->AuthDataSize\r
+ );\r
+ } else {\r
+ PadEntry->Data->AuthDataSize = 0;\r
+ PadEntry->Data->AuthData = NULL;\r
+ }\r
+\r
+ if (PadData->RevocationData != NULL) {\r
+ PadEntry->Data->RevocationDataSize = PadData->RevocationDataSize;\r
+ PadEntry->Data->RevocationData = (VOID *) ALIGN_POINTER (\r
+ ((UINT8 *) (PadEntry->Data + 1) + PadData->AuthDataSize),\r
+ sizeof (UINTN)\r
+ );\r
+ CopyMem (\r
+ PadEntry->Data->RevocationData,\r
+ PadData->RevocationData,\r
+ PadData->RevocationDataSize\r
+ );\r
+ } else {\r
+ PadEntry->Data->RevocationDataSize = 0;\r
+ PadEntry->Data->RevocationData = NULL;\r
+ }\r
+ //\r
+ // Insert the new pad entry.\r
+ //\r
+ InsertTailList (EntryInsertBefore, &PadEntry->List);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function lookup the data entry from IPsec SPD. Return the configuration\r
+ value of the specified SPD Entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector which is an identifier\r
+ of the SPD entry.\r
+ @param[in, out] DataSize On output the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec\r
+ configuration data. The type of the data buffer\r
+ is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSpdEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ )\r
+{\r
+ IPSEC_SPD_ENTRY *SpdEntry;\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;\r
+ EFI_IPSEC_SPD_DATA *SpdData;\r
+ LIST_ENTRY *SpdList;\r
+ LIST_ENTRY *SpdSas;\r
+ LIST_ENTRY *Entry;\r
+ UINTN RequiredSize;\r
+\r
+ SpdSel = &Selector->SpdSelector;\r
+ SpdData = (EFI_IPSEC_SPD_DATA *) Data;\r
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+ NET_LIST_FOR_EACH (Entry, SpdList) {\r
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+\r
+ //\r
+ // Find the required spd entry\r
+ //\r
+ if (CompareSpdSelector (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector\r
+ )) {\r
+\r
+ RequiredSize = IpSecGetSizeOfSpdData (SpdEntry->Data);\r
+ if (*DataSize < RequiredSize) {\r
+ *DataSize = RequiredSize;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ if (SpdData == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *DataSize = RequiredSize;\r
+\r
+ //\r
+ // Extract and fill all SaId array from the spd.sas list\r
+ //\r
+ SpdSas = &SpdEntry->Data->Sas;\r
+ SpdData->SaIdCount = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, SpdSas) {\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);\r
+ CopyMem (\r
+ &SpdData->SaId[SpdData->SaIdCount++],\r
+ SadEntry->Id,\r
+ sizeof (EFI_IPSEC_SA_ID)\r
+ );\r
+ }\r
+ //\r
+ // Fill the other fields in spd data.\r
+ //\r
+ CopyMem (SpdData->Name, SpdEntry->Data->Name, sizeof (SpdData->Name));\r
+\r
+ SpdData->PackageFlag = SpdEntry->Data->PackageFlag;\r
+ SpdData->Action = SpdEntry->Data->Action;\r
+\r
+ if (SpdData->Action != EfiIPsecActionProtect) {\r
+ SpdData->ProcessingPolicy = NULL;\r
+ } else {\r
+ SpdData->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ((UINT8 *) SpdData + sizeof (EFI_IPSEC_SPD_DATA) + (SpdData->SaIdCount - 1) * sizeof (EFI_IPSEC_SA_ID));\r
+\r
+ IpSecDuplicateProcessPolicy (\r
+ SpdData->ProcessingPolicy,\r
+ SpdEntry->Data->ProcessingPolicy\r
+ );\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ This function lookup the data entry from IPsec SAD. Return the configuration\r
+ value of the specified SAD Entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector which is an identifier\r
+ of the SAD entry.\r
+ @param[in, out] DataSize On output, the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec\r
+ configuration data. The type of the data buffer\r
+ is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ )\r
+{\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *SadList;\r
+ EFI_IPSEC_SA_ID *SaId;\r
+ EFI_IPSEC_SA_DATA *SaData;\r
+ UINTN RequiredSize;\r
+\r
+ SaId = &Selector->SaId;\r
+ SaData = (EFI_IPSEC_SA_DATA *) Data;\r
+ SadList = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+ NET_LIST_FOR_EACH (Entry, SadList) {\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+\r
+ //\r
+ // Find the required sad entry.\r
+ //\r
+ if (CompareSaId (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaId,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id\r
+ )) {\r
+ //\r
+ // Calculate the required size of the sad entry.\r
+ // Data Layout is follows:\r
+ // |EFI_IPSEC_SA_DATA\r
+ // |AuthKey\r
+ // |EncryptKey (Optional)\r
+ // |SpdSelector (Optional)\r
+ //\r
+ RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));\r
+\r
+ if (SaId->Proto == EfiIPsecAH) {\r
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength);\r
+ } else {\r
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength);\r
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength);\r
+ }\r
+\r
+ if (SadEntry->Data->SpdEntry != NULL) {\r
+ RequiredSize += SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector);\r
+ }\r
+\r
+\r
+\r
+ if (*DataSize < RequiredSize) {\r
+ *DataSize = RequiredSize;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ //\r
+ // Fill the data fields of sad entry.\r
+ //\r
+ *DataSize = RequiredSize;\r
+ SaData->Mode = SadEntry->Data->Mode;\r
+ SaData->SNCount = SadEntry->Data->SequenceNumber;\r
+ SaData->AntiReplayWindows = SadEntry->Data->AntiReplayWindowSize;\r
+\r
+ CopyMem (\r
+ &SaData->SaLifetime,\r
+ &SadEntry->Data->SaLifetime,\r
+ sizeof (EFI_IPSEC_SA_LIFETIME)\r
+ );\r
+\r
+ ZeroMem (\r
+ &SaData->AlgoInfo,\r
+ sizeof (EFI_IPSEC_ALGO_INFO)\r
+ );\r
+\r
+ if (SaId->Proto == EfiIPsecAH) {\r
+ //\r
+ // Copy AH alogrithm INFO to SaData\r
+ //\r
+ SaData->AlgoInfo.AhAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthAlgoId;\r
+ SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength;\r
+ if (SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength != 0) {\r
+ SaData->AlgoInfo.AhAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));\r
+ CopyMem (\r
+ SaData->AlgoInfo.AhAlgoInfo.AuthKey,\r
+ SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKey,\r
+ SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength\r
+ );\r
+ }\r
+ } else if (SaId->Proto == EfiIPsecESP) {\r
+ //\r
+ // Copy ESP alogrithem INFO to SaData\r
+ //\r
+ SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId;\r
+ SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength;\r
+ if (SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {\r
+ SaData->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));\r
+ CopyMem (\r
+ SaData->AlgoInfo.EspAlgoInfo.AuthKey,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+ SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength\r
+ );\r
+ }\r
+\r
+ SaData->AlgoInfo.EspAlgoInfo.EncAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId;\r
+ SaData->AlgoInfo.EspAlgoInfo.EncKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength;\r
+\r
+ if (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {\r
+ SaData->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (\r
+ ((UINT8 *) (SaData + 1) +\r
+ SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength),\r
+ sizeof (UINTN)\r
+ );\r
+ CopyMem (\r
+ SaData->AlgoInfo.EspAlgoInfo.EncKey,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
+ SaData->AlgoInfo.EspAlgoInfo.EncKeyLength\r
+ );\r
+ }\r
+ }\r
+\r
+ SaData->PathMTU = SadEntry->Data->PathMTU;\r
+\r
+ //\r
+ // Fill the spd selector field of sad data\r
+ //\r
+ if (SadEntry->Data->SpdEntry != NULL) {\r
+\r
+ SaData->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) (\r
+ (UINT8 *)SaData +\r
+ RequiredSize -\r
+ SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector)\r
+ );\r
+\r
+ DuplicateSpdSelector (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdEntry->Selector,\r
+ NULL\r
+ );\r
+\r
+ } else {\r
+\r
+ SaData->SpdSelector = NULL;\r
+ }\r
+\r
+ SaData->ManualSet = SadEntry->Data->ManualSet;\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ This function lookup the data entry from IPsec PAD. Return the configuration\r
+ value of the specified PAD Entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector which is an identifier\r
+ of the PAD entry.\r
+ @param[in, out] DataSize On output the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec\r
+ configuration data. The type of the data buffer\r
+ is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ )\r
+{\r
+ IPSEC_PAD_ENTRY *PadEntry;\r
+ LIST_ENTRY *PadList;\r
+ LIST_ENTRY *Entry;\r
+ EFI_IPSEC_PAD_ID *PadId;\r
+ EFI_IPSEC_PAD_DATA *PadData;\r
+ UINTN RequiredSize;\r
+\r
+ PadId = &Selector->PadId;\r
+ PadData = (EFI_IPSEC_PAD_DATA *) Data;\r
+ PadList = &mConfigData[IPsecConfigDataTypePad];\r
+\r
+ NET_LIST_FOR_EACH (Entry, PadList) {\r
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+\r
+ //\r
+ // Find the required pad entry.\r
+ //\r
+ if (ComparePadId (\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) PadId,\r
+ (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id\r
+ )) {\r
+ //\r
+ // Calculate the required size of the pad entry.\r
+ //\r
+ RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));\r
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + PadEntry->Data->AuthDataSize);\r
+ RequiredSize += PadEntry->Data->RevocationDataSize;\r
+\r
+ if (*DataSize < RequiredSize) {\r
+ *DataSize = RequiredSize;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ //\r
+ // Fill the data fields of pad entry\r
+ //\r
+ *DataSize = RequiredSize;\r
+ PadData->AuthProtocol = PadEntry->Data->AuthProtocol;\r
+ PadData->AuthMethod = PadEntry->Data->AuthMethod;\r
+ PadData->IkeIdFlag = PadEntry->Data->IkeIdFlag;\r
+\r
+ //\r
+ // Copy Authentication data.\r
+ //\r
+ if (PadEntry->Data->AuthData != NULL) {\r
+\r
+ PadData->AuthDataSize = PadEntry->Data->AuthDataSize;\r
+ PadData->AuthData = (VOID *) ALIGN_POINTER ((PadData + 1), sizeof (UINTN));\r
+ CopyMem (\r
+ PadData->AuthData,\r
+ PadEntry->Data->AuthData,\r
+ PadData->AuthDataSize\r
+ );\r
+ } else {\r
+\r
+ PadData->AuthDataSize = 0;\r
+ PadData->AuthData = NULL;\r
+ }\r
+ //\r
+ // Copy Revocation Data.\r
+ //\r
+ if (PadEntry->Data->RevocationData != NULL) {\r
+\r
+ PadData->RevocationDataSize = PadEntry->Data->RevocationDataSize;\r
+ PadData->RevocationData = (VOID *) ALIGN_POINTER (\r
+ ((UINT8 *) (PadData + 1) + PadData->AuthDataSize),\r
+ sizeof (UINTN)\r
+ );\r
+ CopyMem (\r
+ PadData->RevocationData,\r
+ PadEntry->Data->RevocationData,\r
+ PadData->RevocationDataSize\r
+ );\r
+ } else {\r
+\r
+ PadData->RevocationDataSize = 0;\r
+ PadData->RevocationData = NULL;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Copy Source Process Policy to the Destination Process Policy.\r
+\r
+ @param[in] Dst Pointer to the Source Process Policy.\r
+ @param[in] Src Pointer to the Destination Process Policy.\r
+\r
+**/\r
+VOID\r
+IpSecDuplicateProcessPolicy (\r
+ IN EFI_IPSEC_PROCESS_POLICY *Dst,\r
+ IN EFI_IPSEC_PROCESS_POLICY *Src\r
+ )\r
+{\r
+ //\r
+ // Firstly copy the structure content itself.\r
+ //\r
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_PROCESS_POLICY));\r
+\r
+ //\r
+ // Recursively copy the tunnel option if needed.\r
+ //\r
+ if (Dst->Mode != EfiIPsecTunnel) {\r
+ ASSERT (Dst->TunnelOption == NULL);\r
+ } else {\r
+ Dst->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ((Dst + 1), sizeof (UINTN));\r
+ CopyMem (\r
+ Dst->TunnelOption,\r
+ Src->TunnelOption,\r
+ sizeof (EFI_IPSEC_TUNNEL_OPTION)\r
+ );\r
+ }\r
+}\r
+\r
+/**\r
+ Calculate the a whole size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed\r
+ to by the pointer members.\r
+\r
+ @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA.\r
+\r
+ @return the whole size the specified EFI_IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfEfiSpdData (\r
+ IN EFI_IPSEC_SPD_DATA *SpdData\r
+ )\r
+{\r
+ UINTN Size;\r
+\r
+ Size = ALIGN_VARIABLE (sizeof (IPSEC_SPD_DATA));\r
+\r
+ if (SpdData->Action == EfiIPsecActionProtect) {\r
+ Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_PROCESS_POLICY));\r
+\r
+ if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
+ Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_TUNNEL_OPTION));\r
+ }\r
+ }\r
+\r
+ return Size;\r
+}\r
+\r
+/**\r
+ Calculate the a whole size of IPSEC_SPD_DATA which includes the buffer size pointed\r
+ to by the pointer members and the buffer size used by the Sa List.\r
+\r
+ @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA.\r
+\r
+ @return the whole size of IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfSpdData (\r
+ IN IPSEC_SPD_DATA *SpdData\r
+ )\r
+{\r
+ UINTN Size;\r
+ LIST_ENTRY *Link;\r
+\r
+ Size = sizeof (EFI_IPSEC_SPD_DATA) - sizeof (EFI_IPSEC_SA_ID);\r
+\r
+ if (SpdData->Action == EfiIPsecActionProtect) {\r
+ Size += sizeof (EFI_IPSEC_PROCESS_POLICY);\r
+\r
+ if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
+ Size += sizeof (EFI_IPSEC_TUNNEL_OPTION);\r
+ }\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Link, &SpdData->Sas) {\r
+ Size += sizeof (EFI_IPSEC_SA_ID);\r
+ }\r
+\r
+ return Size;\r
+}\r
+\r
+/**\r
+ Get the IPsec Variable.\r
+\r
+ Get the all variables which start with the string contained in VaraiableName.\r
+ Since all IPsec related variable store in continual space, those kinds of\r
+ variable can be searched by the EfiGetNextVariableName. Those variables also are\r
+ returned in a continual buffer.\r
+\r
+ @param[in] VariableName Pointer to a specified Variable Name.\r
+ @param[in] VendorGuid Pointer to a specified Vendor Guid.\r
+ @param[in] Attributes Point to memory location to return the attributes\r
+ of variable. If the point is NULL, the parameter\r
+ would be ignored.\r
+ @param[in, out] DataSize As input, point to the maximum size of return\r
+ Data-Buffer. As output, point to the actual\r
+ size of the returned Data-Buffer.\r
+ @param[in] Data Point to return Data-Buffer.\r
+\r
+ @retval EFI_ABORTED If the Variable size which contained in the variable\r
+ structure doesn't match the variable size obtained\r
+ from the EFIGetVariable.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has\r
+ been updated with the size needed to complete the request.\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval others Other errors found during the variable getting.\r
+**/\r
+EFI_STATUS\r
+IpSecGetVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN UINT32 *Attributes, OPTIONAL\r
+ IN OUT UINTN *DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_GUID VendorGuidI;\r
+ UINTN VariableNameLength;\r
+ CHAR16 *VariableNameI;\r
+ UINTN VariableNameISize;\r
+ UINTN VariableNameISizeNew;\r
+ UINTN VariableIndex;\r
+ UINTN VariableCount;\r
+ IP_SEC_VARIABLE_INFO IpSecVariableInfo;\r
+ UINTN DataSizeI;\r
+\r
+ //\r
+ // The variable name constructor is "VariableName + Info/0001/0002/... + NULL".\r
+ // So the varialbe name is like "VariableNameInfo", "VariableName0001", ...\r
+ // "VariableNameNULL".\r
+ //\r
+ VariableNameLength = StrLen (VariableName);\r
+ VariableNameISize = (VariableNameLength + 5) * sizeof (CHAR16);\r
+ VariableNameI = AllocateZeroPool (VariableNameISize);\r
+ ASSERT (VariableNameI != NULL);\r
+\r
+ //\r
+ // Construct the varible name of ipsecconfig meta data.\r
+ //\r
+ UnicodeSPrint (VariableNameI, VariableNameISize, L"%s%s", VariableName, L"Info");\r
+\r
+ DataSizeI = sizeof (IpSecVariableInfo);\r
+\r
+ Status = gRT->GetVariable (\r
+ VariableNameI,\r
+ VendorGuid,\r
+ Attributes,\r
+ &DataSizeI,\r
+ &IpSecVariableInfo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (*DataSize < IpSecVariableInfo.VariableSize) {\r
+ *DataSize = IpSecVariableInfo.VariableSize;\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ VariableCount = IpSecVariableInfo.VariableCount;\r
+ VariableNameI[0] = L'\0';\r
+\r
+ while (VariableCount != 0) {\r
+ //\r
+ // Get the variable name one by one in the variable database.\r
+ //\r
+ VariableNameISizeNew = VariableNameISize;\r
+ Status = gRT->GetNextVariableName (\r
+ &VariableNameISizeNew,\r
+ VariableNameI,\r
+ &VendorGuidI\r
+ );\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ VariableNameI = ReallocatePool (\r
+ VariableNameISize,\r
+ VariableNameISizeNew,\r
+ VariableNameI\r
+ );\r
+ VariableNameISize = VariableNameISizeNew;\r
+\r
+ Status = gRT->GetNextVariableName (\r
+ &VariableNameISizeNew,\r
+ VariableNameI,\r
+ &VendorGuidI\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ //\r
+ // Check whether the current variable is the required "ipsecconfig".\r
+ //\r
+ if (StrnCmp (VariableNameI, VariableName, VariableNameLength) == 0 ||\r
+ CompareGuid (VendorGuid, &VendorGuidI)\r
+ ) {\r
+ //\r
+ // Parse the variable count of the current ipsecconfig data.\r
+ //\r
+ VariableIndex = StrDecimalToUintn (VariableNameI + VariableNameLength);\r
+ if (VariableIndex!= 0 && VariableIndex <= IpSecVariableInfo.VariableCount) {\r
+ //\r
+ // Get the variable size of the current ipsecconfig data.\r
+ //\r
+ DataSizeI = 0;\r
+ Status = gRT->GetVariable (\r
+ VariableNameI,\r
+ VendorGuid,\r
+ Attributes,\r
+ &DataSizeI,\r
+ NULL\r
+ );\r
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+ //\r
+ // Validate the variable count and variable size.\r
+ //\r
+ if (VariableIndex != IpSecVariableInfo.VariableCount) {\r
+ //\r
+ // If the varaibe is not the last one, its size should be the max\r
+ // size of the single variable.\r
+ //\r
+ if (DataSizeI != IpSecVariableInfo.SingleVariableSize) {\r
+ return EFI_ABORTED;\r
+ }\r
+ } else {\r
+ if (DataSizeI != IpSecVariableInfo.VariableSize % IpSecVariableInfo.SingleVariableSize) {\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+ //\r
+ // Get the variable data of the current ipsecconfig data and\r
+ // store it into user buffer continously.\r
+ //\r
+ Status = gRT->GetVariable (\r
+ VariableNameI,\r
+ VendorGuid,\r
+ Attributes,\r
+ &DataSizeI,\r
+ (UINT8 *) Data + (VariableIndex - 1) * IpSecVariableInfo.SingleVariableSize\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ VariableCount--;\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // The VariableCount in "VariableNameInfo" varaible should have the correct\r
+ // numbers of variables which name starts with VariableName.\r
+ //\r
+ if (VariableCount != 0) {\r
+ Status = EFI_ABORTED;\r
+ }\r
+\r
+ON_EXIT:\r
+ FreePool (VariableNameI);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Set the IPsec variables.\r
+\r
+ Set all IPsec variables which start with the specified variable name. Those variables\r
+ are set one by one.\r
+\r
+ @param[in] VariableName The name of the vendor's variable. It is a\r
+ Null-Terminated Unicode String.\r
+ @param[in] VendorGuid Unify identifier for vendor.\r
+ @param[in] Attributes Point to memory location to return the attributes of\r
+ variable. If the point is NULL, the parameter would be ignored.\r
+ @param[in] DataSize The size in bytes of Data-Buffer.\r
+ @param[in] Data Points to the content of the variable.\r
+\r
+ @retval EFI_SUCCESS The firmware successfully stored the variable and its data, as\r
+ defined by the Attributes.\r
+ @retval others Storing the variables failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecSetVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN UINT32 Attributes,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *VariableNameI;\r
+ UINTN VariableNameSize;\r
+ UINTN VariableIndex;\r
+ IP_SEC_VARIABLE_INFO IpSecVariableInfo;\r
+ UINT64 MaximumVariableStorageSize;\r
+ UINT64 RemainingVariableStorageSize;\r
+ UINT64 MaximumVariableSize;\r
+\r
+ Status = gRT->QueryVariableInfo (\r
+ Attributes,\r
+ &MaximumVariableStorageSize,\r
+ &RemainingVariableStorageSize,\r
+ &MaximumVariableSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // "VariableName + Info/0001/0002/... + NULL"\r
+ //\r
+ VariableNameSize = (StrLen (VariableName) + 5) * sizeof (CHAR16);\r
+ VariableNameI = AllocateZeroPool (VariableNameSize);\r
+\r
+ if (VariableNameI == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Construct the variable of ipsecconfig general information. Like the total\r
+ // numbers of the Ipsecconfig variables, the total size of all ipsecconfig variables.\r
+ //\r
+ UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%s", VariableName, L"Info");\r
+ MaximumVariableSize -= VariableNameSize;\r
+\r
+ IpSecVariableInfo.VariableCount = (UINT32) ((DataSize + (UINTN) MaximumVariableSize - 1) / (UINTN) MaximumVariableSize);\r
+ IpSecVariableInfo.VariableSize = (UINT32) DataSize;\r
+ IpSecVariableInfo.SingleVariableSize = (UINT32) MaximumVariableSize;\r
+\r
+ //\r
+ // Set the variable of ipsecconfig general information.\r
+ //\r
+ Status = gRT->SetVariable (\r
+ VariableNameI,\r
+ VendorGuid,\r
+ Attributes,\r
+ sizeof (IpSecVariableInfo),\r
+ &IpSecVariableInfo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "Error set ipsecconfig meta data with %r\n", Status));\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ for (VariableIndex = 0; VariableIndex < IpSecVariableInfo.VariableCount; VariableIndex++) {\r
+ //\r
+ // Construct and set the variable of ipsecconfig data one by one.\r
+ // The index of variable name begin from 0001, and the varaible name\r
+ // likes "VariableName0001", "VaraiableName0002"....\r
+ //\r
+ UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%04d", VariableName, VariableIndex + 1);\r
+ Status = gRT->SetVariable (\r
+ VariableNameI,\r
+ VendorGuid,\r
+ Attributes,\r
+ (VariableIndex == IpSecVariableInfo.VariableCount - 1) ?\r
+ (DataSize % (UINTN) MaximumVariableSize) :\r
+ (UINTN) MaximumVariableSize,\r
+ (UINT8 *) Data + VariableIndex * (UINTN) MaximumVariableSize\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "Error set ipsecconfig variable data with %r\n", Status));\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+ if (VariableNameI != NULL) {\r
+ FreePool (VariableNameI);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Return the configuration value for the EFI IPsec driver.\r
+\r
+ This function lookup the data entry from IPsec database or IKEv2 configuration\r
+ information. The expected data type and unique identification are described in\r
+ DataType and Selector parameters.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to retrieve.\r
+ @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec\r
+ configuration data entry.\r
+ @param[in, out] DataSize On output the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec configuration data.\r
+ The type of the data buffer associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+ - This is NULL.\r
+ - Selector is NULL.\r
+ - DataSize is NULL.\r
+ - Data is NULL and *DataSize is not zero\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetData (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ )\r
+{\r
+ if (This == NULL || Selector == NULL || DataSize == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (*DataSize != 0 && Data == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (DataType >= IPsecConfigDataTypeMaximum) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return mGetPolicyEntry[DataType](Selector, DataSize, Data);\r
+}\r
+\r
+/**\r
+ Set the security association, security policy and peer authorization configuration\r
+ information for the EFI IPsec driver.\r
+\r
+ This function is used to set the IPsec configuration information of type DataType for\r
+ the EFI IPsec driver.\r
+ The IPsec configuration data has a unique selector/identifier separately to identify\r
+ a data entry. The selector structure depends on DataType's definition.\r
+ Using SetData() with a Data of NULL causes the IPsec configuration data entry identified\r
+ by DataType and Selector to be deleted.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to be set.\r
+ @param[in] Selector Pointer to an entry selector on operated configuration data\r
+ specified by DataType. A NULL Selector causes the entire\r
+ specified-type configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure of the data buffer is\r
+ associated with the DataType.\r
+ @param[in] InsertBefore Pointer to one entry selector which describes the expected\r
+ position the new data entry will be added. If InsertBefore is NULL,\r
+ the new entry will be appended to the end of the database.\r
+\r
+ @retval EFI_SUCCESS The specified configuration entry data was set successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigSetData (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (DataType >= IPsecConfigDataTypeMaximum) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Status = mSetPolicyEntry[DataType](Selector, Data, InsertBefore);\r
+\r
+ if (!EFI_ERROR (Status) && !mSetBySelf) {\r
+ //\r
+ // Save the updated config data into variable.\r
+ //\r
+ IpSecConfigSave ();\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Enumerates the current selector for IPsec configuration data entry.\r
+\r
+ This function is called multiple times to retrieve the entry Selector in IPsec\r
+ configuration database. On each call to GetNextSelector(), the next entry\r
+ Selector are retrieved into the output interface.\r
+\r
+ If the entire IPsec configuration database has been iterated, the error\r
+ EFI_NOT_FOUND is returned.\r
+ If the Selector buffer is too small for the next Selector copy, an\r
+ EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect\r
+ the size of buffer needed.\r
+\r
+ On the initial call to GetNextSelector() to start the IPsec configuration database\r
+ search, a pointer to the buffer with all zero value is passed in Selector. Calls\r
+ to SetData() between calls to GetNextSelector may produce unpredictable results.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of IPsec configuration data to retrieve.\r
+ @param[in, out] SelectorSize The size of the Selector buffer.\r
+ @param[in, out] Selector On input, supplies the pointer to last Selector that was\r
+ returned by GetNextSelector().\r
+ On output, returns one copy of the current entry Selector\r
+ of a given DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+ - This is NULL.\r
+ - SelectorSize is NULL.\r
+ - Selector is NULL.\r
+ @retval EFI_NOT_FOUND The next configuration data entry was not found.\r
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.\r
+ @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter\r
+ has been updated with the size needed to complete the search\r
+ request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetNextSelector (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN OUT UINTN *SelectorSize,\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ IPSEC_COMMON_POLICY_ENTRY *CommonEntry;\r
+ BOOLEAN IsFound;\r
+\r
+ if (This == NULL || Selector == NULL || SelectorSize == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (DataType >= IPsecConfigDataTypeMaximum) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ IsFound = FALSE;\r
+\r
+ NET_LIST_FOR_EACH (Link, &mConfigData[DataType]) {\r
+ CommonEntry = BASE_CR (Link, IPSEC_COMMON_POLICY_ENTRY, List);\r
+\r
+ if (IsFound || mIsZeroSelector[DataType](Selector)) {\r
+ //\r
+ // If found the appointed entry, then duplicate the next one and return,\r
+ // or if the appointed entry is zero, then return the first one directly.\r
+ //\r
+ return mDuplicateSelector[DataType](Selector, CommonEntry->Selector, SelectorSize);\r
+ } else {\r
+ //\r
+ // Set the flag if find the appointed entry.\r
+ //\r
+ IsFound = mCompareSelector[DataType](Selector, CommonEntry->Selector);\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Register an event that is to be signaled whenever a configuration process on the\r
+ specified IPsec configuration information is done.\r
+\r
+ The register function is not surpport now and always returns EFI_UNSUPPORTED.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to be registered the event for.\r
+ @param[in] Event The event to be registered.\r
+\r
+ @retval EFI_SUCCESS The event is registered successfully.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+ @retval EFI_ACCESS_DENIED The Event is already registered for the DataType.\r
+ @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified\r
+ DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigRegisterNotify (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_EVENT Event\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Remove the specified event that was previously registered on the specified IPsec\r
+ configuration data.\r
+\r
+ This function is not support now and alwasy return EFI_UNSUPPORTED.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The configuration data type to remove the registered event for.\r
+ @param[in] Event The event to be unregistered.\r
+\r
+ @retval EFI_SUCCESS The event was removed successfully.\r
+ @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the\r
+ database.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+ @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified\r
+ DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigUnregisterNotify (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_EVENT Event\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Copy whole data in specified EFI_SIPEC_CONFIG_SELECTOR and the Data to a buffer.\r
+\r
+ This function is a caller defined function, and it is called by the IpSecVisitConfigData().\r
+ The orignal caller is IpSecConfigSave(), which calls the IpsecVisitConfigData() to\r
+ copy all types of IPsec Config datas into one buffer and store this buffer into firmware in\r
+ the form of several variables.\r
+\r
+ @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] Selector Points to a EFI_IPSEC_CONFIG_SELECTOR to be copied\r
+ to the buffer.\r
+ @param[in] Data Points to data to be copied to the buffer. The\r
+ Data type is related to the Type.\r
+ @param[in] SelectorSize The size of the Selector.\r
+ @param[in] DataSize The size of the Data.\r
+ @param[in, out] Buffer The buffer to store the Selector and Data.\r
+\r
+ @retval EFI_SUCCESS Copy the Selector and Data to a buffer successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecCopyPolicyEntry (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE Type,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN UINTN SelectorSize,\r
+ IN UINTN DataSize,\r
+ IN OUT IPSEC_VARIABLE_BUFFER *Buffer\r
+ )\r
+{\r
+ IPSEC_VAR_ITEM_HEADER SelectorHeader;\r
+ IPSEC_VAR_ITEM_HEADER DataHeader;\r
+ UINTN EntrySize;\r
+ UINT8 *TempPoint;\r
+\r
+ if (Type == IPsecConfigDataTypeSad) {\r
+ //\r
+ // Don't save automatically-generated sa entry into variable.\r
+ //\r
+ if (((EFI_IPSEC_SA_DATA *) Data)->ManualSet == FALSE) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ //\r
+ // Increase the capacity size of the buffer if needed.\r
+ //\r
+ EntrySize = ALIGN_VARIABLE (sizeof (SelectorHeader));\r
+ EntrySize = ALIGN_VARIABLE (EntrySize + SelectorSize);\r
+ EntrySize = ALIGN_VARIABLE (EntrySize + sizeof (SelectorHeader));\r
+ EntrySize = ALIGN_VARIABLE (EntrySize + DataSize);\r
+\r
+ //EntrySize = SelectorSize + DataSize + 2 * sizeof (SelectorHeader);\r
+ if (Buffer->Capacity - Buffer->Size < EntrySize) {\r
+ //\r
+ // Calculate the required buffer\r
+ //\r
+ Buffer->Capacity += EntrySize;\r
+ TempPoint = AllocatePool (Buffer->Capacity);\r
+\r
+ if (Buffer->Ptr == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Copy the old Buffer to new buffer and free the old one.\r
+ //\r
+ CopyMem (TempPoint, Buffer->Ptr, Buffer->Size);\r
+ FreePool (Buffer->Ptr);\r
+\r
+ Buffer->Ptr = TempPoint;\r
+ }\r
+\r
+ mFixPolicyEntry[Type](Selector, Data);\r
+\r
+ //\r
+ // Fill the selector header and copy it into buffer.\r
+ //\r
+ SelectorHeader.Type = (UINT8) (Type | IPSEC_VAR_ITEM_HEADER_LOGO_BIT);\r
+ SelectorHeader.Size = (UINT16) SelectorSize;\r
+\r
+ CopyMem (\r
+ Buffer->Ptr + Buffer->Size,\r
+ &SelectorHeader,\r
+ sizeof (SelectorHeader)\r
+ );\r
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (SelectorHeader));\r
+\r
+ //\r
+ // Copy the selector into buffer.\r
+ //\r
+ CopyMem (\r
+ Buffer->Ptr + Buffer->Size,\r
+ Selector,\r
+ SelectorSize\r
+ );\r
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + SelectorSize);\r
+\r
+ //\r
+ // Fill the data header and copy it into buffer.\r
+ //\r
+ DataHeader.Type = (UINT8) Type;\r
+ DataHeader.Size = (UINT16) DataSize;\r
+\r
+ CopyMem (\r
+ Buffer->Ptr + Buffer->Size,\r
+ &DataHeader,\r
+ sizeof (DataHeader)\r
+ );\r
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (DataHeader));\r
+ //\r
+ // Copy the data into buffer.\r
+ //\r
+ CopyMem (\r
+ Buffer->Ptr + Buffer->Size,\r
+ Data,\r
+ DataSize\r
+ );\r
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + DataSize);\r
+\r
+ mUnfixPolicyEntry[Type](Selector, Data);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Visit all IPsec Configurations of specified Type and call the caller defined\r
+ interface.\r
+\r
+ @param[in] DataType The specified IPsec Config Data Type.\r
+ @param[in] Routine The function defined by the caller.\r
+ @param[in] Context The data passed to the Routine.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated\r
+ @retval EFI_SUCCESS This function completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecVisitConfigData (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN IPSEC_COPY_POLICY_ENTRY Routine,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS GetNextStatus;\r
+ EFI_STATUS GetDataStatus;\r
+ EFI_STATUS RoutineStatus;\r
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;\r
+ VOID *Data;\r
+ UINTN SelectorSize;\r
+ UINTN DataSize;\r
+ UINTN SelectorBufferSize;\r
+ UINTN DataBufferSize;\r
+ BOOLEAN FirstGetNext;\r
+\r
+ FirstGetNext = TRUE;\r
+ DataBufferSize = 0;\r
+ Data = NULL;\r
+ SelectorBufferSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR);\r
+ Selector = AllocateZeroPool (SelectorBufferSize);\r
+\r
+ if (Selector == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ while (TRUE) {\r
+ //\r
+ // Get the real size of the selector.\r
+ //\r
+ SelectorSize = SelectorBufferSize;\r
+ GetNextStatus = EfiIpSecConfigGetNextSelector (\r
+ &mIpSecConfigInstance,\r
+ DataType,\r
+ &SelectorSize,\r
+ Selector\r
+ );\r
+ if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {\r
+ FreePool (Selector);\r
+ SelectorBufferSize = SelectorSize;\r
+ //\r
+ // Allocate zero pool for the first selector, while store the last\r
+ // selector content for the other selectors.\r
+ //\r
+ if (FirstGetNext) {\r
+ Selector = AllocateZeroPool (SelectorBufferSize);\r
+ } else {\r
+ Selector = AllocateCopyPool (SelectorBufferSize, Selector);\r
+ }\r
+\r
+ if (Selector == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Get the content of the selector.\r
+ //\r
+ GetNextStatus = EfiIpSecConfigGetNextSelector (\r
+ &mIpSecConfigInstance,\r
+ DataType,\r
+ &SelectorSize,\r
+ Selector\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (GetNextStatus)) {\r
+ break;\r
+ }\r
+\r
+ FirstGetNext = FALSE;\r
+\r
+ //\r
+ // Get the real size of the policy entry according to the selector.\r
+ //\r
+ DataSize = DataBufferSize;\r
+ GetDataStatus = EfiIpSecConfigGetData (\r
+ &mIpSecConfigInstance,\r
+ DataType,\r
+ Selector,\r
+ &DataSize,\r
+ Data\r
+ );\r
+ if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {\r
+ if (Data != NULL) {\r
+ FreePool (Data);\r
+ }\r
+\r
+ DataBufferSize = DataSize;\r
+ Data = AllocateZeroPool (DataBufferSize);\r
+\r
+ if (Data == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Get the content of the policy entry according to the selector.\r
+ //\r
+ GetDataStatus = EfiIpSecConfigGetData (\r
+ &mIpSecConfigInstance,\r
+ DataType,\r
+ Selector,\r
+ &DataSize,\r
+ Data\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (GetDataStatus)) {\r
+ break;\r
+ }\r
+ //\r
+ // Prepare the buffer of updated policy entry, which is stored in\r
+ // the continous memory, and then save into variable later.\r
+ //\r
+ RoutineStatus = Routine (\r
+ DataType,\r
+ Selector,\r
+ Data,\r
+ SelectorSize,\r
+ DataSize,\r
+ Context\r
+ );\r
+ if (EFI_ERROR (RoutineStatus)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Data != NULL) {\r
+ FreePool (Data);\r
+ }\r
+\r
+ if (Selector != NULL) {\r
+ FreePool (Selector);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function is the subfunction of EFIIpSecConfigSetData.\r
+\r
+ This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS Saved the configration successfully.\r
+ @retval Others Other errors were found while obtaining the variable.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigSave (\r
+ VOID\r
+ )\r
+{\r
+ IPSEC_VARIABLE_BUFFER Buffer;\r
+ EFI_STATUS Status;\r
+ EFI_IPSEC_CONFIG_DATA_TYPE Type;\r
+\r
+ Buffer.Size = 0;\r
+ Buffer.Capacity = IPSEC_DEFAULT_VARIABLE_SIZE;\r
+ Buffer.Ptr = AllocateZeroPool (Buffer.Capacity);\r
+\r
+ if (Buffer.Ptr == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // For each policy database, prepare the contious buffer to save into variable.\r
+ //\r
+ for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {\r
+ IpSecVisitConfigData (\r
+ Type,\r
+ (IPSEC_COPY_POLICY_ENTRY) IpSecCopyPolicyEntry,\r
+ &Buffer\r
+ );\r
+ }\r
+ //\r
+ // Save the updated policy database into variable.\r
+ //\r
+ Status = IpSecSetVariable (\r
+ IPSECCONFIG_VARIABLE_NAME,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+ Buffer.Size,\r
+ Buffer.Ptr\r
+ );\r
+\r
+ FreePool (Buffer.Ptr);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get the all IPSec configuration variables and store those variables\r
+ to the internal data structure.\r
+\r
+ This founction is called by IpSecConfigInitialize() which is to intialize the\r
+ IPsecConfiguration Protocol.\r
+\r
+ @param[in] Private Point to IPSEC_PRIVATE_DATA.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated\r
+ @retval EFI_SUCCESS Restore the IPsec Configuration successfully.\r
+ @retval others Other errors is found while obtaining the variable.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigRestore (\r
+ IN IPSEC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN BufferSize;\r
+ UINT8 *Buffer;\r
+ IPSEC_VAR_ITEM_HEADER *Header;\r
+ UINT8 *Ptr;\r
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;\r
+ EFI_IPSEC_CONFIG_DATA_TYPE Type;\r
+ VOID *Data;\r
+ UINT8 Value;\r
+ UINTN Size;\r
+\r
+ Value = 0;\r
+ Size = sizeof (Value);\r
+ BufferSize = 0;\r
+ Buffer = NULL;\r
+\r
+ Status = gRT->GetVariable (\r
+ IPSECCONFIG_STATUS_NAME,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ NULL,\r
+ &Size,\r
+ &Value\r
+ );\r
+\r
+ if (!EFI_ERROR (Status) && Value == IPSEC_STATUS_ENABLED) {\r
+ Private->IpSec.DisabledFlag = FALSE;\r
+ }\r
+ //\r
+ // Get the real size of policy database in variable.\r
+ //\r
+ Status = IpSecGetVariable (\r
+ IPSECCONFIG_VARIABLE_NAME,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ NULL,\r
+ &BufferSize,\r
+ Buffer\r
+ );\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+\r
+ Buffer = AllocateZeroPool (BufferSize);\r
+ if (Buffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Get the content of policy database in variable.\r
+ //\r
+ Status = IpSecGetVariable (\r
+ IPSECCONFIG_VARIABLE_NAME,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ NULL,\r
+ &BufferSize,\r
+ Buffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Buffer);\r
+ return Status;\r
+ }\r
+\r
+ for (Ptr = Buffer; Ptr < Buffer + BufferSize;) {\r
+\r
+ Header = (IPSEC_VAR_ITEM_HEADER *) Ptr;\r
+ Type = (EFI_IPSEC_CONFIG_DATA_TYPE) (Header->Type & IPSEC_VAR_ITEM_HEADER_CONTENT_BIT);\r
+ ASSERT (((Header->Type & 0x80) == IPSEC_VAR_ITEM_HEADER_LOGO_BIT) && (Type < IPsecConfigDataTypeMaximum));\r
+\r
+ Selector = (EFI_IPSEC_CONFIG_SELECTOR *) ALIGN_POINTER (Header + 1, sizeof (UINTN));\r
+ Header = (IPSEC_VAR_ITEM_HEADER *) ALIGN_POINTER (\r
+ (UINT8 *) Selector + Header->Size,\r
+ sizeof (UINTN)\r
+ );\r
+ ASSERT (Header->Type == Type);\r
+\r
+ Data = ALIGN_POINTER (Header + 1, sizeof (UINTN));\r
+\r
+ mUnfixPolicyEntry[Type](Selector, Data);\r
+\r
+ //\r
+ // Update each policy entry according to the content in variable.\r
+ //\r
+ mSetBySelf = TRUE;\r
+ Status = EfiIpSecConfigSetData (\r
+ &Private->IpSecConfig,\r
+ Type,\r
+ Selector,\r
+ Data,\r
+ NULL\r
+ );\r
+ mSetBySelf = FALSE;\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Buffer);\r
+ return Status;\r
+ }\r
+\r
+ Ptr = ALIGN_POINTER ((UINT8 *) Data + Header->Size, sizeof (UINTN));\r
+ }\r
+\r
+ FreePool (Buffer);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Install and Initialize IPsecConfig protocol\r
+\r
+ @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish,\r
+ the pointer of IPsecConfig Protocol implementation will copy\r
+ into its IPsecConfig member.\r
+\r
+ @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully.\r
+ @retval Others Initializing the IPsecConfig Protocol failed.\r
+**/\r
+EFI_STATUS\r
+IpSecConfigInitialize (\r
+ IN OUT IPSEC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_IPSEC_CONFIG_DATA_TYPE Type;\r
+\r
+ CopyMem (\r
+ &Private->IpSecConfig,\r
+ &mIpSecConfigInstance,\r
+ sizeof (EFI_IPSEC_CONFIG_PROTOCOL)\r
+ );\r
+\r
+ //\r
+ // Initialize the list head of policy database.\r
+ //\r
+ for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {\r
+ InitializeListHead (&mConfigData[Type]);\r
+ }\r
+ //\r
+ // Restore the content of policy database according to the variable.\r
+ //\r
+ IpSecConfigRestore (Private);\r
+\r
+ return gBS->InstallMultipleProtocolInterfaces (\r
+ &Private->Handle,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ &Private->IpSecConfig,\r
+ NULL\r
+ );\r
+}\r
--- /dev/null
+/** @file\r
+ Definitions related to IPSEC_CONFIG_PROTOCOL implementations.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _IPSEC_CONFIG_IMPL_H_\r
+#define _IPSEC_CONFIG_IMPL_H_\r
+\r
+#include <Protocol/IpSec.h>\r
+#include <Protocol/IpSecConfig.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DebugLib.h>\r
+\r
+#include "IpSecImpl.h"\r
+\r
+#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF\r
+#define EFI_IPSEC_ANY_PORT 0\r
+\r
+#define IPSEC_VAR_ITEM_HEADER_LOGO_BIT 0x80\r
+#define IPSEC_VAR_ITEM_HEADER_CONTENT_BIT 0x7F\r
+\r
+#define IPSECCONFIG_VARIABLE_NAME L"IpSecConfig"\r
+#define IPSECCONFIG_STATUS_NAME L"IpSecStatus"\r
+\r
+#define SIZE_OF_SPD_SELECTOR(x) (UINTN) (sizeof (EFI_IPSEC_SPD_SELECTOR) \\r
+ + sizeof (EFI_IP_ADDRESS_INFO) * ((x)->LocalAddressCount + (x)->RemoteAddressCount))\r
+\r
+#define FIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) - (UINTN) (base))\r
+#define UNFIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) + (UINTN) (base))\r
+\r
+//\r
+// The data structure used to store the genernall information of IPsec configuration.\r
+//\r
+typedef struct {\r
+ UINT32 VariableCount; // the total number of the IPsecConfig variables.\r
+ UINT32 VariableSize; // The total size of all IpsecConfig variables.\r
+ UINT32 SingleVariableSize; // The max size of single variable\r
+} IP_SEC_VARIABLE_INFO;\r
+\r
+typedef struct {\r
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;\r
+ VOID *Data;\r
+ LIST_ENTRY List;\r
+} IPSEC_COMMON_POLICY_ENTRY;\r
+\r
+typedef struct {\r
+ UINT8 *Ptr;\r
+ UINTN Size;\r
+ UINTN Capacity;\r
+} IPSEC_VARIABLE_BUFFER;\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+ UINT8 Type;\r
+ UINT16 Size;\r
+} IPSEC_VAR_ITEM_HEADER;\r
+#pragma pack()\r
+\r
+/**\r
+ The prototype of Copy Source Selector to the Destination Selector.\r
+\r
+ @param[in out] DstSel Pointer of Destination Selector. It would be\r
+ SPD Selector, or SAD Selector or PAD Selector.\r
+ @param[in] SrcSel Pointer of Source Selector. It would be\r
+ SPD Selector, or SAD Selector or PAD Selector.\r
+ @param[in out] Size The size of the Destination Selector. If it\r
+ is not NULL and its value is less than the size of\r
+ Source Selector, the value of Source Selector's\r
+ size will be passed to the caller by this parameter.\r
+\r
+ @retval EFI_INVALID_PARAMETER If the Destination or Source Selector is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source Selector.\r
+ @retval EFI_SUCCESS Copy Source Selector to the Destination\r
+ Selector successfully.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_DUPLICATE_SELECTOR) (\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,\r
+ IN OUT UINTN *Size\r
+ );\r
+\r
+/**\r
+ It is prototype of compare two Selectors. The Selector would be SPD Selector,\r
+ or SAD Selector, or PAD selector.\r
+\r
+ @param[in] Selector1 Pointer of the first Selector.\r
+ @param[in] Selector2 Pointer of the second Selector.\r
+\r
+ @retval TRUE These two Selectors have the same value in certain fields.\r
+ @retval FALSE Not all fields have the same value in these two Selectors.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(*IPSEC_COMPARE_SELECTOR) (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ );\r
+\r
+/**\r
+ The prototype of a function to check if the Selector is Zero by its certain fields.\r
+\r
+ @param[in] Selector Pointer of the Selector.\r
+\r
+ @retval TRUE If the Selector is Zero.\r
+ @retval FALSE If the Selector is not Zero.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(*IPSEC_IS_ZERO_SELECTOR) (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ );\r
+\r
+/**\r
+ The prototype of a function to fix the value of particular members of the Selector.\r
+\r
+ @param[in] Selector Pointer of Selector.\r
+ @param[in] Data Pointer of Data.\r
+\r
+**/\r
+typedef\r
+VOID\r
+(*IPSEC_FIX_POLICY_ENTRY) (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data\r
+ );\r
+\r
+/**\r
+ It is prototype function to define a routine function by the caller of IpSecVisitConfigData().\r
+\r
+ @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE.\r
+ @param[in] Selector Points to EFI_IPSEC_CONFIG_SELECTOR to be copied\r
+ to the buffer.\r
+ @param[in] Data Points to data to be copied to the buffer. The\r
+ Data type is related to the Type.\r
+ @param[in] SelectorSize The size of the Selector.\r
+ @param[in] DataSize The size of the Data.\r
+ @param[in out] Buffer The buffer to store the Selector and Data.\r
+\r
+ @retval EFI_SUCCESS Copied the Selector and Data to a buffer successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_COPY_POLICY_ENTRY) (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE Type,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN UINTN SelectorSize,\r
+ IN UINTN DataSize,\r
+ IN OUT VOID *Context\r
+ );\r
+\r
+/**\r
+ Set the security policy information for the EFI IPsec driver.\r
+\r
+ The IPsec configuration data has a unique selector/identifier separately to\r
+ identify a data entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector on operated\r
+ configuration data specified by DataType.\r
+ A NULL Selector causes the entire specified-type\r
+ configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set.\r
+ @param[in] Context Pointer to one entry selector that describes\r
+ the expected position the new data entry will\r
+ be added. If Context is NULL, the new entry will\r
+ be appended to the end of the database.\r
+\r
+ @retval EFI_INVALID_PARAMETER Certain Parameters are not correct. The Parameter\r
+ requiring a check depends on the Selector type.\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_SET_POLICY_ENTRY) (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ A prototype function definition to lookup the data entry from IPsec. Return the configuration\r
+ value of the specified Entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector that is an identifier\r
+ of the entry.\r
+ @param[in, out] DataSize On output, the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec\r
+ configuration data. The type of the data buffer\r
+ is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*IPSEC_GET_POLICY_ENTRY) (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ IN VOID *Data\r
+ );\r
+\r
+/**\r
+ Compare two SPD Selectors.\r
+\r
+ Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/\r
+ NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the\r
+ Local Addresses and remote Addresses.\r
+\r
+ @param[in] Selector1 Pointer of the first SPD Selector.\r
+ @param[in] Selector2 Pointer of the second SPD Selector.\r
+\r
+ @retval TRUE These two Selectors have the same value in above fields.\r
+ @retval FALSE Not all of the above fields have the same value in these two Selectors.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSpdSelector (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ );\r
+\r
+\r
+/**\r
+ Visit all IPsec Configurations of specified Type and call the caller defined\r
+ interface.\r
+\r
+ @param[in] DataType The specified IPsec Config Data Type.\r
+ @param[in] Routine The function caller defined.\r
+ @param[in] Context The data passed to the Routine.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS This function complete successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecVisitConfigData (\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN IPSEC_COPY_POLICY_ENTRY Routine,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ This function is the subfunction of the EFIIpSecConfigSetData.\r
+\r
+ This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS Saved the configration successfully.\r
+ @retval Others Other errors were found while obtaining the variable.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigSave (\r
+ VOID\r
+ );\r
+\r
+/**\r
+ Initialize IPsecConfig protocol\r
+\r
+ @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish,\r
+ the pointer of IPsecConfig Protocol implementation will copy\r
+ into its IPsecConfig member.\r
+\r
+ @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully.\r
+ @retval Others Initializing the IPsecConfig Protocol failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecConfigInitialize (\r
+ IN OUT IPSEC_PRIVATE_DATA *Private\r
+ );\r
+\r
+/**\r
+ Calculate the entire size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed\r
+ by the pointer members.\r
+\r
+ @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA.\r
+\r
+ @return The entire size of the specified EFI_IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfEfiSpdData (\r
+ IN EFI_IPSEC_SPD_DATA *SpdData\r
+ );\r
+\r
+/**\r
+ Calculate the a entire size of IPSEC_SPD_DATA, which includes the buffer size pointed\r
+ by the pointer members and the buffer size used by Sa List.\r
+\r
+ @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA.\r
+\r
+ @return The entire size of IPSEC_SPD_DATA.\r
+\r
+**/\r
+UINTN\r
+IpSecGetSizeOfSpdData (\r
+ IN IPSEC_SPD_DATA *SpdData\r
+ );\r
+\r
+/**\r
+ Copy Source Process Policy to the Destination Process Policy.\r
+\r
+ @param[in] Dst Pointer to the Source Process Policy.\r
+ @param[in] Src Pointer to the Destination Process Policy.\r
+\r
+**/\r
+VOID\r
+IpSecDuplicateProcessPolicy (\r
+ IN EFI_IPSEC_PROCESS_POLICY *Dst,\r
+ IN EFI_IPSEC_PROCESS_POLICY *Src\r
+ );\r
+\r
+/**\r
+ Compare two SPD Selectors.\r
+\r
+ Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/\r
+ NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the\r
+ Local Addresses and remote Addresses.\r
+\r
+ @param[in] Selector1 Pointer of the first SPD Selector.\r
+ @param[in] Selector2 Pointer of the second SPD Selector.\r
+\r
+ @retval TRUE This two Selector have the same value in above fields.\r
+ @retval FALSE Not all of the above fields have the same value in these two Selectors.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSpdSelector (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ );\r
+\r
+/**\r
+ Compare two SA IDs.\r
+\r
+ @param[in] Selector1 Pointer of the first SA ID.\r
+ @param[in] Selector2 Pointer of the second SA ID.\r
+\r
+ @retval TRUE This two Selectors have the same SA ID.\r
+ @retval FALSE This two Selecotrs don't have the same SA ID.\r
+\r
+**/\r
+BOOLEAN\r
+CompareSaId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ );\r
+\r
+/**\r
+ Compare two PAD IDs.\r
+\r
+ @param[in] Selector1 Pointer of the first PAD ID.\r
+ @param[in] Selector2 Pointer of the second PAD ID.\r
+\r
+ @retval TRUE This two Selectors have the same PAD ID.\r
+ @retval FALSE This two Selecotrs don't have the same PAD ID.\r
+\r
+**/\r
+BOOLEAN\r
+ComparePadId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2\r
+ );\r
+\r
+/**\r
+ Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount\r
+ fields.\r
+\r
+ @param[in] Selector Pointer of the SPD Selector.\r
+\r
+ @retval TRUE If the SPD Selector is Zero.\r
+ @retval FALSE If the SPD Selector is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSpdSelector (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ );\r
+\r
+/**\r
+ Check if the SA ID is Zero by its DestAddress.\r
+\r
+ @param[in] Selector Pointer of the SA ID.\r
+\r
+ @retval TRUE If the SA ID is Zero.\r
+ @retval FALSE If the SA ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroSaId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ );\r
+\r
+/**\r
+ Check if the PAD ID is Zero.\r
+\r
+ @param[in] Selector Pointer of the PAD ID.\r
+\r
+ @retval TRUE If the PAD ID is Zero.\r
+ @retval FALSE If the PAD ID is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsZeroPadId (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ );\r
+\r
+/**\r
+ Copy Source SPD Selector to the Destination SPD Selector.\r
+\r
+ @param[in, out] DstSel Pointer of Destination SPD Selector.\r
+ @param[in] SrcSel Pointer of Source SPD Selector.\r
+ @param[in, out] Size The size of the Destination SPD Selector. If\r
+ it is not NULL and its value is less than the\r
+ size of Source SPD Selector, the value of\r
+ Source SPD Selector's size will be passed to\r
+ the caller by this parameter.\r
+\r
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source SPD Selector.\r
+ @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD\r
+ Selector successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSpdSelector (\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,\r
+ IN OUT UINTN *Size\r
+ );\r
+\r
+/**\r
+ Copy Source SA ID to the Destination SA ID.\r
+\r
+ @param[in, out] DstSel Pointer of the Destination SA ID.\r
+ @param[in] SrcSel Pointer of the Source SA ID.\r
+ @param[in, out] Size The size of the Destination SA ID. If it\r
+ not NULL, and its value is less than the size of\r
+ Source SA ID, the value of Source SA ID's size\r
+ will be passed to the caller by this parameter.\r
+\r
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID.\r
+ @retval EFI_SUCCESS Copied Source SA ID to the Destination SA ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicateSaId (\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,\r
+ IN OUT UINTN *Size\r
+ );\r
+\r
+/**\r
+ Copy Source PAD ID to the Destination PAD ID.\r
+\r
+ @param[in, out] DstSel Pointer of Destination PAD ID.\r
+ @param[in] SrcSel Pointer of Source PAD ID.\r
+ @param[in, out] Size The size of the Destination PAD ID. If it\r
+ not NULL, and its value less than the size of\r
+ Source PAD ID, the value of Source PAD ID's size\r
+ will be passed to the caller by this parameter.\r
+\r
+ @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID.\r
+ @retval EFI_SUCCESS Copied Source PAD ID to the Destination PAD ID successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DuplicatePadId (\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,\r
+ IN OUT UINTN *Size\r
+ );\r
+\r
+/**\r
+ Fix the value of some members of the SPD Selector.\r
+\r
+ This function is called by IpSecCopyPolicyEntry(), which copies the Policy\r
+ Entry into the Variable. Since some members in SPD Selector are pointers,\r
+ a physical address to relative address conversion is required before copying\r
+ this SPD entry into the variable.\r
+\r
+ @param[in] Selector Pointer of SPD Selector.\r
+ @param[in, out] Data Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+FixSpdEntry (\r
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,\r
+ IN OUT EFI_IPSEC_SPD_DATA *Data\r
+ );\r
+\r
+/**\r
+ Fix the value of some members of SA ID.\r
+\r
+ This function is called by IpSecCopyPolicyEntry(), which copies the Policy\r
+ Entry into the Variable. Since some members in SA ID are pointers,\r
+ a physical address to relative address conversion is required before copying\r
+ this SAD into the variable.\r
+\r
+ @param[in] SaId Pointer of SA ID.\r
+ @param[in, out] Data Pointer of SA Data.\r
+\r
+**/\r
+VOID\r
+FixSadEntry (\r
+ IN EFI_IPSEC_SA_ID *SaId,\r
+ IN OUT EFI_IPSEC_SA_DATA *Data\r
+ );\r
+\r
+/**\r
+ Fix the value of some members of PAD ID.\r
+\r
+ This function is called by IpSecCopyPolicyEntry(), which copy the Policy\r
+ Entry into the Variable. Since some members in PAD ID are pointers,\r
+ a physical address to relative address conversion is required before copying\r
+ this PAD into the variable.\r
+\r
+ @param[in] PadId Pointer of PAD ID.\r
+ @param[in, out] Data Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+FixPadEntry (\r
+ IN EFI_IPSEC_PAD_ID *PadId,\r
+ IN OUT EFI_IPSEC_PAD_DATA *Data\r
+ );\r
+\r
+/**\r
+ Recover the value of some members of SPD Selector.\r
+\r
+ This function is corresponding to FixSpdEntry(). It recovers the value of members\r
+ of SPD Selector which fix by the FixSpdEntry().\r
+\r
+ @param[in, out] Selector Pointer of SPD Selector.\r
+ @param[in, out] Data Pointer of SPD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSpdEntry (\r
+ IN OUT EFI_IPSEC_SPD_SELECTOR *Selector,\r
+ IN OUT EFI_IPSEC_SPD_DATA *Data\r
+ );\r
+\r
+\r
+/**\r
+ Recover the value of some members of SA ID.\r
+\r
+ This function is corresponding to FixSadEntry(). It recovers the value of members\r
+ of SAD ID which fix by the FixSadEntry().\r
+\r
+ @param[in, out] SaId Pointer of SAD ID\r
+ @param[in, out] Data Pointer of SAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixSadEntry (\r
+ IN OUT EFI_IPSEC_SA_ID *SaId,\r
+ IN OUT EFI_IPSEC_SA_DATA *Data\r
+ );\r
+\r
+/**\r
+ Recover the value of some members of PAD ID.\r
+\r
+ This function is corresponding to FixPadEntry(). It recovers the value of members\r
+ of PAD ID which fix by the FixPadEntry().\r
+\r
+ @param[in] PadId Pointer of PAD ID\r
+ @param[in, out] Data Pointer of PAD Data.\r
+\r
+**/\r
+VOID\r
+UnfixPadEntry (\r
+ IN EFI_IPSEC_PAD_ID *PadId,\r
+ IN OUT EFI_IPSEC_PAD_DATA *Data\r
+ );\r
+\r
+/**\r
+ Set the security policy information for the EFI IPsec driver.\r
+\r
+ The IPsec configuration data has a unique selector/identifier separately to\r
+ identify a data entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector on operated\r
+ configuration data specified by DataType.\r
+ A NULL Selector causes the entire specified-type\r
+ configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure\r
+ of the data buffer should be EFI_IPSEC_SPD_DATA.\r
+ @param[in] Context Pointer to one entry selector that describes\r
+ the expected position the new data entry will\r
+ be added. If Context is NULL,the new entry will\r
+ be appended the end of database.\r
+\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - Selector is not NULL and its LocalAddress\r
+ is NULL or its RemoteAddress is NULL.\r
+ - Data is not NULL, its Action is Protected,\r
+ and its policy is NULL.\r
+ - Data is not NULL and its Action is not protected\r
+ and its policy is not NULL.\r
+ - The Action of Data is Protected, its policy\r
+ mode is Tunnel, and its tunnel option is NULL.\r
+ - The Action of Data is protected, its policy\r
+ mode is not Tunnel, and it tunnel option is not NULL.\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSpdEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ Set the security association information for the EFI IPsec driver.\r
+\r
+ The IPsec configuration data has a unique selector/identifier separately to\r
+ identify a data entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector on operated\r
+ configuration data specified by DataType.\r
+ A NULL Selector causes the entire specified-type\r
+ configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure\r
+ of the data buffer should be EFI_IPSEC_SA_DATA.\r
+ @param[in] Context Pointer to one entry selector which describes\r
+ the expected position the new data entry will\r
+ be added. If Context is NULL,the new entry will\r
+ be appended to the end of database.\r
+\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetSadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ Set the peer authorization configuration information for the EFI IPsec driver.\r
+\r
+ The IPsec configuration data has a unique selector/identifier separately to\r
+ identify a data entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector on operated\r
+ configuration data specified by DataType.\r
+ A NULL Selector causes the entire specified-type\r
+ configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure\r
+ of the data buffer should be EFI_IPSEC_PAD_DATA.\r
+ @param[in] Context Pointer to one entry selector that describes\r
+ the expected position where the new data entry will\r
+ be added. If Context is NULL, the new entry will\r
+ be appended the end of database.\r
+\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SetPadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function looks up the data entry from IPsec SPD, and returns the configuration\r
+ value of the specified SPD Entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector which is an identifier\r
+ of the SPD entry.\r
+ @param[in, out] DataSize On output the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec\r
+ configuration data. The type of the data buffer\r
+ is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSpdEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ );\r
+\r
+/**\r
+ This function looks up the data entry from IPsec SAD and returns the configuration\r
+ value of the specified SAD Entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector that is an identifier\r
+ of the SAD entry.\r
+ @param[in, out] DataSize On output, the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec\r
+ configuration data. This type of the data buffer\r
+ is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetSadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ );\r
+\r
+/**\r
+ This function looks up the data entry from IPsec PADand returns the configuration\r
+ value of the specified PAD Entry.\r
+\r
+ @param[in] Selector Pointer to an entry selector that is an identifier\r
+ of the PAD entry.\r
+ @param[in, out] DataSize On output the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec\r
+ configuration data. This type of the data buffer\r
+ is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPadEntry (\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ );\r
+\r
+/**\r
+ Return the configuration value for the EFI IPsec driver.\r
+\r
+ This function lookup the data entry from IPsec database or IKEv2 configuration\r
+ information. The expected data type and unique identification are described in\r
+ DataType and Selector parameters.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to retrieve.\r
+ @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec\r
+ configuration data entry.\r
+ @param[in, out] DataSize On output the size of data returned in Data.\r
+ @param[out] Data The buffer to return the contents of the IPsec configuration data.\r
+ The type of the data buffer is associated with the DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+ - This is NULL.\r
+ - Selector is NULL.\r
+ - DataSize is NULL.\r
+ - Data is NULL and *DataSize is not zero\r
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.\r
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.\r
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been\r
+ updated with the size needed to complete the request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetData (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN OUT UINTN *DataSize,\r
+ OUT VOID *Data\r
+ );\r
+\r
+/**\r
+ Set the security association, security policy and peer authorization configuration\r
+ information for the EFI IPsec driver.\r
+\r
+ This function is used to set the IPsec configuration information of type DataType for\r
+ the EFI IPsec driver.\r
+ The IPsec configuration data has a unique selector/identifier separately to identify\r
+ a data entry. The selector structure depends on DataType's definition.\r
+ Using SetData() with a Data of NULL causes the IPsec configuration data entry identified\r
+ by DataType and Selector to be deleted.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to be set.\r
+ @param[in] Selector Pointer to an entry selector on operated configuration data\r
+ specified by DataType. A NULL Selector causes the entire\r
+ specified-type configuration information to be flushed.\r
+ @param[in] Data The data buffer to be set. The structure of the data buffer is\r
+ associated with the DataType.\r
+ @param[in] InsertBefore Pointer to one entry selector which describes the expected\r
+ position the new data entry will be added. If InsertBefore is NULL,\r
+ the new entry will be appended the end of database.\r
+\r
+ @retval EFI_SUCCESS The specified configuration entry data was set successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.\r
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigSetData (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,\r
+ IN VOID *Data,\r
+ IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL\r
+ );\r
+\r
+/**\r
+ Enumerates the current selector for IPsec configuration data entry.\r
+\r
+ This function is called multiple times to retrieve the entry Selector in IPsec\r
+ configuration database. On each call to GetNextSelector(), the next entry\r
+ Selector are retrieved into the output interface.\r
+\r
+ If the entire IPsec configuration database has been iterated, the error\r
+ EFI_NOT_FOUND is returned.\r
+ If the Selector buffer is too small for the next Selector copy, an\r
+ EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect\r
+ the size of buffer needed.\r
+\r
+ On the initial call to GetNextSelector() to start the IPsec configuration database\r
+ search, a pointer to the buffer with all zero value is passed in Selector. Calls\r
+ to SetData() between calls to GetNextSelector may produce unpredictable results.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of IPsec configuration data to retrieve.\r
+ @param[in, out] SelectorSize The size of the Selector buffer.\r
+ @param[in, out] Selector On input, supplies the pointer to last Selector that was\r
+ returned by GetNextSelector().\r
+ On output, returns one copy of the current entry Selector\r
+ of a given DataType.\r
+\r
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:\r
+ - This is NULL.\r
+ - SelectorSize is NULL.\r
+ - Selector is NULL.\r
+ @retval EFI_NOT_FOUND The next configuration data entry was not found.\r
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.\r
+ @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter\r
+ has been updated with the size needed to complete the search\r
+ request.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigGetNextSelector (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN OUT UINTN *SelectorSize,\r
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector\r
+ );\r
+\r
+/**\r
+ Register an event that is to be signaled whenever a configuration process on the\r
+ specified IPsec configuration information is done.\r
+\r
+ The register function is not surpport now and always returns EFI_UNSUPPORTED.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The type of data to be registered the event for.\r
+ @param[in] Event The event to be registered.\r
+\r
+ @retval EFI_SUCCESS The event is registered successfully.\r
+ @retval EFI_INVALID_PARAMETER This is NULL, or Event is NULL.\r
+ @retval EFI_ACCESS_DENIED The Event is already registered for the DataType.\r
+ @retval EFI_UNSUPPORTED The notify registration unsupported, or the specified\r
+ DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigRegisterNotify (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_EVENT Event\r
+ );\r
+\r
+\r
+/**\r
+ Remove the specified event that was previously registered on the specified IPsec\r
+ configuration data.\r
+\r
+ This function is not supported now and always returns EFI_UNSUPPORTED.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.\r
+ @param[in] DataType The configuration data type to remove the registered event for.\r
+ @param[in] Event The event to be unregistered.\r
+\r
+ @retval EFI_SUCCESS The event was removed successfully.\r
+ @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the\r
+ database.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.\r
+ @retval EFI_UNSUPPORTED The notify registration unsupported or the specified\r
+ DataType is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiIpSecConfigUnregisterNotify (\r
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,\r
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,\r
+ IN EFI_EVENT Event\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Common operation for Security.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecCryptIo.h"\r
+//\r
+// Alogrithm's informations for the Encrypt/Decrpt Alogrithm.\r
+//\r
+ENCRYPT_ALGORITHM mIpsecEncryptAlgorithmList[IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE] = {\r
+ {EFI_IPSEC_EALG_NULL, 0, 0, 1, NULL, NULL, NULL, NULL},\r
+ {(UINT8)-1, 0, 0, 0, NULL, NULL, NULL, NULL}\r
+};\r
+//\r
+// Alogrithm's informations for the Authentication algorithm\r
+//\r
+AUTH_ALGORITHM mIpsecAuthAlgorithmList[IPSEC_AUTH_ALGORITHM_LIST_SIZE] = {\r
+ {EFI_IPSEC_AALG_NONE, 0, 0, 0, NULL, NULL, NULL, NULL},\r
+ {EFI_IPSEC_AALG_NULL, 0, 0, 0, NULL, NULL, NULL, NULL},\r
+ {(UINT8)-1, 0, 0, 0, NULL, NULL, NULL, NULL}\r
+};\r
+\r
+\r
+/**\r
+ Get the block size of encrypt alogrithm. The block size is based on the algorithm used.\r
+\r
+ @param[in] AlgorithmId The encrypt algorithm ID.\r
+\r
+ @return The value of block size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptBlockSize (\r
+ IN UINT8 AlgorithmId\r
+ )\r
+{\r
+ UINT8 Index;\r
+\r
+ for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {\r
+ if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {\r
+ //\r
+ // The BlockSize is same with IvSize.\r
+ //\r
+ return mIpsecEncryptAlgorithmList[Index].BlockSize;\r
+ }\r
+ }\r
+\r
+ return (UINTN) -1;\r
+}\r
+\r
+/**\r
+ Get the IV size of encrypt alogrithm. The IV size is based on the algorithm used.\r
+\r
+ @param[in] AlgorithmId The encrypt algorithm ID.\r
+\r
+ @return The value of IV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptIvLength (\r
+ IN UINT8 AlgorithmId\r
+ )\r
+{\r
+ UINT8 Index;\r
+\r
+ for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {\r
+ if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {\r
+ //\r
+ // The BlockSize is same with IvSize.\r
+ //\r
+ return mIpsecEncryptAlgorithmList[Index].IvLength;\r
+ }\r
+ }\r
+\r
+ return (UINTN) -1;\r
+}\r
+\r
+/**\r
+ Get the ICV size of Authenticaion alogrithm. The ICV size is based on the algorithm used.\r
+\r
+ @param[in] AuthAlgorithmId The Authentication algorithm ID.\r
+\r
+ @return The value of ICV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetIcvLength (\r
+ IN UINT8 AuthAlgorithmId\r
+ )\r
+{\r
+ UINT8 Index;\r
+ for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) {\r
+ if (AuthAlgorithmId == mIpsecAuthAlgorithmList[Index].AlgorithmId) {\r
+ return mIpsecAuthAlgorithmList[Index].IcvLength;\r
+ }\r
+ }\r
+ return (UINTN) -1;\r
+}\r
+\r
+/**\r
+ Generate a random data for IV. If the IvSize is zero, not needed to create\r
+ IV and return EFI_SUCCESS.\r
+\r
+ @param[in] IvBuffer The pointer of the IV buffer.\r
+ @param[in] IvSize The IV size.\r
+\r
+ @retval EFI_SUCCESS Create a random data for IV.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecGenerateIv (\r
+ IN UINT8 *IvBuffer,\r
+ IN UINTN IvSize\r
+ )\r
+{\r
+ if (IvSize != 0) {\r
+ //\r
+ //TODO: return CryptGenerateRandom (IvBuffer, IvSize);\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ Definition related to the Security operation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _EFI_IPSEC_CRYPTIO_H_\r
+#define _EFI_IPSEC_CRYPTIO_H_\r
+\r
+#include <Protocol/IpSecConfig.h>\r
+#include <Library/DebugLib.h>\r
+\r
+#define IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE 2\r
+#define IPSEC_AUTH_ALGORITHM_LIST_SIZE 3\r
+\r
+/**\r
+ Prototype of Hash GetContextSize.\r
+\r
+ Retrieves the size, in bytes, of the context buffer required.\r
+\r
+ @return The size, in bytes, of the context buffer required.\r
+\r
+**/\r
+typedef\r
+UINTN\r
+(EFIAPI *CPL_HASH_GETCONTEXTSIZE) (\r
+ VOID\r
+ );\r
+\r
+/**\r
+ Prototype of Hash Operation Initiating.\r
+\r
+ Initialization with a new context.\r
+\r
+\r
+ @param[in,out] Context Input Context.\r
+\r
+ @retval TRUE Initialization Successfully.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(EFIAPI *CPL_HASH_INIT) (\r
+ IN OUT VOID *Context\r
+ );\r
+\r
+/**\r
+ Prototype of HASH update.\r
+ Hash update operation. Continue an Hash message digest operation, processing\r
+ another message block, and updating the Hash context.\r
+\r
+ If Context is NULL, then ASSERT().\r
+ If Data is NULL, then ASSERT().\r
+\r
+ @param[in,out] Context The Specified Context.\r
+ @param[in,out] Data The Input Data to hash.\r
+ @param[in] DataLength The length, in bytes, of Data.\r
+\r
+ @retval TRUE Update data successfully.\r
+ @retval FALSE The Context has been finalized.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_HASH_UPDATE) (\r
+ IN OUT VOID *Context,\r
+ IN CONST VOID *Data,\r
+ IN UINTN DataLength\r
+ );\r
+\r
+/**\r
+ Prototype of Hash finallization.\r
+ Terminate a Hash message digest operation and output the message digest.\r
+\r
+ If Context is NULL, then ASSERT().\r
+ If HashValue is NULL, then ASSERT().\r
+\r
+ @param[in,out] Context The specified Context.\r
+ @param[out] HashValue Pointer to a 16-byte message digest output buffer.\r
+\r
+ @retval TRUE Finalized successfully.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_HASH_FINAL) (\r
+ IN OUT VOID *Context,\r
+ OUT UINT8 *HashValue\r
+ );\r
+\r
+/**\r
+ Prototype of Cipher GetContextSize.\r
+\r
+ Retrieves the size, in bytes, of the context buffer required.\r
+\r
+ @return The size, in bytes, of the context buffer required.\r
+\r
+**/\r
+typedef\r
+UINTN\r
+(EFIAPI *CPL_CIPHER_GETCONTEXTSIZE) (\r
+ VOID\r
+ );\r
+\r
+/**\r
+ Prototype of Cipher initiation.\r
+ Intializes the user-supplied key as the specifed context (key materials) for both\r
+ encryption and decryption operations.\r
+\r
+ If Context is NULL, then ASSERT().\r
+ If Key is NULL, then generate random key for usage.\r
+\r
+ @param[in,out] Context The specified Context.\r
+ @param[in] Key User-supplied TDES key (64/128/192 bits).\r
+ @param[in] KeyBits Key length in bits.\r
+\r
+ @retval TRUE TDES Initialization was successful.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_CIPHER_INIT) (\r
+ IN OUT VOID *Context,\r
+ IN CONST UINT8 *Key,\r
+ IN CONST UINTN KeyBits\r
+ );\r
+\r
+\r
+/**\r
+ Prototype of Cipher encryption.\r
+ Encrypts plaintext message with the specified cipher.\r
+\r
+ If Context is NULL, then ASSERT().\r
+ if InData is NULL, then ASSERT().\r
+ If Size of input data is not multiple of Cipher algorithm related block size,\r
+ then ASSERT().\r
+\r
+ @param[in] Context The specified Context.\r
+ @param[in] InData The input plaintext data to be encrypted.\r
+ @param[out] OutData The resultant encrypted ciphertext.\r
+ @param[in] DataLength Length of input data in bytes.\r
+\r
+ @retval TRUE Encryption successful.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_CIPHER_ENCRYPT) (\r
+ IN VOID *Context,\r
+ IN CONST UINT8 *InData,\r
+ OUT UINT8 *OutData,\r
+ IN CONST UINTN DataLength\r
+ );\r
+\r
+\r
+/**\r
+ Prototype of Cipher decryption.\r
+ Decrypts cipher message with specified cipher.\r
+\r
+ If Context is NULL, then ASSERT().\r
+ if InData is NULL, then ASSERT().\r
+ If Size of input data is not a multiple of a certaion block size , then ASSERT().\r
+\r
+ @param[in] Context The specified Context.\r
+ @param[in] InData The input ciphertext data to be decrypted.\r
+ @param[out] OutData The resultant decrypted plaintext.\r
+ @param[in] DataLength Length of input data in bytes.\r
+\r
+ @retval TRUE Decryption successful.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *CPL_CIPHER_DECRYPT) (\r
+ IN CONST VOID *Context,\r
+ IN CONST UINT8 *InData,\r
+ OUT UINT8 *OutData,\r
+ IN CONST UINTN DataLength\r
+ );\r
+\r
+//\r
+// The struct used to store the informatino and operation of Cipher algorithm.\r
+//\r
+typedef struct _ENCRYPT_ALGORITHM {\r
+//\r
+// The ID of the Algorithm\r
+//\r
+UINT8 AlgorithmId;\r
+//\r
+// The Key length of the Algorithm\r
+//\r
+UINTN KeyLength;\r
+//\r
+// Iv Size of the Algorithm\r
+//\r
+UINTN IvLength;\r
+//\r
+// The Block Size of the Algorithm\r
+//\r
+UINTN BlockSize;\r
+//\r
+// The Function pointer of GetContextSize.\r
+//\r
+CPL_CIPHER_GETCONTEXTSIZE CipherGetContextSize;\r
+//\r
+// The Function pointer of Cipher intitiaion.\r
+//\r
+CPL_CIPHER_INIT CipherInitiate;\r
+//\r
+// The Function pointer of Cipher Encryption.\r
+//\r
+CPL_CIPHER_ENCRYPT CipherEncrypt;\r
+//\r
+// The Function pointer of Cipher Decrption.\r
+//\r
+CPL_CIPHER_DECRYPT CipherDecrypt;\r
+} ENCRYPT_ALGORITHM;\r
+\r
+//\r
+// The struct used to store the informatino and operation of Autahentication algorithm.\r
+//\r
+typedef struct _AUTH_ALGORITHM {\r
+ //\r
+ // ID of the Algorithm\r
+ //\r
+ UINT8 AlgorithmId;\r
+ //\r
+ // The Key length of the Algorithm\r
+ //\r
+ UINTN KeyLength;\r
+ //\r
+ // The ICV length of the Algorithm\r
+ //\r
+ UINTN IcvLength;\r
+ //\r
+ // The block size of the Algorithm\r
+ //\r
+ UINTN BlockSize;\r
+ //\r
+ // The function pointer of GetContextSize.\r
+ //\r
+ CPL_HASH_GETCONTEXTSIZE HashGetContextSize;\r
+ //\r
+ // The function pointer of Initiatoion\r
+ //\r
+ CPL_HASH_INIT HashInitiate;\r
+ //\r
+ // The function pointer of Hash Update.\r
+ //\r
+ CPL_HASH_UPDATE HashUpdate;\r
+ //\r
+ // The fucntion pointer of Hash Final\r
+ //\r
+ CPL_HASH_FINAL HashFinal;\r
+} AUTH_ALGORITHM;\r
+\r
+/**\r
+ Get the IV size of encrypt alogrithm. IV size is different from different algorithm.\r
+\r
+ @param[in] AlgorithmId The encrypt algorithm ID.\r
+\r
+ @return The value of IV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptIvLength (\r
+ IN UINT8 AlgorithmId\r
+ );\r
+\r
+/**\r
+ Get the block size of encrypt alogrithm. Block size is different from different algorithm.\r
+\r
+ @param[in] AlgorithmId The encrypt algorithm ID.\r
+\r
+ @return The value of block size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetEncryptBlockSize (\r
+ IN UINT8 AlgorithmId\r
+ );\r
+\r
+/**\r
+ Get the ICV size of Authenticaion alogrithm. ICV size is different from different algorithm.\r
+\r
+ @param[in] AuthAlgorithmId The Authentication algorithm ID.\r
+\r
+ @return The value of ICV size.\r
+\r
+**/\r
+UINTN\r
+IpSecGetIcvLength (\r
+ IN UINT8 AuthAlgorithmId\r
+ );\r
+\r
+/**\r
+ Generate a random data for IV. If the IvSize is zero, not needed to create\r
+ IV and return EFI_SUCCESS.\r
+\r
+ @param[in] IvBuffer The pointer of the IV buffer.\r
+ @param[in] IvSize The IV size.\r
+\r
+ @retval EFI_SUCCESS Create random data for IV.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecGenerateIv (\r
+ IN UINT8 *IvBuffer,\r
+ IN UINTN IvSize\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ Interface of IPsec printing debug information.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecImpl.h"\r
+#include "IpSecDebug.h"\r
+\r
+//\r
+// The print title for IKEv1 variety phase.\r
+//\r
+CHAR8 *mStateStr[] = {\r
+ "IKEv1_MAIN_1",\r
+ "IKEv1_MAIN_2",\r
+ "IKEv1_MAIN_3",\r
+ "IKEv1_MAIN_ESTABLISHED",\r
+ "IKEv1_QUICK_1",\r
+ "IKEv1_QUICK_2",\r
+ "IKEv1_QUICK_ESTABLISHED"\r
+};\r
+//\r
+// The print title for IKEv1 variety Exchagne.\r
+//\r
+CHAR8 *mExchangeStr[] = {\r
+ "IKEv1 Main Exchange",\r
+ "IKEv1 Info Exchange",\r
+ "IKEv1 Quick Exchange",\r
+ "IKEv1 Unknown Exchange"\r
+};\r
+\r
+//\r
+// The print title for IKEv1 variety Payload.\r
+//\r
+CHAR8 *mPayloadStr[] = {\r
+ "IKEv1 None Payload",\r
+ "IKEv1 SA Payload",\r
+ "IKEv1 Proposal Payload",\r
+ "IKEv1 Transform Payload",\r
+ "IKEv1 KE Payload",\r
+ "IKEv1 ID Payload",\r
+ "IKEv1 Certificate Payload",\r
+ "IKEv1 Certificate Request Payload",\r
+ "IKEv1 Hash Payload",\r
+ "IKEv1 Signature Payload",\r
+ "IKEv1 Nonce Payload",\r
+ "IKEv1 Notify Payload",\r
+ "IKEv1 Delete Payload",\r
+ "IKEv1 Vendor Payload"\r
+};\r
+\r
+/**\r
+ Print the IP address.\r
+\r
+ @param[in] Level Debug print error level. Pass to DEBUG().\r
+ @param[in] Ip Point to a specified IP address.\r
+ @param[in] IpVersion The IP Version.\r
+\r
+**/\r
+VOID\r
+IpSecDumpAddress (\r
+ IN UINTN Level,\r
+ IN EFI_IP_ADDRESS *Ip,\r
+ IN UINT8 IpVersion\r
+ )\r
+{\r
+ if (IpVersion == IP_VERSION_6) {\r
+ DEBUG (\r
+ (Level,\r
+ "%x%x:%x%x:%x%x:%x%x",\r
+ Ip->v6.Addr[0],\r
+ Ip->v6.Addr[1],\r
+ Ip->v6.Addr[2],\r
+ Ip->v6.Addr[3],\r
+ Ip->v6.Addr[4],\r
+ Ip->v6.Addr[5],\r
+ Ip->v6.Addr[6],\r
+ Ip->v6.Addr[7])\r
+ );\r
+ DEBUG (\r
+ (Level,\r
+ ":%x%x:%x%x:%x%x:%x%x\n",\r
+ Ip->v6.Addr[8],\r
+ Ip->v6.Addr[9],\r
+ Ip->v6.Addr[10],\r
+ Ip->v6.Addr[11],\r
+ Ip->v6.Addr[12],\r
+ Ip->v6.Addr[13],\r
+ Ip->v6.Addr[14],\r
+ Ip->v6.Addr[15])\r
+ );\r
+ } else {\r
+ DEBUG (\r
+ (Level,\r
+ "%d.%d.%d.%d\n",\r
+ Ip->v4.Addr[0],\r
+ Ip->v4.Addr[1],\r
+ Ip->v4.Addr[2],\r
+ Ip->v4.Addr[3])\r
+ );\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Print IKEv1 Current states.\r
+\r
+ @param[in] Previous The Previous state of IKEv1.\r
+ @param[in] Current The current state of IKEv1.\r
+\r
+**/\r
+VOID\r
+IpSecDumpState (\r
+ IN UINT32 Previous,\r
+ IN UINT32 Current\r
+ )\r
+{\r
+ if (Previous == Current) {\r
+ DEBUG ((DEBUG_INFO, "\n****Current state is %a\n", mStateStr[Previous]));\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, "\n****Change state from %a to %a\n", mStateStr[Previous], mStateStr[Current]));\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Print the buffer in form of Hex.\r
+\r
+ @param[in] Title The strings to be printed before the data of the buffer.\r
+ @param[in] Data Points to buffer to be printed.\r
+ @param[in] DataSize The size of the buffer to be printed.\r
+\r
+**/\r
+VOID\r
+IpSecDumpBuf (\r
+ IN CHAR8 *Title,\r
+ IN UINT8 *Data,\r
+ IN UINTN DataSize\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN DataIndex;\r
+ UINTN BytesRemaining;\r
+ UINTN BytesToPrint;\r
+\r
+ DataIndex = 0;\r
+ BytesRemaining = DataSize;\r
+\r
+ DEBUG ((DEBUG_INFO, "==%a %d bytes==\n", Title, DataSize));\r
+\r
+ while (BytesRemaining > 0) {\r
+\r
+ BytesToPrint = (BytesRemaining > IPSEC_DEBUG_BYTE_PER_LINE) ? IPSEC_DEBUG_BYTE_PER_LINE : BytesRemaining;\r
+\r
+ for (Index = 0; Index < BytesToPrint; Index++) {\r
+ DEBUG ((DEBUG_INFO, " 0x%02x,", Data[DataIndex++]));\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "\n"));\r
+ BytesRemaining -= BytesToPrint;\r
+ }\r
+\r
+}\r
--- /dev/null
+/** @file\r
+ The definition of functions and MACROs used for IPsec debug information print.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _EFI_IPSEC_DEBUG_H_\r
+#define _EFI_IPSEC_DEBUG_H_\r
+\r
+#include <Library/DebugLib.h>\r
+\r
+#define IPSEC_DUMP_ADDRESS(Level, Ip, Version) IpSecDumpAddress (Level, Ip, Version)\r
+#define IPSEC_DUMP_STATE(Previous, Current) IpSecDumpState (Previous, Current)\r
+#define IPSEC_DUMP_PACKET(Packet, Direction, IpVersion) IpSecDumpPacket (Packet, Direction, IpVersion)\r
+#define IPSEC_DUMP_PAYLOAD(IkePayload) IpSecDumpPayload (IkePayload)\r
+#define IPSEC_DUMP_BUF(Title, Data, DataSize) IpSecDumpBuf (Title, Data, DataSize)\r
+\r
+#define IPSEC_DEBUG_BYTE_PER_LINE 8\r
+\r
+\r
+/**\r
+ Print the IP address.\r
+\r
+ @param[in] Level Debug print error level. Pass to DEBUG().\r
+ @param[in] Ip Point to specified IP address.\r
+ @param[in] IpVersion The IP Version.\r
+\r
+**/\r
+VOID\r
+IpSecDumpAddress (\r
+ IN UINTN Level,\r
+ IN EFI_IP_ADDRESS *Ip,\r
+ IN UINT8 IpVersion\r
+ );\r
+\r
+/**\r
+ Print IKEv1 Current states.\r
+\r
+ @param[in] Previous The Previous state of IKEv1.\r
+ @param[in] Current The current state of IKEv1.\r
+\r
+**/\r
+VOID\r
+IpSecDumpState (\r
+ IN UINT32 Previous,\r
+ IN UINT32 Current\r
+ );\r
+\r
+/**\r
+ Print the Ike Packet.\r
+\r
+ @param[in] Packet Point to IKE packet to be printed.\r
+ @param[in] Direction Point to the IKE packet is inbound or outbound.\r
+ @param[in] IpVersion Specified IP Version.\r
+\r
+**/\r
+/*\r
+VOID\r
+IpSecDumpPacket (\r
+ IN IKE_PACKET *Packet,\r
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,\r
+ IN UINT8 IpVersion\r
+ );\r
+*/\r
+\r
+/**\r
+ Print the IKE Paylolad.\r
+\r
+ @param[in] IkePayload Points to the payload to be printed.\r
+\r
+**/\r
+/*\r
+VOID\r
+IpSecDumpPayload (\r
+ IN IKE_PAYLOAD *IkePayload\r
+ );\r
+*/\r
+/**\r
+ Print the buffer in form of Hex.\r
+\r
+ @param[in] Title The strings to be printed before the data of the buffer.\r
+ @param[in] Data Points to the buffer to be printed.\r
+ @param[in] DataSize The size of the buffer to be printed.\r
+\r
+**/\r
+VOID\r
+IpSecDumpBuf (\r
+ IN CHAR8 *Title,\r
+ IN UINT8 *Data,\r
+ IN UINTN DataSize\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Driver Binding Protocol for IPsec Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/UdpIoLib.h>\r
+#include "IpSecConfigImpl.h"\r
+#include "IpSecDebug.h"\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to test.\r
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCES This driver supports this device.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ //\r
+ //TODO: Add Udp4Protocol and Udp6Protocol testing.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCES This driver is added to ControllerHandle\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle\r
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.\r
+ Currently not implemented.\r
+ @retval other This driver does not support this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ //\r
+ //TODO: Add Udp4Io and Udp6Io creation for the IKE.\r
+ //\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Stop this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of a device to stop the driver on.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of\r
+ children is zero, stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCES This driver removed ControllerHandle.\r
+ @retval other This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ )\r
+{\r
+ //\r
+ //TODO: Add UdpIo4 and UdpIo6 destruction when the Udp driver unload or stop.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gIpSecDriverBinding = {\r
+ IpSecDriverBindingSupported,\r
+ IpSecDriverBindingStart,\r
+ IpSecDriverBindingStop,\r
+ 0xa,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+/**\r
+ This is a callback function when the mIpSecInstance.DisabledEvent is signaled.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IpSecCleanupAllSa (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IPSEC_PRIVATE_DATA *Private;\r
+ UINT8 Value;\r
+ EFI_STATUS Status;\r
+\r
+ Private = (IPSEC_PRIVATE_DATA *) Context;\r
+\r
+ //\r
+ // Set the Status Variable\r
+ //\r
+ Value = IPSEC_STATUS_DISABLED;\r
+ Status = gRT->SetVariable (\r
+ IPSECCONFIG_STATUS_NAME,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+ sizeof (Value),\r
+ &Value\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Private->IpSec.DisabledFlag = TRUE;\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+ both device drivers and bus drivers.\r
+\r
+ The entry point for IPsec driver which installs the driver binding,\r
+ component name protocol, IPsec Config protcolon, and IPsec protocol in\r
+ its ImageHandle.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_ALREADY_STARTED The IPsec driver has been already loaded.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+ @retval Others The operation is failed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecDriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IPSEC_PRIVATE_DATA *Private;\r
+ EFI_IPSEC_PROTOCOL *IpSec;\r
+\r
+ //\r
+ // Check whether ipsec protocol has already been installed.\r
+ //\r
+ Status = gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &IpSec);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_WARN, "_ModuleEntryPoint: IpSec has been already loaded\n"));\r
+ Status = EFI_ALREADY_STARTED;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, (VOID **) &mDpc);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to locate EfiDpcProtocol\n"));\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Private = AllocateZeroPool (sizeof (IPSEC_PRIVATE_DATA));\r
+\r
+ if (Private == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to allocate private data\n"));\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Create disable event to cleanup all sa when ipsec disabled by user.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ IpSecCleanupAllSa,\r
+ Private,\r
+ &mIpSecInstance.DisabledEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to create disable event\n"));\r
+ goto ON_FREE_PRIVATE;\r
+ }\r
+\r
+ Private->Signature = IPSEC_PRIVATE_DATA_SIGNATURE;\r
+ Private->ImageHandle = ImageHandle;\r
+ CopyMem (&Private->IpSec, &mIpSecInstance, sizeof (EFI_IPSEC_PROTOCOL));\r
+\r
+ //\r
+ // Initilize Private's members. Thess members is used for IKE.\r
+ //\r
+ InitializeListHead (&Private->Udp4List);\r
+ InitializeListHead (&Private->Udp6List);\r
+ InitializeListHead (&Private->Ikev1SessionList);\r
+ InitializeListHead (&Private->Ikev1EstablishedList);\r
+ InitializeListHead (&Private->Ikev2SessionList);\r
+ InitializeListHead (&Private->Ikev2EstablishedList);\r
+\r
+ //\r
+ // Initialize the ipsec config data and restore it from variable.\r
+ //\r
+ Status = IpSecConfigInitialize (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to initialize IpSecConfig\n"));\r
+ goto ON_CLOSE_EVENT;\r
+ }\r
+ //\r
+ // Install ipsec protocol which is used by ip driver to process ipsec header.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Private->Handle,\r
+ &gEfiIpSecProtocolGuid,\r
+ &Private->IpSec,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_UNINSTALL_CONFIG;\r
+ }\r
+\r
+ Status = EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gIpSecDriverBinding,\r
+ ImageHandle,\r
+ &gIpSecComponentName,\r
+ &gIpSecComponentName2\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_UNINSTALL_CONFIG;\r
+ }\r
+\r
+ return Status;\r
+\r
+ON_UNINSTALL_CONFIG:\r
+ gBS->UninstallProtocolInterface (\r
+ Private->Handle,\r
+ &gEfiIpSecConfigProtocolGuid,\r
+ &Private->IpSecConfig\r
+ );\r
+ON_CLOSE_EVENT:\r
+ gBS->CloseEvent (mIpSecInstance.DisabledEvent);\r
+ mIpSecInstance.DisabledEvent = NULL;\r
+ON_FREE_PRIVATE:\r
+ FreePool (Private);\r
+ON_EXIT:\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+## @file\r
+# Component description file for IpSec module.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = IpSecDxe\r
+ FILE_GUID = EE8367C0-A1D6-4565-8F89-EF628547B722\r
+ MODULE_TYPE = UEFI_DRIVER\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = IpSecDriverEntryPoint\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+ IpSecConfigImpl.c\r
+ IpSecConfigImpl.h\r
+ IpSecCryptIo.h\r
+ IpSecCryptIo.c\r
+ IpSecDebug.h\r
+ ComponentName.c\r
+ IpSecImpl.c\r
+ IpSecDebug.c\r
+ IpSecSaEngine.c\r
+ IpSecDriver.c\r
+ IpSecImpl.h\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+ MemoryAllocationLib\r
+ BaseLib\r
+ UefiLib\r
+ UefiBootServicesTableLib\r
+ UefiRuntimeServicesTableLib\r
+ UefiDriverEntryPoint\r
+ BaseMemoryLib\r
+ DebugLib\r
+ PrintLib\r
+ DpcLib\r
+ NetLib\r
+\r
+[Protocols]\r
+ gEfiIp4ConfigProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiIpSecConfigProtocolGuid # PROTOCOL ALWAYS_PRODUCED\r
+ gEfiIpSecProtocolGuid # PROTOCOL ALWAYS_PRODUCED\r
--- /dev/null
+/** @file\r
+ The implementation of IPsec Protocol\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecConfigImpl.h"\r
+\r
+EFI_IPSEC_PROTOCOL mIpSecInstance = { IpSecProcess, NULL, TRUE };\r
+\r
+extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];\r
+\r
+/**\r
+ Check if the specified Address is the Valid Address Range.\r
+\r
+ This function checks if the bytes after prefixed length are all Zero in this\r
+ Address. This Address is supposed to point to a range address, meaning it only\r
+ gives the correct prefixed address.\r
+\r
+ @param[in] IpVersion The IP version.\r
+ @param[in] Address Points to EFI_IP_ADDRESS to be checked.\r
+ @param[in] PrefixLength The PrefixeLength of this address.\r
+\r
+ @retval TRUE The address is a vaild address range.\r
+ @retval FALSE The address is not a vaild address range.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecValidAddressRange (\r
+ IN UINT8 IpVersion,\r
+ IN EFI_IP_ADDRESS *Address,\r
+ IN UINT8 PrefixLength\r
+ )\r
+{\r
+ UINT8 Div;\r
+ UINT8 Mod;\r
+ UINT8 Mask;\r
+ UINT8 AddrLen;\r
+ UINT8 *Addr;\r
+ EFI_IP_ADDRESS ZeroAddr;\r
+\r
+ if (PrefixLength == 0) {\r
+ return TRUE;\r
+ }\r
+\r
+ AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);\r
+\r
+ if (AddrLen <= PrefixLength) {\r
+ return FALSE;\r
+ }\r
+\r
+ Div = (UINT8) (PrefixLength / 8);\r
+ Mod = (UINT8) (PrefixLength % 8);\r
+ Addr = (UINT8 *) Address;\r
+ ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));\r
+\r
+ //\r
+ // Check whether the mod part of host scope is zero or not.\r
+ //\r
+ if (Mod > 0) {\r
+ Mask = (UINT8) (0xFF << (8 - Mod));\r
+\r
+ if ((Addr[Div] | Mask) != Mask) {\r
+ return FALSE;\r
+ }\r
+\r
+ Div++;\r
+ }\r
+ //\r
+ // Check whether the div part of host scope is zero or not.\r
+ //\r
+ if (CompareMem (\r
+ &Addr[Div],\r
+ &ZeroAddr,\r
+ sizeof (EFI_IP_ADDRESS) - Div\r
+ ) != 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Extrct the Address Range from a Address.\r
+\r
+ This function keep the prefix address and zero other part address.\r
+\r
+ @param[in] Address Point to a specified address.\r
+ @param[in] PrefixLength The prefix length.\r
+ @param[out] Range Contain the return Address Range.\r
+\r
+**/\r
+VOID\r
+IpSecExtractAddressRange (\r
+ IN EFI_IP_ADDRESS *Address,\r
+ IN UINT8 PrefixLength,\r
+ OUT EFI_IP_ADDRESS *Range\r
+ )\r
+{\r
+ UINT8 Div;\r
+ UINT8 Mod;\r
+ UINT8 Mask;\r
+ UINT8 *Addr;\r
+\r
+ if (PrefixLength == 0) {\r
+ return ;\r
+ }\r
+\r
+ Div = (UINT8) (PrefixLength / 8);\r
+ Mod = (UINT8) (PrefixLength % 8);\r
+ Addr = (UINT8 *) Range;\r
+\r
+ CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));\r
+\r
+ //\r
+ // Zero the mod part of host scope.\r
+ //\r
+ if (Mod > 0) {\r
+ Mask = (UINT8) (0xFF << (8 - Mod));\r
+ Addr[Div] = (UINT8) (Addr[Div] & Mask);\r
+ Div++;\r
+ }\r
+ //\r
+ // Zero the div part of host scope.\r
+ //\r
+ ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);\r
+\r
+}\r
+\r
+/**\r
+ Checks if the IP Address in the address range of AddressInfos specified.\r
+\r
+ @param[in] IpVersion The IP version.\r
+ @param[in] IpAddr Point to EFI_IP_ADDRESS to be check.\r
+ @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check\r
+ the IP Address is matched.\r
+ @param[in] AddressCount The total numbers of the AddressInfo.\r
+\r
+ @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.\r
+ @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecMatchIpAddress (\r
+ IN UINT8 IpVersion,\r
+ IN EFI_IP_ADDRESS *IpAddr,\r
+ IN EFI_IP_ADDRESS_INFO *AddressInfo,\r
+ IN UINT32 AddressCount\r
+ )\r
+{\r
+ EFI_IP_ADDRESS Range;\r
+ UINT32 Index;\r
+ BOOLEAN IsMatch;\r
+\r
+ IsMatch = FALSE;\r
+\r
+ for (Index = 0; Index < AddressCount; Index++) {\r
+ //\r
+ // Check whether the target address is in the address range\r
+ // if it's a valid range of address.\r
+ //\r
+ if (IpSecValidAddressRange (\r
+ IpVersion,\r
+ &AddressInfo[Index].Address,\r
+ AddressInfo[Index].PrefixLength\r
+ )) {\r
+ //\r
+ // Get the range of the target address belongs to.\r
+ //\r
+ ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));\r
+ IpSecExtractAddressRange (\r
+ IpAddr,\r
+ AddressInfo[Index].PrefixLength,\r
+ &Range\r
+ );\r
+\r
+ if (CompareMem (\r
+ &Range,\r
+ &AddressInfo[Index].Address,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ ) == 0) {\r
+ //\r
+ // The target address is in the address range.\r
+ //\r
+ IsMatch = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (CompareMem (\r
+ IpAddr,\r
+ &AddressInfo[Index].Address,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ ) == 0) {\r
+ //\r
+ // The target address is exact same as the address.\r
+ //\r
+ IsMatch = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ return IsMatch;\r
+}\r
+\r
+/**\r
+ Check if the specified Protocol and Prot is supported by the specified SPD Entry.\r
+\r
+ This function is the subfunction of IPsecLookUpSpdEntry() that is used to\r
+ check if the sent/received IKE packet has the related SPD entry support.\r
+\r
+ @param[in] Protocol The Protocol to be checked.\r
+ @param[in] IpPayload Point to IP Payload to be check.\r
+ @param[in] SpdProtocol The Protocol supported by SPD.\r
+ @param[in] SpdLocalPort The Local Port in SPD.\r
+ @param[in] SpdRemotePort The Remote Port in SPD.\r
+ @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving.\r
+\r
+ @retval TRUE The Protocol and Port are supported by the SPD Entry.\r
+ @retval FALSE The Protocol and Port are not supported by the SPD Entry.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecMatchNextLayerProtocol (\r
+ IN UINT8 Protocol,\r
+ IN UINT8 *IpPayload,\r
+ IN UINT16 SpdProtocol,\r
+ IN UINT16 SpdLocalPort,\r
+ IN UINT16 SpdRemotePort,\r
+ IN BOOLEAN IsOutbound\r
+ )\r
+{\r
+ BOOLEAN IsMatch;\r
+\r
+ if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {\r
+ return TRUE;\r
+ }\r
+\r
+ IsMatch = FALSE;\r
+\r
+ if (SpdProtocol == Protocol) {\r
+ switch (Protocol) {\r
+ case EFI_IP_PROTO_UDP:\r
+ case EFI_IP_PROTO_TCP:\r
+ //\r
+ // For udp and tcp, (0, 0) means no need to check local and remote\r
+ // port. The payload is passed from upper level, which means it should\r
+ // be in network order.\r
+ //\r
+ IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
+ IsMatch = (BOOLEAN) (IsMatch ||\r
+ (IsOutbound &&\r
+ (BOOLEAN)(\r
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&\r
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort\r
+ )\r
+ ));\r
+\r
+ IsMatch = (BOOLEAN) (IsMatch ||\r
+ (!IsOutbound &&\r
+ (BOOLEAN)(\r
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&\r
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort\r
+ )\r
+ ));\r
+ break;\r
+\r
+ case EFI_IP_PROTO_ICMP:\r
+ //\r
+ // For icmpv4, type code is replaced with local port and remote port,\r
+ // and (0, 0) means no need to check.\r
+ //\r
+ IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
+ IsMatch = (BOOLEAN) (IsMatch ||\r
+ (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
+ ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
+ )\r
+ );\r
+ break;\r
+\r
+ case IP6_ICMP:\r
+ //\r
+ // For icmpv6, type code is replaced with local port and remote port,\r
+ // and (0, 0) means no need to check.\r
+ //\r
+ IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
+\r
+ IsMatch = (BOOLEAN) (IsMatch ||\r
+ (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
+ ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
+ )\r
+ );\r
+ break;\r
+\r
+ default:\r
+ IsMatch = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ return IsMatch;\r
+}\r
+\r
+/**\r
+ Find the SAD through a specified SPD's SAD list.\r
+\r
+ @param[in] SadList SAD list related to a specified SPD entry.\r
+ @param[in] DestAddress The destination address used to find the SAD entry.\r
+\r
+ @return The pointer to a certain SAD entry.\r
+\r
+**/\r
+IPSEC_SAD_ENTRY *\r
+IpSecLookupSadBySpd (\r
+ IN LIST_ENTRY *SadList,\r
+ IN EFI_IP_ADDRESS *DestAddress\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+\r
+ for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
+\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);\r
+ //\r
+ // Find the right sad entry which contains the appointed dest address.\r
+ //\r
+ if (CompareMem (\r
+ &SadEntry->Id->DestAddress,\r
+ DestAddress,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ ) == 0) {\r
+ return SadEntry;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Find the SAD through whole SAD list.\r
+\r
+ @param[in] Spi The SPI used to search the SAD entry.\r
+ @param[in] DestAddress The destination used to search the SAD entry.\r
+\r
+ @return the pointer to a certain SAD entry.\r
+\r
+**/\r
+IPSEC_SAD_ENTRY *\r
+IpSecLookupSadBySpi (\r
+ IN UINT32 Spi,\r
+ IN EFI_IP_ADDRESS *DestAddress\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *SadList;\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+\r
+ SadList = &mConfigData[IPsecConfigDataTypeSad];\r
+\r
+ for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {\r
+\r
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
+ //\r
+ // Find the right sad entry which contain the appointed spi and dest addr.\r
+ //\r
+ if (SadEntry->Id->Spi == Spi && CompareMem (\r
+ &SadEntry->Id->DestAddress,\r
+ DestAddress,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ ) == 0) {\r
+\r
+ return SadEntry;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Look up if there is existing SAD entry for specified IP packet sending.\r
+\r
+ This function is called by the IPsecProcess when there is some IP packet needed to\r
+ send out. This function checks if there is an existing SAD entry that can be serviced\r
+ to this IP packet sending. If no existing SAD entry could be used, this\r
+ function will invoke an IPsec Key Exchange Negotiation.\r
+\r
+ @param[in] Private Points to private data.\r
+ @param[in] NicHandle Points to a NIC handle.\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in] IpHead The IP Header of packet to be sent out.\r
+ @param[in] IpPayload The IP Payload to be sent out.\r
+ @param[in] OldLastHead The Last protocol of the IP packet.\r
+ @param[in] SpdEntry Points to a related SPD entry.\r
+ @param[out] SadEntry Contains the Point of a related SAD entry.\r
+\r
+ @retval EFI_DEVICE_ERROR One of following conditions is TRUE:\r
+ - If don't find related UDP service.\r
+ - Sequence Number is used up.\r
+ - Extension Sequence Number is used up.\r
+ @retval EFI_DEVICE_ERROR GC_TODO: Add description for return value.\r
+ @retval EFI_NOT_READY No existing SAD entry could be used.\r
+ @retval EFI_SUCCESS Find the related SAD entry.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecLookupSadEntry (\r
+ IN IPSEC_PRIVATE_DATA *Private,\r
+ IN EFI_HANDLE NicHandle,\r
+ IN UINT8 IpVersion,\r
+ IN VOID *IpHead,\r
+ IN UINT8 *IpPayload,\r
+ IN UINT8 OldLastHead,\r
+ IN IPSEC_SPD_ENTRY *SpdEntry,\r
+ OUT IPSEC_SAD_ENTRY **SadEntry\r
+ )\r
+{\r
+ IPSEC_SAD_ENTRY *Entry;\r
+ IPSEC_SAD_DATA *Data;\r
+ EFI_IP_ADDRESS DestIp;\r
+ UINT32 SeqNum32;\r
+\r
+ *SadEntry = NULL;\r
+ //\r
+ // Parse the destination address from ip header.\r
+ //\r
+ ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));\r
+ if (IpVersion == IP_VERSION_4) {\r
+ CopyMem (\r
+ &DestIp,\r
+ &((IP4_HEAD *) IpHead)->Dst,\r
+ sizeof (IP4_ADDR)\r
+ );\r
+ } else {\r
+ CopyMem (\r
+ &DestIp,\r
+ &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ );\r
+ }\r
+ //\r
+ // Find the sad entry in the spd.sas list according to the dest address.\r
+ //\r
+ Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp);\r
+\r
+ if (Entry == NULL) {\r
+\r
+ if (OldLastHead != IP6_ICMP ||\r
+ (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)\r
+ ) {\r
+ //\r
+ // TODO: Start ike negotiation process except the request packet of ping.\r
+ //\r
+ //IkeNegotiate (UdpService, SpdEntry, &DestIp);\r
+ }\r
+\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ Data = Entry->Data;\r
+\r
+ if (!Data->ManualSet) {\r
+ if (Data->ESNEnabled) {\r
+ //\r
+ // Validate the 64bit sn number if 64bit sn enabled.\r
+ //\r
+ if (Data->SequenceNumber + 1 < Data->SequenceNumber) {\r
+ //\r
+ // TODO: Re-negotiate SA\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ } else {\r
+ //\r
+ // Validate the 32bit sn number if 64bit sn disabled.\r
+ //\r
+ SeqNum32 = (UINT32) Data->SequenceNumber;\r
+ if (SeqNum32 + 1 < SeqNum32) {\r
+ //\r
+ // TODO: Re-negotiate SA\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+ }\r
+\r
+ *SadEntry = Entry;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Find a PAD entry according to a remote IP address.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in] IpAddr Points to remote IP address.\r
+\r
+ @return the pointer of related PAD entry.\r
+\r
+**/\r
+IPSEC_PAD_ENTRY *\r
+IpSecLookupPadEntry (\r
+ IN UINT8 IpVersion,\r
+ IN EFI_IP_ADDRESS *IpAddr\r
+ )\r
+{\r
+ LIST_ENTRY *PadList;\r
+ LIST_ENTRY *Entry;\r
+ EFI_IP_ADDRESS_INFO *IpAddrInfo;\r
+ IPSEC_PAD_ENTRY *PadEntry;\r
+\r
+ PadList = &mConfigData[IPsecConfigDataTypePad];\r
+\r
+ for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {\r
+\r
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
+ IpAddrInfo = &PadEntry->Id->Id.IpAddress;\r
+ //\r
+ // Find the right pad entry which contain the appointed dest addr.\r
+ //\r
+ if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {\r
+ return PadEntry;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Check if the specified IP packet can be serviced by this SPD entry.\r
+\r
+ @param[in] SpdEntry Point to SPD entry.\r
+ @param[in] IpVersion Version of IP.\r
+ @param[in] IpHead Point to IP header.\r
+ @param[in] IpPayload Point to IP payload.\r
+ @param[in] Protocol The Last protocol of IP packet.\r
+ @param[in] IsOutbound Traffic direction.\r
+\r
+ @retval EFI_IPSEC_ACTION The support action of SPD entry.\r
+ @retval -1 If the input packet header doesn't match the SpdEntry.\r
+\r
+**/\r
+EFI_IPSEC_ACTION\r
+IpSecLookupSpdEntry (\r
+ IN IPSEC_SPD_ENTRY *SpdEntry,\r
+ IN UINT8 IpVersion,\r
+ IN VOID *IpHead,\r
+ IN UINT8 *IpPayload,\r
+ IN UINT8 Protocol,\r
+ IN BOOLEAN IsOutbound\r
+ )\r
+{\r
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;\r
+ IP4_HEAD *Ip4;\r
+ EFI_IP6_HEADER *Ip6;\r
+ EFI_IP_ADDRESS SrcAddr;\r
+ EFI_IP_ADDRESS DstAddr;\r
+ BOOLEAN SpdMatch;\r
+\r
+ ASSERT (SpdEntry != NULL);\r
+ SpdSel = SpdEntry->Selector;\r
+ Ip4 = (IP4_HEAD *) IpHead;\r
+ Ip6 = (EFI_IP6_HEADER *) IpHead;\r
+\r
+ ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));\r
+ ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));\r
+\r
+ //\r
+ // Parse the source and destination address from ip header.\r
+ //\r
+ if (IpVersion == IP_VERSION_4) {\r
+ CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));\r
+ CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));\r
+ } else {\r
+ CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ }\r
+ //\r
+ // Check the local and remote addresses for outbound traffic\r
+ //\r
+ SpdMatch = (BOOLEAN)(IsOutbound &&\r
+ IpSecMatchIpAddress (\r
+ IpVersion,\r
+ &SrcAddr,\r
+ SpdSel->LocalAddress,\r
+ SpdSel->LocalAddressCount\r
+ ) &&\r
+ IpSecMatchIpAddress (\r
+ IpVersion,\r
+ &DstAddr,\r
+ SpdSel->RemoteAddress,\r
+ SpdSel->RemoteAddressCount\r
+ )\r
+ );\r
+\r
+ //\r
+ // Check the local and remote addresses for inbound traffic\r
+ //\r
+ SpdMatch = (BOOLEAN) (SpdMatch ||\r
+ (!IsOutbound &&\r
+ IpSecMatchIpAddress (\r
+ IpVersion,\r
+ &DstAddr,\r
+ SpdSel->LocalAddress,\r
+ SpdSel->LocalAddressCount\r
+ ) &&\r
+ IpSecMatchIpAddress (\r
+ IpVersion,\r
+ &SrcAddr,\r
+ SpdSel->RemoteAddress,\r
+ SpdSel->RemoteAddressCount\r
+ )\r
+ ));\r
+\r
+ //\r
+ // Check the next layer protocol and local and remote ports.\r
+ //\r
+ SpdMatch = (BOOLEAN) (SpdMatch &&\r
+ IpSecMatchNextLayerProtocol (\r
+ Protocol,\r
+ IpPayload,\r
+ SpdSel->NextLayerProtocol,\r
+ SpdSel->LocalPort,\r
+ SpdSel->RemotePort,\r
+ IsOutbound\r
+ )\r
+ );\r
+\r
+ if (SpdMatch) {\r
+ //\r
+ // Find the right spd entry if match the 5 key elements.\r
+ //\r
+ return SpdEntry->Data->Action;\r
+ }\r
+\r
+ return (EFI_IPSEC_ACTION) - 1;\r
+}\r
+\r
+/**\r
+ Handles IPsec packet processing for inbound and outbound IP packets.\r
+\r
+ The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.\r
+ The behavior is that it can perform one of the following actions:\r
+ bypass the packet, discard the packet, or protect the packet.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance.\r
+ @param[in] NicHandle Instance of the network interface.\r
+ @param[in] IpVersion IPV4 or IPV6.\r
+ @param[in, out] IpHead Pointer to the IP Header.\r
+ @param[in] LastHead The protocol of the next layer to be processed by IPsec.\r
+ @param[in] OptionsBuffer Pointer to the options buffer.\r
+ @param[in] OptionsLength Length of the options buffer.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments.\r
+ @param[in] FragmentCount Number of fragments.\r
+ @param[in] TrafficDirection Traffic direction.\r
+ @param[out] RecycleSignal Event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.\r
+ @retval EFI_SUCCESS The packet was protected.\r
+ @retval EFI_ACCESS_DENIED The packet was discarded.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecProcess (\r
+ IN EFI_IPSEC_PROTOCOL *This,\r
+ IN EFI_HANDLE NicHandle,\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ IN UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer,\r
+ IN UINT32 OptionsLength,\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection,\r
+ OUT EFI_EVENT *RecycleSignal\r
+ )\r
+{\r
+ IPSEC_PRIVATE_DATA *Private;\r
+ IPSEC_SPD_ENTRY *SpdEntry;\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+ LIST_ENTRY *SpdList;\r
+ LIST_ENTRY *Entry;\r
+ EFI_IPSEC_ACTION Action;\r
+ EFI_STATUS Status;\r
+ UINT8 *IpPayload;\r
+ UINT8 OldLastHead;\r
+ BOOLEAN IsOutbound;\r
+\r
+ Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (This);\r
+ IpPayload = (*FragmentTable)[0].FragmentBuffer;\r
+ IsOutbound = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE);\r
+ OldLastHead = *LastHead;\r
+ *RecycleSignal = NULL;\r
+\r
+ if (!IsOutbound) {\r
+ //\r
+ // For inbound traffic, process the ipsec header of the packet.\r
+ //\r
+ Status = IpSecProtectInboundPacket (\r
+ IpVersion,\r
+ IpHead,\r
+ LastHead,\r
+ OptionsBuffer,\r
+ OptionsLength,\r
+ FragmentTable,\r
+ FragmentCount,\r
+ &SpdEntry,\r
+ RecycleSignal\r
+ );\r
+\r
+ if (Status == EFI_ACCESS_DENIED) {\r
+ //\r
+ // The packet is denied to access.\r
+ //\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (Status == EFI_SUCCESS) {\r
+ //\r
+ // Check the spd entry if the packet is accessible.\r
+ //\r
+ if (SpdEntry == NULL) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto ON_EXIT;\r
+ }\r
+ Action = IpSecLookupSpdEntry (\r
+ SpdEntry,\r
+ IpVersion,\r
+ IpHead,\r
+ IpPayload,\r
+ *LastHead,\r
+ IsOutbound\r
+ );\r
+\r
+ if (Action != EfiIPsecActionProtect) {\r
+ //\r
+ // Discard the packet if the spd entry is not protect.\r
+ //\r
+ gBS->SignalEvent (*RecycleSignal);\r
+ *RecycleSignal = NULL;\r
+ Status = EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];\r
+\r
+ for (Entry = SpdList->ForwardLink; Entry != SpdList; Entry = Entry->ForwardLink) {\r
+ //\r
+ // For outbound and non-ipsec Inbound traffic: check the spd entry.\r
+ //\r
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);\r
+ Action = IpSecLookupSpdEntry (\r
+ SpdEntry,\r
+ IpVersion,\r
+ IpHead,\r
+ IpPayload,\r
+ OldLastHead,\r
+ IsOutbound\r
+ );\r
+\r
+ switch (Action) {\r
+\r
+ case EfiIPsecActionProtect:\r
+\r
+ if (IsOutbound) {\r
+ //\r
+ // For outbound traffic, lookup the sad entry.\r
+ //\r
+ Status = IpSecLookupSadEntry (\r
+ Private,\r
+ NicHandle,\r
+ IpVersion,\r
+ IpHead,\r
+ IpPayload,\r
+ OldLastHead,\r
+ SpdEntry,\r
+ &SadEntry\r
+ );\r
+\r
+ if (SadEntry != NULL) {\r
+ //\r
+ // Process the packet by the found sad entry.\r
+ //\r
+ Status = IpSecProtectOutboundPacket (\r
+ IpVersion,\r
+ IpHead,\r
+ LastHead,\r
+ OptionsBuffer,\r
+ OptionsLength,\r
+ FragmentTable,\r
+ FragmentCount,\r
+ SadEntry,\r
+ RecycleSignal\r
+ );\r
+\r
+ } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {\r
+ //\r
+ // TODO: if no need return not ready to upper layer, change here.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {\r
+ //\r
+ // For inbound icmpv6 traffic except ping request, accept the packet\r
+ // although no sad entry associated with protect spd entry.\r
+ //\r
+ IpSecLookupSadEntry (\r
+ Private,\r
+ NicHandle,\r
+ IpVersion,\r
+ IpHead,\r
+ IpPayload,\r
+ OldLastHead,\r
+ SpdEntry,\r
+ &SadEntry\r
+ );\r
+ if (SadEntry == NULL) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ goto ON_EXIT;\r
+\r
+ case EfiIPsecActionBypass:\r
+ Status = EFI_SUCCESS;\r
+ goto ON_EXIT;\r
+\r
+ case EfiIPsecActionDiscard:\r
+ goto ON_EXIT;\r
+\r
+ default:\r
+ //\r
+ // Discard the packet if no spd entry match.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ The definitions related to IPsec protocol implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _IP_SEC_IMPL_H_\r
+#define _IP_SEC_IMPL_H_\r
+\r
+#include <Uefi.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Protocol/IpSec.h>\r
+#include <Protocol/IpSecConfig.h>\r
+#include <Protocol/Dpc.h>\r
+#include <Protocol/ComponentName.h>\r
+#include <Protocol/ComponentName2.h>\r
+\r
+typedef struct _IPSEC_PRIVATE_DATA IPSEC_PRIVATE_DATA;\r
+typedef struct _IPSEC_SPD_ENTRY IPSEC_SPD_ENTRY;\r
+typedef struct _IPSEC_PAD_ENTRY IPSEC_PAD_ENTRY;\r
+typedef struct _IPSEC_SPD_DATA IPSEC_SPD_DATA;\r
+\r
+#define IPSEC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('I', 'P', 'S', 'E')\r
+\r
+#define IPSEC_PRIVATE_DATA_FROM_IPSEC(a) CR (a, IPSEC_PRIVATE_DATA, IpSec, IPSEC_PRIVATE_DATA_SIGNATURE)\r
+#define IPSEC_PRIVATE_DATA_FROM_UDP4LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp4List, IPSEC_PRIVATE_DATA_SIGNATURE)\r
+#define IPSEC_PRIVATE_DATA_FROM_UDP6LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp6List, IPSEC_PRIVATE_DATA_SIGNATURE)\r
+#define IPSEC_UDP_SERVICE_FROM_LIST(a) BASE_CR (a, IKE_UDP_SERVICE, List)\r
+#define IPSEC_SPD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SPD_ENTRY, List)\r
+#define IPSEC_SAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SAD_ENTRY, List)\r
+#define IPSEC_PAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_PAD_ENTRY, List)\r
+#define IPSEC_SAD_ENTRY_FROM_SPD(a) BASE_CR (a, IPSEC_SAD_ENTRY, BySpd)\r
+\r
+#define IPSEC_STATUS_DISABLED 0\r
+#define IPSEC_STATUS_ENABLED 1\r
+#define IPSEC_ESP_PROTOCOL 50\r
+#define IPSEC_AH_PROTOCOL 51\r
+#define IPSEC_DEFAULT_VARIABLE_SIZE 0x100\r
+\r
+//\r
+// Internal Structure Definition\r
+//\r
+#pragma pack(1)\r
+typedef struct _EFI_AH_HEADER {\r
+ UINT8 NextHeader;\r
+ UINT8 PayloadLen;\r
+ UINT16 Reserved;\r
+ UINT32 Spi;\r
+ UINT32 SequenceNumber;\r
+} EFI_AH_HEADER;\r
+\r
+typedef struct _EFI_ESP_HEADER {\r
+ UINT32 Spi;\r
+ UINT32 SequenceNumber;\r
+} EFI_ESP_HEADER;\r
+\r
+typedef struct _EFI_ESP_TAIL {\r
+ UINT8 PaddingLength;\r
+ UINT8 NextHeader;\r
+} EFI_ESP_TAIL;\r
+#pragma pack()\r
+\r
+struct _IPSEC_SPD_DATA {\r
+ CHAR16 Name[100];\r
+ UINT32 PackageFlag;\r
+ EFI_IPSEC_ACTION Action;\r
+ EFI_IPSEC_PROCESS_POLICY *ProcessingPolicy;\r
+ LIST_ENTRY Sas;\r
+};\r
+\r
+struct _IPSEC_SPD_ENTRY {\r
+ EFI_IPSEC_SPD_SELECTOR *Selector;\r
+ IPSEC_SPD_DATA *Data;\r
+ LIST_ENTRY List;\r
+};\r
+\r
+typedef struct _IPSEC_SAD_DATA {\r
+ EFI_IPSEC_MODE Mode;\r
+ UINT64 SequenceNumber;\r
+ UINT8 AntiReplayWindowSize;\r
+ UINT64 AntiReplayBitmap[4]; // bitmap for received packet\r
+ EFI_IPSEC_ALGO_INFO AlgoInfo;\r
+ EFI_IPSEC_SA_LIFETIME SaLifetime;\r
+ UINT32 PathMTU;\r
+ IPSEC_SPD_ENTRY *SpdEntry;\r
+ BOOLEAN ESNEnabled; // Extended (64-bit) SN enabled\r
+ BOOLEAN ManualSet;\r
+} IPSEC_SAD_DATA;\r
+\r
+typedef struct _IPSEC_SAD_ENTRY {\r
+ EFI_IPSEC_SA_ID *Id;\r
+ IPSEC_SAD_DATA *Data;\r
+ LIST_ENTRY List;\r
+ LIST_ENTRY BySpd; // Linked on IPSEC_SPD_DATA.Sas\r
+} IPSEC_SAD_ENTRY;\r
+\r
+struct _IPSEC_PAD_ENTRY {\r
+ EFI_IPSEC_PAD_ID *Id;\r
+ EFI_IPSEC_PAD_DATA *Data;\r
+ LIST_ENTRY List;\r
+};\r
+\r
+typedef struct _IPSEC_RECYCLE_CONTEXT {\r
+ EFI_IPSEC_FRAGMENT_DATA *FragmentTable;\r
+ UINT8 *PayloadBuffer;\r
+} IPSEC_RECYCLE_CONTEXT;\r
+\r
+struct _IPSEC_PRIVATE_DATA {\r
+ UINT32 Signature;\r
+ EFI_HANDLE Handle; // Virtual handle to install private prtocol\r
+ EFI_HANDLE ImageHandle;\r
+ EFI_IPSEC_PROTOCOL IpSec;\r
+ EFI_IPSEC_CONFIG_PROTOCOL IpSecConfig;\r
+ BOOLEAN SetBySelf;\r
+ LIST_ENTRY Udp4List;\r
+ UINTN Udp4Num;\r
+ LIST_ENTRY Udp6List;\r
+ UINTN Udp6Num;\r
+ LIST_ENTRY Ikev1SessionList;\r
+ LIST_ENTRY Ikev1EstablishedList;\r
+ LIST_ENTRY Ikev2SessionList;\r
+ LIST_ENTRY Ikev2EstablishedList;\r
+ BOOLEAN IsIPsecDisabling;\r
+};\r
+\r
+/**\r
+ This function processes the inbound traffic with IPsec.\r
+\r
+ It checks the received packet security property, trims the ESP/AH header, and then\r
+ returns without an IPsec protected IP Header and FragmentTable.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in, out] IpHead Points to IP header containing the ESP/AH header\r
+ to be trimed on input, and without ESP/AH header\r
+ on return.\r
+ @param[in] LastHead The Last Header in IP header on return.\r
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
+ @param[in] OptionsLength Length of the options buffer. It is optional.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec\r
+ protected on input, and without IPsec protected\r
+ on return.\r
+ @param[in] FragmentCount Number of fragments.\r
+ @param[out] SpdEntry Pointer to contain the address of SPD entry on return.\r
+ @param[out] RecycleEvent Event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The operation is successful.\r
+ @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecProtectInboundPacket (\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ IN UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer, OPTIONAL\r
+ IN UINT32 OptionsLength, OPTIONAL\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ OUT IPSEC_SPD_ENTRY **SpdEntry,\r
+ OUT EFI_EVENT *RecycleEvent\r
+ );\r
+\r
+\r
+/**\r
+ This fucntion processes the output traffic with IPsec.\r
+\r
+ It protected the sending packet by encrypting it payload and inserting ESP/AH header\r
+ in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in, out] IpHead Point to IP header containing the orginal IP header\r
+ to be processed on input, and inserted ESP/AH header\r
+ on return.\r
+ @param[in] LastHead The Last Header in IP header.\r
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
+ @param[in] OptionsLength Length of the options buffer. It is optional.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
+ IPsec on input, and with IPsec protected\r
+ on return.\r
+ @param[in] FragmentCount Number of fragments.\r
+ @param[in] SadEntry Related SAD entry.\r
+ @param[out] RecycleEvent Event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The operation is successful.\r
+ @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecProtectOutboundPacket (\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ IN UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer, OPTIONAL\r
+ IN UINT32 OptionsLength, OPTIONAL\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ IN IPSEC_SAD_ENTRY *SadEntry,\r
+ OUT EFI_EVENT *RecycleEvent\r
+ );\r
+\r
+/**\r
+ Check if the IP Address in the address range of AddressInfos specified.\r
+\r
+ @param[in] IpVersion The IP version.\r
+ @param[in] IpAddr Points to EFI_IP_ADDRESS to be check.\r
+ @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check\r
+ the IP Address is matched.\r
+ @param[in] AddressCount The total numbers of the AddressInfo.\r
+\r
+ @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.\r
+ @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.\r
+\r
+**/\r
+BOOLEAN\r
+IpSecMatchIpAddress (\r
+ IN UINT8 IpVersion,\r
+ IN EFI_IP_ADDRESS *IpAddr,\r
+ IN EFI_IP_ADDRESS_INFO *AddressInfo,\r
+ IN UINT32 AddressCount\r
+ );\r
+\r
+/**\r
+ Find a PAD entry according to remote IP address.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in] IpAddr Point to remote IP address.\r
+\r
+ @return The pointer of related PAD entry.\r
+\r
+**/\r
+IPSEC_PAD_ENTRY *\r
+IpSecLookupPadEntry (\r
+ IN UINT8 IpVersion,\r
+ IN EFI_IP_ADDRESS *IpAddr\r
+ );\r
+\r
+/**\r
+ Find the SAD through whole SAD list.\r
+\r
+ @param[in] Spi The SPI used to search the SAD entry.\r
+ @param[in] DestAddress The destination used to search the SAD entry.\r
+\r
+ @return The pointer to a certain SAD entry.\r
+\r
+**/\r
+IPSEC_SAD_ENTRY *\r
+IpSecLookupSadBySpi (\r
+ IN UINT32 Spi,\r
+ IN EFI_IP_ADDRESS *DestAddress\r
+ )\r
+;\r
+\r
+/**\r
+ Handles IPsec packet processing for inbound and outbound IP packets.\r
+\r
+ The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.\r
+ The behavior is that it can perform one of the following actions:\r
+ bypass the packet, discard the packet, or protect the packet.\r
+\r
+ @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance.\r
+ @param[in] NicHandle Instance of the network interface.\r
+ @param[in] IpVersion IPV4 or IPV6.\r
+ @param[in, out] IpHead Pointer to the IP Header.\r
+ @param[in] LastHead The protocol of the next layer to be processed by IPsec.\r
+ @param[in] OptionsBuffer Pointer to the options buffer.\r
+ @param[in] OptionsLength Length of the options buffer.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments.\r
+ @param[in] FragmentCount Number of fragments.\r
+ @param[in] TrafficDirection Traffic direction.\r
+ @param[out] RecycleSignal Event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.\r
+ @retval EFI_SUCCESS The packet was protected.\r
+ @retval EFI_ACCESS_DENIED The packet was discarded.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IpSecProcess (\r
+ IN EFI_IPSEC_PROTOCOL *This,\r
+ IN EFI_HANDLE NicHandle,\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ IN UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer,\r
+ IN UINT32 OptionsLength,\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection,\r
+ OUT EFI_EVENT *RecycleSignal\r
+ );\r
+\r
+extern EFI_DPC_PROTOCOL *mDpc;\r
+extern EFI_IPSEC_PROTOCOL mIpSecInstance;\r
+\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2;\r
+extern EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName;\r
+\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ IPsec inbound and outbound traffic processing.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IpSecImpl.h"\r
+#include "IpSecDebug.h"\r
+#include "IpSecCryptIo.h"\r
+\r
+extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];\r
+\r
+/**\r
+ The call back function of NetbufFromExt.\r
+\r
+ @param[in] Arg The argument passed from the caller.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IpSecOnRecyclePacket (\r
+ IN VOID *Arg\r
+ )\r
+{\r
+}\r
+\r
+/**\r
+ This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP\r
+ is released.\r
+\r
+ @param[in] Event The related event.\r
+ @param[in] Context The data passed by the caller.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IpSecRecycleCallback (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
+\r
+ RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;\r
+\r
+ if (RecycleContext->FragmentTable != NULL) {\r
+ FreePool (RecycleContext->FragmentTable);\r
+ }\r
+\r
+ if (RecycleContext->PayloadBuffer != NULL) {\r
+ FreePool (RecycleContext->PayloadBuffer);\r
+ }\r
+\r
+ FreePool (RecycleContext);\r
+ gBS->CloseEvent (Event);\r
+\r
+}\r
+\r
+/**\r
+ Calculate the extension header of IP. The return length only doesn't contain\r
+ the fixed IP header length.\r
+\r
+ @param[in] IpHead Points to an IP head to be calculated.\r
+ @param[in] LastHead Points to the last header of the IP header.\r
+\r
+ @return The length of the extension header.\r
+\r
+**/\r
+UINT16\r
+IpSecGetPlainExtHeadSize (\r
+ IN VOID *IpHead,\r
+ IN UINT8 *LastHead\r
+ )\r
+{\r
+ UINT16 Size;\r
+\r
+ Size = (UINT16) (LastHead - (UINT8 *) IpHead);\r
+\r
+ if (Size > sizeof (EFI_IP6_HEADER)) {\r
+ //\r
+ // * (LastHead+1) point the last header's length but not include the first\r
+ // 8 octers, so this formluation add 8 at the end.\r
+ //\r
+ Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);\r
+ } else {\r
+ Size = 0;\r
+ }\r
+\r
+ return Size;\r
+}\r
+\r
+/**\r
+ Authenticate the IpSec Payload and store the result in the IcvBuffer.\r
+\r
+ @param[in] BufferToAuth The buffer to be Authenticated.\r
+ @param[in] AuthSize The size of the buffer to be Authenticated.\r
+ @param[in, out] IcvBuffer The buffer to store the ICV.\r
+ @param[in] IcvSize The size of ICV.\r
+ @param[in] Key The Key passed to the CryptLib to generate a\r
+ CRYPT_HANDLE.\r
+ @param[in] AuthAlgId The Authentication Algorithm ID.\r
+\r
+ @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list.\r
+ @retval EFI_SUCCESS Authenticated the payload successfully.\r
+ @retval otherwise Authentication of the payload failed.\r
+**/\r
+EFI_STATUS\r
+IpSecAuthPayload (\r
+ IN UINT8 *BufferToAuth,\r
+ IN UINTN AuthSize,\r
+ IN OUT UINT8 *IcvBuffer,\r
+ IN UINTN IcvSize,\r
+ IN VOID *Key,\r
+ IN UINT8 AuthAlgId\r
+ )\r
+{\r
+ switch (AuthAlgId) {\r
+ case EFI_IPSEC_AALG_NONE :\r
+ case EFI_IPSEC_AALG_NULL :\r
+ return EFI_SUCCESS;\r
+\r
+ default:\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+}\r
+\r
+/**\r
+ Verify if the Authentication payload is correct.\r
+\r
+ @param[in] EspBuffer Points to the ESP wrapped buffer.\r
+ @param[in] EspSize The size of the ESP wrapped buffer.\r
+ @param[in] SadEntry The related SAD entry to store the authentication\r
+ algorithm key.\r
+ @param[in] IcvSize The length of ICV.\r
+\r
+ @retval EFI_SUCCESS The authentication data is correct.\r
+ @retval EFI_ACCESS_DENIED The authentication data is not correct.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspAuthVerifyPayload (\r
+ IN UINT8 *EspBuffer,\r
+ IN UINTN EspSize,\r
+ IN IPSEC_SAD_ENTRY *SadEntry,\r
+ IN UINTN *IcvSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN AuthSize;\r
+ UINT8 IcvBuffer[12];\r
+\r
+ //\r
+ // Calculate the size of authentication payload.\r
+ //\r
+ *IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
+ AuthSize = EspSize - *IcvSize;\r
+\r
+ //\r
+ // Calculate the icv buffer and size of the payload.\r
+ //\r
+ Status = IpSecAuthPayload (\r
+ EspBuffer,\r
+ AuthSize,\r
+ IcvBuffer,\r
+ *IcvSize,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // Compare the calculated icv and the appended original icv.\r
+ //\r
+ if (CompareMem (EspBuffer + AuthSize, IcvBuffer, *IcvSize) == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));\r
+ return EFI_ACCESS_DENIED;\r
+}\r
+\r
+/**\r
+ ESP Decrypt the payload.\r
+\r
+ @param[in, out] PayloadBuffer Pointer to the buffer containing the ESP wrapped;\r
+ to be decrypted on input, and plaintext on return. The\r
+ number of bytes of data to be decrypted is\r
+ specified by EncryptSize.\r
+ @param[in] EncryptSize The size of the PayloadBuffer as input.\r
+ @param[in] SadEntry The related SAD entry.\r
+ @param[in] IvSize The size of IV.\r
+ @param[out] PlainPayloadSize Contains the return value of decrypted size.\r
+ @param[out] PaddingSize Contains the return value of Padding size.\r
+ @param[out] NextHeader Contains the return value of the last protocol header\r
+ of the IP packet.\r
+\r
+ @retval EFI_UNSUPPORTED The Algorithm pointed to by the SAD entry is not supported.\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspDecryptPayload (\r
+ IN OUT UINT8 *PayloadBuffer,\r
+ IN UINTN EncryptSize,\r
+ IN IPSEC_SAD_ENTRY *SadEntry,\r
+ IN UINTN *IvSize,\r
+ OUT UINTN *PlainPayloadSize,\r
+ OUT UINTN *PaddingSize,\r
+ OUT UINT8 *NextHeader\r
+ )\r
+{\r
+ EFI_ESP_TAIL *EspTail;\r
+\r
+ switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {\r
+ case EFI_IPSEC_EALG_NULL:\r
+ EspTail = (EFI_ESP_TAIL *) (PayloadBuffer + EncryptSize - sizeof (EFI_ESP_TAIL));\r
+ *PaddingSize = EspTail->PaddingLength;\r
+ *NextHeader = EspTail->NextHeader;\r
+ *PlainPayloadSize = EncryptSize - EspTail->PaddingLength - sizeof (EFI_ESP_TAIL);\r
+ break;\r
+\r
+ case EFI_IPSEC_EALG_3DESCBC:\r
+ case EFI_IPSEC_EALG_AESCBC:\r
+ //\r
+ // TODO: support these algorithm\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+ default :\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ ESP Encrypt the payload.\r
+\r
+ @param[in, out] BufferToEncrypt Pointer to the buffer containing plaintext to be\r
+ encrypted on input, and ciphertext on return. The\r
+ number of bytes of data to be encrypted is\r
+ specified by EncryptSize.\r
+ @param[in, out] EncryptSize The size of the plaintext on input, and the size of the\r
+ ciphertext on return.\r
+ @param[in] IvBuffer Points to IV data.\r
+ @param[in] IvSize Size of IV.\r
+ @param[in] SadEntry Related SAD entry.\r
+\r
+ @retval EFI_UNSUPPORTED The Algorithm pointed by SAD entry is not supported.\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspEncryptPayload (\r
+ IN OUT UINT8 *BufferToEncrypt,\r
+ IN OUT UINTN EncryptSize,\r
+ IN UINT8 *IvBuffer,\r
+ IN UINTN IvSize,\r
+ IN IPSEC_SAD_ENTRY *SadEntry\r
+ )\r
+{\r
+ switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {\r
+ case EFI_IPSEC_EALG_NULL:\r
+ return EFI_SUCCESS;\r
+\r
+ case EFI_IPSEC_EALG_3DESCBC:\r
+ case EFI_IPSEC_EALG_AESCBC:\r
+ //\r
+ // TODO: support these algorithms\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+ default :\r
+ return EFI_UNSUPPORTED;\r
+\r
+ }\r
+}\r
+\r
+/**\r
+ The actual entry to relative function processes the inbound traffic of ESP header.\r
+\r
+ This function is the subfunction of IpSecProtectInboundPacket(). It checks the\r
+ received packet security property and trim the ESP header and then returns without\r
+ an IPsec protected IP Header and FramgmentTable.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in, out] IpHead Points to the IP header containing the ESP header\r
+ to be trimed on input, and without ESP header\r
+ on return.\r
+ @param[out] LastHead The Last Header in IP header on return.\r
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
+ @param[in] OptionsLength Length of the options buffer. It is optional.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec\r
+ protected on input, and without IPsec protected\r
+ on return.\r
+ @param[in] FragmentCount The number of fragments.\r
+ @param[out] SpdEntry Pointer to contain the address of SPD entry on return.\r
+ @param[out] RecycleEvent The event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The operation was successful.\r
+ @retval EFI_ACCESS_DENIED One or more following conditions is TRUE:\r
+ - ESP header was not found.\r
+ - The related SAD entry was not found.\r
+ - The related SAD entry does not support the ESP protocol.\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspInboundPacket (\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ OUT UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer, OPTIONAL\r
+ IN UINT32 OptionsLength, OPTIONAL\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ OUT IPSEC_SPD_ENTRY **SpdEntry,\r
+ OUT EFI_EVENT *RecycleEvent\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ NET_BUF *Payload;\r
+ UINTN EspSize;\r
+ UINTN IvSize;\r
+ UINTN PlainPayloadSize;\r
+ UINTN PaddingSize;\r
+ UINTN IcvSize;\r
+ UINT8 *ProcessBuffer;\r
+ EFI_IP_ADDRESS DestIp;\r
+ EFI_ESP_HEADER *EspHeader;\r
+ EFI_ESP_TAIL *EspTail;\r
+ EFI_IPSEC_SA_ID *SaId;\r
+ IPSEC_SAD_DATA *SadData;\r
+ IPSEC_SAD_ENTRY *SadEntry;\r
+ IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
+ UINT32 Spi;\r
+ UINT8 NextHeader;\r
+ UINT16 IpSecHeadSize;\r
+\r
+ Status = EFI_SUCCESS;\r
+ Payload = NULL;\r
+ ProcessBuffer = NULL;\r
+ RecycleContext = NULL;\r
+ *RecycleEvent = NULL;\r
+ PlainPayloadSize = 0;\r
+ NextHeader = 0;\r
+ //\r
+ // Build netbuf from fragment table first.\r
+ //\r
+ Payload = NetbufFromExt (\r
+ (NET_FRAGMENT *) *FragmentTable,\r
+ *FragmentCount,\r
+ 0,\r
+ sizeof (EFI_ESP_HEADER),\r
+ IpSecOnRecyclePacket,\r
+ NULL\r
+ );\r
+ if (Payload == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Get the esp size and eso header from netbuf.\r
+ //\r
+ EspSize = Payload->TotalSize;\r
+ EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);\r
+ if (EspHeader == NULL) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Parse destination address from ip header.\r
+ //\r
+ ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));\r
+ if (IpVersion == IP_VERSION_4) {\r
+ CopyMem (\r
+ &DestIp,\r
+ &((IP4_HEAD *) IpHead)->Dst,\r
+ sizeof (IP4_ADDR)\r
+ );\r
+ } else {\r
+ CopyMem (\r
+ &DestIp,\r
+ &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ }\r
+ //\r
+ // Lookup sad entry according to the spi and dest address.\r
+ //\r
+ Spi = NTOHL (EspHeader->Spi);\r
+ SadEntry = IpSecLookupSadBySpi (Spi, &DestIp);\r
+ if (SadEntry == NULL) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ SaId = SadEntry->Id;\r
+ SadData = SadEntry->Data;\r
+\r
+ //\r
+ // Only support esp protocol currently.\r
+ //\r
+ if (SaId->Proto != EfiIPsecESP) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (!SadData->ManualSet) {\r
+ //\r
+ // TODO: Check sa lifetime and sequence number\r
+ //\r
+ }\r
+ //\r
+ // Allocate buffer for decryption and authentication by esp.\r
+ //\r
+ ProcessBuffer = AllocateZeroPool (EspSize);\r
+ if (ProcessBuffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);\r
+\r
+ //\r
+ // Authenticate the esp wrapped buffer by the sad entry if has auth key.\r
+ //\r
+ IcvSize = 0;\r
+ if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+ Status = IpSecEspAuthVerifyPayload (\r
+ ProcessBuffer,\r
+ EspSize,\r
+ SadEntry,\r
+ &IcvSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Decrypt the payload by the sad entry if has decrypt key.\r
+ //\r
+ IvSize = 0;\r
+ if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+ Status = IpSecEspDecryptPayload (\r
+ ProcessBuffer + sizeof (EFI_ESP_HEADER),\r
+ EspSize - sizeof (EFI_ESP_HEADER) - IcvSize,\r
+ SadEntry,\r
+ &IvSize,\r
+ &PlainPayloadSize,\r
+ &PaddingSize,\r
+ &NextHeader\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ } else {\r
+ EspTail = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));\r
+ PaddingSize = EspTail->PaddingLength;\r
+ NextHeader = EspTail->NextHeader;\r
+ PlainPayloadSize = EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize - sizeof (EFI_ESP_TAIL) - PaddingSize;\r
+ }\r
+ //\r
+ // TODO: handle anti-replay window\r
+ //\r
+ //\r
+ // Decryption and authentication with esp has been done, so it's time to\r
+ // reload the new packet, create recycle event and fixup ip header.\r
+ //\r
+ RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
+ if (RecycleContext == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ IpSecRecycleCallback,\r
+ RecycleContext,\r
+ RecycleEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // TODO: Who take responsible to handle the original fragment table?\r
+ //\r
+ *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
+ if (*FragmentTable == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ RecycleContext->PayloadBuffer = ProcessBuffer;\r
+ RecycleContext->FragmentTable = *FragmentTable;\r
+ (*FragmentTable)[0].FragmentBuffer = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;\r
+ (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize;\r
+ *FragmentCount = 1;\r
+\r
+ //\r
+ // Update the total length field in ip header since processed by esp.\r
+ //\r
+ if (IpVersion == IP_VERSION_4) {\r
+ ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + PlainPayloadSize));\r
+ } else {\r
+ IpSecHeadSize = IpSecGetPlainExtHeadSize (IpHead, LastHead);\r
+ ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));\r
+ }\r
+ //\r
+ // Update the next layer field in ip header since esp header inserted.\r
+ //\r
+ *LastHead = NextHeader;\r
+\r
+ //\r
+ // Update the spd association of the sad entry.\r
+ //\r
+ *SpdEntry = SadData->SpdEntry;\r
+\r
+ON_EXIT:\r
+ if (Payload != NULL) {\r
+ NetbufFree (Payload);\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ if (ProcessBuffer != NULL) {\r
+ FreePool (ProcessBuffer);\r
+ }\r
+\r
+ if (RecycleContext != NULL) {\r
+ FreePool (RecycleContext);\r
+ }\r
+\r
+ if (*RecycleEvent != NULL) {\r
+ gBS->CloseEvent (*RecycleEvent);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The actual entry to the relative function processes the output traffic using the ESP protocol.\r
+\r
+ This function is the subfunction of IpSecProtectOutboundPacket(). It protected\r
+ the sending packet by encrypting its payload and inserting ESP header in the orginal\r
+ IP header, then return the IpHeader and IPsec protected Fragmentable.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in, out] IpHead Points to IP header containing the orginal IP header\r
+ to be processed on input, and inserted ESP header\r
+ on return.\r
+ @param[in] LastHead The Last Header in IP header.\r
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
+ @param[in] OptionsLength Length of the options buffer. It is optional.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
+ IPsec on input, and with IPsec protected\r
+ on return.\r
+ @param[in] FragmentCount The number of fragments.\r
+ @param[in] SadEntry The related SAD entry.\r
+ @param[out] RecycleEvent The event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The operation was successful.\r
+ @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecEspOutboundPacket (\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ IN UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer, OPTIONAL\r
+ IN UINT32 OptionsLength, OPTIONAL\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ IN IPSEC_SAD_ENTRY *SadEntry,\r
+ OUT EFI_EVENT *RecycleEvent\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ EFI_IPSEC_SA_ID *SaId;\r
+ IPSEC_SAD_DATA *SadData;\r
+ IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
+ UINT8 *ProcessBuffer;\r
+ UINTN BytesCopied;\r
+ INTN EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4\r
+ UINTN EspSize; // Total size of esp wrapped ip payload\r
+ UINTN IvSize; // Size of IV, optional, might be 0\r
+ UINTN PlainPayloadSize;// Original IP payload size\r
+ UINTN PaddingSize; // Size of padding\r
+ UINTN EncryptSize; // Size of data to be encrypted, start after IV and\r
+ // stop before ICV\r
+ UINTN IcvSize; // Size of ICV, optional, might be 0\r
+ UINT8 *RestOfPayload; // Start of Payload after IV\r
+ UINT8 *Padding; // Start address of padding\r
+ EFI_ESP_HEADER *EspHeader; // Start address of ESP frame\r
+ EFI_ESP_TAIL *EspTail; // Address behind padding\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ SaId = SadEntry->Id;\r
+ SadData = SadEntry->Data;\r
+ ProcessBuffer = NULL;\r
+ RecycleContext = NULL;\r
+ *RecycleEvent = NULL;\r
+\r
+ if (!SadData->ManualSet &&\r
+ SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&\r
+ SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL\r
+ ) {\r
+ //\r
+ // Invalid manual sad entry configuration.\r
+ //\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Calculate enctrypt block size, need iv by default and 4 bytes alignment.\r
+ //\r
+ EncryptBlockSize = 4;\r
+\r
+ if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+ EncryptBlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
+\r
+ if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Calculate the plain payload size accroding to the fragment table.\r
+ //\r
+ PlainPayloadSize = 0;\r
+ for (Index = 0; Index < *FragmentCount; Index++) {\r
+ PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;\r
+ }\r
+ //\r
+ // Calculate icv size, optional by default and 4 bytes alignment.\r
+ //\r
+ IcvSize = 0;\r
+ if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+ IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
+ if (IcvSize % 4 != 0) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Calcuate the total size of esp wrapped ip payload.\r
+ //\r
+ IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
+ EncryptSize = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;\r
+ PaddingSize = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);\r
+ EspSize = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;\r
+\r
+ ProcessBuffer = AllocateZeroPool (EspSize);\r
+ if (ProcessBuffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Calculate esp header and esp tail including header, payload and padding.\r
+ //\r
+ EspHeader = (EFI_ESP_HEADER *) ProcessBuffer;\r
+ RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;\r
+ Padding = RestOfPayload + PlainPayloadSize;\r
+ EspTail = (EFI_ESP_TAIL *) (Padding + PaddingSize);\r
+\r
+ //\r
+ // Fill the sn and spi fields in esp header.\r
+ //\r
+ EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);\r
+ EspHeader->Spi = HTONL (SaId->Spi);\r
+\r
+ //\r
+ // Copy the rest of payload (after iv) from the original fragment buffer.\r
+ //\r
+ BytesCopied = 0;\r
+ for (Index = 0; Index < *FragmentCount; Index++) {\r
+ CopyMem (\r
+ (RestOfPayload + BytesCopied),\r
+ (*FragmentTable)[Index].FragmentBuffer,\r
+ (*FragmentTable)[Index].FragmentLength\r
+ );\r
+ BytesCopied += (*FragmentTable)[Index].FragmentLength;\r
+ }\r
+ //\r
+ // Fill the padding buffer by natural number sequence.\r
+ //\r
+ for (Index = 0; Index < PaddingSize; Index++) {\r
+ Padding[Index] = (UINT8) (Index + 1);\r
+ }\r
+ //\r
+ // Fill the padding length and next header fields in esp tail.\r
+ //\r
+ EspTail->PaddingLength = (UINT8) PaddingSize;\r
+ EspTail->NextHeader = *LastHead;\r
+\r
+ //\r
+ // Generate iv at random by crypt library.\r
+ //\r
+ Status = IpSecGenerateIv (\r
+ (UINT8 *) (EspHeader + 1),\r
+ IvSize\r
+ );\r
+\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Encrypt the payload (after iv) by the sad entry if has encrypt key.\r
+ //\r
+ if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
+ Status = IpSecEspEncryptPayload (\r
+ RestOfPayload,\r
+ EncryptSize,\r
+ (UINT8 *) (EspHeader + 1),\r
+ IvSize,\r
+ SadEntry\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Authenticate the esp wrapped buffer by the sad entry if has auth key.\r
+ //\r
+ if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
+ Status = IpSecAuthPayload (\r
+ ProcessBuffer,\r
+ EspSize - IcvSize,\r
+ ProcessBuffer + EspSize - IcvSize,\r
+ IcvSize,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ //\r
+ // Encryption and authentication with esp has been done, so it's time to\r
+ // reload the new packet, create recycle event and fixup ip header.\r
+ //\r
+ RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
+ if (RecycleContext == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ IpSecRecycleCallback,\r
+ RecycleContext,\r
+ RecycleEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // TODO: Who take responsible to handle the original fragment table?\r
+ //\r
+ *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
+ if (*FragmentTable == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ RecycleContext->FragmentTable = *FragmentTable;\r
+ RecycleContext->PayloadBuffer = ProcessBuffer;\r
+ (*FragmentTable)[0].FragmentBuffer = ProcessBuffer;\r
+ (*FragmentTable)[0].FragmentLength = (UINT32) EspSize;\r
+ *FragmentCount = 1;\r
+\r
+ //\r
+ // Update the total length field in ip header since processed by esp.\r
+ //\r
+ if (IpVersion == IP_VERSION_4) {\r
+ ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + EspSize));\r
+ } else {\r
+ ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);\r
+ }\r
+ //\r
+ // Update the next layer field in ip header since esp header inserted.\r
+ //\r
+ *LastHead = IPSEC_ESP_PROTOCOL;\r
+\r
+ //\r
+ // Increase the sn number in sad entry according to rfc4303.\r
+ //\r
+ SadData->SequenceNumber++;\r
+\r
+ON_EXIT:\r
+ if (EFI_ERROR (Status)) {\r
+ if (ProcessBuffer != NULL) {\r
+ FreePool (ProcessBuffer);\r
+ }\r
+\r
+ if (RecycleContext != NULL) {\r
+ FreePool (RecycleContext);\r
+ }\r
+\r
+ if (*RecycleEvent != NULL) {\r
+ gBS->CloseEvent (*RecycleEvent);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This function processes the inbound traffic with IPsec.\r
+\r
+ It checks the received packet security property, trims the ESP/AH header, and then\r
+ returns without an IPsec protected IP Header and FragmentTable.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in, out] IpHead Points to IP header containing the ESP/AH header\r
+ to be trimed on input, and without ESP/AH header\r
+ on return.\r
+ @param[in] LastHead The Last Header in IP header on return.\r
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
+ @param[in] OptionsLength Length of the options buffer. It is optional.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec\r
+ protected on input, and without IPsec protected\r
+ on return.\r
+ @param[in] FragmentCount The number of fragments.\r
+ @param[out] SpdEntry Pointer to contain the address of SPD entry on return.\r
+ @param[out] RecycleEvent The event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The operation was successful.\r
+ @retval EFI_UNSUPPORTED The IPSEC protocol is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecProtectInboundPacket (\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ IN UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer, OPTIONAL\r
+ IN UINT32 OptionsLength, OPTIONAL\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ OUT IPSEC_SPD_ENTRY **SpdEntry,\r
+ OUT EFI_EVENT *RecycleEvent\r
+ )\r
+{\r
+ if (*LastHead == IPSEC_ESP_PROTOCOL) {\r
+ //\r
+ // Process the esp ipsec header of the inbound traffic.\r
+ //\r
+ return IpSecEspInboundPacket (\r
+ IpVersion,\r
+ IpHead,\r
+ LastHead,\r
+ OptionsBuffer,\r
+ OptionsLength,\r
+ FragmentTable,\r
+ FragmentCount,\r
+ SpdEntry,\r
+ RecycleEvent\r
+ );\r
+ }\r
+ //\r
+ // The other protocols are not supported.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ This function processes the output traffic with IPsec.\r
+\r
+ It protected the sending packet by encrypting it payload and inserting ESP/AH header\r
+ in the orginal IP header, then returns the IpHeader and IPsec protected Fragmentable.\r
+\r
+ @param[in] IpVersion The version of IP.\r
+ @param[in, out] IpHead Points to IP header containing the orginal IP header\r
+ to be processed on input, and inserted ESP/AH header\r
+ on return.\r
+ @param[in] LastHead The Last Header in the IP header.\r
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
+ @param[in] OptionsLength Length of the options buffer. It is optional.\r
+ @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
+ IPsec on input, and with IPsec protected\r
+ on return.\r
+ @param[in] FragmentCount The number of fragments.\r
+ @param[in] SadEntry The related SAD entry.\r
+ @param[out] RecycleEvent The event for recycling of resources.\r
+\r
+ @retval EFI_SUCCESS The operation was successful.\r
+ @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+IpSecProtectOutboundPacket (\r
+ IN UINT8 IpVersion,\r
+ IN OUT VOID *IpHead,\r
+ IN UINT8 *LastHead,\r
+ IN VOID *OptionsBuffer, OPTIONAL\r
+ IN UINT32 OptionsLength, OPTIONAL\r
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
+ IN UINT32 *FragmentCount,\r
+ IN IPSEC_SAD_ENTRY *SadEntry,\r
+ OUT EFI_EVENT *RecycleEvent\r
+ )\r
+{\r
+ if (SadEntry->Id->Proto == EfiIPsecESP) {\r
+ //\r
+ // Process the esp ipsec header of the outbound traffic.\r
+ //\r
+ return IpSecEspOutboundPacket (\r
+ IpVersion,\r
+ IpHead,\r
+ LastHead,\r
+ OptionsBuffer,\r
+ OptionsLength,\r
+ FragmentTable,\r
+ FragmentCount,\r
+ SadEntry,\r
+ RecycleEvent\r
+ );\r
+ }\r
+ //\r
+ // The other protocols are not supported.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+}\r
--- /dev/null
+/** @file\r
+ UEFI Component Name(2) protocol implementation for Mtftp6 driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for bus drivers\r
+ attempting to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that attempts to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName = {\r
+ Mtftp6ComponentNameGetDriverName,\r
+ Mtftp6ComponentNameGetControllerName,\r
+ "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp6ComponentNameGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp6ComponentNameGetControllerName,\r
+ "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp6DriverNameTable[] = {\r
+ {\r
+ "eng;en",\r
+ L"MTFTP6 Network Service Driver"\r
+ },\r
+ {\r
+ NULL,\r
+ NULL\r
+ }\r
+};\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mMtftp6DriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN)(This == &gMtftp6ComponentName)\r
+ );\r
+}\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ attempting to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that attempts to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
--- /dev/null
+/** @file\r
+ Driver Binding functions and Service Binding functions\r
+ implementation for Mtftp6 Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gMtftp6DriverBinding = {\r
+ Mtftp6DriverBindingSupported,\r
+ Mtftp6DriverBindingStart,\r
+ Mtftp6DriverBindingStop,\r
+ 0xa,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL gMtftp6ServiceBindingTemplate = {\r
+ Mtftp6ServiceBindingCreateChild,\r
+ Mtftp6ServiceBindingDestroyChild\r
+};\r
+\r
+\r
+/**\r
+ Destory the MTFTP6 service. The MTFTP6 service may be partly initialized,\r
+ or partly destroyed. If a resource is destroyed, it is marked as such in\r
+ case the destroy failed and is called again later.\r
+\r
+ @param[in] Service The MTFTP6 service to be destroyed.\r
+\r
+**/\r
+VOID\r
+Mtftp6DestroyService (\r
+ IN MTFTP6_SERVICE *Service\r
+ )\r
+{\r
+ //\r
+ // Make sure all children instances have been already destoryed.\r
+ //\r
+ ASSERT (Service->ChildrenNum == 0);\r
+\r
+ if (Service->DummyUdpIo != NULL) {\r
+ UdpIoFreeIo (Service->DummyUdpIo);\r
+ }\r
+\r
+ if (Service->Timer != NULL) {\r
+ gBS->CloseEvent (Service->Timer);\r
+ }\r
+\r
+ FreePool (Service);\r
+}\r
+\r
+\r
+/**\r
+ Create then initialize a MTFTP6 service binding instance.\r
+\r
+ @param[in] Controller The controller to install the MTFTP6 service\r
+ binding on.\r
+ @param[in] Image The driver binding image of the MTFTP6 driver.\r
+ @param[out] Service The variable to receive the created service\r
+ binding instance.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to create the instance\r
+ @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep connection with UDP.\r
+ @retval EFI_SUCCESS The service instance is created for the controller.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6CreateService (\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE Image,\r
+ OUT MTFTP6_SERVICE **Service\r
+ )\r
+{\r
+ MTFTP6_SERVICE *Mtftp6Srv;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (Service != NULL);\r
+\r
+ *Service = NULL;\r
+ Mtftp6Srv = AllocateZeroPool (sizeof (MTFTP6_SERVICE));\r
+\r
+ if (Mtftp6Srv == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Mtftp6Srv->Signature = MTFTP6_SERVICE_SIGNATURE;\r
+ Mtftp6Srv->Controller = Controller;\r
+ Mtftp6Srv->Image = Image;\r
+ Mtftp6Srv->InDestory = FALSE;\r
+ Mtftp6Srv->ChildrenNum = 0;\r
+\r
+ CopyMem (\r
+ &Mtftp6Srv->ServiceBinding,\r
+ &gMtftp6ServiceBindingTemplate,\r
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
+ );\r
+\r
+ InitializeListHead (&Mtftp6Srv->Children);\r
+\r
+ //\r
+ // Create a internal timer for all instances.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ Mtftp6OnTimerTick,\r
+ Mtftp6Srv,\r
+ &Mtftp6Srv->Timer\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Mtftp6Srv);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Create a dummy Udp6Io to build parent-child relationship between Udp6 driver\r
+ // and Mtftp6 driver.\r
+ //\r
+ Mtftp6Srv->DummyUdpIo = UdpIoCreateIo (\r
+ Controller,\r
+ Image,\r
+ Mtftp6ConfigDummyUdpIo,\r
+ UDP_IO_UDP6_VERSION,\r
+ NULL\r
+ );\r
+\r
+ if (Mtftp6Srv->DummyUdpIo == NULL) {\r
+ gBS->CloseEvent (Mtftp6Srv->Timer);\r
+ FreePool (Mtftp6Srv);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ *Service = Mtftp6Srv;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Destroy the MTFTP6 instance and recycle the resources.\r
+\r
+ @param[in] Instance The pointer to the MTFTP6 instance.\r
+\r
+**/\r
+VOID\r
+Mtftp6DestroyInstance (\r
+ IN MTFTP6_INSTANCE *Instance\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ MTFTP6_BLOCK_RANGE *Block;\r
+\r
+ if (Instance->Config != NULL) {\r
+ FreePool (Instance->Config);\r
+ }\r
+\r
+ if (Instance->Token != NULL && Instance->Token->Event != NULL) {\r
+ gBS->SignalEvent (Instance->Token->Event);\r
+ }\r
+\r
+ if (Instance->LastPacket != NULL) {\r
+ NetbufFree (Instance->LastPacket);\r
+ }\r
+\r
+ if (Instance->UdpIo!= NULL) {\r
+ UdpIoFreeIo (Instance->UdpIo);\r
+ }\r
+\r
+ if (Instance->McastUdpIo != NULL) {\r
+ UdpIoFreeIo (Instance->McastUdpIo);\r
+ }\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {\r
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+ RemoveEntryList (Entry);\r
+ FreePool (Block);\r
+ }\r
+\r
+ FreePool (Instance);\r
+}\r
+\r
+\r
+/**\r
+ Create the MTFTP6 instance and initialize it.\r
+\r
+ @param[in] Service The pointer to the MTFTP6 service.\r
+ @param[out] Instance The pointer to the MTFTP6 instance.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+ @retval EFI_SUCCESS The MTFTP6 instance is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6CreateInstance (\r
+ IN MTFTP6_SERVICE *Service,\r
+ OUT MTFTP6_INSTANCE **Instance\r
+ )\r
+{\r
+ MTFTP6_INSTANCE *Mtftp6Ins;\r
+\r
+ *Instance = NULL;\r
+ Mtftp6Ins = AllocateZeroPool (sizeof (MTFTP6_INSTANCE));\r
+\r
+ if (Mtftp6Ins == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Mtftp6Ins->Signature = MTFTP6_INSTANCE_SIGNATURE;\r
+ Mtftp6Ins->InDestory = FALSE;\r
+ Mtftp6Ins->Service = Service;\r
+\r
+ CopyMem (\r
+ &Mtftp6Ins->Mtftp6,\r
+ &gMtftp6ProtocolTemplate,\r
+ sizeof (EFI_MTFTP6_PROTOCOL)\r
+ );\r
+\r
+ InitializeListHead (&Mtftp6Ins->Link);\r
+ InitializeListHead (&Mtftp6Ins->BlkList);\r
+\r
+ *Instance = Mtftp6Ins;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+ both device drivers and bus drivers.\r
+\r
+ Entry point of the MTFTP6 driver to install various protocols.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.\r
+ @param[in] SystemTable The pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ return EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gMtftp6DriverBinding,\r
+ ImageHandle,\r
+ &gMtftp6ComponentName,\r
+ &gMtftp6ComponentName2\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle. This service\r
+ is called by the EFI boot service ConnectController(). In\r
+ order to make drivers as small as possible, there are calling\r
+ restrictions for this service. ConnectController() must\r
+ follow these calling restrictions. If any other agent wishes to call\r
+ Supported(), it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to test\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child.\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval Others This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ return gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ Start this driver on ControllerHandle. This service is called by the\r
+ EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start() it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval Others This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ MTFTP6_SERVICE *Service;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Directly return if driver is already running on this Nic handle.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiMtftp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ //\r
+ // Create Mtftp6 service for this Nic handle\r
+ //\r
+ Status = Mtftp6CreateService (\r
+ Controller,\r
+ This->DriverBindingHandle,\r
+ &Service\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ ASSERT (Service != NULL);\r
+\r
+ //\r
+ // Start the internal timer to track the packet retransmission.\r
+ //\r
+ Status = gBS->SetTimer (\r
+ Service->Timer,\r
+ TimerPeriodic,\r
+ TICKS_PER_SECOND\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Install the Mtftp6 service on the Nic handle.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Controller,\r
+ &gEfiMtftp6ServiceBindingProtocolGuid,\r
+ &Service->ServiceBinding,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ Mtftp6DestroyService (Service);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Stop this driver on ControllerHandle. This service is called by the\r
+ EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop() it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
+ children is zero, stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ )\r
+{\r
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;\r
+ MTFTP6_SERVICE *Service;\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_HANDLE NicHandle;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ //\r
+ // Locate the Nic handle to retrieve the Mtftp6 private data.\r
+ //\r
+ NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp6ProtocolGuid);\r
+\r
+ if (NicHandle == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ NicHandle,\r
+ &gEfiMtftp6ServiceBindingProtocolGuid,\r
+ (VOID **) &ServiceBinding,\r
+ This->DriverBindingHandle,\r
+ NicHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Service = MTFTP6_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+ if (Service->InDestory) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (NumberOfChildren == 0) {\r
+ //\r
+ // Destory the Mtftp6 service if there is no Mtftp6 child instance left.\r
+ //\r
+ Service->InDestory = TRUE;\r
+\r
+ gBS->UninstallProtocolInterface (\r
+ NicHandle,\r
+ &gEfiMtftp6ServiceBindingProtocolGuid,\r
+ ServiceBinding\r
+ );\r
+\r
+ Mtftp6DestroyService (Service);\r
+\r
+ } else {\r
+ //\r
+ // Destory the Mtftp6 child instance one by one.\r
+ //\r
+ while (!IsListEmpty (&Service->Children)) {\r
+ Instance = NET_LIST_HEAD (&Service->Children, MTFTP6_INSTANCE, Link);\r
+ Mtftp6ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);\r
+ }\r
+\r
+ if (Service->ChildrenNum != 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Creates a child handle and installs a protocol.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+ then a new handle is created. If it is a pointer to an existing\r
+ UEFI handle, then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+ @retval Others The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ )\r
+{\r
+ MTFTP6_SERVICE *Service;\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ VOID *Udp6;\r
+\r
+ if (This == NULL || ChildHandle == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Service = MTFTP6_SERVICE_FROM_THIS (This);\r
+\r
+ Status = Mtftp6CreateInstance (Service, &Instance);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ ASSERT (Instance != NULL);\r
+\r
+ //\r
+ // Install the Mtftp6 protocol on the new child handle.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ ChildHandle,\r
+ &gEfiMtftp6ProtocolGuid,\r
+ &Instance->Mtftp6,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Instance->Handle = *ChildHandle;\r
+\r
+ //\r
+ // Open the Udp6 protocol by child.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Service->DummyUdpIo->UdpHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ (VOID **) &Udp6,\r
+ gMtftp6DriverBinding.DriverBindingHandle,\r
+ Instance->Handle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Instance->Handle,\r
+ &gEfiMtftp6ProtocolGuid,\r
+ &Instance->Mtftp6,\r
+ NULL\r
+ );\r
+\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Add the new Mtftp6 instance into the children list of Mtftp6 service.\r
+ //\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ InsertTailList (&Service->Children, &Instance->Link);\r
+ Service->ChildrenNum++;\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ Mtftp6DestroyInstance (Instance);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Destroys a child handle with a protocol installed on it.\r
+\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in] ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.\r
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+ @retval Others The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ MTFTP6_SERVICE *Service;\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_MTFTP6_PROTOCOL *Mtftp6;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (This == NULL || ChildHandle == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Locate the Nic handle to retrieve the Mtftp6 private data.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ChildHandle,\r
+ &gEfiMtftp6ProtocolGuid,\r
+ (VOID **) &Mtftp6,\r
+ gMtftp6DriverBinding.DriverBindingHandle,\r
+ ChildHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Instance = MTFTP6_INSTANCE_FROM_THIS (Mtftp6);\r
+ Service = MTFTP6_SERVICE_FROM_THIS (This);\r
+\r
+ if (Instance->Service != Service) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check whether the instance already in destory state.\r
+ //\r
+ if (Instance->InDestory) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Instance->InDestory = TRUE;\r
+\r
+ gBS->CloseProtocol (\r
+ Service->DummyUdpIo->UdpHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ gMtftp6DriverBinding.DriverBindingHandle,\r
+ ChildHandle\r
+ );\r
+\r
+ //\r
+ // Uninstall the MTFTP6 protocol first to enable a top down destruction.\r
+ //\r
+ Status = gBS->UninstallProtocolInterface (\r
+ ChildHandle,\r
+ &gEfiMtftp6ProtocolGuid,\r
+ Mtftp6\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Instance->InDestory = FALSE;\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Remove the Mtftp6 instance from the children list of Mtftp6 service.\r
+ //\r
+ RemoveEntryList (&Instance->Link);\r
+ Service->ChildrenNum --;\r
+\r
+ Mtftp6DestroyInstance (Instance);\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ Driver Binding functions and Service Binding functions\r
+ declaration for Mtftp6 Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_DRIVER_H__\r
+#define __EFI_MTFTP6_DRIVER_H__\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+\r
+extern EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2;\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle. This service\r
+ is called by the EFI boot service ConnectController(). In\r
+ order to make drivers as small as possible, there are a few calling\r
+ restrictions for this service. ConnectController() must\r
+ follow these calling restrictions. If any other agent wishes to call\r
+ Supported(), it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to test.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval Others This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ );\r
+\r
+/**\r
+ Start this driver on ControllerHandle. This service is called by the\r
+ EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start() it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval Others This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ );\r
+\r
+/**\r
+ Stop this driver on ControllerHandle. This service is called by the\r
+ EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop(), it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
+ children is zero, stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
+ @retval Others This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ );\r
+\r
+/**\r
+ Creates a child handle and installs a protocol.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+ then a new handle is created. If it is a pointer to an existing\r
+ UEFI handle, then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+ @retval Others The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ );\r
+\r
+/**\r
+ Destroys a child handle with a protocol installed on it.\r
+\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in] ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.\r
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+ @retval Others The child handle was not destroyed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ );\r
+\r
+#endif\r
--- /dev/null
+## @file\r
+# Component description file for Mtftp6 module.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = Mtftp6Dxe\r
+ FILE_GUID = 99F03B99-98D8-49dd-A8D3-3219D0FFE41E\r
+ MODULE_TYPE = UEFI_DRIVER\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = Mtftp6DriverEntryPoint\r
+ UNLOAD_IMAGE = NetLibDefaultUnload\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC\r
+#\r
+# DRIVER_BINDING = gMtftp6DriverBinding\r
+# COMPONENT_NAME = gMtftp6ComponentName\r
+# COMPONENT_NAME2 = gMtftp6ComponentName2\r
+#\r
+\r
+[Sources]\r
+ Mtftp6Driver.c\r
+ Mtftp6Driver.h\r
+ Mtftp6Impl.c\r
+ Mtftp6Impl.h\r
+ Mtftp6Option.c\r
+ Mtftp6Option.h\r
+ Mtftp6Support.h\r
+ Mtftp6Support.c\r
+ Mtftp6Rrq.c\r
+ Mtftp6Wrq.c\r
+ ComponentName.c\r
+\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+ UefiLib\r
+ BaseLib\r
+ UefiBootServicesTableLib\r
+ UefiRuntimeServicesTableLib\r
+ UefiDriverEntryPoint\r
+ DebugLib\r
+ NetLib\r
+ UdpIoLib\r
+\r
+\r
+[Protocols]\r
+ gEfiUdp6ServiceBindingProtocolGuid\r
+ gEfiUdp6ProtocolGuid\r
+ gEfiMtftp6ServiceBindingProtocolGuid\r
+ gEfiMtftp6ProtocolGuid\r
+\r
--- /dev/null
+/** @file\r
+ This EFI_MTFTP6_PROTOCOL interface implementation.\r
+\r
+ It supports the following RFCs:\r
+ RFC1350 - THE TFTP PROTOCOL (REVISION 2)\r
+ RFC2090 - TFTP Multicast Option\r
+ RFC2347 - TFTP Option Extension\r
+ RFC2348 - TFTP Blocksize Option\r
+ RFC2349 - TFTP Timeout Interval and Transfer Size Options\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate = {\r
+ EfiMtftp6GetModeData,\r
+ EfiMtftp6Configure,\r
+ EfiMtftp6GetInfo,\r
+ EfiMtftp6ParseOptions,\r
+ EfiMtftp6ReadFile,\r
+ EfiMtftp6WriteFile,\r
+ EfiMtftp6ReadDirectory,\r
+ EfiMtftp6Poll\r
+ };\r
+\r
+/**\r
+ Returns the current operating mode data for the MTFTP6 instance.\r
+\r
+ The GetModeData() function returns the current operating mode and\r
+ cached data packet for the MTFTP6 instance.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode\r
+ data is returned.\r
+\r
+ @retval EFI_SUCCESS The configuration data was returned successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or ModeData is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetModeData (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ OUT EFI_MTFTP6_MODE_DATA *ModeData\r
+ )\r
+{\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (This == NULL || ModeData == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+\r
+ //\r
+ // Copy back the configure data if the instance already configured.\r
+ //\r
+ if (Instance->Config != NULL) {\r
+ CopyMem (\r
+ &ModeData->ConfigData,\r
+ Instance->Config,\r
+ sizeof (EFI_MTFTP6_CONFIG_DATA)\r
+ );\r
+ } else {\r
+ ZeroMem (\r
+ &ModeData->ConfigData,\r
+ sizeof (EFI_MTFTP6_CONFIG_DATA)\r
+ );\r
+ }\r
+\r
+ //\r
+ // Set the current support options in mode data.\r
+ //\r
+ ModeData->SupportedOptionCount = MTFTP6_SUPPORTED_OPTIONS_NUM;\r
+ ModeData->SupportedOptions = (UINT8 **) mMtftp6SupportedOptions;\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Initializes, changes, or resets the default operational setting for\r
+ this EFI MTFTPv6 Protocol driver instance.\r
+\r
+ The Configure() function is used to set and change the configuration\r
+ data for this EFI MTFTPv6 Protocol driver instance. The configuration\r
+ data can be reset to startup defaults by calling Configure() with\r
+ MtftpConfigData set to NULL. Whenever the instance is reset, any\r
+ pending operation is aborted. By changing the EFI MTFTPv6 Protocol\r
+ driver instance configuration data, the client can connect to\r
+ different MTFTPv6 servers. The configuration parameters in\r
+ MtftpConfigData are used as the default parameters in later MTFTPv6\r
+ operations and can be overridden in later operations.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] MtftpConfigData Pointer to the configuration data structure.\r
+\r
+ @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:\r
+ - This is NULL.\r
+ - MtftpConfigData.StationIp is neither zero nor one\r
+ of the configured IP addresses in the underlying IPv6 driver.\r
+ - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there\r
+ is some MTFTP background operation in progress.\r
+ - MtftpCofigData.LocalPort is already in use.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be\r
+ allocated.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI\r
+ MTFTPv6 Protocol driver instance is not configured.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Configure (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL\r
+ )\r
+{\r
+ MTFTP6_SERVICE *Service;\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+ EFI_UDP6_CONFIG_DATA Udp6Cfg;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (MtftpConfigData != NULL && !NetIp6IsValidUnicast (&MtftpConfigData->ServerIp)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+ Service = Instance->Service;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (MtftpConfigData == NULL) {\r
+ //\r
+ // Configure the instance as NULL to abort the current session.\r
+ //\r
+ Mtftp6OperationClean (Instance, EFI_ABORTED);\r
+ FreePool (Instance->Config);\r
+ Instance->Config = NULL;\r
+ } else {\r
+ //\r
+ // It's not allowed to configure one instance twice without configure null.\r
+ //\r
+ if (Instance->Config != NULL) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Allocate the configure buffer of the instance and store the user's data.\r
+ //\r
+ Instance->Config = AllocateZeroPool (sizeof (EFI_MTFTP6_CONFIG_DATA));\r
+\r
+ if (Instance->Config == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ CopyMem (Instance->Config, MtftpConfigData, sizeof (EFI_MTFTP6_CONFIG_DATA));\r
+\r
+ //\r
+ // Don't configure the udpio here because each operation might override\r
+ // the configuration, so delay udpio configuration in each operation.\r
+ //\r
+ Instance->UdpIo = UdpIoCreateIo (\r
+ Service->Controller,\r
+ Service->Image,\r
+ Mtftp6ConfigDummyUdpIo,\r
+ UDP_IO_UDP6_VERSION,\r
+ NULL\r
+ );\r
+\r
+ if (Instance->UdpIo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Continue to configure the downside Udp6 instance by user's data.\r
+ //\r
+ ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+ Udp6Cfg.AcceptPromiscuous = FALSE;\r
+ Udp6Cfg.AcceptAnyPort = FALSE;\r
+ Udp6Cfg.AllowDuplicatePort = FALSE;\r
+ Udp6Cfg.TrafficClass = 0;\r
+ Udp6Cfg.HopLimit = 128;\r
+ Udp6Cfg.ReceiveTimeout = 0;\r
+ Udp6Cfg.TransmitTimeout = 0;\r
+ Udp6Cfg.StationPort = Instance->Config->LocalPort;\r
+ Udp6Cfg.RemotePort = Instance->Config->InitialServerPort;\r
+\r
+ CopyMem (\r
+ &Udp6Cfg.StationAddress,\r
+ &Instance->Config->StationIp,\r
+ sizeof(EFI_IPv6_ADDRESS)\r
+ );\r
+\r
+ CopyMem (\r
+ &Udp6Cfg.RemoteAddress,\r
+ &Instance->Config->ServerIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+\r
+ Udp6 = Instance->UdpIo->Protocol.Udp6;\r
+ Status = Udp6->Configure (Udp6, &Udp6Cfg);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+ if (EFI_ERROR (Status)) {\r
+ if (Instance->Config != NULL) {\r
+ FreePool (Instance->Config);\r
+ Instance->Config = NULL;\r
+ }\r
+ if (Instance->UdpIo != NULL) {\r
+ UdpIoFreeIo (Instance->UdpIo);\r
+ Instance->UdpIo = NULL;\r
+ }\r
+ }\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Get the information of the download from the server.\r
+\r
+ The GetInfo() function assembles an MTFTPv6 request packet\r
+ with options, sends it to the MTFTPv6 server, and may return\r
+ an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries\r
+ occur only if no response packets are received from the MTFTPv6\r
+ server before the timeout expires.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the\r
+ default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()\r
+ function are used.\r
+ @param[in] Filename Pointer to ASCIIZ file name string.\r
+ @param[in] ModeStr Pointer to ASCIIZ mode string. If NULL, octet will be used.\r
+ @param[in] OptionCount Number of option/value string pairs in OptionList.\r
+ @param[in] OptionList Pointer to array of option/value string pairs. Ignored if\r
+ OptionCount is zero.\r
+ @param[out] PacketLength The number of bytes in the returned packet.\r
+ @param[out] Packet The pointer to the received packet. This buffer must be freed by\r
+ the caller.\r
+\r
+ @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Filename is NULL.\r
+ - OptionCount is not zero and OptionList is NULL.\r
+ - One or more options in OptionList have wrong format.\r
+ - PacketLength is NULL.\r
+ - OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+ @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by\r
+ this implementation.\r
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet.\r
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received and the Packet is set to NULL.\r
+ Note: It is not defined in UEFI 2.3 Specification.\r
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received and the Packet is set to NULL.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received and the Packet is set to NULL.\r
+ @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received and the Packet is set to NULL.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet.\r
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+ @retval EFI_NO_MEDIA There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetInfo (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL,\r
+ IN UINT8 *Filename,\r
+ IN UINT8 *ModeStr OPTIONAL,\r
+ IN UINT8 OptionCount,\r
+ IN EFI_MTFTP6_OPTION *OptionList OPTIONAL,\r
+ OUT UINT32 *PacketLength,\r
+ OUT EFI_MTFTP6_PACKET **Packet OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_MTFTP6_TOKEN Token;\r
+ MTFTP6_GETINFO_CONTEXT Context;\r
+\r
+ if (This == NULL ||\r
+ Filename == NULL ||\r
+ PacketLength == NULL ||\r
+ (OptionCount != 0 && OptionList == NULL) ||\r
+ (OverrideData != NULL && !NetIp6IsValidUnicast (&OverrideData->ServerIp))\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Packet != NULL) {\r
+ *Packet = NULL;\r
+ }\r
+\r
+ *PacketLength = 0;\r
+\r
+ Context.Packet = Packet;\r
+ Context.PacketLen = PacketLength;\r
+ Context.Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Fill fields of the Token for GetInfo operation.\r
+ //\r
+ Token.Status = EFI_SUCCESS;\r
+ Token.Event = NULL;\r
+ Token.OverrideData = OverrideData;\r
+ Token.Filename = Filename;\r
+ Token.ModeStr = ModeStr;\r
+ Token.OptionCount = OptionCount;\r
+ Token.OptionList = OptionList;\r
+ Token.BufferSize = 0;\r
+ Token.Buffer = NULL;\r
+ Token.Context = &Context;\r
+ Token.CheckPacket = Mtftp6CheckPacket;\r
+ Token.TimeoutCallback = NULL;\r
+ Token.PacketNeeded = NULL;\r
+\r
+ //\r
+ // Start the GetInfo operation by issue the Token.\r
+ //\r
+ Status = Mtftp6OperationStart (This, &Token, EFI_MTFTP6_OPCODE_RRQ);\r
+\r
+ if (Status == EFI_ABORTED) {\r
+ //\r
+ // Return the status if failed to issue.\r
+ //\r
+ return Context.Status;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Parse the options in an MTFTPv6 OACK packet.\r
+\r
+ The ParseOptions() function parses the option fields in an MTFTPv6 OACK\r
+ packet and returns the number of options that were found, and optionally,\r
+ a list of pointers to the options in the packet. If one or more of the\r
+ option fields are not valid, then EFI_PROTOCOL_ERROR is returned and\r
+ *OptionCount and *OptionList stop at the last valid option.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] PacketLen Length of the OACK packet to be parsed.\r
+ @param[in] Packet Pointer to the OACK packet to be parsed.\r
+ @param[out] OptionCount Pointer to the number of options in the following OptionList.\r
+ @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the\r
+ OptionList points to the corresponding MTFTP option buffer\r
+ in the Packet. Call the EFI Boot Service FreePool() to\r
+ release the OptionList if the options in this OptionList\r
+ are not needed anymore.\r
+\r
+ @retval EFI_SUCCESS The OACK packet was valid and the OptionCount and\r
+ OptionList parameters have been updated.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - PacketLen is 0.\r
+ - Packet is NULL or Packet is not a valid MTFTPv6 packet.\r
+ - OptionCount is NULL.\r
+ @retval EFI_NOT_FOUND No options were found in the OACK packet.\r
+ @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated.\r
+ @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ParseOptions (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN UINT32 PacketLen,\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ OUT UINT32 *OptionCount,\r
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL\r
+ )\r
+{\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return Mtftp6ParseStart (Packet, PacketLen, OptionCount, OptionList);\r
+}\r
+\r
+\r
+/**\r
+ Download a file from an MTFTPv6 server.\r
+\r
+ The ReadFile() function is used to initialize and start an MTFTPv6 download\r
+ process, and optionally, wait for completion. When the download operation\r
+ completes, whether successfully or not, the Token.Status field is updated\r
+ by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it\r
+ is not NULL.\r
+ Data can be downloaded from the MTFTPv6 server into either of the following\r
+ locations:\r
+ - A fixed buffer that is pointed to by Token.Buffer\r
+ - A download service function that is pointed to by Token.CheckPacket.\r
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+ will be called first. If the call is successful, the packet will be stored\r
+ in Token.Buffer.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the token structure to provide the parameters that are\r
+ used in this operation.\r
+\r
+ @retval EFI_SUCCESS The data file has been transferred successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the\r
+ downloaded data in downloading process.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_ABORTED Current operation is aborted by user.\r
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received.\r
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.\r
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+ @retval EFI_NO_MEDIA There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadFile (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token\r
+ )\r
+{\r
+ return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_RRQ);\r
+}\r
+\r
+\r
+/**\r
+ Send a file to an MTFTPv6 server.\r
+\r
+ The WriteFile() function is used to initialize an uploading operation\r
+ with the given option list and optionally wait for completion. If one\r
+ or more of the options is not supported by the server, the unsupported\r
+ options are ignored and a standard TFTP process starts instead. When\r
+ the upload process completes, whether successfully or not, Token.Event\r
+ is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.\r
+ The caller can supply the data to be uploaded in the following two modes:\r
+ - Through the user-provided buffer\r
+ - Through a callback function\r
+ With the user-provided buffer, the Token.BufferSize field indicates\r
+ the length of the buffer, and the driver will upload the data in the\r
+ buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver\r
+ will call this callback function to get more data from the user to upload.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the token structure to provide the parameters that are\r
+ used in this operation.\r
+\r
+ @retval EFI_SUCCESS The upload session has started.\r
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Filename is NULL.\r
+ - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+ - One or more options in Token.OptionList have wrong format.\r
+ - Token.Buffer and Token.PacketNeeded are both NULL.\r
+ - Token.OverrideData.ServerIp is not a valid unicast IPv6 address.\r
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not\r
+ supported by this implementation.\r
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6WriteFile (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token\r
+ )\r
+{\r
+ return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_WRQ);\r
+}\r
+\r
+\r
+/**\r
+ Download a data file directory from an MTFTPv6 server.\r
+\r
+ The ReadDirectory() function is used to return a list of files on the\r
+ MTFTPv6 server that are logically (or operationally) related to\r
+ Token.Filename. The directory request packet that is sent to the server\r
+ is built with the option list that was provided by the caller, if present.\r
+ The file information that the server returns is put into either of\r
+ the following locations:\r
+ - A fixed buffer that is pointed to by Token.Buffer.\r
+ - A download service function that is pointed to by Token.CheckPacket.\r
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+ will be called first. If the call is successful, the packet will be stored\r
+ in Token.Buffer.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the token structure to provide the parameters that are\r
+ used in this operation.\r
+\r
+ @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded.\r
+ @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Filename is NULL.\r
+ - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+ - One or more options in Token.OptionList have wrong format.\r
+ - Token.Buffer and Token.CheckPacket are both NULL.\r
+ - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not\r
+ supported by this implementation.\r
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadDirectory (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token\r
+ )\r
+{\r
+ return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_DIR);\r
+}\r
+\r
+\r
+/**\r
+ Polls for incoming data packets and processes outgoing data packets.\r
+\r
+ The Poll() function can be used by network drivers and applications\r
+ to increase the rate that data packets are moved between the\r
+ communications device and the transmit and receive queues. In some\r
+ systems, the periodic timer event in the managed network driver may\r
+ not poll the underlying communications device fast enough to transmit\r
+ and/or receive all data packets without missing incoming packets or\r
+ dropping outgoing packets. Drivers and applications that are\r
+ experiencing packet loss should try calling the Poll() function\r
+ more often.\r
+\r
+ @param[in] This The MTFTP6 protocol instance.\r
+\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.\r
+ Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Poll (\r
+ IN EFI_MTFTP6_PROTOCOL *This\r
+ )\r
+{\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+\r
+ //\r
+ // Check the instance whether configured or in destory.\r
+ //\r
+ if (Instance->Config == NULL) {\r
+ return EFI_NOT_STARTED;\r
+ } else if (Instance->InDestory) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Udp6 = Instance->UdpIo->Protocol.Udp6;\r
+\r
+ return Udp6->Poll (Udp6);\r
+}\r
--- /dev/null
+/** @file\r
+ Mtftp6 internal data structure and definition declaration.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved. <BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_IMPL_H__\r
+#define __EFI_MTFTP6_IMPL_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Udp6.h>\r
+#include <Protocol/Mtftp6.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/NetLib.h>\r
+\r
+typedef struct _MTFTP6_SERVICE MTFTP6_SERVICE;\r
+typedef struct _MTFTP6_INSTANCE MTFTP6_INSTANCE;\r
+\r
+#include "Mtftp6Driver.h"\r
+#include "Mtftp6Option.h"\r
+#include "Mtftp6Support.h"\r
+\r
+#define MTFTP6_SERVICE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'S')\r
+#define MTFTP6_INSTANCE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'I')\r
+\r
+#define MTFTP6_DEFAULT_SERVER_CMD_PORT 69\r
+#define MTFTP6_DEFAULT_TIMEOUT 3\r
+#define MTFTP6_GET_MAPPING_TIMEOUT 3\r
+#define MTFTP6_DEFAULT_MAX_RETRY 5\r
+#define MTFTP6_DEFAULT_BLK_SIZE 512\r
+#define MTFTP6_TICK_PER_SECOND 10000000U\r
+\r
+#define MTFTP6_SERVICE_FROM_THIS(a) CR (a, MTFTP6_SERVICE, ServiceBinding, MTFTP6_SERVICE_SIGNATURE)\r
+#define MTFTP6_INSTANCE_FROM_THIS(a) CR (a, MTFTP6_INSTANCE, Mtftp6, MTFTP6_INSTANCE_SIGNATURE)\r
+\r
+extern EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate;\r
+\r
+typedef struct _MTFTP6_GETINFO_CONTEXT{\r
+ EFI_MTFTP6_PACKET **Packet;\r
+ UINT32 *PacketLen;\r
+ EFI_STATUS Status;\r
+} MTFTP6_GETINFO_CONTEXT;\r
+\r
+//\r
+// Control block for MTFTP6 instance, it's per configuration data.\r
+//\r
+struct _MTFTP6_INSTANCE {\r
+ UINT32 Signature;\r
+ EFI_HANDLE Handle;\r
+ LIST_ENTRY Link;\r
+ EFI_MTFTP6_PROTOCOL Mtftp6;\r
+ MTFTP6_SERVICE *Service;\r
+ EFI_MTFTP6_CONFIG_DATA *Config;\r
+\r
+ EFI_MTFTP6_TOKEN *Token;\r
+ MTFTP6_EXT_OPTION_INFO ExtInfo;\r
+\r
+ UINT16 BlkSize;\r
+ UINT16 LastBlk;\r
+ LIST_ENTRY BlkList;\r
+\r
+ EFI_IPv6_ADDRESS ServerIp;\r
+ UINT16 ServerCmdPort;\r
+ UINT16 ServerDataPort;\r
+ UDP_IO *UdpIo;\r
+\r
+ EFI_IPv6_ADDRESS McastIp;\r
+ UINT16 McastPort;\r
+ UDP_IO *McastUdpIo;\r
+\r
+ NET_BUF *LastPacket;\r
+ UINT32 CurRetry;\r
+ UINT32 MaxRetry;\r
+ UINT32 PacketToLive;\r
+ UINT32 Timeout;\r
+\r
+ EFI_TPL OldTpl;\r
+ BOOLEAN IsTransmitted;\r
+ BOOLEAN IsMaster;\r
+ BOOLEAN InDestory;\r
+};\r
+\r
+//\r
+// Control block for MTFTP6 service, it's per Nic handle.\r
+//\r
+struct _MTFTP6_SERVICE {\r
+ UINT32 Signature;\r
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;\r
+ EFI_HANDLE Controller;\r
+ EFI_HANDLE Image;\r
+\r
+ UINT16 ChildrenNum;\r
+ LIST_ENTRY Children;\r
+ //\r
+ // It is used to be as internal calculagraph for all instances.\r
+ //\r
+ EFI_EVENT Timer;\r
+ //\r
+ // It is used to maintain the parent-child relationship between\r
+ // mtftp driver and udp driver.\r
+ //\r
+ UDP_IO *DummyUdpIo;\r
+ BOOLEAN InDestory;\r
+};\r
+\r
+/**\r
+ Returns the current operating mode data for the MTFTP6 instance.\r
+\r
+ The GetModeData() function returns the current operating mode and\r
+ cached data packet for the MTFTP6 instance.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode\r
+ data is returned.\r
+\r
+ @retval EFI_SUCCESS The configuration data was returned successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.\r
+ @retval EFI_INVALID_PARAMETER This is NULL, or ModeData is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetModeData (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ OUT EFI_MTFTP6_MODE_DATA *ModeData\r
+ );\r
+\r
+/**\r
+ Initializes, changes, or resets the default operational setting for\r
+ this EFI MTFTPv6 Protocol driver instance.\r
+\r
+ The Configure() function is used to set and change the configuration\r
+ data for this EFI MTFTPv6 Protocol driver instance. The configuration\r
+ data can be reset to startup defaults by calling Configure() with\r
+ MtftpConfigData set to NULL. Whenever the instance is reset, any\r
+ pending operation is aborted. By changing the EFI MTFTPv6 Protocol\r
+ driver instance configuration data, the client can connect to\r
+ different MTFTPv6 servers. The configuration parameters in\r
+ MtftpConfigData are used as the default parameters in later MTFTPv6\r
+ operations and can be overridden in later operations.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] MtftpConfigData Pointer to the configuration data structure.\r
+\r
+ @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully.\r
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:\r
+ - This is NULL.\r
+ - MtftpConfigData.StationIp is neither zero nor one\r
+ of the configured IP addresses in the underlying IPv6 driver.\r
+ - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there\r
+ is some MTFTP background operation in progress.\r
+ - MtftpCofigData.LocalPort is already in use.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be\r
+ allocated.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI\r
+ MTFTPv6 Protocol driver instance is not configured.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Configure (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Get the information of the download from the server.\r
+\r
+ The GetInfo() function assembles an MTFTPv6 request packet\r
+ with options, sends it to the MTFTPv6 server, and may return\r
+ an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries\r
+ occur only if no response packets are received from the MTFTPv6\r
+ server before the timeout expires.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the\r
+ default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()\r
+ function are used.\r
+ @param[in] Filename Pointer to ASCIIZ file name string.\r
+ @param[in] ModeStr Pointer to ASCIIZ mode string. If NULL, octet will be used\r
+ @param[in] OptionCount Number of option/value string pairs in OptionList.\r
+ @param[in] OptionList Pointer to array of option/value string pairs. Ignored if\r
+ OptionCount is zero.\r
+ @param[out] PacketLength The number of bytes in the returned packet.\r
+ @param[out] Packet The pointer to the received packet. This buffer must be freed by\r
+ the caller.\r
+\r
+ @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Filename is NULL.\r
+ - OptionCount is not zero and OptionList is NULL.\r
+ - One or more options in OptionList have wrong format.\r
+ - PacketLength is NULL.\r
+ - OverrideData.ServerIp is not a valid unicast IPv6 address.\r
+ @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by\r
+ this implementation.\r
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet.\r
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received, and the Packet is set to NULL.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received, and the Packet is set to NULL.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received, and the Packet is set to NULL.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received, and the Packet is set to NULL.\r
+ @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received, and the Packet is set to NULL.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet.\r
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6GetInfo (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL,\r
+ IN UINT8 *Filename,\r
+ IN UINT8 *ModeStr OPTIONAL,\r
+ IN UINT8 OptionCount,\r
+ IN EFI_MTFTP6_OPTION *OptionList OPTIONAL,\r
+ OUT UINT32 *PacketLength,\r
+ OUT EFI_MTFTP6_PACKET **Packet OPTIONAL\r
+ );\r
+\r
+/**\r
+ Parse the options in an MTFTPv6 OACK packet.\r
+\r
+ The ParseOptions() function parses the option fields in an MTFTPv6 OACK\r
+ packet and returns the number of options that were found, and optionally,\r
+ a list of pointers to the options in the packet. If one or more of the\r
+ option fields are not valid, then EFI_PROTOCOL_ERROR is returned and\r
+ *OptionCount and *OptionList stop at the last valid option.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] PacketLen Length of the OACK packet to be parsed.\r
+ @param[in] Packet Pointer to the OACK packet to be parsed.\r
+ @param[out] OptionCount Pointer to the number of options in the following OptionList.\r
+ @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the\r
+ OptionList points to the corresponding MTFTP option buffer\r
+ in the Packet. Call the EFI Boot Service FreePool() to\r
+ release the OptionList if the options in this OptionList\r
+ are not needed any more.\r
+\r
+ @retval EFI_SUCCESS The OACK packet was valid and the OptionCount, and\r
+ OptionList parameters have been updated.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - PacketLen is 0.\r
+ - Packet is NULL or Packet is not a valid MTFTPv6 packet.\r
+ - OptionCount is NULL.\r
+ @retval EFI_NOT_FOUND No options were found in the OACK packet.\r
+ @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated.\r
+ @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ParseOptions (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN UINT32 PacketLen,\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ OUT UINT32 *OptionCount,\r
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL\r
+ );\r
+\r
+/**\r
+ Download a file from an MTFTPv6 server.\r
+\r
+ The ReadFile() function is used to initialize and start an MTFTPv6 download\r
+ process and optionally wait for completion. When the download operation\r
+ completes, whether successfully or not, the Token.Status field is updated\r
+ by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it\r
+ is not NULL.\r
+ Data can be downloaded from the MTFTPv6 server into either of the following\r
+ locations:\r
+ - A fixed buffer that is pointed to by Token.Buffer.\r
+ - A download service function that is pointed to by Token.CheckPacket.\r
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+ will be called first. If the call is successful, the packet will be stored\r
+ in Token.Buffer.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the token structure to provide the parameters that are\r
+ used in this operation.\r
+\r
+ @retval EFI_SUCCESS The data file has been transferred successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the\r
+ downloaded data in downloading process.\r
+ Note: It does not match the UEFI 2.3 Specification.\r
+ @retval EFI_ABORTED Current operation is aborted by user.\r
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received.\r
+ Note: It is not defined in the UEFI 2.3 Specification.\r
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received.\r
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.\r
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadFile (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Send a file to an MTFTPv6 server.\r
+\r
+ The WriteFile() function is used to initialize an uploading operation\r
+ with the given option list, and optionally, wait for completion. If one\r
+ or more of the options is not supported by the server, the unsupported\r
+ options are ignored and a standard TFTP process starts instead. When\r
+ the upload process completes, whether successfully or not, Token.Event\r
+ is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.\r
+ The caller can supply the data to be uploaded in the following two modes:\r
+ - Through the user-provided buffer.\r
+ - Through a callback function.\r
+ With the user-provided buffer, the Token.BufferSize field indicates\r
+ the length of the buffer, and the driver will upload the data in the\r
+ buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver\r
+ will call this callback function to get more data from the user to upload.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the token structure to provide the parameters that are\r
+ used in this operation.\r
+\r
+ @retval EFI_SUCCESS The upload session has started.\r
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Filename is NULL.\r
+ - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+ - One or more options in Token.OptionList have wrong format.\r
+ - Token.Buffer and Token.PacketNeeded are both NULL.\r
+ - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not\r
+ supported by this implementation.\r
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6WriteFile (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Download a data file directory from an MTFTPv6 server.\r
+\r
+ The ReadDirectory() function is used to return a list of files on the\r
+ MTFTPv6 server that are logically (or operationally) related to\r
+ Token.Filename. The directory request packet that is sent to the server\r
+ is built with the option list that was provided by caller, if present.\r
+ The file information that the server returns is put into either of\r
+ the following locations:\r
+ - A fixed buffer that is pointed to by Token.Buffer.\r
+ - A download service function that is pointed to by Token.CheckPacket.\r
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket\r
+ will be called first. If the call is successful, the packet will be stored\r
+ in Token.Buffer.\r
+\r
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the token structure to provide the parameters that are\r
+ used in this operation.\r
+\r
+ @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded.\r
+ @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token.Filename is NULL.\r
+ - Token.OptionCount is not zero and Token.OptionList is NULL.\r
+ - One or more options in Token.OptionList have wrong format.\r
+ - Token.Buffer and Token.CheckPacket are both NULL.\r
+ - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.\r
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not\r
+ supported by this implementation.\r
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.\r
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6ReadDirectory (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Polls for incoming data packets and processes outgoing data packets.\r
+\r
+ The Poll() function can be used by network drivers and applications\r
+ to increase the rate that data packets are moved between the\r
+ communications device and the transmit and receive queues.In some\r
+ systems, the periodic timer event in the managed network driver may\r
+ not poll the underlying communications device fast enough to transmit\r
+ and/or receive all data packets without missing incoming packets or\r
+ dropping outgoing packets. Drivers and applications that are\r
+ experiencing packet loss should try calling the Poll() function\r
+ more often.\r
+\r
+ @param[in] This The MTFTP6 protocol instance.\r
+\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.\r
+ Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiMtftp6Poll (\r
+ IN EFI_MTFTP6_PROTOCOL *This\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Mtftp6 option parse functions implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM] = {\r
+ "blksize",\r
+ "timeout",\r
+ "tsize",\r
+ "multicast"\r
+};\r
+\r
+\r
+/**\r
+ Parse the NULL terminated ASCII string of multicast option.\r
+\r
+ @param[in] Str The pointer to the Ascii string of multicast option.\r
+ @param[in] ExtInfo The pointer to the option information to be filled.\r
+\r
+ @retval EFI_SUCCESS Parse the multicast option successfully.\r
+ @retval EFI_INVALID_PARAMETER The string is malformatted.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of\r
+ resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseMcastOption (\r
+ IN UINT8 *Str,\r
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 Num;\r
+ CHAR8 *Ip6Str;\r
+ CHAR8 *TempStr;\r
+\r
+ //\r
+ // The multicast option is formated like "addr,port,mc"\r
+ // The server can also omit the ip and port, use ",,1"\r
+ //\r
+ if (*Str == ',') {\r
+\r
+ ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS));\r
+ } else {\r
+\r
+ Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str);\r
+ if (Ip6Str == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // The IPv6 address locates before comma in the input Str.\r
+ //\r
+ TempStr = Ip6Str;\r
+ while ((*TempStr != '\0') && (*TempStr != ',')) {\r
+ TempStr++;\r
+ }\r
+\r
+ *TempStr = '\0';\r
+\r
+ Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp);\r
+ FreePool (Ip6Str);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ while ((*Str != '\0') && (*Str != ',')) {\r
+ Str++;\r
+ }\r
+ }\r
+\r
+ if (*Str != ',') {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Str++;\r
+\r
+ //\r
+ // Convert the port setting. the server can send us a port number or\r
+ // empty string. such as the port in ",,1"\r
+ //\r
+ if (*Str == ',') {\r
+\r
+ ExtInfo->McastPort = 0;\r
+ } else {\r
+\r
+ Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);\r
+\r
+ if (Num > 65535) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ExtInfo->McastPort = (UINT16) Num;\r
+\r
+ while (NET_IS_DIGIT (*Str)) {\r
+ Str++;\r
+ }\r
+ }\r
+\r
+ if (*Str != ',') {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Str++;\r
+\r
+ //\r
+ // Check the master/slave setting, 1 for master, 0 for slave.\r
+ //\r
+ Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);\r
+\r
+ if (Num != 0 && Num != 1) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ExtInfo->IsMaster = (BOOLEAN) (Num == 1);\r
+\r
+ while (NET_IS_DIGIT (*Str)) {\r
+ Str++;\r
+ }\r
+\r
+ if (*Str != '\0') {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Parse the MTFTP6 extesion options.\r
+\r
+ @param[in] Options The pointer to the extension options list.\r
+ @param[in] Count The num of the extension options.\r
+ @param[in] IsRequest If FALSE, the extension options is included\r
+ by a request packet.\r
+ @param[in] ExtInfo The pointer to the option information to be filled.\r
+\r
+ @retval EFI_SUCCESS Parse the multicast option successfully.\r
+ @retval EFI_INVALID_PARAMETER There is one option is malformatted at least.\r
+ @retval EFI_UNSUPPORTED There is one option is not supported at least.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseExtensionOption (\r
+ IN EFI_MTFTP6_OPTION *Options,\r
+ IN UINT32 Count,\r
+ IN BOOLEAN IsRequest,\r
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_MTFTP6_OPTION *Opt;\r
+ UINT32 Index;\r
+ UINT32 Value;\r
+\r
+ ExtInfo->BitMap = 0;\r
+\r
+ for (Index = 0; Index < Count; Index++) {\r
+\r
+ Opt = Options + Index;\r
+\r
+ if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) {\r
+ //\r
+ // block size option, valid value is between [8, 65464]\r
+ //\r
+ Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);\r
+\r
+ if ((Value < 8) || (Value > 65464)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ExtInfo->BlkSize = (UINT16) Value;\r
+ ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT;\r
+\r
+ } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) {\r
+ //\r
+ // timeout option, valid value is between [1, 255]\r
+ //\r
+ Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);\r
+\r
+ if (Value < 1 || Value > 255) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ExtInfo->Timeout = (UINT8) Value;\r
+ ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT;\r
+\r
+ } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) {\r
+ //\r
+ // tsize option, the biggest transfer supported is 4GB with block size option\r
+ //\r
+ ExtInfo->Tsize = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);\r
+ ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT;\r
+\r
+ } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) {\r
+ //\r
+ // Multicast option, if it is a request, the value must be a zero string,\r
+ // otherwise, it must be like "addr,port,mc" string, mc indicates master.\r
+ //\r
+ if (!IsRequest) {\r
+\r
+ Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else if (*(Opt->ValueStr) != '\0') {\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT;\r
+\r
+ } else if (IsRequest) {\r
+ //\r
+ // If it's a request, unsupported; else if it's a reply, ignore.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Go through the packet to fill the options array with the start\r
+ addresses of each MTFTP option name/value pair.\r
+\r
+ @param[in] Packet The packet to be checked.\r
+ @param[in] PacketLen The length of the packet.\r
+ @param[in, out] Count The num of the Options on input.\r
+ The actual one on output.\r
+ @param[in] Options The option array to be filled.\r
+ It is optional.\r
+\r
+ @retval EFI_SUCCESS The packet has been parsed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is malformatted.\r
+ @retval EFI_BUFFER_TOO_SMALL The Options array is too small.\r
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParsePacketOption (\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 PacketLen,\r
+ IN OUT UINT32 *Count,\r
+ IN EFI_MTFTP6_OPTION *Options OPTIONAL\r
+ )\r
+{\r
+ UINT8 *Cur;\r
+ UINT8 *Last;\r
+ UINT8 Num;\r
+ UINT8 *Name;\r
+ UINT8 *Value;\r
+\r
+ Num = 0;\r
+ Cur = (UINT8 *) Packet + MTFTP6_OPCODE_LEN;\r
+ Last = (UINT8 *) Packet + PacketLen - 1;\r
+\r
+ //\r
+ // process option name and value pairs.\r
+ // The last byte is always zero.\r
+ //\r
+ while (Cur < Last) {\r
+ Name = Cur;\r
+\r
+ while (*Cur != 0) {\r
+ Cur++;\r
+ }\r
+\r
+ if (Cur == Last) {\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ Value = ++Cur;\r
+\r
+ while (*Cur != 0) {\r
+ Cur++;\r
+ }\r
+\r
+ Num++;\r
+\r
+ if (Options != NULL && Num <= *Count) {\r
+ Options[Num - 1].OptionStr = Name;\r
+ Options[Num - 1].ValueStr = Value;\r
+ }\r
+\r
+ Cur++;\r
+ }\r
+\r
+ //\r
+ // Return buffer too small if the buffer passed-in isn't enough.\r
+ //\r
+ if (*Count < Num || Options == NULL) {\r
+ *Count = Num;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ *Count = Num;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Go through the packet, generate option list array and fill it\r
+ by the result of parse options.\r
+\r
+ @param[in] Packet The packet to be checked.\r
+ @param[in] PacketLen The length of the packet.\r
+ @param[in, out] OptionCount The num of the Options on input.\r
+ The actual one on output.\r
+ @param[out] OptionList The option list array to be generated\r
+ and filled. It is optional.\r
+\r
+ @retval EFI_SUCCESS The packet has been parsed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is malformatted.\r
+ @retval EFI_PROTOCOL_ERROR There is one option is malformatted at least.\r
+ @retval EFI_NOT_FOUND The packet has no options.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array.\r
+ @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseStart (\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 PacketLen,\r
+ IN OUT UINT32 *OptionCount,\r
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (PacketLen == 0 || Packet == NULL || OptionCount == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *OptionCount = 0;\r
+\r
+ if (OptionList != NULL) {\r
+ *OptionList = NULL;\r
+ }\r
+\r
+ if (NTOHS (Packet->OpCode) != EFI_MTFTP6_OPCODE_OACK) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // The last byte must be zero to terminate the options.\r
+ //\r
+ if (*((UINT8 *) Packet + PacketLen - 1) != 0) {\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ //\r
+ // Parse packet with NULL buffer for the first time to get the number\r
+ // of options in the packet.\r
+ //\r
+ Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, NULL);\r
+\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Return not found if there is no option parsed.\r
+ //\r
+ if (*OptionCount == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Only need parse out the number of options.\r
+ //\r
+ if (OptionList == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Allocate the buffer according to the option number parsed before.\r
+ //\r
+ *OptionList = AllocateZeroPool (*OptionCount * sizeof (EFI_MTFTP6_OPTION));\r
+\r
+ if (*OptionList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Parse packet with allocated buffer for the second time to fill the pointer array\r
+ // of the options in the packet.\r
+ //\r
+ Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, *OptionList);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ Mtftp6 option parse functions declaration.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_OPTION_H__\r
+#define __EFI_MTFTP6_OPTION_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+\r
+#include <Library/NetLib.h>\r
+#include <Library/UdpIoLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+\r
+#define MTFTP6_SUPPORTED_OPTIONS_NUM 4\r
+#define MTFTP6_OPCODE_LEN 2\r
+#define MTFTP6_ERRCODE_LEN 2\r
+#define MTFTP6_BLKNO_LEN 2\r
+#define MTFTP6_DATA_HEAD_LEN 4\r
+\r
+//\r
+// The bit map definition for Mtftp6 extension options.\r
+//\r
+#define MTFTP6_OPT_BLKSIZE_BIT 0x01\r
+#define MTFTP6_OPT_TIMEOUT_BIT 0x02\r
+#define MTFTP6_OPT_TSIZE_BIT 0x04\r
+#define MTFTP6_OPT_MCAST_BIT 0x08\r
+\r
+extern CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM];\r
+\r
+typedef struct {\r
+ UINT16 BlkSize;\r
+ UINT8 Timeout;\r
+ UINT32 Tsize;\r
+ EFI_IPv6_ADDRESS McastIp;\r
+ UINT16 McastPort;\r
+ BOOLEAN IsMaster;\r
+ UINT32 BitMap;\r
+} MTFTP6_EXT_OPTION_INFO;\r
+\r
+/**\r
+ Parse the Ascii string of multi-cast option.\r
+\r
+ @param[in] Str The pointer to the Ascii string of multi-cast option.\r
+ @param[in] ExtInfo The pointer to the option information to be filled.\r
+\r
+ @retval EFI_SUCCESS Parse the multicast option successfully.\r
+ @retval EFI_INVALID_PARAMETER The string is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseMcastOption (\r
+ IN UINT8 *Str,\r
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo\r
+ );\r
+\r
+\r
+/**\r
+ Parse the MTFTP6 extesion options.\r
+\r
+ @param[in] Options The pointer to the extension options list.\r
+ @param[in] Count The num of the extension options.\r
+ @param[in] IsRequest If FALSE, the extension options is included\r
+ by a request packet.\r
+ @param[in] ExtInfo The pointer to the option information to be filled.\r
+\r
+ @retval EFI_SUCCESS Parse the multi-cast option successfully.\r
+ @retval EFI_INVALID_PARAMETER An option is malformatted.\r
+ @retval EFI_UNSUPPORTED An option is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseExtensionOption (\r
+ IN EFI_MTFTP6_OPTION *Options,\r
+ IN UINT32 Count,\r
+ IN BOOLEAN IsRequest,\r
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo\r
+ );\r
+\r
+\r
+/**\r
+ Go through the packet to fill the options array with the start\r
+ addresses of each MTFTP option name/value pair.\r
+\r
+ @param[in] Packet The packet to be checked.\r
+ @param[in] PacketLen The length of the packet.\r
+ @param[in, out] Count The num of the Options on input.\r
+ The actual one on output.\r
+ @param[in] Options The option array to be filled\r
+ it's optional.\r
+\r
+ @retval EFI_SUCCESS The packet has been parsed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is malformatted\r
+ @retval EFI_BUFFER_TOO_SMALL The Options array is too small\r
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParsePacketOption (\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 PacketLen,\r
+ IN OUT UINT32 *Count,\r
+ IN EFI_MTFTP6_OPTION *Options OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ Go through the packet, generate option list array and fill it\r
+ by the result of parse options.\r
+\r
+ @param[in] Packet The packet to be checked.\r
+ @param[in] PacketLen The length of the packet.\r
+ @param[in, out] OptionCount The num of the Options on input.\r
+ The actual one on output.\r
+ @param[out] OptionList The option list array to be generated\r
+ and filled. It is optional.\r
+\r
+ @retval EFI_SUCCESS The packet has been parsed successfully.\r
+ @retval EFI_INVALID_PARAMETER The packet is malformatted.\r
+ @retval EFI_PROTOCOL_ERROR An option is malformatted.\r
+ @retval EFI_NOT_FOUND The packet has no options.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array.\r
+ @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ParseStart (\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 PacketLen,\r
+ IN OUT UINT32 *OptionCount,\r
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Mtftp6 Rrq process functions implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+/**\r
+ Build and send a ACK packet for download.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] BlockNum The block number to be acked.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.\r
+ @retval EFI_SUCCESS The ACK has been sent.\r
+ @retval Others Failed to send the ACK.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqSendAck (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 BlockNum\r
+ )\r
+{\r
+ EFI_MTFTP6_PACKET *Ack;\r
+ NET_BUF *Packet;\r
+\r
+ //\r
+ // Allocate net buffer to create ack packet.\r
+ //\r
+ Packet = NetbufAlloc (sizeof (EFI_MTFTP6_ACK_HEADER));\r
+\r
+ if (Packet == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Ack = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (\r
+ Packet,\r
+ sizeof (EFI_MTFTP6_ACK_HEADER),\r
+ FALSE\r
+ );\r
+ ASSERT (Ack != NULL);\r
+\r
+ Ack->Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK);\r
+ Ack->Ack.Block[0] = HTONS (BlockNum);\r
+\r
+ //\r
+ // Reset current retry count of the instance.\r
+ //\r
+ Instance->CurRetry = 0;\r
+\r
+ return Mtftp6TransmitPacket (Instance, Packet);\r
+}\r
+\r
+\r
+/**\r
+ Deliver the received data block to the user, which can be saved\r
+ in the user provide buffer or through the CheckPacket callback.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Packet The pointer to the received packet.\r
+ @param[in] Len The packet length.\r
+ @param[out] UdpPacket The net buf of the received packet.\r
+\r
+ @retval EFI_SUCCESS The data was saved successfully.\r
+ @retval EFI_ABORTED The user tells to abort by return an error through\r
+ CheckPacket.\r
+ @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small, and buffer length is\r
+ updated to the actual buffer size needed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqSaveBlock (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 Len,\r
+ OUT NET_BUF **UdpPacket\r
+ )\r
+{\r
+ EFI_MTFTP6_TOKEN *Token;\r
+ EFI_STATUS Status;\r
+ UINT16 Block;\r
+ UINT64 Start;\r
+ UINT32 DataLen;\r
+ UINT64 TotalBlock;\r
+ BOOLEAN Completed;\r
+\r
+ Completed = FALSE;\r
+ Token = Instance->Token;\r
+ Block = NTOHS (Packet->Data.Block);\r
+ DataLen = Len - MTFTP6_DATA_HEAD_LEN;\r
+\r
+ //\r
+ // This is the last block, save the block num\r
+ //\r
+ if (DataLen < Instance->BlkSize) {\r
+ Completed = TRUE;\r
+ Instance->LastBlk = Block;\r
+ Mtftp6SetLastBlockNum (&Instance->BlkList, Block);\r
+ }\r
+\r
+ //\r
+ // Remove this block number from the file hole. If Mtftp6RemoveBlockNum\r
+ // returns EFI_NOT_FOUND, the block has been saved, don't save it again.\r
+ // Note that : For bigger files, allowing the block counter to roll over\r
+ // to accept transfers of unlimited size. So TotalBlock is memorised as\r
+ // continuous block counter.\r
+ //\r
+ Status = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &TotalBlock);\r
+\r
+ if (Status == EFI_NOT_FOUND) {\r
+ return EFI_SUCCESS;\r
+ } else if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Token->CheckPacket != NULL) {\r
+ //\r
+ // Callback to the check packet routine with the received packet.\r
+ //\r
+ Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the Udp6Io might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if user aborted the current session.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+ (UINT8 *) "User aborted download"\r
+ );\r
+\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+\r
+ if (Token->Buffer != NULL) {\r
+\r
+ Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize);\r
+ if (Start + DataLen <= Token->BufferSize) {\r
+ CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);\r
+ //\r
+ // Update the file size when received the last block\r
+ //\r
+ if ((Instance->LastBlk == Block) && Completed) {\r
+ Token->BufferSize = Start + DataLen;\r
+ }\r
+ } else if (Instance->LastBlk != 0) {\r
+ //\r
+ // Don't save the data if the buffer is too small, return\r
+ // EFI_BUFFER_TOO_SMALL if received the last packet. This\r
+ // will give a accurate file length.\r
+ //\r
+ Token->BufferSize = Start + DataLen;\r
+\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if no enough buffer.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_DISK_FULL,\r
+ (UINT8 *) "User provided memory block is too small"\r
+ );\r
+\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Process the received data packets. It will save the block\r
+ then send back an ACK if it is active.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Packet The pointer to the received packet.\r
+ @param[in] Len The length of the packet.\r
+ @param[out] UdpPacket The net buf of received packet.\r
+ @param[out] IsCompleted If TRUE, the download has been completed.\r
+ Otherwise, the download has not been completed.\r
+\r
+ @retval EFI_SUCCESS The data packet was successfully processed.\r
+ @retval EFI_ABORTED The download was aborted by the user.\r
+ @retval EFI_BUFFER_TOO_SMALL The user-provided buffer is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqHandleData (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 Len,\r
+ OUT NET_BUF **UdpPacket,\r
+ OUT BOOLEAN *IsCompleted\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT16 BlockNum;\r
+ INTN Expected;\r
+\r
+ *IsCompleted = FALSE;\r
+ BlockNum = NTOHS (Packet->Data.Block);\r
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+ ASSERT (Expected >= 0);\r
+\r
+ //\r
+ // If we are active and received an unexpected packet, retransmit\r
+ // the last ACK then restart receiving. If we are passive, save\r
+ // the block.\r
+ //\r
+ if (Instance->IsMaster && (Expected != BlockNum)) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+\r
+ Mtftp6TransmitPacket (Instance, Instance->LastPacket);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Reset the passive client's timer whenever it received a valid data packet.\r
+ //\r
+ if (!Instance->IsMaster) {\r
+ Instance->PacketToLive = Instance->Timeout * 2;\r
+ }\r
+\r
+ //\r
+ // Check whether we have received all the blocks. Send the ACK if we\r
+ // are active (unicast client or master client for multicast download).\r
+ // If we have received all the blocks, send an ACK even if we are passive\r
+ // to tell the server that we are done.\r
+ //\r
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+ if (Instance->IsMaster || Expected < 0) {\r
+ if (Expected < 0) {\r
+ //\r
+ // If we are passive client, then the just received Block maybe\r
+ // isn't the last block. We need to send an ACK to the last block\r
+ // to inform the server that we are done. If we are active client,\r
+ // the Block == Instance->LastBlock.\r
+ //\r
+ BlockNum = Instance->LastBlk;\r
+ *IsCompleted = TRUE;\r
+\r
+ } else {\r
+ BlockNum = (UINT16) (Expected - 1);\r
+ }\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+\r
+ Mtftp6RrqSendAck (Instance, BlockNum);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Validate whether the options received in the server's OACK packet is valid.\r
+ The options are valid only if:\r
+ 1. The server doesn't include options not requested by us.\r
+ 2. The server can only use smaller blksize than that is requested.\r
+ 3. The server can only use the same timeout as requested.\r
+ 4. The server doesn't change its multicast channel.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] ReplyInfo The pointer to options information in reply packet.\r
+ @param[in] RequestInfo The pointer to requested options info.\r
+\r
+ @retval TRUE If the option in the OACK is valid.\r
+ @retval FALSE If the option is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+Mtftp6RrqOackValid (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN MTFTP6_EXT_OPTION_INFO *ReplyInfo,\r
+ IN MTFTP6_EXT_OPTION_INFO *RequestInfo\r
+ )\r
+{\r
+ //\r
+ // It is invalid for server to return options we don't request\r
+ //\r
+ if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Server can only specify a smaller block size to be used and\r
+ // return the timeout matches that requested.\r
+ //\r
+ if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||\r
+ (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))\r
+ ) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // The server can send ",,master" to client to change its master\r
+ // setting. But if it use the specific multicast channel, it can't\r
+ // change the setting.\r
+ //\r
+ if (((ReplyInfo->BitMap & MTFTP6_OPT_MCAST_BIT) != 0) && !NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {\r
+\r
+ if (!NetIp6IsUnspecifiedAddr (&ReplyInfo->McastIp) && CompareMem (\r
+ &ReplyInfo->McastIp,\r
+ &Instance->McastIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ ) != 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((ReplyInfo->McastPort != 0) && (ReplyInfo->McastPort != Instance->McastPort)) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Configure Udp6Io to receive a packet from a multicast address.\r
+\r
+ @param[in] McastIo The pointer to the mcast Udp6Io.\r
+ @param[in] Context The pointer to the context.\r
+\r
+ @retval EFI_SUCCESS The mcast Udp6Io was successfully configured.\r
+ @retval Others Failed to configure the Udp6Io.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6RrqConfigMcastUdpIo (\r
+ IN UDP_IO *McastIo,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+ EFI_UDP6_CONFIG_DATA *Udp6Cfg;\r
+ EFI_IPv6_ADDRESS Group;\r
+ MTFTP6_INSTANCE *Instance;\r
+\r
+ Udp6 = McastIo->Protocol.Udp6;\r
+ Udp6Cfg = &(McastIo->Config.Udp6);\r
+ Instance = (MTFTP6_INSTANCE *) Context;\r
+\r
+ //\r
+ // Set the configure data for the mcast Udp6Io.\r
+ //\r
+ ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+ Udp6Cfg->AcceptPromiscuous = FALSE;\r
+ Udp6Cfg->AcceptAnyPort = FALSE;\r
+ Udp6Cfg->AllowDuplicatePort = FALSE;\r
+ Udp6Cfg->TrafficClass = 0;\r
+ Udp6Cfg->HopLimit = 128;\r
+ Udp6Cfg->ReceiveTimeout = 0;\r
+ Udp6Cfg->TransmitTimeout = 0;\r
+ Udp6Cfg->StationPort = Instance->McastPort;\r
+ Udp6Cfg->RemotePort = 0;\r
+\r
+ CopyMem (\r
+ &Udp6Cfg->RemoteAddress,\r
+ &Instance->ServerIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+\r
+ //\r
+ // Configure the mcast Udp6Io.\r
+ //\r
+ Status = Udp6->Configure (Udp6, Udp6Cfg);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Join the multicast group\r
+ //\r
+ CopyMem (&Group, &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ return Udp6->Groups (Udp6, TRUE, &Group);\r
+}\r
+\r
+\r
+/**\r
+ Process the OACK packet for Rrq.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Packet The pointer to the received packet.\r
+ @param[in] Len The length of the packet.\r
+ @param[out] UdpPacket The net buf of received packet.\r
+ @param[out] IsCompleted If TRUE, the download has been completed.\r
+ Otherwise, the download has not been completed.\r
+\r
+ @retval EFI_DEVICE_ERROR Failed to create/start a multicast Udp6 child.\r
+ @retval EFI_TFTP_ERROR An error happened during the process.\r
+ @retval EFI_SUCCESS The OACK packet successfully processed.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqHandleOack (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 Len,\r
+ OUT NET_BUF **UdpPacket,\r
+ OUT BOOLEAN *IsCompleted\r
+ )\r
+{\r
+ EFI_MTFTP6_OPTION *Options;\r
+ UINT32 Count;\r
+ MTFTP6_EXT_OPTION_INFO ExtInfo;\r
+ EFI_STATUS Status;\r
+ INTN Expected;\r
+\r
+ *IsCompleted = FALSE;\r
+\r
+ //\r
+ // If already started the master download, don't change the\r
+ // setting. Master download always succeeds.\r
+ //\r
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+ ASSERT (Expected != -1);\r
+\r
+ if (Instance->IsMaster && Expected != 1) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
+\r
+ //\r
+ // Parse the options in the packet.\r
+ //\r
+ Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Parse the extensive options in the packet.\r
+ //\r
+ Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);\r
+\r
+ if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) {\r
+ //\r
+ // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.\r
+ //\r
+ if (Status != EFI_OUT_OF_RESOURCES) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if invalid packet.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+ (UINT8 *) "Mal-formated OACK packet"\r
+ );\r
+ }\r
+\r
+ return EFI_TFTP_ERROR;\r
+ }\r
+\r
+ if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) {\r
+\r
+ //\r
+ // Save the multicast info. Always update the Master, only update the\r
+ // multicast IP address, block size, timeoute at the first time. If IP\r
+ // address is updated, create a UDP child to receive the multicast.\r
+ //\r
+ Instance->IsMaster = ExtInfo.IsMaster;\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {\r
+ if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if invalid multi-cast setting.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+ (UINT8 *) "Illegal multicast setting"\r
+ );\r
+\r
+ return EFI_TFTP_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create a UDP child then start receive the multicast from it.\r
+ //\r
+ CopyMem (\r
+ &Instance->McastIp,\r
+ &ExtInfo.McastIp,\r
+ sizeof (EFI_IP_ADDRESS)\r
+ );\r
+\r
+ Instance->McastPort = ExtInfo.McastPort;\r
+ Instance->McastUdpIo = UdpIoCreateIo (\r
+ Instance->Service->Controller,\r
+ Instance->Service->Image,\r
+ Mtftp6RrqConfigMcastUdpIo,\r
+ UDP_IO_UDP6_VERSION,\r
+ Instance\r
+ );\r
+\r
+ if (Instance->McastUdpIo == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = UdpIoRecvDatagram (\r
+ Instance->McastUdpIo,\r
+ Mtftp6RrqInput,\r
+ Instance,\r
+ 0\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if failed to create Udp6Io to receive.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION,\r
+ (UINT8 *) "Failed to create socket to receive multicast packet"\r
+ );\r
+\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Update the parameters used.\r
+ //\r
+ if (ExtInfo.BlkSize != 0) {\r
+ Instance->BlkSize = ExtInfo.BlkSize;\r
+ }\r
+\r
+ if (ExtInfo.Timeout != 0) {\r
+ Instance->Timeout = ExtInfo.Timeout;\r
+ }\r
+ }\r
+\r
+ } else {\r
+\r
+ Instance->IsMaster = TRUE;\r
+\r
+ if (ExtInfo.BlkSize != 0) {\r
+ Instance->BlkSize = ExtInfo.BlkSize;\r
+ }\r
+\r
+ if (ExtInfo.Timeout != 0) {\r
+ Instance->Timeout = ExtInfo.Timeout;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send an ACK to (Expected - 1) which is 0 for unicast download,\r
+ // or tell the server we want to receive the Expected block.\r
+ //\r
+ return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1));\r
+}\r
+\r
+\r
+/**\r
+ The packet process callback for Mtftp6 download.\r
+\r
+ @param[in] UdpPacket The pointer to the packet received.\r
+ @param[in] UdpEpt The pointer to the Udp6 access point.\r
+ @param[in] IoStatus The status from Udp6 instance.\r
+ @param[in] Context The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6RrqInput (\r
+ IN NET_BUF *UdpPacket,\r
+ IN UDP_END_POINT *UdpEpt,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_MTFTP6_PACKET *Packet;\r
+ BOOLEAN IsCompleted;\r
+ BOOLEAN IsMcast;\r
+ EFI_STATUS Status;\r
+ UINT16 Opcode;\r
+ UINT32 TotalNum;\r
+ UINT32 Len;\r
+\r
+ Instance = (MTFTP6_INSTANCE *) Context;\r
+\r
+ NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);\r
+\r
+ Status = EFI_SUCCESS;\r
+ Packet = NULL;\r
+ IsCompleted = FALSE;\r
+ IsMcast = FALSE;\r
+ TotalNum = 0;\r
+\r
+ //\r
+ // Return error status if Udp6 instance failed to receive.\r
+ //\r
+ if (EFI_ERROR (IoStatus)) {\r
+ Status = IoStatus;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ ASSERT (UdpPacket != NULL);\r
+\r
+ if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Find the port this packet is from to restart receive correctly.\r
+ //\r
+ if (CompareMem (\r
+ Ip6Swap128 (&UdpEpt->LocalAddr.v6),\r
+ &Instance->McastIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ ) == 0) {\r
+ IsMcast = TRUE;\r
+ } else {\r
+ IsMcast = FALSE;\r
+ }\r
+\r
+ //\r
+ // Client send initial request to server's listening port. Server\r
+ // will select a UDP port to communicate with the client. The server\r
+ // is required to use the same port as RemotePort to multicast the\r
+ // data.\r
+ //\r
+ if (UdpEpt->RemotePort != Instance->ServerDataPort) {\r
+ if (Instance->ServerDataPort != 0) {\r
+ goto ON_EXIT;\r
+ } else {\r
+ //\r
+ // For the subsequent exchange of requests, reconfigure the udpio as\r
+ // (serverip, serverport, localip, localport).\r
+ // Ususally, the client set serverport as 0 to receive and reset it\r
+ // once the first packet arrives to send ack.\r
+ //\r
+ Instance->ServerDataPort = UdpEpt->RemotePort;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
+ //\r
+ Len = UdpPacket->TotalSize;\r
+ TotalNum = UdpPacket->BlockOpNum;\r
+\r
+ if (TotalNum > 1) {\r
+ Packet = AllocateZeroPool (Len);\r
+\r
+ if (Packet == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
+\r
+ } else {\r
+ Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
+ ASSERT (Packet != NULL);\r
+ }\r
+\r
+ Opcode = NTOHS (Packet->OpCode);\r
+\r
+ //\r
+ // Callback to the user's CheckPacket if provided. Abort the transmission\r
+ // if CheckPacket returns an EFI_ERROR code.\r
+ //\r
+ if ((Instance->Token->CheckPacket != NULL) &&\r
+ (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)\r
+ ) {\r
+\r
+ Status = Instance->Token->CheckPacket (\r
+ &Instance->Mtftp6,\r
+ Instance->Token,\r
+ (UINT16) Len,\r
+ Packet\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Send an error message to the server to inform it\r
+ //\r
+ if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (UdpPacket);\r
+ UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if user aborted the current session.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+ (UINT8 *) "User aborted the transfer"\r
+ );\r
+ }\r
+\r
+ Status = EFI_ABORTED;\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Switch the process routines by the operation code.\r
+ //\r
+ switch (Opcode) {\r
+ case EFI_MTFTP6_OPCODE_DATA:\r
+ if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Handle the data packet of Rrq.\r
+ //\r
+ Status = Mtftp6RrqHandleData (\r
+ Instance,\r
+ Packet,\r
+ Len,\r
+ &UdpPacket,\r
+ &IsCompleted\r
+ );\r
+ break;\r
+\r
+ case EFI_MTFTP6_OPCODE_OACK:\r
+ if (IsMcast || Len <= MTFTP6_OPCODE_LEN) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Handle the Oack packet of Rrq.\r
+ //\r
+ Status = Mtftp6RrqHandleOack (\r
+ Instance,\r
+ Packet,\r
+ Len,\r
+ &UdpPacket,\r
+ &IsCompleted\r
+ );\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Drop and return eror if received error message.\r
+ //\r
+ Status = EFI_TFTP_ERROR;\r
+ break;\r
+ }\r
+\r
+ON_EXIT:\r
+ //\r
+ // Free the resources, then if !EFI_ERROR (Status), restart the\r
+ // receive, otherwise end the session.\r
+ //\r
+ if (Packet != NULL && TotalNum > 1) {\r
+ FreePool (Packet);\r
+ }\r
+ if (UdpPacket != NULL) {\r
+ NetbufFree (UdpPacket);\r
+ }\r
+ if (!EFI_ERROR (Status) && !IsCompleted) {\r
+ if (IsMcast) {\r
+ Status = UdpIoRecvDatagram (\r
+ Instance->McastUdpIo,\r
+ Mtftp6RrqInput,\r
+ Instance,\r
+ 0\r
+ );\r
+ } else {\r
+ Status = UdpIoRecvDatagram (\r
+ Instance->UdpIo,\r
+ Mtftp6RrqInput,\r
+ Instance,\r
+ 0\r
+ );\r
+ }\r
+ }\r
+ //\r
+ // Clean up the current session if failed to continue.\r
+ //\r
+ if (EFI_ERROR (Status) || IsCompleted) {\r
+ Mtftp6OperationClean (Instance, Status);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Start the Mtftp6 instance to download. It first initializes some\r
+ of the internal states, then builds and sends an RRQ reqeuest packet.\r
+ Finally, it starts receive for the downloading.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Operation The operation code of current packet.\r
+\r
+ @retval EFI_SUCCESS The Mtftp6 is started to download.\r
+ @retval Others Failed to start to download.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqStart (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 Operation\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // The valid block number range are [1, 0xffff]. For example:\r
+ // the client sends an RRQ request to the server, the server\r
+ // transfers the DATA1 block. If option negoitation is ongoing,\r
+ // the server will send back an OACK, then client will send ACK0.\r
+ //\r
+ Status = Mtftp6InitBlockRange (&Instance->BlkList, 1, 0xffff);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Mtftp6SendRequest (Instance, Operation);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return UdpIoRecvDatagram (\r
+ Instance->UdpIo,\r
+ Mtftp6RrqInput,\r
+ Instance,\r
+ 0\r
+ );\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Mtftp6 support functions implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+/**\r
+ Allocate a MTFTP block range, then init it to the range of [Start, End].\r
+\r
+ @param[in] Start The start block number.\r
+ @param[in] End The last block number in the range.\r
+\r
+ @return Range The range of the allocated block buffer.\r
+\r
+**/\r
+MTFTP6_BLOCK_RANGE *\r
+Mtftp6AllocateRange (\r
+ IN UINT16 Start,\r
+ IN UINT16 End\r
+ )\r
+{\r
+ MTFTP6_BLOCK_RANGE *Range;\r
+\r
+ Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));\r
+\r
+ if (Range == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ InitializeListHead (&Range->Link);\r
+ Range->Start = Start;\r
+ Range->End = End;\r
+ Range->Bound = End;\r
+\r
+ return Range;\r
+}\r
+\r
+\r
+/**\r
+ Initialize the block range for either RRQ or WRQ. RRQ and WRQ have\r
+ different requirements for Start and End. For example, during startup,\r
+ WRQ initializes its whole valid block range to [0, 0xffff]. This\r
+ is bacause the server will send an ACK0 to inform the user to start the\r
+ upload. When the client receives an ACK0, it will remove 0 from the range,\r
+ get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
+ without option negotiation, the server will directly send the BLOCK1\r
+ in response to the client's RRQ. When received BLOCK1, the client will\r
+ remove it from the block range and send an ACK. It also works if there\r
+ is option negotiation.\r
+\r
+ @param[in] Head The block range head to initialize.\r
+ @param[in] Start The Start block number.\r
+ @param[in] End The last block number.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range.\r
+ @retval EFI_SUCCESS The initial block range is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6InitBlockRange (\r
+ IN LIST_ENTRY *Head,\r
+ IN UINT16 Start,\r
+ IN UINT16 End\r
+ )\r
+{\r
+ MTFTP6_BLOCK_RANGE *Range;\r
+\r
+ Range = Mtftp6AllocateRange (Start, End);\r
+\r
+ if (Range == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ InsertTailList (Head, &Range->Link);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Get the first valid block number on the range list.\r
+\r
+ @param[in] Head The block range head.\r
+\r
+ @retval ==-1 If the block range is empty.\r
+ @retval >-1 The first valid block number.\r
+\r
+**/\r
+INTN\r
+Mtftp6GetNextBlockNum (\r
+ IN LIST_ENTRY *Head\r
+ )\r
+{\r
+ MTFTP6_BLOCK_RANGE *Range;\r
+\r
+ if (IsListEmpty (Head)) {\r
+ return -1;\r
+ }\r
+\r
+ Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);\r
+ return Range->Start;\r
+}\r
+\r
+\r
+/**\r
+ Set the last block number of the block range list. It\r
+ removes all the blocks after the Last. MTFTP initialize the\r
+ block range to the maximum possible range, such as [0, 0xffff]\r
+ for WRQ. When it gets the last block number, it calls\r
+ this function to set the last block number.\r
+\r
+ @param[in] Head The block range list.\r
+ @param[in] Last The last block number.\r
+\r
+**/\r
+VOID\r
+Mtftp6SetLastBlockNum (\r
+ IN LIST_ENTRY *Head,\r
+ IN UINT16 Last\r
+ )\r
+{\r
+ MTFTP6_BLOCK_RANGE *Range;\r
+\r
+ //\r
+ // Iterate from the tail to head to remove the block number\r
+ // after the last.\r
+ //\r
+ while (!IsListEmpty (Head)) {\r
+ Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);\r
+\r
+ if (Range->Start > Last) {\r
+ RemoveEntryList (&Range->Link);\r
+ FreePool (Range);\r
+ continue;\r
+ }\r
+\r
+ if (Range->End > Last) {\r
+ Range->End = Last;\r
+ }\r
+ return ;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Remove the block number from the block range list.\r
+\r
+ @param[in] Head The block range list to remove from.\r
+ @param[in] Num The block number to remove.\r
+ @param[in] Completed Whether Num is the last block number\r
+ @param[out] TotalBlock The continuous block number in all\r
+\r
+ @retval EFI_NOT_FOUND The block number isn't in the block range list.\r
+ @retval EFI_SUCCESS The block number has been removed from the list.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RemoveBlockNum (\r
+ IN LIST_ENTRY *Head,\r
+ IN UINT16 Num,\r
+ IN BOOLEAN Completed,\r
+ OUT UINT64 *TotalBlock\r
+ )\r
+{\r
+ MTFTP6_BLOCK_RANGE *Range;\r
+ MTFTP6_BLOCK_RANGE *NewRange;\r
+ LIST_ENTRY *Entry;\r
+\r
+ NET_LIST_FOR_EACH (Entry, Head) {\r
+\r
+ //\r
+ // Each block represents a hole [Start, End] in the file,\r
+ // skip to the first range with End >= Num\r
+ //\r
+ Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+\r
+ if (Range->End < Num) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // There are three different cases for Start\r
+ // 1. (Start > Num) && (End >= Num):\r
+ // because all the holes before this one has the condition of\r
+ // End < Num, so this block number has been removed.\r
+ //\r
+ // 2. (Start == Num) && (End >= Num):\r
+ // Need to increase the Start by one, and if End == Num, this\r
+ // hole has been removed completely, remove it.\r
+ //\r
+ // 3. (Start < Num) && (End >= Num):\r
+ // if End == Num, only need to decrease the End by one because\r
+ // we have (Start < Num) && (Num == End), so (Start <= End - 1).\r
+ // if (End > Num), the hold is splited into two holes, with\r
+ // [Start, Num - 1] and [Num + 1, End].\r
+ //\r
+ if (Range->Start > Num) {\r
+ return EFI_NOT_FOUND;\r
+\r
+ } else if (Range->Start == Num) {\r
+ Range->Start++;\r
+\r
+ //\r
+ // Note that: RFC 1350 does not mention block counter roll-over,\r
+ // but several TFTP hosts implement the roll-over be able to accept\r
+ // transfers of unlimited size. There is no consensus, however, whether\r
+ // the counter should wrap around to zero or to one. Many implementations\r
+ // wrap to zero, because this is the simplest to implement. Here we choose\r
+ // this solution.\r
+ //\r
+ *TotalBlock = Num;\r
+\r
+ if (Range->Round > 0) {\r
+ *TotalBlock += Range->Bound + MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1;\r
+ }\r
+\r
+ if (Range->Start > Range->Bound) {\r
+ Range->Start = 0;\r
+ Range->Round ++;\r
+ }\r
+\r
+ if ((Range->Start > Range->End) || Completed) {\r
+ RemoveEntryList (&Range->Link);\r
+ FreePool (Range);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ } else {\r
+ if (Range->End == Num) {\r
+ Range->End--;\r
+ } else {\r
+ NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);\r
+\r
+ if (NewRange == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Range->End = Num - 1;\r
+ NetListInsertAfter (&Range->Link, &NewRange->Link);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+ Configure the opened Udp6 instance until the corresponding Ip6 instance\r
+ has been configured.\r
+\r
+ @param[in] UdpIo The pointer to the Udp6 Io.\r
+ @param[in] UdpCfgData The pointer to the Udp6 configure data.\r
+\r
+ @retval EFI_SUCCESS Configure the Udp6 instance successfully.\r
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not\r
+ been configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6GetMapping (\r
+ IN UDP_IO *UdpIo,\r
+ IN EFI_UDP6_CONFIG_DATA *UdpCfgData\r
+ )\r
+{\r
+ EFI_IP6_MODE_DATA Ip6Mode;\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+ EFI_STATUS Status;\r
+ EFI_EVENT Event;\r
+\r
+ Event = NULL;\r
+ Udp6 = UdpIo->Protocol.Udp6;\r
+\r
+ //\r
+ // Create a timer to check whether the Ip6 instance configured or not.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = gBS->SetTimer (\r
+ Event,\r
+ TimerRelative,\r
+ MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Check the Ip6 mode data till timeout.\r
+ //\r
+ while (EFI_ERROR (gBS->CheckEvent (Event))) {\r
+\r
+ Udp6->Poll (Udp6);\r
+\r
+ Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ if (Ip6Mode.IsConfigured) {\r
+ //\r
+ // Continue to configure the Udp6 instance.\r
+ //\r
+ Status = Udp6->Configure (Udp6, UdpCfgData);\r
+ } else {\r
+ Status = EFI_NO_MAPPING;\r
+ }\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ if (Event != NULL) {\r
+ gBS->CloseEvent (Event);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ The dummy configure routine for create a new Udp6 Io.\r
+\r
+ @param[in] UdpIo The pointer to the Udp6 Io.\r
+ @param[in] Context The pointer to the context.\r
+\r
+ @retval EFI_SUCCESS This value is always returned.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ConfigDummyUdpIo (\r
+ IN UDP_IO *UdpIo,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ The configure routine for Mtftp6 instance to transmit/receive.\r
+\r
+ @param[in] UdpIo The pointer to the Udp6 Io.\r
+ @param[in] ServerIp The pointer to the server address.\r
+ @param[in] ServerPort The pointer to the server port.\r
+ @param[in] LocalIp The pointer to the local address.\r
+ @param[in] LocalPort The pointer to the local port.\r
+\r
+ @retval EFI_SUCCESS Configured the Udp6 Io for Mtftp6 successfully.\r
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been\r
+ configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ConfigUdpIo (\r
+ IN UDP_IO *UdpIo,\r
+ IN EFI_IPv6_ADDRESS *ServerIp,\r
+ IN UINT16 ServerPort,\r
+ IN EFI_IPv6_ADDRESS *LocalIp,\r
+ IN UINT16 LocalPort\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+ EFI_UDP6_CONFIG_DATA *Udp6Cfg;\r
+\r
+ Udp6 = UdpIo->Protocol.Udp6;\r
+ Udp6Cfg = &(UdpIo->Config.Udp6);\r
+\r
+ ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+ //\r
+ // Set the Udp6 Io configure data.\r
+ //\r
+ Udp6Cfg->AcceptPromiscuous = FALSE;\r
+ Udp6Cfg->AcceptAnyPort = FALSE;\r
+ Udp6Cfg->AllowDuplicatePort = FALSE;\r
+ Udp6Cfg->TrafficClass = 0;\r
+ Udp6Cfg->HopLimit = 128;\r
+ Udp6Cfg->ReceiveTimeout = 0;\r
+ Udp6Cfg->TransmitTimeout = 0;\r
+ Udp6Cfg->StationPort = LocalPort;\r
+ Udp6Cfg->RemotePort = ServerPort;\r
+\r
+ CopyMem (\r
+ &Udp6Cfg->StationAddress,\r
+ LocalIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+\r
+ CopyMem (\r
+ &Udp6Cfg->RemoteAddress,\r
+ ServerIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+\r
+ //\r
+ // Configure the Udp6 instance with current configure data.\r
+ //\r
+ Status = Udp6->Configure (Udp6, Udp6Cfg);\r
+\r
+ if (Status == EFI_NO_MAPPING) {\r
+\r
+ return Mtftp6GetMapping (UdpIo, Udp6Cfg);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Build and transmit the request packet for the Mtftp6 instance.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Operation The operation code of this packet.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request.\r
+ @retval EFI_SUCCESS The request is built and sent.\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendRequest (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 Operation\r
+ )\r
+{\r
+ EFI_MTFTP6_PACKET *Packet;\r
+ EFI_MTFTP6_OPTION *Options;\r
+ EFI_MTFTP6_TOKEN *Token;\r
+ NET_BUF *Nbuf;\r
+ UINT8 *Mode;\r
+ UINT8 *Cur;\r
+ UINT32 Len1;\r
+ UINT32 Len2;\r
+ UINT32 Len;\r
+ UINTN Index;\r
+\r
+ Token = Instance->Token;\r
+ Options = Token->OptionList;\r
+ Mode = Token->ModeStr;\r
+\r
+ if (Mode == NULL) {\r
+ Mode = (UINT8 *) "octet";\r
+ }\r
+\r
+ //\r
+ // The header format of RRQ/WRQ packet is:\r
+ //\r
+ // 2 bytes string 1 byte string 1 byte\r
+ // ------------------------------------------------\r
+ // | Opcode | Filename | 0 | Mode | 0 |\r
+ // ------------------------------------------------\r
+ //\r
+ // The common option format is:\r
+ //\r
+ // string 1 byte string 1 byte\r
+ // ---------------------------------------\r
+ // | OptionStr | 0 | ValueStr | 0 |\r
+ // ---------------------------------------\r
+ //\r
+\r
+ //\r
+ // Compute the size of new Mtftp6 packet.\r
+ //\r
+ Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename);\r
+ Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode);\r
+ Len = Len1 + Len2 + 4;\r
+\r
+ for (Index = 0; Index < Token->OptionCount; Index++) {\r
+ Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
+ Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
+ Len += Len1 + Len2 + 2;\r
+ }\r
+\r
+ //\r
+ // Allocate a packet then copy the data.\r
+ //\r
+ if ((Nbuf = NetbufAlloc (Len)) == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Copy the opcode, filename and mode into packet.\r
+ //\r
+ Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+ ASSERT (Packet != NULL);\r
+\r
+ Packet->OpCode = HTONS (Operation);\r
+ Cur = Packet->Rrq.Filename;\r
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename);\r
+ Cur += AsciiStrLen ((CHAR8 *) Token->Filename) + 1;\r
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode);\r
+ Cur += AsciiStrLen ((CHAR8 *) Mode) + 1;\r
+\r
+ //\r
+ // Copy all the extension options into the packet.\r
+ //\r
+ for (Index = 0; Index < Token->OptionCount; ++Index) {\r
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr);\r
+ Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1;\r
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr);\r
+ Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1;\r
+ }\r
+\r
+ //\r
+ // Save the packet buf for retransmit\r
+ //\r
+ if (Instance->LastPacket != NULL) {\r
+ NetbufFree (Instance->LastPacket);\r
+ }\r
+\r
+ Instance->LastPacket = Nbuf;\r
+ Instance->CurRetry = 0;\r
+\r
+ return Mtftp6TransmitPacket (Instance, Nbuf);\r
+}\r
+\r
+\r
+/**\r
+ Build and send an error packet.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] ErrCode The error code in the packet.\r
+ @param[in] ErrInfo The error message in the packet.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet.\r
+ @retval EFI_SUCCESS The error packet is transmitted.\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendError (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 ErrCode,\r
+ IN UINT8* ErrInfo\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ EFI_MTFTP6_PACKET *TftpError;\r
+ UINT32 Len;\r
+\r
+ //\r
+ // Allocate a packet then copy the data.\r
+ //\r
+ Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));\r
+ Nbuf = NetbufAlloc (Len);\r
+\r
+ if (Nbuf == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+\r
+ if (TftpError == NULL) {\r
+ NetbufFree (Nbuf);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TftpError->OpCode = HTONS (EFI_MTFTP6_OPCODE_ERROR);\r
+ TftpError->Error.ErrorCode = HTONS (ErrCode);\r
+\r
+ AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo);\r
+\r
+ //\r
+ // Save the packet buf for retransmit\r
+ //\r
+ if (Instance->LastPacket != NULL) {\r
+ NetbufFree (Instance->LastPacket);\r
+ }\r
+\r
+ Instance->LastPacket = Nbuf;\r
+ Instance->CurRetry = 0;\r
+\r
+ return Mtftp6TransmitPacket (Instance, Nbuf);\r
+}\r
+\r
+\r
+/**\r
+ The callback function called when the packet is transmitted.\r
+\r
+ @param[in] Packet The pointer to the packet.\r
+ @param[in] UdpEpt The pointer to the Udp6 access point.\r
+ @param[in] IoStatus The result of the transmission.\r
+ @param[in] Context The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnPacketSent (\r
+ IN NET_BUF *Packet,\r
+ IN UDP_END_POINT *UdpEpt,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ NetbufFree (Packet);\r
+ *(BOOLEAN *) Context = TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Send the packet for the Mtftp6 instance.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Packet The pointer to the packet to be sent.\r
+\r
+ @retval EFI_SUCCESS The packet was sent out\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6TransmitPacket (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ EFI_UDP6_PROTOCOL *Udp6;\r
+ EFI_UDP6_CONFIG_DATA Udp6CfgData;\r
+ EFI_STATUS Status;\r
+ UINT16 *Temp;\r
+ UINT16 Value;\r
+ UINT16 OpCode;\r
+\r
+ ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));\r
+ Udp6 = Instance->UdpIo->Protocol.Udp6;\r
+\r
+ //\r
+ // Set the live time of the packet.\r
+ //\r
+ Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);\r
+\r
+ Temp = (UINT16 *) NetbufGetByte (Packet, 0, NULL);\r
+ ASSERT (Temp != NULL);\r
+\r
+ Value = *Temp;\r
+ OpCode = NTOHS (Value);\r
+\r
+ if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {\r
+ //\r
+ // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as\r
+ // (serverip, 69, localip, localport) to send.\r
+ // Usually local address and local port are both default as zero.\r
+ //\r
+ Status = Udp6->Configure (Udp6, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Mtftp6ConfigUdpIo (\r
+ Instance->UdpIo,\r
+ &Instance->ServerIp,\r
+ Instance->ServerCmdPort,\r
+ &Instance->Config->StationIp,\r
+ Instance->Config->LocalPort\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Get the current local address and port by get Udp6 mode data.\r
+ //\r
+ Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ NET_GET_REF (Packet);\r
+\r
+ Instance->IsTransmitted = FALSE;\r
+\r
+ Status = UdpIoSendDatagram (\r
+ Instance->UdpIo,\r
+ Packet,\r
+ NULL,\r
+ NULL,\r
+ Mtftp6OnPacketSent,\r
+ &Instance->IsTransmitted\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ NET_PUT_REF (Packet);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Poll till the packet sent out from the ip6 queue.\r
+ //\r
+ gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+ while (!Instance->IsTransmitted) {\r
+ Udp6->Poll (Udp6);\r
+ }\r
+\r
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // For the subsequent exchange of such requests, reconfigure the Udp6Io as\r
+ // (serverip, 0, localip, localport) to receive.\r
+ // Currently local address and local port are specified by Udp6 mode data.\r
+ //\r
+ Status = Udp6->Configure (Udp6, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Mtftp6ConfigUdpIo (\r
+ Instance->UdpIo,\r
+ &Instance->ServerIp,\r
+ Instance->ServerDataPort,\r
+ &Udp6CfgData.StationAddress,\r
+ Udp6CfgData.StationPort\r
+ );\r
+ } else {\r
+ //\r
+ // For the data exchange, configure the Udp6Io as (serverip, dataport,\r
+ // localip, localport) to send/receive.\r
+ // Currently local address and local port are specified by Udp6 mode data.\r
+ //\r
+ Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {\r
+\r
+ Status = Udp6->Configure (Udp6, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Mtftp6ConfigUdpIo (\r
+ Instance->UdpIo,\r
+ &Instance->ServerIp,\r
+ Instance->ServerDataPort,\r
+ &Udp6CfgData.StationAddress,\r
+ Udp6CfgData.StationPort\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ NET_GET_REF (Packet);\r
+\r
+ Instance->IsTransmitted = FALSE;\r
+\r
+ Status = UdpIoSendDatagram (\r
+ Instance->UdpIo,\r
+ Packet,\r
+ NULL,\r
+ NULL,\r
+ Mtftp6OnPacketSent,\r
+ &Instance->IsTransmitted\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ NET_PUT_REF (Packet);\r
+ }\r
+\r
+ //\r
+ // Poll till the packet sent out from the ip6 queue.\r
+ //\r
+ gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+ while (!Instance->IsTransmitted) {\r
+ Udp6->Poll (Udp6);\r
+ }\r
+\r
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Check packet for GetInfo callback routine.\r
+\r
+ GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect\r
+ the first packet from server, then abort the session.\r
+\r
+ @param[in] This The pointer to the Mtftp6 protocol.\r
+ @param[in] Token The pointer to the Mtftp6 token.\r
+ @param[in] PacketLen The length of the packet.\r
+ @param[in] Packet The pointer to the received packet.\r
+\r
+ @retval EFI_ABORTED Abort the Mtftp6 operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6CheckPacket (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token,\r
+ IN UINT16 PacketLen,\r
+ IN EFI_MTFTP6_PACKET *Packet\r
+ )\r
+{\r
+ MTFTP6_GETINFO_CONTEXT *Context;\r
+ UINT16 OpCode;\r
+\r
+ Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;\r
+ OpCode = NTOHS (Packet->OpCode);\r
+\r
+ //\r
+ // Set the GetInfo's return status according to the OpCode.\r
+ //\r
+ switch (OpCode) {\r
+ case EFI_MTFTP6_OPCODE_ERROR:\r
+ Context->Status = EFI_TFTP_ERROR;\r
+ break;\r
+\r
+ case EFI_MTFTP6_OPCODE_OACK:\r
+ Context->Status = EFI_SUCCESS;\r
+ break;\r
+\r
+ default:\r
+ Context->Status = EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ //\r
+ // Allocate buffer then copy the packet over. Use gBS->AllocatePool\r
+ // in case NetAllocatePool will implements something tricky.\r
+ //\r
+ *(Context->Packet) = AllocateZeroPool (PacketLen);\r
+\r
+ if (*(Context->Packet) == NULL) {\r
+ Context->Status = EFI_OUT_OF_RESOURCES;\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ *(Context->PacketLen) = PacketLen;\r
+ CopyMem (*(Context->Packet), Packet, PacketLen);\r
+\r
+ return EFI_ABORTED;\r
+}\r
+\r
+\r
+/**\r
+ Clean up the current Mtftp6 operation.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Result The result to be returned to the user.\r
+\r
+**/\r
+VOID\r
+Mtftp6OperationClean (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN EFI_STATUS Result\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ MTFTP6_BLOCK_RANGE *Block;\r
+\r
+ //\r
+ // Clean up the current token and event.\r
+ //\r
+ if (Instance->Token != NULL) {\r
+ Instance->Token->Status = Result;\r
+ if (Instance->Token->Event != NULL) {\r
+ gBS->SignalEvent (Instance->Token->Event);\r
+ }\r
+ Instance->Token = NULL;\r
+ }\r
+\r
+ //\r
+ // Clean up the corresponding Udp6Io.\r
+ //\r
+ if (Instance->UdpIo != NULL) {\r
+ UdpIoCleanIo (Instance->UdpIo);\r
+ }\r
+\r
+ if (Instance->McastUdpIo != NULL) {\r
+ UdpIoFreeIo (Instance->McastUdpIo);\r
+ Instance->McastUdpIo = NULL;\r
+ }\r
+\r
+ //\r
+ // Clean up the stored last packet.\r
+ //\r
+ if (Instance->LastPacket != NULL) {\r
+ NetbufFree (Instance->LastPacket);\r
+ Instance->LastPacket = NULL;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {\r
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+ RemoveEntryList (Entry);\r
+ FreePool (Block);\r
+ }\r
+\r
+ //\r
+ // Reinitialize the corresponding fields of the Mtftp6 operation.\r
+ //\r
+ ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
+ ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));\r
+ ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ Instance->ServerCmdPort = 0;\r
+ Instance->ServerDataPort = 0;\r
+ Instance->McastPort = 0;\r
+ Instance->BlkSize = 0;\r
+ Instance->LastBlk = 0;\r
+ Instance->PacketToLive = 0;\r
+ Instance->MaxRetry = 0;\r
+ Instance->CurRetry = 0;\r
+ Instance->Timeout = 0;\r
+ Instance->IsMaster = TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Start the Mtftp6 instance to perform the operation, such as read file,\r
+ write file, and read directory.\r
+\r
+ @param[in] This The MTFTP session.\r
+ @param[in] Token The token than encapsues the user's request.\r
+ @param[in] OpCode The operation to perform.\r
+\r
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.\r
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.\r
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.\r
+ @retval EFI_SUCCESS The operation is successfully started.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6OperationStart (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token,\r
+ IN UINT16 OpCode\r
+ )\r
+{\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_STATUS Status;\r
+\r
+ if (This == NULL ||\r
+ Token == NULL ||\r
+ Token->Filename == NULL ||\r
+ (Token->OptionCount != 0 && Token->OptionList == NULL) ||\r
+ (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // At least define one method to collect the data for download.\r
+ //\r
+ if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&\r
+ Token->Buffer == NULL &&\r
+ Token->CheckPacket == NULL\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // At least define one method to provide the data for upload.\r
+ //\r
+ if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+\r
+ if (Instance->Config == NULL) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Instance->Token != NULL) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Parse the extension options in the request packet.\r
+ //\r
+ if (Token->OptionCount != 0) {\r
+\r
+ Status = Mtftp6ParseExtensionOption (\r
+ Token->OptionList,\r
+ Token->OptionCount,\r
+ TRUE,\r
+ &Instance->ExtInfo\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Initialize runtime data from config data or override data.\r
+ //\r
+ Instance->Token = Token;\r
+ Instance->ServerCmdPort = Instance->Config->InitialServerPort;\r
+ Instance->ServerDataPort = 0;\r
+ Instance->MaxRetry = Instance->Config->TryCount;\r
+ Instance->Timeout = Instance->Config->TimeoutValue;\r
+ Instance->IsMaster = TRUE;\r
+\r
+ CopyMem (\r
+ &Instance->ServerIp,\r
+ &Instance->Config->ServerIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+\r
+ if (Token->OverrideData != NULL) {\r
+ Instance->ServerCmdPort = Token->OverrideData->ServerPort;\r
+ Instance->MaxRetry = Token->OverrideData->TryCount;\r
+ Instance->Timeout = Token->OverrideData->TimeoutValue;\r
+\r
+ CopyMem (\r
+ &Instance->ServerIp,\r
+ &Token->OverrideData->ServerIp,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ }\r
+\r
+ //\r
+ // Set default value for undefined parameters.\r
+ //\r
+ if (Instance->ServerCmdPort == 0) {\r
+ Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;\r
+ }\r
+ if (Instance->BlkSize == 0) {\r
+ Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;\r
+ }\r
+ if (Instance->MaxRetry == 0) {\r
+ Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;\r
+ }\r
+ if (Instance->Timeout == 0) {\r
+ Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;\r
+ }\r
+\r
+ Token->Status = EFI_NOT_READY;\r
+\r
+ //\r
+ // Switch the routines by the operation code.\r
+ //\r
+ switch (OpCode) {\r
+ case EFI_MTFTP6_OPCODE_RRQ:\r
+ Status = Mtftp6RrqStart (Instance, OpCode);\r
+ break;\r
+\r
+ case EFI_MTFTP6_OPCODE_DIR:\r
+ Status = Mtftp6RrqStart (Instance, OpCode);\r
+ break;\r
+\r
+ case EFI_MTFTP6_OPCODE_WRQ:\r
+ Status = Mtftp6WrqStart (Instance, OpCode);\r
+ break;\r
+\r
+ default:\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Return immediately for asynchronous or poll the instance for synchronous.\r
+ //\r
+ gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+ if (Token->Event == NULL) {\r
+ while (Token->Status == EFI_NOT_READY) {\r
+ This->Poll (This);\r
+ }\r
+ return Token->Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ Mtftp6OperationClean (Instance, Status);\r
+ gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ The timer ticking routine for the Mtftp6 instance.\r
+\r
+ @param[in] Event The pointer to the ticking event.\r
+ @param[in] Context The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnTimerTick (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ MTFTP6_SERVICE *Service;\r
+ MTFTP6_INSTANCE *Instance;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ EFI_MTFTP6_TOKEN *Token;\r
+ EFI_STATUS Status;\r
+\r
+ Service = (MTFTP6_SERVICE *) Context;\r
+\r
+ //\r
+ // Iterate through all the children of the Mtftp service instance. Time\r
+ // out the packet. If maximum retries reached, clean the session up.\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {\r
+\r
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);\r
+\r
+ if (Instance->Token == NULL) {\r
+ continue;\r
+ }\r
+\r
+ if (Instance->PacketToLive > 0) {\r
+ Instance->PacketToLive--;\r
+ continue;\r
+ }\r
+\r
+ Instance->CurRetry++;\r
+ Token = Instance->Token;\r
+\r
+ if (Token->TimeoutCallback != NULL) {\r
+ //\r
+ // Call the timeout callback routine if has.\r
+ //\r
+ Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+ (UINT8 *) "User aborted the transfer in time out"\r
+ );\r
+ Mtftp6OperationClean (Instance, EFI_ABORTED);\r
+ continue;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Retransmit the packet if haven't reach the maxmium retry count,\r
+ // otherwise exit the transfer.\r
+ //\r
+ if (Instance->CurRetry < Instance->MaxRetry) {\r
+ Mtftp6TransmitPacket (Instance, Instance->LastPacket);\r
+ } else {\r
+ Mtftp6OperationClean (Instance, EFI_TIMEOUT);\r
+ continue;\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/** @file\r
+ Mtftp6 support functions declaration.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_MTFTP6_SUPPORT_H__\r
+#define __EFI_MTFTP6_SUPPORT_H__\r
+\r
+//\r
+// The structure representing a range of block numbers, [Start, End].\r
+// It is used to remember the holes in the MTFTP block space. If all\r
+// the holes are filled in, then the download or upload has completed.\r
+//\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ INTN Start;\r
+ INTN End;\r
+ INTN Round;\r
+ INTN Bound;\r
+} MTFTP6_BLOCK_RANGE;\r
+\r
+\r
+/**\r
+ Initialize the block range for either RRQ or WRQ. RRQ and WRQ have\r
+ different requirements for Start and End. For example, during startup,\r
+ WRQ initializes its whole valid block range to [0, 0xffff]. This\r
+ is because the server will send an ACK0 to inform the user to start the\r
+ upload. When the client receives an ACK0, it will remove 0 from the range,\r
+ get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
+ without option negotiation, the server will directly send us the BLOCK1\r
+ in response to the client's RRQ. When BLOCK1 is received, the client will\r
+ remove it from the block range and send an ACK. It also works if there\r
+ is option negotiation.\r
+\r
+ @param[in] Head The block range head to initialize.\r
+ @param[in] Start The Start block number.\r
+ @param[in] End The last block number.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range.\r
+ @retval EFI_SUCCESS The initial block range is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6InitBlockRange (\r
+ IN LIST_ENTRY *Head,\r
+ IN UINT16 Start,\r
+ IN UINT16 End\r
+ );\r
+\r
+\r
+/**\r
+ Get the first valid block number on the range list.\r
+\r
+ @param[in] Head The block range head.\r
+\r
+ @retval ==-1 If the block range is empty.\r
+ @retval >-1 The first valid block number.\r
+\r
+**/\r
+INTN\r
+Mtftp6GetNextBlockNum (\r
+ IN LIST_ENTRY *Head\r
+ );\r
+\r
+\r
+/**\r
+ Set the last block number of the block range list. It\r
+ removes all the blocks after the Last. MTFTP initialize the\r
+ block range to the maximum possible range, such as [0, 0xffff]\r
+ for WRQ. When it gets the last block number, it calls\r
+ this function to set the last block number.\r
+\r
+ @param[in] Head The block range list.\r
+ @param[in] Last The last block number.\r
+\r
+**/\r
+VOID\r
+Mtftp6SetLastBlockNum (\r
+ IN LIST_ENTRY *Head,\r
+ IN UINT16 Last\r
+ );\r
+\r
+\r
+/**\r
+ Remove the block number from the block range list.\r
+\r
+ @param[in] Head The block range list to remove from.\r
+ @param[in] Num The block number to remove.\r
+ @param[in] Completed Whether Num is the last block number\r
+ @param[out] TotalBlock The continuous block number in all\r
+\r
+ @retval EFI_NOT_FOUND The block number isn't in the block range list.\r
+ @retval EFI_SUCCESS The block number has been removed from the list.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RemoveBlockNum (\r
+ IN LIST_ENTRY *Head,\r
+ IN UINT16 Num,\r
+ IN BOOLEAN Completed,\r
+ OUT UINT64 *TotalBlock\r
+ );\r
+\r
+\r
+/**\r
+ Build and transmit the request packet for the Mtftp6 instance.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Operation The operation code of this packet.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request.\r
+ @retval EFI_SUCCESS The request was built and sent.\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendRequest (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 Operation\r
+ );\r
+\r
+\r
+/**\r
+ Build and send an error packet.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] ErrCode The error code in the packet.\r
+ @param[in] ErrInfo The error message in the packet.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet.\r
+ @retval EFI_SUCCESS The error packet was transmitted.\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendError (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 ErrCode,\r
+ IN UINT8* ErrInfo\r
+ );\r
+\r
+\r
+/**\r
+ Send the packet for the Mtftp6 instance.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Packet The pointer to the packet to be sent.\r
+\r
+ @retval EFI_SUCCESS The packet was sent out\r
+ @retval Others Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6TransmitPacket (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+\r
+/**\r
+ Check packet for GetInfo callback routine.\r
+\r
+ @param[in] This The pointer to the Mtftp6 protocol.\r
+ @param[in] Token The pointer to the Mtftp6 token.\r
+ @param[in] PacketLen The length of the packet\r
+ @param[in] Packet The pointer to the received packet.\r
+\r
+ @retval EFI_SUCCESS The check process passed successfully.\r
+ @retval EFI_ABORTED Abort the Mtftp6 operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6CheckPacket (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token,\r
+ IN UINT16 PacketLen,\r
+ IN EFI_MTFTP6_PACKET *Packet\r
+ );\r
+\r
+\r
+/**\r
+ The dummy configure routine for create a new Udp6 Io.\r
+\r
+ @param[in] UdpIo The pointer to the Udp6 Io.\r
+ @param[in] Context The pointer to the context.\r
+\r
+ @retval EFI_SUCCESS The value is always returned.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ConfigDummyUdpIo (\r
+ IN UDP_IO *UdpIo,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ The configure routine for the Mtftp6 instance to transmit/receive.\r
+\r
+ @param[in] UdpIo The pointer to the Udp6 Io.\r
+ @param[in] ServerIp The pointer to the server address.\r
+ @param[in] ServerPort The pointer to the server port.\r
+ @param[in] LocalIp The pointer to the local address.\r
+ @param[in] LocalPort The pointer to the local port.\r
+\r
+ @retval EFI_SUCCESS Configure the Udp6 Io for Mtftp6 successfully.\r
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been\r
+ configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ConfigUdpIo (\r
+ IN UDP_IO *UdpIo,\r
+ IN EFI_IPv6_ADDRESS *ServerIp,\r
+ IN UINT16 ServerPort,\r
+ IN EFI_IPv6_ADDRESS *LocalIp,\r
+ IN UINT16 LocalPort\r
+ );\r
+\r
+\r
+/**\r
+ Clean up the current Mtftp6 operation.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Result The result to be returned to the user.\r
+\r
+**/\r
+VOID\r
+Mtftp6OperationClean (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN EFI_STATUS Result\r
+ );\r
+\r
+\r
+/**\r
+ Start the Mtftp6 instance to perform the operation, such as read file,\r
+ write file, and read directory.\r
+\r
+ @param[in] This The MTFTP session\r
+ @param[in] Token The token that encapsulates the user's request.\r
+ @param[in] OpCode The operation to perform.\r
+\r
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.\r
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.\r
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.\r
+ @retval EFI_SUCCESS The operation was successfully started.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6OperationStart (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token,\r
+ IN UINT16 OpCode\r
+ );\r
+\r
+\r
+/**\r
+ The timer ticking routine for the Mtftp6 instance.\r
+\r
+ @param[in] Event The pointer to the ticking event.\r
+ @param[in] Context The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnTimerTick (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ The packet process callback for Mtftp6 upload.\r
+\r
+ @param[in] UdpPacket The pointer to the packet received.\r
+ @param[in] UdpEpt The pointer to the Udp6 access point.\r
+ @param[in] IoStatus The status from the Udp6 instance.\r
+ @param[in] Context The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6WrqInput (\r
+ IN NET_BUF *UdpPacket,\r
+ IN UDP_END_POINT *UdpEpt,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ Start the Mtftp6 instance to upload. It will first init some states,\r
+ then send the WRQ request packet, and start to receive the packet.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Operation The operation code of current packet.\r
+\r
+ @retval EFI_SUCCESS The Mtftp6 was started to upload.\r
+ @retval Others Failed to start to upload.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqStart (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 Operation\r
+ );\r
+\r
+\r
+/**\r
+ The packet process callback for Mtftp6 download.\r
+\r
+ @param[in] UdpPacket The pointer to the packet received.\r
+ @param[in] UdpEpt The pointer to the Udp6 access point.\r
+ @param[in] IoStatus The status from Udp6 instance.\r
+ @param[in] Context The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6RrqInput (\r
+ IN NET_BUF *UdpPacket,\r
+ IN UDP_END_POINT *UdpEpt,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ Start the Mtftp6 instance to download. It first initializes some\r
+ of the internal states then builds and sends an RRQ reqeuest packet.\r
+ Finally, it starts receive for the downloading.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Operation The operation code of current packet.\r
+\r
+ @retval EFI_SUCCESS The Mtftp6 was started to download.\r
+ @retval Others Failed to start to download.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RrqStart (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 Operation\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Mtftp6 Wrq process functions implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+\r
+/**\r
+ Build and send a Mtftp6 data packet for upload.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] BlockNum The block num to be sent.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.\r
+ @retval EFI_SUCCESS The data packet was sent.\r
+ @retval EFI_ABORTED The user aborted this process.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqSendBlock (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 BlockNum\r
+ )\r
+{\r
+ EFI_MTFTP6_PACKET *Packet;\r
+ EFI_MTFTP6_TOKEN *Token;\r
+ NET_BUF *UdpPacket;\r
+ EFI_STATUS Status;\r
+ UINT16 DataLen;\r
+ UINT8 *DataBuf;\r
+ UINT64 Start;\r
+\r
+ //\r
+ // Allocate net buffer to create data packet.\r
+ //\r
+ UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN);\r
+\r
+ if (UdpPacket == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (\r
+ UdpPacket,\r
+ MTFTP6_DATA_HEAD_LEN,\r
+ FALSE\r
+ );\r
+ ASSERT (Packet != NULL);\r
+\r
+ Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA);\r
+ Packet->Data.Block = HTONS (BlockNum);\r
+\r
+ //\r
+ // Read the block from either the buffer or PacketNeeded callback\r
+ //\r
+ Token = Instance->Token;\r
+ DataLen = Instance->BlkSize;\r
+\r
+ if (Token->Buffer != NULL) {\r
+ Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);\r
+\r
+ if (Token->BufferSize < Start + Instance->BlkSize) {\r
+ DataLen = (UINT16) (Token->BufferSize - Start);\r
+ Instance->LastBlk = BlockNum;\r
+ Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);\r
+ }\r
+\r
+ if (DataLen > 0) {\r
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
+ CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);\r
+ }\r
+\r
+ } else {\r
+ //\r
+ // Get data from PacketNeeded\r
+ //\r
+ DataBuf = NULL;\r
+ Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf);\r
+\r
+ if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {\r
+ if (DataBuf != NULL) {\r
+ gBS->FreePool (DataBuf);\r
+ }\r
+ //\r
+ // The received packet has already been freed.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+ (UINT8 *) "User aborted the transfer"\r
+ );\r
+\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ if (DataLen < Instance->BlkSize) {\r
+ Instance->LastBlk = BlockNum;\r
+ Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);\r
+ }\r
+\r
+ if (DataLen > 0) {\r
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
+ CopyMem (Packet->Data.Data, DataBuf, DataLen);\r
+ gBS->FreePool (DataBuf);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Reset current retry count of the instance.\r
+ //\r
+ Instance->CurRetry = 0;\r
+\r
+ return Mtftp6TransmitPacket (Instance, UdpPacket);\r
+}\r
+\r
+\r
+/**\r
+ Function to handle received ACK packet. If the ACK number matches the\r
+ expected block number, with more data pending, send the next\r
+ block. Otherwise, tell the caller that we are done.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Packet The pointer to the received packet.\r
+ @param[in] Len The length of the packet.\r
+ @param[out] UdpPacket The net buf of received packet.\r
+ @param[out] IsCompleted If TRUE, the upload has been completed.\r
+ Otherwise, the upload has not been completed.\r
+\r
+ @retval EFI_SUCCESS The ACK packet successfully processed.\r
+ @retval EFI_TFTP_ERROR The block number loops back.\r
+ @retval Others Failed to transmit the next data packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqHandleAck (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 Len,\r
+ OUT NET_BUF **UdpPacket,\r
+ OUT BOOLEAN *IsCompleted\r
+ )\r
+{\r
+ UINT16 AckNum;\r
+ INTN Expected;\r
+ UINT64 TotalBlock;\r
+\r
+ *IsCompleted = FALSE;\r
+ AckNum = NTOHS (Packet->Ack.Block[0]);\r
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+ ASSERT (Expected >= 0);\r
+\r
+ //\r
+ // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput\r
+ // restart receive.\r
+ //\r
+ if (Expected != AckNum) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Remove the acked block number, if this is the last block number,\r
+ // tell the Mtftp6WrqInput to finish the transfer. This is the last\r
+ // block number if the block range are empty..\r
+ //\r
+ Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock);\r
+\r
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+ if (Expected < 0) {\r
+ //\r
+ // The block range is empty. It may either because the the last\r
+ // block has been ACKed, or the sequence number just looped back,\r
+ // that is, there is more than 0xffff blocks.\r
+ //\r
+ if (Instance->LastBlk == AckNum) {\r
+ ASSERT (Instance->LastBlk >= 1);\r
+ *IsCompleted = TRUE;\r
+ return EFI_SUCCESS;\r
+\r
+ } else {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if block number rolls back.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+ (UINT8 *) "Block number rolls back, not supported, try blksize option"\r
+ );\r
+\r
+ return EFI_TFTP_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free the receive buffer before send new packet since it might need\r
+ // reconfigure udpio.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+\r
+ return Mtftp6WrqSendBlock (Instance, (UINT16) Expected);\r
+}\r
+\r
+\r
+/**\r
+ Check whether the received OACK is valid. The OACK is valid\r
+ only if:\r
+ 1. It only include options requested by us.\r
+ 2. It can only include a smaller block size.\r
+ 3. It can't change the proposed time out value.\r
+ 4. Other requirements of the individal MTFTP6 options as required.\r
+\r
+ @param[in] ReplyInfo The pointer to options information in reply packet.\r
+ @param[in] RequestInfo The pointer to requested options information.\r
+\r
+ @retval TRUE If the option in OACK is valid.\r
+ @retval FALSE If the option is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+Mtftp6WrqOackValid (\r
+ IN MTFTP6_EXT_OPTION_INFO *ReplyInfo,\r
+ IN MTFTP6_EXT_OPTION_INFO *RequestInfo\r
+ )\r
+{\r
+ //\r
+ // It is invalid for server to return options we don't request\r
+ //\r
+ if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Server can only specify a smaller block size to be used and\r
+ // return the timeout matches that requested.\r
+ //\r
+ if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||\r
+ (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))\r
+ ) {\r
+\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Process the OACK packet for Wrq.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Packet The pointer to the received packet.\r
+ @param[in] Len The length of the packet.\r
+ @param[out] UdpPacket The net buf of received packet.\r
+ @param[out] IsCompleted If TRUE, the upload has been completed.\r
+ Otherwise, the upload has not been completed.\r
+\r
+ @retval EFI_SUCCESS The OACK packet successfully processed.\r
+ @retval EFI_TFTP_ERROR An TFTP communication error happened.\r
+ @retval Others Failed to process the OACK packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqHandleOack (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN EFI_MTFTP6_PACKET *Packet,\r
+ IN UINT32 Len,\r
+ OUT NET_BUF **UdpPacket,\r
+ OUT BOOLEAN *IsCompleted\r
+ )\r
+{\r
+ EFI_MTFTP6_OPTION *Options;\r
+ UINT32 Count;\r
+ MTFTP6_EXT_OPTION_INFO ExtInfo;\r
+ EFI_MTFTP6_PACKET Dummy;\r
+ EFI_STATUS Status;\r
+ INTN Expected;\r
+\r
+ *IsCompleted = FALSE;\r
+\r
+ //\r
+ // Ignore the OACK if already started the upload\r
+ //\r
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
+\r
+ if (Expected != 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Parse and validate the options from server\r
+ //\r
+ ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
+\r
+ Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);\r
+\r
+ if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) {\r
+ //\r
+ // Don't send a MTFTP error packet when out of resource, it can\r
+ // only make it worse.\r
+ //\r
+ if (Status != EFI_OUT_OF_RESOURCES) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (*UdpPacket);\r
+ *UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if invalid Oack packet received.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
+ (UINT8 *) "Mal-formated OACK packet"\r
+ );\r
+ }\r
+\r
+ return EFI_TFTP_ERROR;\r
+ }\r
+\r
+ if (ExtInfo.BlkSize != 0) {\r
+ Instance->BlkSize = ExtInfo.BlkSize;\r
+ }\r
+\r
+ if (ExtInfo.Timeout != 0) {\r
+ Instance->Timeout = ExtInfo.Timeout;\r
+ }\r
+\r
+ //\r
+ // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck,\r
+ // which will start the transmission of the first data block.\r
+ //\r
+ Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK);\r
+ Dummy.Ack.Block[0] = 0;\r
+\r
+ return Mtftp6WrqHandleAck (\r
+ Instance,\r
+ &Dummy,\r
+ sizeof (EFI_MTFTP6_ACK_HEADER),\r
+ UdpPacket,\r
+ IsCompleted\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ The packet process callback for Mtftp6 upload.\r
+\r
+ @param[in] UdpPacket The pointer to the packet received.\r
+ @param[in] UdpEpt The pointer to the Udp6 access point.\r
+ @param[in] IoStatus The status from Udp6 instance.\r
+ @param[in] Context The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6WrqInput (\r
+ IN NET_BUF *UdpPacket,\r
+ IN UDP_END_POINT *UdpEpt,\r
+ IN EFI_STATUS IoStatus,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ MTFTP6_INSTANCE *Instance;\r
+ EFI_MTFTP6_PACKET *Packet;\r
+ BOOLEAN IsCompleted;\r
+ EFI_STATUS Status;\r
+ UINT32 TotalNum;\r
+ UINT32 Len;\r
+ UINT16 Opcode;\r
+\r
+ Instance = (MTFTP6_INSTANCE *) Context;\r
+\r
+ NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);\r
+\r
+ IsCompleted = FALSE;\r
+ Packet = NULL;\r
+ Status = EFI_SUCCESS;\r
+ TotalNum = 0;\r
+\r
+ //\r
+ // Return error status if Udp6 instance failed to receive.\r
+ //\r
+ if (EFI_ERROR (IoStatus)) {\r
+ Status = IoStatus;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ ASSERT (UdpPacket != NULL);\r
+\r
+ if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Client send initial request to server's listening port. Server\r
+ // will select a UDP port to communicate with the client.\r
+ //\r
+ if (UdpEpt->RemotePort != Instance->ServerDataPort) {\r
+ if (Instance->ServerDataPort != 0) {\r
+ goto ON_EXIT;\r
+ } else {\r
+ Instance->ServerDataPort = UdpEpt->RemotePort;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
+ //\r
+ Len = UdpPacket->TotalSize;\r
+ TotalNum = UdpPacket->BlockOpNum;\r
+\r
+ if (TotalNum > 1) {\r
+ Packet = AllocateZeroPool (Len);\r
+\r
+ if (Packet == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
+\r
+ } else {\r
+ Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
+ ASSERT (Packet != NULL);\r
+ }\r
+\r
+ Opcode = NTOHS (Packet->OpCode);\r
+\r
+ //\r
+ // Callback to the user's CheckPacket if provided. Abort the transmission\r
+ // if CheckPacket returns an EFI_ERROR code.\r
+ //\r
+ if (Instance->Token->CheckPacket != NULL &&\r
+ (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)\r
+ ) {\r
+\r
+ Status = Instance->Token->CheckPacket (\r
+ &Instance->Mtftp6,\r
+ Instance->Token,\r
+ (UINT16) Len,\r
+ Packet\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Send an error message to the server to inform it\r
+ //\r
+ if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {\r
+ //\r
+ // Free the received packet before send new packet in ReceiveNotify,\r
+ // since the udpio might need to be reconfigured.\r
+ //\r
+ NetbufFree (UdpPacket);\r
+ UdpPacket = NULL;\r
+ //\r
+ // Send the Mtftp6 error message if user aborted the current session.\r
+ //\r
+ Mtftp6SendError (\r
+ Instance,\r
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+ (UINT8 *) "User aborted the transfer"\r
+ );\r
+ }\r
+\r
+ Status = EFI_ABORTED;\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Switch the process routines by the operation code.\r
+ //\r
+ switch (Opcode) {\r
+ case EFI_MTFTP6_OPCODE_ACK:\r
+ if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Handle the Ack packet of Wrq.\r
+ //\r
+ Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted);\r
+ break;\r
+\r
+ case EFI_MTFTP6_OPCODE_OACK:\r
+ if (Len <= MTFTP6_OPCODE_LEN) {\r
+ goto ON_EXIT;\r
+ }\r
+ //\r
+ // Handle the Oack packet of Wrq.\r
+ //\r
+ Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted);\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Drop and return eror if received error message.\r
+ //\r
+ Status = EFI_TFTP_ERROR;\r
+ break;\r
+ }\r
+\r
+ON_EXIT:\r
+ //\r
+ // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
+ // restart the receive, otherwise end the session.\r
+ //\r
+ if (Packet != NULL && TotalNum > 1) {\r
+ FreePool (Packet);\r
+ }\r
+\r
+ if (UdpPacket != NULL) {\r
+ NetbufFree (UdpPacket);\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) && !IsCompleted) {\r
+ Status = UdpIoRecvDatagram (\r
+ Instance->UdpIo,\r
+ Mtftp6WrqInput,\r
+ Instance,\r
+ 0\r
+ );\r
+ }\r
+ //\r
+ // Clean up the current session if failed to continue.\r
+ //\r
+ if (EFI_ERROR (Status) || IsCompleted) {\r
+ Mtftp6OperationClean (Instance, Status);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Start the Mtftp6 instance to upload. It will first init some states,\r
+ then send the WRQ request packet, and start to receive the packet.\r
+\r
+ @param[in] Instance The pointer to the Mtftp6 instance.\r
+ @param[in] Operation The operation code of the current packet.\r
+\r
+ @retval EFI_SUCCESS The Mtftp6 was started to upload.\r
+ @retval Others Failed to start to upload.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6WrqStart (\r
+ IN MTFTP6_INSTANCE *Instance,\r
+ IN UINT16 Operation\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // The valid block number range are [0, 0xffff]. For example:\r
+ // the client sends an WRQ request to the server, the server\r
+ // ACK with an ACK0 to let client start transfer the first\r
+ // packet.\r
+ //\r
+ Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Mtftp6SendRequest (Instance, Operation);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return UdpIoRecvDatagram (\r
+ Instance->UdpIo,\r
+ Mtftp6WrqInput,\r
+ Instance,\r
+ 0\r
+ );\r
+}\r
+\r
--- /dev/null
+## @file\r
+#\r
+# This package provides network modules that conform to UEFI 2.2 specification.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials are licensed and made available under\r
+# the terms and conditions of the BSD License which accompanies this distribution.\r
+# The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ DEC_SPECIFICATION = 0x00010005\r
+ PACKAGE_NAME = NetworkPkg\r
+ PACKAGE_GUID = 947988BE-8D5C-471a-893D-AD181C46BEBB\r
+ PACKAGE_VERSION = 0.92\r
--- /dev/null
+## @file\r
+# UEFI 2.2 Network Module Package for All Architectures\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ PLATFORM_NAME = NetworkPkg\r
+ PLATFORM_GUID = 3FD34E9B-E90C-44e1-B510-1F632A509F10\r
+ PLATFORM_VERSION = 0.92\r
+ DSC_SPECIFICATION = 0x00010005\r
+ OUTPUT_DIRECTORY = Build/NetworkPkg\r
+ SUPPORTED_ARCHITECTURES = IA32|IPF|X64|EBC|ARM\r
+ BUILD_TARGETS = DEBUG|RELEASE\r
+ SKUID_IDENTIFIER = DEFAULT\r
+\r
+[LibraryClasses]\r
+ BaseLib|MdePkg/Library/BaseLib/BaseLib.inf\r
+ BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf\r
+ DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf\r
+ HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf\r
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf\r
+ PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf\r
+ PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf\r
+ UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf\r
+ UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf\r
+ UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf\r
+ UefiLib|MdePkg/Library/UefiLib/UefiLib.inf\r
+ UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf\r
+ UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf\r
+\r
+ DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf\r
+ NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf\r
+ IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf\r
+ UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
+\r
+[LibraryClasses.common.UEFI_DRIVER]\r
+ DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf\r
+\r
+[LibraryClasses.common.UEFI_APPLICATION]\r
+ DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf\r
+ FileHandleLib|ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.inf\r
+ ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf\r
+\r
+[PcdsFeatureFlag]\r
+ gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable|TRUE\r
+ gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable|TRUE\r
+\r
+[PcdsFixedAtBuild]\r
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f\r
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000\r
+\r
+###################################################################################################\r
+#\r
+# Components Section - list of the modules and components that will be processed by compilation\r
+# tools and the EDK II tools to generate PE32/PE32+/Coff image files.\r
+#\r
+# Note: The EDK II DSC file is not used to specify how compiled binary images get placed\r
+# into firmware volume images. This section is just a list of modules to compile from\r
+# source into UEFI-compliant binaries.\r
+# It is the FDF file that contains information on combining binary files into firmware\r
+# volume images, whose concept is beyond UEFI and is described in PI specification.\r
+# Binary modules do not need to be listed in this section, as they should be\r
+# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),\r
+# Logo (Logo.bmp), and etc.\r
+# There may also be modules listed in this section that are not required in the FDF file,\r
+# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be\r
+# generated for it, but the binary will not be put into any firmware volume.\r
+#\r
+###################################################################################################\r
+\r
+[Components]\r
+ NetworkPkg/IpSecDxe/IpSecDxe.inf\r
+ NetworkPkg/Ip6Dxe/Ip6Dxe.inf\r
+ NetworkPkg/TcpDxe/TcpDxe.inf\r
+ NetworkPkg/Udp6Dxe/Udp6Dxe.inf\r
+ NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf\r
+ NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf\r
+\r
+\r
+[Components.IA32, Components.X64, Components.IPF]\r
+ NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf\r
+ NetworkPkg/Application/Ping6/Ping6.inf\r
+ NetworkPkg/Application/IfConfig6/IfConfig6.inf\r
+ NetworkPkg/Application/IpsecConfig/IpSecConfig.inf\r
+ NetworkPkg/Application/VConfig/VConfig.inf\r
--- /dev/null
+/** @file\r
+ Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and\r
+ EFI_COMPONENT_NAME2_PROTOCOL.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This, and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language, from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r
+\r
+///\r
+/// EFI Component Name Protocol\r
+///\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName = {\r
+ TcpComponentNameGetDriverName,\r
+ TcpComponentNameGetControllerName,\r
+ "eng"\r
+};\r
+\r
+///\r
+/// EFI Component Name 2 Protocol\r
+///\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName,\r
+ "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = {\r
+ {\r
+ "eng;en",\r
+ L"TCP Network Service Driver"\r
+ },\r
+ {\r
+ NULL,\r
+ NULL\r
+ }\r
+};\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This, and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mTcpDriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN) (This == &gTcpComponentName)\r
+ );\r
+}\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language, from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
--- /dev/null
+/** @file\r
+ Implementation of the Socket.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "SockImpl.h"\r
+\r
+/**\r
+ Get the first buffer block in the specific socket buffer.\r
+\r
+ @param[in] Sockbuf Pointer to the socket buffer.\r
+\r
+ @return Pointer to the first buffer in the queue. NULL if the queue is empty.\r
+\r
+**/\r
+NET_BUF *\r
+SockBufFirst (\r
+ IN SOCK_BUFFER *Sockbuf\r
+ )\r
+{\r
+ LIST_ENTRY *NetbufList;\r
+\r
+ NetbufList = &(Sockbuf->DataQueue->BufList);\r
+\r
+ if (IsListEmpty (NetbufList)) {\r
+ return NULL;\r
+ }\r
+\r
+ return NET_LIST_HEAD (NetbufList, NET_BUF, List);\r
+}\r
+\r
+/**\r
+ Get the next buffer block in the specific socket buffer.\r
+\r
+ @param[in] Sockbuf Pointer to the socket buffer.\r
+ @param[in] SockEntry Pointer to the buffer block prior to the required one.\r
+\r
+ @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is\r
+ the tail or head entry.\r
+\r
+**/\r
+NET_BUF *\r
+SockBufNext (\r
+ IN SOCK_BUFFER *Sockbuf,\r
+ IN NET_BUF *SockEntry\r
+ )\r
+{\r
+ LIST_ENTRY *NetbufList;\r
+\r
+ NetbufList = &(Sockbuf->DataQueue->BufList);\r
+\r
+ if ((SockEntry->List.ForwardLink == NetbufList) ||\r
+ (SockEntry->List.BackLink == &SockEntry->List) ||\r
+ (SockEntry->List.ForwardLink == &SockEntry->List)\r
+ ) {\r
+\r
+ return NULL;\r
+ }\r
+\r
+ return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List);\r
+}\r
+\r
+/**\r
+ User provided callback function for NetbufFromExt.\r
+\r
+ @param[in] Event The Event this notify function registered to, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SockFreeFoo (\r
+ IN EFI_EVENT Event\r
+ )\r
+{\r
+ return;\r
+}\r
+\r
+/**\r
+ Get the length of the data that can be retrieved from the socket\r
+ receive buffer.\r
+\r
+ @param[in] SockBuffer Pointer to the socket receive buffer.\r
+ @param[out] IsUrg Pointer to a BOOLEAN variable.\r
+ If TRUE the data is OOB.\r
+ @param[in] BufLen The maximum length of the data buffer to\r
+ store the received data in the socket layer.\r
+\r
+ @return The length of the data can be retreived.\r
+\r
+**/\r
+UINT32\r
+SockTcpDataToRcv (\r
+ IN SOCK_BUFFER *SockBuffer,\r
+ OUT BOOLEAN *IsUrg,\r
+ IN UINT32 BufLen\r
+ )\r
+{\r
+ NET_BUF *RcvBufEntry;\r
+ UINT32 DataLen;\r
+ TCP_RSV_DATA *TcpRsvData;\r
+ BOOLEAN Urg;\r
+\r
+ ASSERT ((SockBuffer != NULL) && (IsUrg != NULL) && (BufLen > 0));\r
+\r
+ //\r
+ // Get the first socket receive buffer\r
+ //\r
+ RcvBufEntry = SockBufFirst (SockBuffer);\r
+ ASSERT (RcvBufEntry != NULL);\r
+\r
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;\r
+\r
+ //\r
+ // Check whether the receive data is out of bound. If yes, calculate the maximum\r
+ // allowed length of the urgent data and output it.\r
+ //\r
+ *IsUrg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);\r
+\r
+ if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) {\r
+\r
+ DataLen = MIN (TcpRsvData->UrgLen, BufLen);\r
+\r
+ if (DataLen < TcpRsvData->UrgLen) {\r
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen;\r
+ } else {\r
+ TcpRsvData->UrgLen = 0;\r
+ }\r
+\r
+ return DataLen;\r
+\r
+ }\r
+\r
+ //\r
+ // Process the next socket receive buffer to get the maximum allowed length\r
+ // of the received data.\r
+ //\r
+ DataLen = RcvBufEntry->TotalSize;\r
+\r
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);\r
+\r
+ while ((BufLen > DataLen) && (RcvBufEntry != NULL)) {\r
+\r
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;\r
+\r
+ Urg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);\r
+\r
+ if (*IsUrg != Urg) {\r
+ break;\r
+ }\r
+\r
+ if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {\r
+\r
+ if (TcpRsvData->UrgLen + DataLen < BufLen) {\r
+ TcpRsvData->UrgLen = 0;\r
+ } else {\r
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen);\r
+ }\r
+\r
+ return MIN (TcpRsvData->UrgLen + DataLen, BufLen);\r
+\r
+ }\r
+\r
+ DataLen += RcvBufEntry->TotalSize;\r
+\r
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);\r
+ }\r
+\r
+ DataLen = MIN (BufLen, DataLen);\r
+ return DataLen;\r
+}\r
+\r
+/**\r
+ Copy data from socket buffer to an application provided receive buffer.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] TcpRxData Pointer to the application provided receive buffer.\r
+ @param[in] RcvdBytes The maximum length of the data can be copied.\r
+ @param[in] IsUrg If TRUE the data is Out of Bound, FALSE the data is normal.\r
+\r
+**/\r
+VOID\r
+SockSetTcpRxData (\r
+ IN SOCKET *Sock,\r
+ IN VOID *TcpRxData,\r
+ IN UINT32 RcvdBytes,\r
+ IN BOOLEAN IsUrg\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT32 CopyBytes;\r
+ UINT32 OffSet;\r
+ EFI_TCP4_RECEIVE_DATA *RxData;\r
+ EFI_TCP4_FRAGMENT_DATA *Fragment;\r
+\r
+ RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData;\r
+\r
+ OffSet = 0;\r
+\r
+ ASSERT (RxData->DataLength >= RcvdBytes);\r
+\r
+ RxData->DataLength = RcvdBytes;\r
+ RxData->UrgentFlag = IsUrg;\r
+\r
+ //\r
+ // Copy the CopyBytes data from socket receive buffer to RxData.\r
+ //\r
+ for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) {\r
+\r
+ Fragment = &RxData->FragmentTable[Index];\r
+ CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes);\r
+\r
+ NetbufQueCopy (\r
+ Sock->RcvBuffer.DataQueue,\r
+ OffSet,\r
+ CopyBytes,\r
+ Fragment->FragmentBuffer\r
+ );\r
+\r
+ Fragment->FragmentLength = CopyBytes;\r
+ RcvdBytes -= CopyBytes;\r
+ OffSet += CopyBytes;\r
+ }\r
+}\r
+\r
+/**\r
+ Process the send token.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockProcessSndToken (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ UINT32 FreeSpace;\r
+ SOCK_TOKEN *SockToken;\r
+ UINT32 DataLen;\r
+ SOCK_IO_TOKEN *SndToken;\r
+ EFI_TCP4_TRANSMIT_DATA *TxData;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT ((Sock != NULL) && (SockStream == Sock->Type));\r
+\r
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);\r
+\r
+ //\r
+ // to determine if process a send token using\r
+ // socket layer flow control policy\r
+ //\r
+ while ((FreeSpace >= Sock->SndBuffer.LowWater) && !IsListEmpty (&Sock->SndTokenList)) {\r
+\r
+ SockToken = NET_LIST_HEAD (\r
+ &(Sock->SndTokenList),\r
+ SOCK_TOKEN,\r
+ TokenList\r
+ );\r
+\r
+ //\r
+ // process this token\r
+ //\r
+ RemoveEntryList (&(SockToken->TokenList));\r
+ InsertTailList (\r
+ &(Sock->ProcessingSndTokenList),\r
+ &(SockToken->TokenList)\r
+ );\r
+\r
+ //\r
+ // Proceess it in the light of SockType\r
+ //\r
+ SndToken = (SOCK_IO_TOKEN *) SockToken->Token;\r
+ TxData = SndToken->Packet.TxData;\r
+\r
+ DataLen = TxData->DataLength;\r
+ Status = SockProcessTcpSndData (Sock, TxData);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto OnError;\r
+ }\r
+\r
+ if (DataLen >= FreeSpace) {\r
+ FreeSpace = 0;\r
+\r
+ } else {\r
+ FreeSpace -= DataLen;\r
+\r
+ }\r
+ }\r
+\r
+ return;\r
+\r
+OnError:\r
+\r
+ RemoveEntryList (&SockToken->TokenList);\r
+ SIGNAL_TOKEN (SockToken->Token, Status);\r
+ FreePool (SockToken);\r
+}\r
+\r
+/**\r
+ Get received data from the socket layer to the receive token.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in, out] RcvToken Pointer to the application provided receive token.\r
+\r
+ @return The length of data received in this token.\r
+\r
+**/\r
+UINT32\r
+SockProcessRcvToken (\r
+ IN OUT SOCKET *Sock,\r
+ IN OUT SOCK_IO_TOKEN *RcvToken\r
+ )\r
+{\r
+ UINT32 TokenRcvdBytes;\r
+ EFI_TCP4_RECEIVE_DATA *RxData;\r
+ BOOLEAN IsUrg;\r
+\r
+ ASSERT (Sock != NULL);\r
+\r
+ ASSERT (SockStream == Sock->Type);\r
+\r
+ RxData = RcvToken->Packet.RxData;\r
+\r
+ TokenRcvdBytes = SockTcpDataToRcv (\r
+ &Sock->RcvBuffer,\r
+ &IsUrg,\r
+ RxData->DataLength\r
+ );\r
+\r
+ //\r
+ // Copy data from RcvBuffer of socket to user\r
+ // provided RxData and set the fields in TCP RxData\r
+ //\r
+ SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg);\r
+\r
+ NetbufQueTrim (Sock->RcvBuffer.DataQueue, TokenRcvdBytes);\r
+ SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS);\r
+\r
+ return TokenRcvdBytes;\r
+}\r
+\r
+/**\r
+ Process the TCP send data, buffer the tcp txdata, and append\r
+ the buffer to socket send buffer, then try to send it.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] TcpTxData Pointer to the application provided send buffer.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockProcessTcpSndData (\r
+ IN SOCKET *Sock,\r
+ IN VOID *TcpTxData\r
+ )\r
+{\r
+ NET_BUF *SndData;\r
+ EFI_STATUS Status;\r
+ EFI_TCP4_TRANSMIT_DATA *TxData;\r
+\r
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData;\r
+\r
+ //\r
+ // transform this TxData into a NET_BUFFER\r
+ // and insert it into Sock->SndBuffer\r
+ //\r
+ SndData = NetbufFromExt (\r
+ (NET_FRAGMENT *) TxData->FragmentTable,\r
+ TxData->FragmentCount,\r
+ 0,\r
+ 0,\r
+ SockFreeFoo,\r
+ NULL\r
+ );\r
+\r
+ if (NULL == SndData) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockKProcessSndData: Failed to call NetBufferFromExt\n")\r
+ );\r
+\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData);\r
+\r
+ //\r
+ // notify the low layer protocol to handle this send token\r
+ //\r
+ if (TxData->Urgent) {\r
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ if (TxData->Push) {\r
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // low layer protocol should really handle the sending\r
+ // process when catching SOCK_SND request\r
+ //\r
+ Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Flush the tokens in the specific token list.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in, out] PendingTokenList Pointer to the token list to be flushed.\r
+\r
+**/\r
+VOID\r
+SockFlushPendingToken (\r
+ IN SOCKET *Sock,\r
+ IN OUT LIST_ENTRY *PendingTokenList\r
+ )\r
+{\r
+ SOCK_TOKEN *SockToken;\r
+ SOCK_COMPLETION_TOKEN *Token;\r
+\r
+ ASSERT ((Sock != NULL) && (PendingTokenList != NULL));\r
+\r
+ while (!IsListEmpty (PendingTokenList)) {\r
+ SockToken = NET_LIST_HEAD (\r
+ PendingTokenList,\r
+ SOCK_TOKEN,\r
+ TokenList\r
+ );\r
+\r
+ Token = SockToken->Token;\r
+ SIGNAL_TOKEN (Token, Sock->SockError);\r
+\r
+ RemoveEntryList (&(SockToken->TokenList));\r
+ FreePool (SockToken);\r
+ }\r
+}\r
+\r
+/**\r
+ Wake up the connection token while the connection is successfully established,\r
+ then try to process any pending send token.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockWakeConnToken (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ ASSERT (Sock->ConnectionToken != NULL);\r
+\r
+ SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS);\r
+ Sock->ConnectionToken = NULL;\r
+\r
+ //\r
+ // check to see if some pending send token existed?\r
+ //\r
+ SockProcessSndToken (Sock);\r
+}\r
+\r
+/**\r
+ Wake up the listen token while the connection is established successfully.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockWakeListenToken (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ SOCKET *Parent;\r
+ SOCK_TOKEN *SockToken;\r
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;\r
+\r
+ Parent = Sock->Parent;\r
+\r
+ ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock));\r
+\r
+ if (!IsListEmpty (&Parent->ListenTokenList)) {\r
+ SockToken = NET_LIST_HEAD (\r
+ &Parent->ListenTokenList,\r
+ SOCK_TOKEN,\r
+ TokenList\r
+ );\r
+\r
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token;\r
+ ListenToken->NewChildHandle = Sock->SockHandle;\r
+\r
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);\r
+\r
+ RemoveEntryList (&SockToken->TokenList);\r
+ FreePool (SockToken);\r
+\r
+ RemoveEntryList (&Sock->ConnectionList);\r
+\r
+ Parent->ConnCnt--;\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "SockWakeListenToken: accept a socket, now conncnt is %d",\r
+ Parent->ConnCnt)\r
+ );\r
+\r
+ Sock->Parent = NULL;\r
+ }\r
+}\r
+\r
+/**\r
+ Wake up the receive token while some data is received.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockWakeRcvToken (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ UINT32 RcvdBytes;\r
+ UINT32 TokenRcvdBytes;\r
+ SOCK_TOKEN *SockToken;\r
+ SOCK_IO_TOKEN *RcvToken;\r
+\r
+ ASSERT (Sock->RcvBuffer.DataQueue != NULL);\r
+\r
+ RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize;\r
+\r
+ ASSERT (RcvdBytes > 0);\r
+\r
+ while (RcvdBytes > 0 && !IsListEmpty (&Sock->RcvTokenList)) {\r
+\r
+ SockToken = NET_LIST_HEAD (\r
+ &Sock->RcvTokenList,\r
+ SOCK_TOKEN,\r
+ TokenList\r
+ );\r
+\r
+ RcvToken = (SOCK_IO_TOKEN *) SockToken->Token;\r
+ TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken);\r
+\r
+ if (0 == TokenRcvdBytes) {\r
+ return ;\r
+ }\r
+\r
+ RemoveEntryList (&(SockToken->TokenList));\r
+ FreePool (SockToken);\r
+ RcvdBytes -= TokenRcvdBytes;\r
+ }\r
+}\r
+\r
+/**\r
+ Create a socket with initial data SockInitData.\r
+\r
+ @param[in] SockInitData Pointer to the initial data of the socket.\r
+\r
+ @return Pointer to the newly created socket, return NULL when an exception occurs.\r
+\r
+**/\r
+SOCKET *\r
+SockCreate (\r
+ IN SOCK_INIT_DATA *SockInitData\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ SOCKET *Parent;\r
+ EFI_STATUS Status;\r
+ EFI_GUID *TcpProtocolGuid;\r
+ UINTN ProtocolLength;\r
+\r
+ ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL));\r
+ ASSERT (SockInitData->Type == SockStream);\r
+ ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN));\r
+\r
+ if (SockInitData->IpVersion == IP_VERSION_4) {\r
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;\r
+ ProtocolLength = sizeof (EFI_TCP4_PROTOCOL);\r
+ } else {\r
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;\r
+ ProtocolLength = sizeof (EFI_TCP6_PROTOCOL);\r
+ }\r
+\r
+\r
+ Parent = SockInitData->Parent;\r
+\r
+ if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n",\r
+ Parent->ConnCnt,\r
+ Parent->BackLog)\r
+ );\r
+\r
+ return NULL;\r
+ }\r
+\r
+ Sock = AllocateZeroPool (sizeof (SOCKET));\r
+ if (NULL == Sock) {\r
+\r
+ DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n"));\r
+ return NULL;\r
+ }\r
+\r
+ InitializeListHead (&Sock->Link);\r
+ InitializeListHead (&Sock->ConnectionList);\r
+ InitializeListHead (&Sock->ListenTokenList);\r
+ InitializeListHead (&Sock->RcvTokenList);\r
+ InitializeListHead (&Sock->SndTokenList);\r
+ InitializeListHead (&Sock->ProcessingSndTokenList);\r
+\r
+ EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK);\r
+\r
+ Sock->SndBuffer.DataQueue = NetbufQueAlloc ();\r
+ if (NULL == Sock->SndBuffer.DataQueue) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockCreate: No resource to allocate SndBuffer for new socket\n")\r
+ );\r
+\r
+ goto OnError;\r
+ }\r
+\r
+ Sock->RcvBuffer.DataQueue = NetbufQueAlloc ();\r
+ if (NULL == Sock->RcvBuffer.DataQueue) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockCreate: No resource to allocate RcvBuffer for new socket\n")\r
+ );\r
+\r
+ goto OnError;\r
+ }\r
+\r
+ Sock->Signature = SOCK_SIGNATURE;\r
+\r
+ Sock->Parent = Parent;\r
+ Sock->BackLog = SockInitData->BackLog;\r
+ Sock->ProtoHandler = SockInitData->ProtoHandler;\r
+ Sock->SndBuffer.HighWater = SockInitData->SndBufferSize;\r
+ Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize;\r
+ Sock->Type = SockInitData->Type;\r
+ Sock->DriverBinding = SockInitData->DriverBinding;\r
+ Sock->State = SockInitData->State;\r
+ Sock->CreateCallback = SockInitData->CreateCallback;\r
+ Sock->DestroyCallback = SockInitData->DestroyCallback;\r
+ Sock->Context = SockInitData->Context;\r
+\r
+ Sock->SockError = EFI_ABORTED;\r
+ Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER;\r
+ Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER;\r
+\r
+ Sock->IpVersion = SockInitData->IpVersion;\r
+\r
+ //\r
+ // Install protocol on Sock->SockHandle\r
+ //\r
+ CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength);\r
+\r
+ //\r
+ // copy the protodata into socket\r
+ //\r
+ CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize);\r
+\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Sock->SockHandle,\r
+ TcpProtocolGuid,\r
+ &Sock->NetProtocol,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockCreate: Install TCP protocol in socket failed with %r\n",\r
+ Status)\r
+ );\r
+\r
+ goto OnError;\r
+ }\r
+\r
+ if (Parent != NULL) {\r
+ ASSERT (Parent->BackLog > 0);\r
+ ASSERT (SOCK_IS_LISTENING (Parent));\r
+\r
+ //\r
+ // need to add it into Parent->ConnectionList\r
+ // if the Parent->ConnCnt < Parent->BackLog\r
+ //\r
+ Parent->ConnCnt++;\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "SockCreate: Create a new socket and add to parent, now conncnt is %d\n",\r
+ Parent->ConnCnt)\r
+ );\r
+\r
+ InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList);\r
+ }\r
+\r
+ if (Sock->CreateCallback != NULL) {\r
+ Status = Sock->CreateCallback (Sock, Sock->Context);\r
+ if (EFI_ERROR (Status)) {\r
+ goto OnError;\r
+ }\r
+ }\r
+\r
+ return Sock;\r
+\r
+OnError:\r
+\r
+ if (Sock->SockHandle != NULL) {\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Sock->SockHandle,\r
+ TcpProtocolGuid,\r
+ &Sock->NetProtocol,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ if (NULL != Sock->SndBuffer.DataQueue) {\r
+ NetbufQueFree (Sock->SndBuffer.DataQueue);\r
+ }\r
+\r
+ if (NULL != Sock->RcvBuffer.DataQueue) {\r
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);\r
+ }\r
+\r
+ FreePool (Sock);\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Destroy a socket.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockDestroy (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ VOID *SockProtocol;\r
+ EFI_GUID *TcpProtocolGuid;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (SockStream == Sock->Type);\r
+\r
+ if (Sock->DestroyCallback != NULL) {\r
+ Sock->DestroyCallback (Sock, Sock->Context);\r
+ }\r
+\r
+ //\r
+ // Flush the completion token buffered\r
+ // by sock and rcv, snd buffer\r
+ //\r
+ if (!SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+ SockConnFlush (Sock);\r
+ SockSetState (Sock, SO_CLOSED);\r
+ Sock->ConfigureState = SO_UNCONFIGURED;\r
+\r
+ }\r
+ //\r
+ // Destory the RcvBuffer Queue and SendBuffer Queue\r
+ //\r
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);\r
+ NetbufQueFree (Sock->SndBuffer.DataQueue);\r
+\r
+ //\r
+ // Remove it from parent connection list if needed\r
+ //\r
+ if (Sock->Parent != NULL) {\r
+\r
+ RemoveEntryList (&(Sock->ConnectionList));\r
+ (Sock->Parent->ConnCnt)--;\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "SockDestory: Delete a unaccepted socket from parent now conncnt is %d\n",\r
+ Sock->Parent->ConnCnt)\r
+ );\r
+\r
+ Sock->Parent = NULL;\r
+ }\r
+\r
+ //\r
+ // Set the protocol guid and driver binding handle\r
+ // in the light of Sock->SockType\r
+ //\r
+ if (Sock->IpVersion == IP_VERSION_4) {\r
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;\r
+ } else {\r
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;\r
+ }\r
+\r
+ //\r
+ // Retrieve the protocol installed on this sock\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Sock->SockHandle,\r
+ TcpProtocolGuid,\r
+ &SockProtocol,\r
+ Sock->DriverBinding,\r
+ Sock->SockHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockDestroy: Open protocol installed on socket failed with %r\n",\r
+ Status)\r
+ );\r
+\r
+ goto FreeSock;\r
+ }\r
+\r
+ //\r
+ // Uninstall the protocol installed on this sock\r
+ // in the light of Sock->SockType\r
+ //\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Sock->SockHandle,\r
+ TcpProtocolGuid,\r
+ SockProtocol,\r
+ NULL\r
+ );\r
+\r
+FreeSock:\r
+\r
+ FreePool (Sock);\r
+}\r
+\r
+/**\r
+ Flush the sndBuffer and rcvBuffer of socket.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockConnFlush (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ SOCKET *Child;\r
+\r
+ ASSERT (Sock != NULL);\r
+\r
+ //\r
+ // Clear the flag in this socket\r
+ //\r
+ Sock->Flag = 0;\r
+\r
+ //\r
+ // Flush the SndBuffer and RcvBuffer of Sock\r
+ //\r
+ NetbufQueFlush (Sock->SndBuffer.DataQueue);\r
+ NetbufQueFlush (Sock->RcvBuffer.DataQueue);\r
+\r
+ //\r
+ // Signal the pending token\r
+ //\r
+ if (Sock->ConnectionToken != NULL) {\r
+ SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError);\r
+ Sock->ConnectionToken = NULL;\r
+ }\r
+\r
+ if (Sock->CloseToken != NULL) {\r
+ SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError);\r
+ Sock->CloseToken = NULL;\r
+ }\r
+\r
+ SockFlushPendingToken (Sock, &(Sock->ListenTokenList));\r
+ SockFlushPendingToken (Sock, &(Sock->RcvTokenList));\r
+ SockFlushPendingToken (Sock, &(Sock->SndTokenList));\r
+ SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList));\r
+\r
+ //\r
+ // Destroy the pending connection, if it is a listening socket\r
+ //\r
+ if (SOCK_IS_LISTENING (Sock)) {\r
+ while (!IsListEmpty (&Sock->ConnectionList)) {\r
+ Child = NET_LIST_HEAD (\r
+ &Sock->ConnectionList,\r
+ SOCKET,\r
+ ConnectionList\r
+ );\r
+\r
+ SockDestroyChild (Child);\r
+ }\r
+\r
+ Sock->ConnCnt = 0;\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Set the state of the socket.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in] State The new socket state to be set.\r
+\r
+**/\r
+VOID\r
+SockSetState (\r
+ IN OUT SOCKET *Sock,\r
+ IN UINT8 State\r
+ )\r
+{\r
+ Sock->State = State;\r
+}\r
+\r
+/**\r
+ Clone a new socket, including its associated protocol control block.\r
+\r
+ @param[in] Sock Pointer to the socket to be cloned.\r
+\r
+ @return Pointer to the newly cloned socket. If NULL, an error condition occurred.\r
+\r
+**/\r
+SOCKET *\r
+SockClone (\r
+ IN SOCKET *Sock\r
+ )\r
+{\r
+ SOCKET *ClonedSock;\r
+ SOCK_INIT_DATA InitData;\r
+\r
+ InitData.BackLog = Sock->BackLog;\r
+ InitData.Parent = Sock;\r
+ InitData.State = Sock->State;\r
+ InitData.ProtoHandler = Sock->ProtoHandler;\r
+ InitData.Type = Sock->Type;\r
+ InitData.RcvBufferSize = Sock->RcvBuffer.HighWater;\r
+ InitData.SndBufferSize = Sock->SndBuffer.HighWater;\r
+ InitData.DriverBinding = Sock->DriverBinding;\r
+ InitData.IpVersion = Sock->IpVersion;\r
+ InitData.Protocol = &(Sock->NetProtocol);\r
+ InitData.CreateCallback = Sock->CreateCallback;\r
+ InitData.DestroyCallback = Sock->DestroyCallback;\r
+ InitData.Context = Sock->Context;\r
+ InitData.ProtoData = Sock->ProtoReserved;\r
+ InitData.DataSize = sizeof (Sock->ProtoReserved);\r
+\r
+ ClonedSock = SockCreate (&InitData);\r
+\r
+ if (NULL == ClonedSock) {\r
+ DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n"));\r
+ return NULL;\r
+ }\r
+\r
+ SockSetState (ClonedSock, SO_CONNECTING);\r
+ ClonedSock->ConfigureState = Sock->ConfigureState;\r
+\r
+ return ClonedSock;\r
+}\r
+\r
+/**\r
+ Called by the low layer protocol to indicate the socket a connection is\r
+ established.\r
+\r
+ This function just changes the socket's state to SO_CONNECTED\r
+ and signals the token used for connection establishment.\r
+\r
+ @param[in, out] Sock Pointer to the socket associated with the\r
+ established connection.\r
+\r
+**/\r
+VOID\r
+SockConnEstablished (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+\r
+ ASSERT (SO_CONNECTING == Sock->State);\r
+\r
+ SockSetState (Sock, SO_CONNECTED);\r
+\r
+ if (NULL == Sock->Parent) {\r
+ SockWakeConnToken (Sock);\r
+ } else {\r
+ SockWakeListenToken (Sock);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Called by the low layer protocol to indicate the connection is closed.\r
+\r
+ This function flushes the socket, sets the state to SO_CLOSED, and signals\r
+ the close token.\r
+\r
+ @param[in, out] Sock Pointer to the socket associated with the closed\r
+ connection.\r
+\r
+**/\r
+VOID\r
+SockConnClosed (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ if (Sock->CloseToken != NULL) {\r
+ SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS);\r
+ Sock->CloseToken = NULL;\r
+ }\r
+\r
+ SockConnFlush (Sock);\r
+ SockSetState (Sock, SO_CLOSED);\r
+\r
+ if (Sock->Parent != NULL) {\r
+ SockDestroyChild (Sock);\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ Called by low layer protocol to indicate that some data was sent or processed.\r
+\r
+ This function trims the sent data in the socket send buffer, and signals the data\r
+ token if proper.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in] Count The length of the data processed or sent, in bytes.\r
+\r
+**/\r
+VOID\r
+SockDataSent (\r
+ IN OUT SOCKET *Sock,\r
+ IN UINT32 Count\r
+ )\r
+{\r
+ SOCK_TOKEN *SockToken;\r
+ SOCK_COMPLETION_TOKEN *SndToken;\r
+\r
+ ASSERT (!IsListEmpty (&Sock->ProcessingSndTokenList));\r
+ ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize);\r
+\r
+ NetbufQueTrim (Sock->SndBuffer.DataQueue, Count);\r
+\r
+ //\r
+ // To check if we can signal some snd token in this socket\r
+ //\r
+ while (Count > 0) {\r
+ SockToken = NET_LIST_HEAD (\r
+ &(Sock->ProcessingSndTokenList),\r
+ SOCK_TOKEN,\r
+ TokenList\r
+ );\r
+\r
+ SndToken = SockToken->Token;\r
+\r
+ if (SockToken->RemainDataLen <= Count) {\r
+\r
+ RemoveEntryList (&(SockToken->TokenList));\r
+ SIGNAL_TOKEN (SndToken, EFI_SUCCESS);\r
+ Count -= SockToken->RemainDataLen;\r
+ FreePool (SockToken);\r
+ } else {\r
+\r
+ SockToken->RemainDataLen -= Count;\r
+ Count = 0;\r
+ }\r
+ }\r
+\r
+ //\r
+ // to judge if we can process some send token in\r
+ // Sock->SndTokenList, if so process those send token\r
+ //\r
+ SockProcessSndToken (Sock);\r
+}\r
+\r
+/**\r
+ Called by the low layer protocol to copy some data in the socket send\r
+ buffer starting from the specific offset to a buffer provided by\r
+ the caller.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Offset The start point of the data to be copied.\r
+ @param[in] Len The length of the data to be copied.\r
+ @param[out] Dest Pointer to the destination to copy the data.\r
+\r
+ @return The data size copied.\r
+\r
+**/\r
+UINT32\r
+SockGetDataToSend (\r
+ IN SOCKET *Sock,\r
+ IN UINT32 Offset,\r
+ IN UINT32 Len,\r
+ OUT UINT8 *Dest\r
+ )\r
+{\r
+ ASSERT ((Sock != NULL) && SockStream == Sock->Type);\r
+\r
+ return NetbufQueCopy (\r
+ Sock->SndBuffer.DataQueue,\r
+ Offset,\r
+ Len,\r
+ Dest\r
+ );\r
+}\r
+\r
+/**\r
+ Called by the low layer protocol to deliver received data to socket layer.\r
+\r
+ This function will append the data to the socket receive buffer, set the\r
+ urgent data length, and then check if any receive token can be signaled.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in, out] NetBuffer Pointer to the buffer that contains the received data.\r
+ @param[in] UrgLen The length of the urgent data in the received data.\r
+\r
+**/\r
+VOID\r
+SockDataRcvd (\r
+ IN OUT SOCKET *Sock,\r
+ IN OUT NET_BUF *NetBuffer,\r
+ IN UINT32 UrgLen\r
+ )\r
+{\r
+ ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) &&\r
+ UrgLen <= NetBuffer->TotalSize);\r
+\r
+ NET_GET_REF (NetBuffer);\r
+\r
+ ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen;\r
+\r
+ NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer);\r
+\r
+ SockWakeRcvToken (Sock);\r
+}\r
+\r
+/**\r
+ Get the length of the free space of the specific socket buffer.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Which Flag to indicate which socket buffer to check:\r
+ either send buffer or receive buffer.\r
+\r
+ @return The length of the free space, in bytes.\r
+\r
+**/\r
+UINT32\r
+SockGetFreeSpace (\r
+ IN SOCKET *Sock,\r
+ IN UINT32 Which\r
+ )\r
+{\r
+ UINT32 BufferCC;\r
+ SOCK_BUFFER *SockBuffer;\r
+\r
+ ASSERT (Sock != NULL && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which)));\r
+\r
+ if (SOCK_SND_BUF == Which) {\r
+ SockBuffer = &(Sock->SndBuffer);\r
+ } else {\r
+ SockBuffer = &(Sock->RcvBuffer);\r
+ }\r
+\r
+ BufferCC = (SockBuffer->DataQueue)->BufSize;\r
+\r
+ if (BufferCC >= SockBuffer->HighWater) {\r
+\r
+ return 0;\r
+ }\r
+\r
+ return SockBuffer->HighWater - BufferCC;\r
+}\r
+\r
+/**\r
+ Called by the low layer protocol to indicate that there will be no more data\r
+ from the communication peer.\r
+\r
+ This function sets the socket's state to SO_NO_MORE_DATA and signals all queued\r
+ IO tokens with the error status EFI_CONNECTION_FIN.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockNoMoreData (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ EFI_STATUS Err;\r
+\r
+ SOCK_NO_MORE_DATA (Sock);\r
+\r
+ if (!IsListEmpty (&Sock->RcvTokenList)) {\r
+\r
+ ASSERT (0 == GET_RCV_DATASIZE (Sock));\r
+\r
+ Err = Sock->SockError;\r
+\r
+ SOCK_ERROR (Sock, EFI_CONNECTION_FIN);\r
+\r
+ SockFlushPendingToken (Sock, &Sock->RcvTokenList);\r
+\r
+ SOCK_ERROR (Sock, Err);\r
+\r
+ }\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ The function declaration that provided for Socket Interface.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _SOCK_IMPL_H_\r
+#define _SOCK_IMPL_H_\r
+\r
+#include "Socket.h"\r
+\r
+/**\r
+ Signal a event with the given status.\r
+\r
+ @param[in] Token The token's event is to be signaled.\r
+ @param[in] TokenStatus The status to be sent with the event.\r
+\r
+**/\r
+#define SIGNAL_TOKEN(Token, TokenStatus) \\r
+ do { \\r
+ (Token)->Status = (TokenStatus); \\r
+ gBS->SignalEvent ((Token)->Event); \\r
+ } while (0)\r
+\r
+#define SOCK_HEADER_SPACE (60 + 60 + 72)\r
+\r
+/**\r
+ Process the TCP send data, buffer the tcp txdata and append\r
+ the buffer to socket send buffer, then try to send it.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] TcpTxData Pointer to the application provided send buffer.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockProcessTcpSndData (\r
+ IN SOCKET *Sock,\r
+ IN VOID *TcpTxData\r
+ );\r
+\r
+/**\r
+ Get received data from the socket layer to the receive token.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in, out] RcvToken Pointer to the application provided receive token.\r
+\r
+ @return The length of data received in this token.\r
+\r
+**/\r
+UINT32\r
+SockProcessRcvToken (\r
+ IN OUT SOCKET *Sock,\r
+ IN OUT SOCK_IO_TOKEN *RcvToken\r
+ );\r
+\r
+/**\r
+ Flush the sndBuffer and rcvBuffer of socket.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockConnFlush (\r
+ IN OUT SOCKET *Sock\r
+ );\r
+\r
+/**\r
+ Create a socket with initial data SockInitData.\r
+\r
+ @param[in] SockInitData Pointer to the initial data of the socket.\r
+\r
+ @return Pointer to the newly created socket, return NULL when exception occured.\r
+\r
+**/\r
+SOCKET *\r
+SockCreate (\r
+ IN SOCK_INIT_DATA *SockInitData\r
+ );\r
+\r
+/**\r
+ Destroy a socket.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockDestroy (\r
+ IN OUT SOCKET *Sock\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Interface function of the Socket.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "SockImpl.h"\r
+\r
+/**\r
+ Check whether the Event is in the List.\r
+\r
+ @param[in] List Pointer to the token list to be searched.\r
+ @param[in] Event The event to be checked.\r
+\r
+ @retval TRUE The specific Event exists in the List.\r
+ @retval FALSE The specific Event is not in the List.\r
+\r
+**/\r
+BOOLEAN\r
+SockTokenExistedInList (\r
+ IN LIST_ENTRY *List,\r
+ IN EFI_EVENT Event\r
+ )\r
+{\r
+ LIST_ENTRY *ListEntry;\r
+ SOCK_TOKEN *SockToken;\r
+\r
+ NET_LIST_FOR_EACH (ListEntry, List) {\r
+ SockToken = NET_LIST_USER_STRUCT (\r
+ ListEntry,\r
+ SOCK_TOKEN,\r
+ TokenList\r
+ );\r
+\r
+ if (Event == SockToken->Token->Event) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Call SockTokenExistedInList() to check whether the Event is\r
+ in the related socket's lists.\r
+\r
+ @param[in] Sock Pointer to the instance's socket.\r
+ @param[in] Event The event to be checked.\r
+\r
+ @retval TRUE The Event exists in related socket's lists.\r
+ @retval FALSE The Event is not in related socket's lists.\r
+\r
+**/\r
+BOOLEAN\r
+SockTokenExisted (\r
+ IN SOCKET *Sock,\r
+ IN EFI_EVENT Event\r
+ )\r
+{\r
+\r
+ if (SockTokenExistedInList (&Sock->SndTokenList, Event) ||\r
+ SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) ||\r
+ SockTokenExistedInList (&Sock->RcvTokenList, Event) ||\r
+ SockTokenExistedInList (&Sock->ListenTokenList, Event)\r
+ ) {\r
+\r
+ return TRUE;\r
+ }\r
+\r
+ if ((Sock->ConnectionToken != NULL) && (Sock->ConnectionToken->Event == Event)) {\r
+\r
+ return TRUE;\r
+ }\r
+\r
+ if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Buffer a token into the specific list of the socket Sock.\r
+\r
+ @param[in] Sock Pointer to the instance's socket.\r
+ @param[in] List Pointer to the list to store the token.\r
+ @param[in] Token Pointer to the token to be buffered.\r
+ @param[in] DataLen The data length of the buffer contained in Token.\r
+\r
+ @return Pointer to the token that wraps Token. If NULL, an error condition occurred.\r
+\r
+**/\r
+SOCK_TOKEN *\r
+SockBufferToken (\r
+ IN SOCKET *Sock,\r
+ IN LIST_ENTRY *List,\r
+ IN VOID *Token,\r
+ IN UINT32 DataLen\r
+ )\r
+{\r
+ SOCK_TOKEN *SockToken;\r
+\r
+ SockToken = AllocateZeroPool (sizeof (SOCK_TOKEN));\r
+ if (NULL == SockToken) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockBufferIOToken: No Memory to allocate SockToken\n")\r
+ );\r
+\r
+ return NULL;\r
+ }\r
+\r
+ SockToken->Sock = Sock;\r
+ SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token;\r
+ SockToken->RemainDataLen = DataLen;\r
+ InsertTailList (List, &SockToken->TokenList);\r
+\r
+ return SockToken;\r
+}\r
+\r
+/**\r
+ Destory the socket Sock and its associated protocol control block.\r
+\r
+ @param[in, out] Sock The socket to be destroyed.\r
+\r
+ @retval EFI_SUCCESS The socket Sock was destroyed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockDestroyChild (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));\r
+\r
+ if (Sock->IsDestroyed) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Sock->IsDestroyed = TRUE;\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockDestroyChild: Get the lock to access socket failed with %r\n",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // force protocol layer to detach the PCB\r
+ //\r
+ Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockDestroyChild: Protocol detach socket failed with %r\n",\r
+ Status)\r
+ );\r
+\r
+ Sock->IsDestroyed = FALSE;\r
+ } else if (SOCK_IS_CONFIGURED (Sock)) {\r
+\r
+ SockConnFlush (Sock);\r
+ SockSetState (Sock, SO_CLOSED);\r
+\r
+ Sock->ConfigureState = SO_UNCONFIGURED;\r
+ }\r
+\r
+ EfiReleaseLock (&(Sock->Lock));\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ SockDestroy (Sock);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create a socket and its associated protocol control block\r
+ with the intial data SockInitData and protocol specific\r
+ data ProtoData.\r
+\r
+ @param[in] SockInitData Inital data to setting the socket.\r
+\r
+ @return Pointer to the newly created socket. If NULL, an error condition occured.\r
+\r
+**/\r
+SOCKET *\r
+SockCreateChild (\r
+ IN SOCK_INIT_DATA *SockInitData\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // create a new socket\r
+ //\r
+ Sock = SockCreate (SockInitData);\r
+ if (NULL == Sock) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockCreateChild: No resource to create a new socket\n")\r
+ );\r
+\r
+ return NULL;\r
+ }\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockCreateChild: Get the lock to access socket failed with %r\n",\r
+ Status)\r
+ );\r
+\r
+ SockDestroy (Sock);\r
+ return NULL;\r
+ }\r
+ //\r
+ // inform the protocol layer to attach the socket\r
+ // with a new protocol control block\r
+ //\r
+ Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockCreateChild: Protocol failed to attach a socket with %r\n",\r
+ Status)\r
+ );\r
+\r
+ SockDestroy (Sock);\r
+ Sock = NULL;\r
+ }\r
+\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Sock;\r
+}\r
+\r
+/**\r
+ Configure the specific socket Sock using configuration data ConfigData.\r
+\r
+ @param[in] Sock Pointer to the socket to be configured.\r
+ @param[in] ConfigData Pointer to the configuration data.\r
+\r
+ @retval EFI_SUCCESS The socket configured successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is already configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConfigure (\r
+ IN SOCKET *Sock,\r
+ IN VOID *ConfigData\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockConfigure: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_CONFIGURED (Sock)) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto OnExit;\r
+ }\r
+\r
+ ASSERT (Sock->State == SO_CLOSED);\r
+\r
+ Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData);\r
+\r
+OnExit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Initiate a connection establishment process.\r
+\r
+ @param[in] Sock Pointer to the socket to initiate the initate the\r
+ connection.\r
+ @param[in] Token Pointer to the token used for the connection\r
+ operation.\r
+\r
+ @retval EFI_SUCCESS The connection initialized successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not configured to\r
+ be an active one, or the token is already in one of\r
+ this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConnect (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT Event;\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockConnect: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_NO_MAPPING (Sock)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto OnExit;\r
+ }\r
+\r
+ if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+ Status = EFI_NOT_STARTED;\r
+ goto OnExit;\r
+ }\r
+\r
+ if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto OnExit;\r
+ }\r
+\r
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;\r
+\r
+ if (SockTokenExisted (Sock, Event)) {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto OnExit;\r
+ }\r
+\r
+ Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token;\r
+ SockSetState (Sock, SO_CONNECTING);\r
+ Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL);\r
+\r
+OnExit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Issue a listen token to get an existed connected network instance\r
+ or wait for a connection if there is none.\r
+\r
+ @param[in] Sock Pointer to the socket to accept connections.\r
+ @param[in] Token The token to accept a connection.\r
+\r
+ @retval EFI_SUCCESS Either a connection is accpeted or the Token is\r
+ buffered for further acception.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not configured to\r
+ be a passive one, or the token is already in one of\r
+ this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockAccept (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ )\r
+{\r
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;\r
+ LIST_ENTRY *ListEntry;\r
+ EFI_STATUS Status;\r
+ SOCKET *Socket;\r
+ EFI_EVENT Event;\r
+\r
+ ASSERT (SockStream == Sock->Type);\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockAccept: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_NO_MAPPING (Sock)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ if (!SOCK_IS_LISTENING (Sock)) {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;\r
+\r
+ if (SockTokenExisted (Sock, Event)) {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token;\r
+\r
+ //\r
+ // Check if a connection has already in this Sock->ConnectionList\r
+ //\r
+ NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) {\r
+\r
+ Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList);\r
+\r
+ if (SOCK_IS_CONNECTED (Socket)) {\r
+ ListenToken->NewChildHandle = Socket->SockHandle;\r
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);\r
+\r
+ RemoveEntryList (ListEntry);\r
+\r
+ ASSERT (Socket->Parent != NULL);\r
+\r
+ Socket->Parent->ConnCnt--;\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "SockAccept: Accept a socket, now conncount is %d",\r
+ Socket->Parent->ConnCnt)\r
+ );\r
+ Socket->Parent = NULL;\r
+\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Buffer this token for latter incoming connection request\r
+ //\r
+ if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) {\r
+\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+Exit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Issue a token with data to the socket to send out.\r
+\r
+ @param[in] Sock Pointer to the socket to process the token with\r
+ data.\r
+ @param[in] Token The token with data that needs to send out.\r
+\r
+ @retval EFI_SUCCESS The token processed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not in a\r
+ synchronized state , or the token is already in one\r
+ of this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limits.\r
+\r
+**/\r
+EFI_STATUS\r
+SockSend (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ )\r
+{\r
+ SOCK_IO_TOKEN *SndToken;\r
+ EFI_EVENT Event;\r
+ UINT32 FreeSpace;\r
+ EFI_TCP4_TRANSMIT_DATA *TxData;\r
+ EFI_STATUS Status;\r
+ SOCK_TOKEN *SockToken;\r
+ UINT32 DataLen;\r
+\r
+ ASSERT (SockStream == Sock->Type);\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockSend: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_NO_MAPPING (Sock)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ SndToken = (SOCK_IO_TOKEN *) Token;\r
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData;\r
+\r
+ if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // check if a token is already in the token buffer\r
+ //\r
+ Event = SndToken->Token.Event;\r
+\r
+ if (SockTokenExisted (Sock, Event)) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ DataLen = TxData->DataLength;\r
+\r
+ //\r
+ // process this sending token now or buffer it only?\r
+ //\r
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);\r
+\r
+ if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) {\r
+\r
+ SockToken = SockBufferToken (\r
+ Sock,\r
+ &Sock->SndTokenList,\r
+ SndToken,\r
+ DataLen\r
+ );\r
+\r
+ if (NULL == SockToken) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ }\r
+ } else {\r
+\r
+ SockToken = SockBufferToken (\r
+ Sock,\r
+ &Sock->ProcessingSndTokenList,\r
+ SndToken,\r
+ DataLen\r
+ );\r
+\r
+ if (NULL == SockToken) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockSend: Failed to buffer IO token into socket processing SndToken List\n",\r
+ Status)\r
+ );\r
+\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Exit;\r
+ }\r
+\r
+ Status = SockProcessTcpSndData (Sock, TxData);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockSend: Failed to process Snd Data\n",\r
+ Status)\r
+ );\r
+\r
+ RemoveEntryList (&(SockToken->TokenList));\r
+ FreePool (SockToken);\r
+ }\r
+ }\r
+\r
+Exit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Issue a token to get data from the socket.\r
+\r
+ @param[in] Sock Pointer to the socket to get data from.\r
+ @param[in] Token The token to store the received data from the\r
+ socket.\r
+\r
+ @retval EFI_SUCCESS The token processed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not in a\r
+ synchronized state , or the token is already in one\r
+ of this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+ @retval EFI_CONNECTION_FIN The connection is closed and there is no more data.\r
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRcv (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ )\r
+{\r
+ SOCK_IO_TOKEN *RcvToken;\r
+ UINT32 RcvdBytes;\r
+ EFI_STATUS Status;\r
+ EFI_EVENT Event;\r
+\r
+ ASSERT (SockStream == Sock->Type);\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockRcv: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_NO_MAPPING (Sock)) {\r
+\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ RcvToken = (SOCK_IO_TOKEN *) Token;\r
+\r
+ //\r
+ // check if a token is already in the token buffer of this socket\r
+ //\r
+ Event = RcvToken->Token.Event;\r
+ if (SockTokenExisted (Sock, Event)) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ RcvToken = (SOCK_IO_TOKEN *) Token;\r
+ RcvdBytes = GET_RCV_DATASIZE (Sock);\r
+\r
+ //\r
+ // check whether an error has happened before\r
+ //\r
+ if (EFI_ABORTED != Sock->SockError) {\r
+\r
+ SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError);\r
+ Sock->SockError = EFI_ABORTED;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // check whether can not receive and there is no any\r
+ // data buffered in Sock->RcvBuffer\r
+ //\r
+ if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) {\r
+\r
+ Status = EFI_CONNECTION_FIN;\r
+ goto Exit;\r
+ }\r
+\r
+ if (RcvdBytes != 0) {\r
+ Status = SockProcessRcvToken (Sock, RcvToken);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL);\r
+ } else {\r
+\r
+ if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+Exit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Reset the socket and its associated protocol control block.\r
+\r
+ @param[in, out] Sock Pointer to the socket to be flushed.\r
+\r
+ @retval EFI_SUCCESS The socket is flushed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockFlush (\r
+ IN OUT SOCKET *Sock\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (SockStream == Sock->Type);\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockFlush: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (!SOCK_IS_CONFIGURED (Sock)) {\r
+\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockFlush: Protocol failed handling SOCK_FLUSH with %r",\r
+ Status)\r
+ );\r
+\r
+ goto Exit;\r
+ }\r
+\r
+ SOCK_ERROR (Sock, EFI_ABORTED);\r
+ SockConnFlush (Sock);\r
+ SockSetState (Sock, SO_CLOSED);\r
+\r
+ Sock->ConfigureState = SO_UNCONFIGURED;\r
+\r
+Exit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Close or abort the socket associated connection.\r
+\r
+ @param[in, out] Sock Pointer to the socket of the connection to close\r
+ or abort.\r
+ @param[in] Token The token for a close operation.\r
+ @param[in] OnAbort TRUE for aborting the connection; FALSE to close it.\r
+\r
+ @retval EFI_SUCCESS The close or abort operation initialized\r
+ successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not in a\r
+ synchronized state , or the token is already in one\r
+ of this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockClose (\r
+ IN OUT SOCKET *Sock,\r
+ IN VOID *Token,\r
+ IN BOOLEAN OnAbort\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT Event;\r
+\r
+ ASSERT (SockStream == Sock->Type);\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockClose: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_NO_MAPPING (Sock)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ if (SOCK_IS_DISCONNECTING (Sock)) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;\r
+\r
+ if (SockTokenExisted (Sock, Event)) {\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto Exit;\r
+ }\r
+\r
+ Sock->CloseToken = Token;\r
+ SockSetState (Sock, SO_DISCONNECTING);\r
+\r
+ if (OnAbort) {\r
+ Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL);\r
+ } else {\r
+ Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL);\r
+ }\r
+\r
+Exit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get the mode data of the low layer protocol.\r
+\r
+ @param[in] Sock Pointer to the socket to get mode data from.\r
+ @param[in, out] Mode Pointer to the data to store the low layer mode\r
+ information.\r
+\r
+ @retval EFI_SUCCESS The mode data was obtained successfully.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGetMode (\r
+ IN SOCKET *Sock,\r
+ IN OUT VOID *Mode\r
+ )\r
+{\r
+ return Sock->ProtoHandler (Sock, SOCK_MODE, Mode);\r
+}\r
+\r
+/**\r
+ Configure the low level protocol to join a multicast group for\r
+ this socket's connection.\r
+\r
+ @param[in] Sock Pointer to the socket of the connection to join the\r
+ specific multicast group.\r
+ @param[in] GroupInfo Pointer to the multicast group info.\r
+\r
+ @retval EFI_SUCCESS The configuration completed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGroup (\r
+ IN SOCKET *Sock,\r
+ IN VOID *GroupInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+\r
+ if (EFI_ERROR (Status)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockGroup: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo);\r
+\r
+Exit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Add or remove route information in IP route table associated\r
+ with this socket.\r
+\r
+ @param[in] Sock Pointer to the socket associated with the IP route\r
+ table to operate on.\r
+ @param[in] RouteInfo Pointer to the route information to be processed.\r
+\r
+ @retval EFI_SUCCESS The route table updated successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRoute (\r
+ IN SOCKET *Sock,\r
+ IN VOID *RouteInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "SockRoute: Get the access for socket failed with %r",\r
+ Status)\r
+ );\r
+\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ if (SOCK_IS_NO_MAPPING (Sock)) {\r
+ Status = EFI_NO_MAPPING;\r
+ goto Exit;\r
+ }\r
+\r
+ if (SOCK_IS_UNCONFIGURED (Sock)) {\r
+ Status = EFI_NOT_STARTED;\r
+ goto Exit;\r
+ }\r
+\r
+ Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo);\r
+\r
+Exit:\r
+ EfiReleaseLock (&(Sock->Lock));\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Common head file for TCP socket.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _SOCKET_H_\r
+#define _SOCKET_H_\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Tcp4.h>\r
+#include <Protocol/Tcp6.h>\r
+\r
+#include <Library/NetLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/DpcLib.h>\r
+\r
+#define SOCK_SND_BUF 0\r
+#define SOCK_RCV_BUF 1\r
+\r
+#define SOCK_BUFF_LOW_WATER (2 * 1024)\r
+#define SOCK_RCV_BUFF_SIZE (8 * 1024)\r
+#define SOCK_SND_BUFF_SIZE (8 * 1024)\r
+#define SOCK_BACKLOG 5\r
+\r
+#define PROTO_RESERVED_LEN 20\r
+\r
+#define SO_NO_MORE_DATA 0x0001\r
+\r
+//\r
+//\r
+//\r
+// When a socket is created it enters into SO_UNCONFIGURED,\r
+// no actions can be taken on this socket, only after calling\r
+// SockConfigure. The state transition diagram of socket is\r
+// as following:\r
+//\r
+// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING\r
+// ^ | |\r
+// | ---> SO_LISTENING |\r
+// | |\r
+// |------------------SO_DISCONNECTING<-- SO_CONNECTED\r
+//\r
+// A passive socket can only go into SO_LISTENING and\r
+// SO_UNCONFIGURED state. SO_XXXING state is a middle state\r
+// when a socket is undergoing a protocol procedure such\r
+// as requesting a TCP connection.\r
+//\r
+//\r
+//\r
+\r
+///\r
+/// Socket state\r
+///\r
+#define SO_CLOSED 0\r
+#define SO_LISTENING 1\r
+#define SO_CONNECTING 2\r
+#define SO_CONNECTED 3\r
+#define SO_DISCONNECTING 4\r
+\r
+///\r
+/// Socket configure state\r
+///\r
+#define SO_UNCONFIGURED 0\r
+#define SO_CONFIGURED_ACTIVE 1\r
+#define SO_CONFIGURED_PASSIVE 2\r
+#define SO_NO_MAPPING 3\r
+\r
+///\r
+/// The request issued from socket layer to protocol layer.\r
+///\r
+#define SOCK_ATTACH 0 ///< Attach current socket to a new PCB\r
+#define SOCK_DETACH 1 ///< Detach current socket from the PCB\r
+#define SOCK_CONFIGURE 2 ///< Configure attached PCB\r
+#define SOCK_FLUSH 3 ///< Flush attached PCB\r
+#define SOCK_SND 4 ///< Need protocol to send something\r
+#define SOCK_SNDPUSH 5 ///< Need protocol to send pushed data\r
+#define SOCK_SNDURG 6 ///< Need protocol to send urgent data\r
+#define SOCK_CONSUMED 7 ///< Application has retrieved data from socket\r
+#define SOCK_CONNECT 8 ///< Need to connect to a peer\r
+#define SOCK_CLOSE 9 ///< Need to close the protocol process\r
+#define SOCK_ABORT 10 ///< Need to reset the protocol process\r
+#define SOCK_POLL 11 ///< Need to poll to the protocol layer\r
+#define SOCK_ROUTE 12 ///< Need to add a route information\r
+#define SOCK_MODE 13 ///< Need to get the mode data of the protocol\r
+#define SOCK_GROUP 14 ///< Need to join a mcast group\r
+\r
+/**\r
+ Set socket SO_NO_MORE_DATA flag.\r
+\r
+ @param[in] Sock Pointer to the socket\r
+\r
+**/\r
+#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA)\r
+\r
+/**\r
+ Check whether the socket is unconfigured.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is unconfigued.\r
+ @retval FALSE The socket is not unconfigued.\r
+\r
+**/\r
+#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED)\r
+\r
+/**\r
+ Check whether the socket is configured.\r
+\r
+ @param[in] Sock Pointer to the socket\r
+\r
+ @retval TRUE The socket is configued\r
+ @retval FALSE The socket is not configued\r
+\r
+**/\r
+#define SOCK_IS_CONFIGURED(Sock) \\r
+ (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \\r
+ ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE))\r
+\r
+/**\r
+ Check whether the socket is configured to active mode.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is configued to active mode.\r
+ @retval FALSE The socket is not configued to active mode.\r
+\r
+**/\r
+#define SOCK_IS_CONFIGURED_ACTIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE)\r
+\r
+/**\r
+ Check whether the socket is configured to passive mode.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is configued to passive mode.\r
+ @retval FALSE The socket is not configued to passive mode.\r
+\r
+**/\r
+#define SOCK_IS_CONNECTED_PASSIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)\r
+\r
+/**\r
+ Check whether the socket is mapped.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is not mapping.\r
+ @retval FALSE The socket is mapped.\r
+\r
+**/\r
+#define SOCK_IS_NO_MAPPING(Sock) ((Sock)->ConfigureState == SO_NO_MAPPING)\r
+\r
+/**\r
+ Check whether the socket is closed.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is closed.\r
+ @retval FALSE The socket is not closed.\r
+\r
+**/\r
+#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED)\r
+\r
+/**\r
+ Check whether the socket is listening.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is listening.\r
+ @retval FALSE The socket is not listening.\r
+\r
+**/\r
+#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING)\r
+\r
+/**\r
+ Check whether the socket is connecting.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is connecting.\r
+ @retval FALSE The socket is not connecting.\r
+\r
+**/\r
+#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING)\r
+\r
+/**\r
+ Check whether the socket has connected.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket has connected.\r
+ @retval FALSE The socket has not connected.\r
+\r
+**/\r
+#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED)\r
+\r
+/**\r
+ Check whether the socket is disconnecting.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is disconnecting.\r
+ @retval FALSE The socket is not disconnecting.\r
+\r
+**/\r
+#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING)\r
+\r
+/**\r
+ Check whether the socket is no more data.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @retval TRUE The socket is no more data.\r
+ @retval FALSE The socket still has data.\r
+\r
+**/\r
+#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA))\r
+\r
+/**\r
+ Set the size of the receive buffer.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Size The size to set.\r
+\r
+**/\r
+#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size))\r
+\r
+/**\r
+ Get the size of the receive buffer.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @return The receive buffer size.\r
+\r
+**/\r
+#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater)\r
+\r
+/**\r
+ Get the size of the receive data.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @return The received data size.\r
+\r
+**/\r
+#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize)\r
+\r
+/**\r
+ Set the size of the send buffer.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Size The size to set.\r
+\r
+**/\r
+#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size))\r
+\r
+/**\r
+ Get the size of the send buffer.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @return The send buffer size.\r
+\r
+**/\r
+#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater)\r
+\r
+/**\r
+ Get the size of the send data.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @return The send data size.\r
+\r
+**/\r
+#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize)\r
+\r
+/**\r
+ Set the backlog value of the socket.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Value The value to set.\r
+\r
+**/\r
+#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value))\r
+\r
+/**\r
+ Get the backlog value of the socket.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+\r
+ @return The backlog value.\r
+\r
+**/\r
+#define GET_BACKLOG(Sock) ((Sock)->BackLog)\r
+\r
+/**\r
+ Set the socket with error state.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Error The error state.\r
+\r
+**/\r
+#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error))\r
+\r
+#define SOCK_SIGNATURE SIGNATURE_32 ('S', 'O', 'C', 'K')\r
+\r
+#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE)\r
+\r
+#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock)\r
+\r
+#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) ((Type *) (((SOCK_TOKEN *) (SockToken))->Token))\r
+\r
+typedef struct _TCP_SOCKET SOCKET;\r
+\r
+///\r
+/// Socket completion token\r
+///\r
+typedef struct _SOCK_COMPLETION_TOKEN {\r
+ EFI_EVENT Event; ///< The event to be issued\r
+ EFI_STATUS Status; ///< The status to be issued\r
+} SOCK_COMPLETION_TOKEN;\r
+\r
+typedef union {\r
+ VOID *RxData;\r
+ VOID *TxData;\r
+} SOCK_IO_DATA;\r
+\r
+///\r
+/// The application token with data packet\r
+///\r
+typedef struct _SOCK_IO_TOKEN {\r
+ SOCK_COMPLETION_TOKEN Token;\r
+ SOCK_IO_DATA Packet;\r
+} SOCK_IO_TOKEN;\r
+\r
+///\r
+/// The socket type.\r
+///\r
+typedef enum {\r
+ SockDgram, ///< This socket providing datagram service\r
+ SockStream ///< This socket providing stream service\r
+} SOCK_TYPE;\r
+\r
+///\r
+/// The buffer structure of rcvd data and send data used by socket.\r
+///\r
+typedef struct _SOCK_BUFFER {\r
+ UINT32 HighWater; ///< The buffersize upper limit of sock_buffer\r
+ UINT32 LowWater; ///< The low warter mark of sock_buffer\r
+ NET_BUF_QUEUE *DataQueue; ///< The queue to buffer data\r
+} SOCK_BUFFER;\r
+\r
+/**\r
+ The handler of protocol for request from socket.\r
+\r
+ @param[in] Socket The socket issuing the request to protocol.\r
+ @param[in] Request The request issued by socket.\r
+ @param[in] RequestData The request related data.\r
+\r
+ @retval EFI_SUCCESS The socket request is completed successfully.\r
+ @retval other The error status returned by the corresponding TCP\r
+ layer function.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*SOCK_PROTO_HANDLER) (\r
+ IN SOCKET *Socket,\r
+ IN UINT8 Request,\r
+ IN VOID *RequestData\r
+ );\r
+\r
+/**\r
+ The Callback funtion called after the TCP socket is created.\r
+\r
+ @param[in] This Pointer to the socket just created.\r
+ @param[in] Context Context of the socket.\r
+\r
+ @retval EFI_SUCCESS This protocol installed successfully.\r
+ @retval other Some error occured.\r
+\r
+**/\r
+typedef\r
+EFI_STATUS\r
+(*SOCK_CREATE_CALLBACK) (\r
+ IN SOCKET *This,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ The callback function called before the TCP socket is to be destroyed.\r
+\r
+ @param[in] This The TCP socket to be destroyed.\r
+ @param[in] Context The context.\r
+\r
+**/\r
+typedef\r
+VOID\r
+(*SOCK_DESTROY_CALLBACK) (\r
+ IN SOCKET *This,\r
+ IN VOID *Context\r
+ );\r
+\r
+///\r
+/// The initialize data for create a new socket.\r
+///\r
+typedef struct _SOCK_INIT_DATA {\r
+ SOCK_TYPE Type;\r
+ UINT8 State;\r
+\r
+ SOCKET *Parent; ///< The parent of this socket\r
+ UINT32 BackLog; ///< The connection limit for listening socket\r
+ UINT32 SndBufferSize; ///< The high warter mark of send buffer\r
+ UINT32 RcvBufferSize; ///< The high warter mark of receive buffer\r
+ UINT8 IpVersion;\r
+ VOID *Protocol; ///< The pointer to protocol function template\r
+ ///< wanted to install on socket\r
+\r
+ //\r
+ // Callbacks after socket is created and before socket is to be destroyed.\r
+ //\r
+ SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created\r
+ SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied\r
+ VOID *Context; ///< The context of the callback\r
+\r
+ //\r
+ // Opaque protocol data.\r
+ //\r
+ VOID *ProtoData;\r
+ UINT32 DataSize;\r
+\r
+ SOCK_PROTO_HANDLER ProtoHandler; ///< The handler of protocol for socket request\r
+\r
+ EFI_HANDLE DriverBinding; ///< The driver binding handle\r
+} SOCK_INIT_DATA;\r
+\r
+///\r
+/// The union type of TCP and UDP protocol.\r
+///\r
+typedef union _NET_PROTOCOL {\r
+ EFI_TCP4_PROTOCOL Tcp4Protocol; ///< Tcp4 protocol\r
+ EFI_TCP6_PROTOCOL Tcp6Protocol; ///< Tcp6 protocol\r
+} NET_PROTOCOL;\r
+///\r
+/// The socket structure representing a network service access point.\r
+///\r
+struct _TCP_SOCKET {\r
+ //\r
+ // Socket description information\r
+ //\r
+ UINT32 Signature; ///< Signature of the socket\r
+ EFI_HANDLE SockHandle; ///< The virtual handle of the socket\r
+ EFI_HANDLE DriverBinding; ///< Socket's driver binding protocol\r
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ LIST_ENTRY Link;\r
+ UINT8 ConfigureState;\r
+ SOCK_TYPE Type;\r
+ UINT8 State;\r
+ UINT16 Flag;\r
+ EFI_LOCK Lock; ///< The lock of socket\r
+ SOCK_BUFFER SndBuffer; ///< Send buffer of application's data\r
+ SOCK_BUFFER RcvBuffer; ///< Receive buffer of received data\r
+ EFI_STATUS SockError; ///< The error returned by low layer protocol\r
+ BOOLEAN IsDestroyed;\r
+\r
+ //\r
+ // Fields used to manage the connection request\r
+ //\r
+ UINT32 BackLog; ///< the limit of connection to this socket\r
+ UINT32 ConnCnt; ///< the current count of connections to it\r
+ SOCKET *Parent; ///< listening parent that accept the connection\r
+ LIST_ENTRY ConnectionList; ///< the connections maintained by this socket\r
+ //\r
+ // The queue to buffer application's asynchronous token\r
+ //\r
+ LIST_ENTRY ListenTokenList;\r
+ LIST_ENTRY RcvTokenList;\r
+ LIST_ENTRY SndTokenList;\r
+ LIST_ENTRY ProcessingSndTokenList;\r
+\r
+ SOCK_COMPLETION_TOKEN *ConnectionToken; ///< app's token to signal if connected\r
+ SOCK_COMPLETION_TOKEN *CloseToken; ///< app's token to signal if closed\r
+ //\r
+ // Interface for low level protocol\r
+ //\r
+ SOCK_PROTO_HANDLER ProtoHandler; ///< The request handler of protocol\r
+ UINT8 ProtoReserved[PROTO_RESERVED_LEN]; ///< Data fields reserved for protocol\r
+ UINT8 IpVersion;\r
+ NET_PROTOCOL NetProtocol; ///< TCP or UDP protocol socket used\r
+ //\r
+ // Callbacks after socket is created and before socket is to be destroyed.\r
+ //\r
+ SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created\r
+ SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied\r
+ VOID *Context; ///< The context of the callback\r
+};\r
+\r
+///\r
+/// The token structure buffered in socket layer.\r
+///\r
+typedef struct _SOCK_TOKEN {\r
+ LIST_ENTRY TokenList; ///< The entry to add in the token list\r
+ SOCK_COMPLETION_TOKEN *Token; ///< The application's token\r
+ UINT32 RemainDataLen; ///< Unprocessed data length\r
+ SOCKET *Sock; ///< The poninter to the socket this token\r
+ ///< belongs to\r
+} SOCK_TOKEN;\r
+\r
+///\r
+/// Reserved data to access the NET_BUF delivered by TCP driver.\r
+///\r
+typedef struct _TCP_RSV_DATA {\r
+ UINT32 UrgLen;\r
+} TCP_RSV_DATA;\r
+\r
+//\r
+// Socket provided oprerations for low layer protocol implemented in SockImpl.c\r
+//\r
+\r
+/**\r
+ Set the state of the socket.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in] State The new socket state to be set.\r
+\r
+**/\r
+VOID\r
+SockSetState (\r
+ IN OUT SOCKET *Sock,\r
+ IN UINT8 State\r
+ );\r
+\r
+/**\r
+ Clone a new socket including its associated protocol control block.\r
+\r
+ @param[in] Sock Pointer to the socket to be cloned.\r
+\r
+ @return Pointer to the newly cloned socket. If NULL, an error condition occurred.\r
+\r
+**/\r
+SOCKET *\r
+SockClone (\r
+ IN SOCKET *Sock\r
+ );\r
+\r
+/**\r
+ Called by the low layer protocol to indicate the socket a connection is\r
+ established.\r
+\r
+ This function just changes the socket's state to SO_CONNECTED\r
+ and signals the token used for connection establishment.\r
+\r
+ @param[in, out] Sock Pointer to the socket associated with the\r
+ established connection.\r
+\r
+**/\r
+VOID\r
+SockConnEstablished (\r
+ IN OUT SOCKET *Sock\r
+ );\r
+\r
+/**\r
+ Called by the low layer protocol to indicate that the connection is closed.\r
+\r
+ This function flushes the socket, sets the state to SO_CLOSED, and signals\r
+ the close token.\r
+\r
+ @param[in, out] Sock Pointer to the socket associated with the closed\r
+ connection.\r
+\r
+**/\r
+VOID\r
+SockConnClosed (\r
+ IN OUT SOCKET *Sock\r
+ );\r
+\r
+/**\r
+ Called by low layer protocol to indicate that some data is sent or processed.\r
+\r
+ This function trims the sent data in the socket send buffer and signals the data\r
+ token, if proper.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in] Count The length of the data processed or sent, in bytes.\r
+\r
+**/\r
+VOID\r
+SockDataSent (\r
+ IN OUT SOCKET *Sock,\r
+ IN UINT32 Count\r
+ );\r
+\r
+/**\r
+ Called by the low layer protocol to copy some data in socket send\r
+ buffer starting from the specific offset to a buffer provided by\r
+ the caller.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Offset The start point of the data to be copied.\r
+ @param[in] Len The length of the data to be copied.\r
+ @param[out] Dest Pointer to the destination to copy the data.\r
+\r
+ @return The data size copied.\r
+\r
+**/\r
+UINT32\r
+SockGetDataToSend (\r
+ IN SOCKET *Sock,\r
+ IN UINT32 Offset,\r
+ IN UINT32 Len,\r
+ OUT UINT8 *Dest\r
+ );\r
+\r
+/**\r
+ Called by the low layer protocol to deliver received data to socket layer.\r
+\r
+ This function appends the data to the socket receive buffer, set the\r
+ urgent data length, then checks if any receive token can be signaled.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+ @param[in, out] NetBuffer Pointer to the buffer that contains the received data.\r
+ @param[in] UrgLen The length of the urgent data in the received data.\r
+\r
+**/\r
+VOID\r
+SockDataRcvd (\r
+ IN OUT SOCKET *Sock,\r
+ IN OUT NET_BUF *NetBuffer,\r
+ IN UINT32 UrgLen\r
+ );\r
+\r
+/**\r
+ Get the length of the free space of the specific socket buffer.\r
+\r
+ @param[in] Sock Pointer to the socket.\r
+ @param[in] Which Flag to indicate which socket buffer to check:\r
+ either send buffer or receive buffer.\r
+\r
+ @return The length of the free space, in bytes.\r
+\r
+**/\r
+UINT32\r
+SockGetFreeSpace (\r
+ IN SOCKET *Sock,\r
+ IN UINT32 Which\r
+ );\r
+\r
+/**\r
+ Called by the low layer protocol to indicate that there will be no more data\r
+ from the communication peer.\r
+\r
+ This function sets the socket's state to SO_NO_MORE_DATA and signals all queued\r
+ IO tokens with the error status EFI_CONNECTION_FIN.\r
+\r
+ @param[in, out] Sock Pointer to the socket.\r
+\r
+**/\r
+VOID\r
+SockNoMoreData (\r
+ IN OUT SOCKET *Sock\r
+ );\r
+\r
+//\r
+// Socket provided operations for user interface implemented in SockInterface.c\r
+//\r
+\r
+/**\r
+ Create a socket and its associated protocol control block\r
+ with the intial data SockInitData and protocol specific\r
+ data ProtoData.\r
+\r
+ @param[in] SockInitData Inital data to setting the socket.\r
+\r
+ @return Pointer to the newly created socket. If NULL, an error condition occured.\r
+\r
+**/\r
+SOCKET *\r
+SockCreateChild (\r
+ IN SOCK_INIT_DATA *SockInitData\r
+ );\r
+\r
+/**\r
+ Destory the socket Sock and its associated protocol control block.\r
+\r
+ @param[in, out] Sock The socket to be destroyed.\r
+\r
+ @retval EFI_SUCCESS The socket Sock was destroyed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockDestroyChild (\r
+ IN OUT SOCKET *Sock\r
+ );\r
+\r
+/**\r
+ Configure the specific socket Sock using configuration data ConfigData.\r
+\r
+ @param[in] Sock Pointer to the socket to be configured.\r
+ @param[in] ConfigData Pointer to the configuration data.\r
+\r
+ @retval EFI_SUCCESS The socket configured successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is already configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConfigure (\r
+ IN SOCKET *Sock,\r
+ IN VOID *ConfigData\r
+ );\r
+\r
+/**\r
+ Initiate a connection establishment process.\r
+\r
+ @param[in] Sock Pointer to the socket to initiate the initate the\r
+ connection.\r
+ @param[in] Token Pointer to the token used for the connection\r
+ operation.\r
+\r
+ @retval EFI_SUCCESS The connection initialized successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not configured to\r
+ be an active one, or the token is already in one of\r
+ this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockConnect (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ );\r
+\r
+/**\r
+ Issue a listen token to get an existed connected network instance,\r
+ or wait for a connection if there is none.\r
+\r
+ @param[in] Sock Pointer to the socket to accept connections.\r
+ @param[in] Token The token to accept a connection.\r
+\r
+ @retval EFI_SUCCESS Either a connection is accepted or the Token is\r
+ buffered for further acceptance.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not configured to\r
+ be a passive one, or the token is already in one of\r
+ this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockAccept (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ );\r
+\r
+/**\r
+ Issue a token with data to the socket to send out.\r
+\r
+ @param[in] Sock Pointer to the socket to process the token with\r
+ data.\r
+ @param[in] Token The token with data that needs to send out.\r
+\r
+ @retval EFI_SUCCESS The token processed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not in a\r
+ synchronized state , or the token is already in one\r
+ of this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockSend (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ );\r
+\r
+/**\r
+ Issue a token to get data from the socket.\r
+\r
+ @param[in] Sock Pointer to the socket to get data from.\r
+ @param[in] Token The token to store the received data from the\r
+ socket.\r
+\r
+ @retval EFI_SUCCESS The token processed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not in a\r
+ synchronized state , or the token is already in one\r
+ of this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+ @retval EFI_CONNECTION_FIN The connection is closed and there is no more data.\r
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRcv (\r
+ IN SOCKET *Sock,\r
+ IN VOID *Token\r
+ );\r
+\r
+/**\r
+ Reset the socket and its associated protocol control block.\r
+\r
+ @param[in, out] Sock Pointer to the socket to be flushed.\r
+\r
+ @retval EFI_SUCCESS The socket flushed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+\r
+**/\r
+EFI_STATUS\r
+SockFlush (\r
+ IN OUT SOCKET *Sock\r
+ );\r
+\r
+/**\r
+ Close or abort the socket associated connection.\r
+\r
+ @param[in, out] Sock Pointer to the socket of the connection to close\r
+ or abort.\r
+ @param[in] Token The token for close operation.\r
+ @param[in] OnAbort TRUE for aborting the connection, FALSE to close it.\r
+\r
+ @retval EFI_SUCCESS The close or abort operation initialized\r
+ successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the\r
+ socket is closed, or the socket is not in a\r
+ synchronized state , or the token is already in one\r
+ of this socket's lists.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockClose (\r
+ IN OUT SOCKET *Sock,\r
+ IN VOID *Token,\r
+ IN BOOLEAN OnAbort\r
+ );\r
+\r
+/**\r
+ Get the mode data of the low layer protocol.\r
+\r
+ @param[in] Sock Pointer to the socket to get mode data from.\r
+ @param[in, out] Mode Pointer to the data to store the low layer mode\r
+ information.\r
+\r
+ @retval EFI_SUCCESS The mode data was obtained successfully.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGetMode (\r
+ IN SOCKET *Sock,\r
+ IN OUT VOID *Mode\r
+ );\r
+\r
+/**\r
+ Configure the low level protocol to join a multicast group for\r
+ this socket's connection.\r
+\r
+ @param[in] Sock Pointer to the socket of the connection to join the\r
+ specific multicast group.\r
+ @param[in] GroupInfo Pointer to the multicast group information.\r
+\r
+ @retval EFI_SUCCESS The configuration completed successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockGroup (\r
+ IN SOCKET *Sock,\r
+ IN VOID *GroupInfo\r
+ );\r
+\r
+/**\r
+ Add or remove route information in IP route table associated\r
+ with this socket.\r
+\r
+ @param[in] Sock Pointer to the socket associated with the IP route\r
+ table to operate on.\r
+ @param[in] RouteInfo Pointer to the route information to be processed.\r
+\r
+ @retval EFI_SUCCESS The route table updated successfully.\r
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.\r
+ @retval EFI_NO_MAPPING The IP address configuration operation is not\r
+ finished.\r
+ @retval EFI_NOT_STARTED The socket is not configured.\r
+\r
+**/\r
+EFI_STATUS\r
+SockRoute (\r
+ IN SOCKET *Sock,\r
+ IN VOID *RouteInfo\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The implementation of a dispatch routine for processing TCP requests.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+ Add or remove a route entry in the IP route table associated with this TCP instance.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] RouteInfo Pointer to the route information to be processed.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The driver instance has not been started.\r
+ @retval EFI_NO_MAPPING When using the default address, configuration(DHCP,\r
+ BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.\r
+ @retval EFI_NOT_FOUND This route is not in the routing table\r
+ (when RouteInfo->DeleteRoute is TRUE).\r
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table\r
+ (when RouteInfo->DeleteRoute is FALSE).\r
+**/\r
+EFI_STATUS\r
+Tcp4Route (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP4_ROUTE_INFO *RouteInfo\r
+ )\r
+{\r
+ IP_IO_IP_PROTOCOL Ip;\r
+\r
+ Ip = Tcb->IpInfo->Ip;\r
+\r
+ ASSERT (Ip.Ip4!= NULL);\r
+\r
+ return Ip.Ip4->Routes (\r
+ Ip.Ip4,\r
+ RouteInfo->DeleteRoute,\r
+ RouteInfo->SubnetAddress,\r
+ RouteInfo->SubnetMask,\r
+ RouteInfo->GatewayAddress\r
+ );\r
+\r
+}\r
+\r
+/**\r
+ Get the operational settings of this TCPv4 instance.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in, out] Mode Pointer to the buffer to store the operational\r
+ settings.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED No configuration data is available because this\r
+ instance hasn't been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp4GetMode (\r
+ IN TCP_CB *Tcb,\r
+ IN OUT TCP4_MODE_DATA *Mode\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_TCP4_CONFIG_DATA *ConfigData;\r
+ EFI_TCP4_ACCESS_POINT *AccessPoint;\r
+ EFI_TCP4_OPTION *Option;\r
+ EFI_IP4_PROTOCOL *Ip;\r
+\r
+ Sock = Tcb->Sk;\r
+\r
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Mode->Tcp4State != NULL) {\r
+ *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State;\r
+ }\r
+\r
+ if (Mode->Tcp4ConfigData != NULL) {\r
+\r
+ ConfigData = Mode->Tcp4ConfigData;\r
+ AccessPoint = &(ConfigData->AccessPoint);\r
+ Option = ConfigData->ControlOption;\r
+\r
+ ConfigData->TypeOfService = Tcb->Tos;\r
+ ConfigData->TimeToLive = Tcb->Ttl;\r
+\r
+ AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr;\r
+\r
+ CopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ AccessPoint->SubnetMask = Tcb->SubnetMask;\r
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);\r
+\r
+ CopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);\r
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);\r
+\r
+ if (Option != NULL) {\r
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);\r
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);\r
+\r
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;\r
+ Option->DataRetries = Tcb->MaxRexmit;\r
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;\r
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;\r
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;\r
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;\r
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;\r
+\r
+ Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));\r
+ Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));\r
+ Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));\r
+\r
+ Option->EnableSelectiveAck = FALSE;\r
+ Option->EnablePathMtuDiscovery = FALSE;\r
+ }\r
+ }\r
+\r
+ Ip = Tcb->IpInfo->Ip.Ip4;\r
+ ASSERT (Ip != NULL);\r
+\r
+ return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);\r
+}\r
+\r
+/**\r
+ Get the operational settings of this TCPv6 instance.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in, out] Mode Pointer to the buffer to store the operational\r
+ settings.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED No configuration data is available because this\r
+ instance hasn't been started.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp6GetMode (\r
+ IN TCP_CB *Tcb,\r
+ IN OUT TCP6_MODE_DATA *Mode\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_TCP6_CONFIG_DATA *ConfigData;\r
+ EFI_TCP6_ACCESS_POINT *AccessPoint;\r
+ EFI_TCP6_OPTION *Option;\r
+ EFI_IP6_PROTOCOL *Ip;\r
+\r
+ Sock = Tcb->Sk;\r
+\r
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Mode->Tcp6State != NULL) {\r
+ *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State);\r
+ }\r
+\r
+ if (Mode->Tcp6ConfigData != NULL) {\r
+\r
+ ConfigData = Mode->Tcp6ConfigData;\r
+ AccessPoint = &(ConfigData->AccessPoint);\r
+ Option = ConfigData->ControlOption;\r
+\r
+ ConfigData->TrafficClass = Tcb->Tos;\r
+ ConfigData->HopLimit = Tcb->Ttl;\r
+\r
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);\r
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);\r
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);\r
+\r
+ IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);\r
+ IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);\r
+\r
+ if (Option != NULL) {\r
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);\r
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);\r
+\r
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;\r
+ Option->DataRetries = Tcb->MaxRexmit;\r
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;\r
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;\r
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;\r
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;\r
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;\r
+\r
+ Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));\r
+ Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));\r
+ Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));\r
+\r
+ Option->EnableSelectiveAck = FALSE;\r
+ Option->EnablePathMtuDiscovery = FALSE;\r
+ }\r
+ }\r
+\r
+ Ip = Tcb->IpInfo->Ip.Ip6;\r
+ ASSERT (Ip != NULL);\r
+\r
+ return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData);\r
+}\r
+\r
+/**\r
+ If TcpAp->StationPort isn't zero, check whether the access point\r
+ is registered, else generate a random station port for this\r
+ access point.\r
+\r
+ @param[in] TcpAp Pointer to the access point.\r
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6\r
+\r
+ @retval EFI_SUCCESS The check passed or the port is assigned.\r
+ @retval EFI_INVALID_PARAMETER The non-zero station port is already used.\r
+ @retval EFI_OUT_OF_RESOURCES No port can be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpBind (\r
+ IN TCP_ACCESS_POINT *TcpAp,\r
+ IN UINT8 IpVersion\r
+ )\r
+{\r
+ BOOLEAN Cycle;\r
+ EFI_IP_ADDRESS Local;\r
+ UINT16 *Port;\r
+ UINT16 *RandomPort;\r
+\r
+ if (IpVersion == IP_VERSION_4) {\r
+ CopyMem (&Local, &TcpAp->Tcp4Ap.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ Port = &TcpAp->Tcp4Ap.StationPort;\r
+ RandomPort = &mTcp4RandomPort;\r
+ } else {\r
+ IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress);\r
+ Port = &TcpAp->Tcp6Ap.StationPort;\r
+ RandomPort = &mTcp6RandomPort;\r
+ }\r
+\r
+ if (0 != *Port) {\r
+ //\r
+ // Check if a same endpoing is bound.\r
+ //\r
+ if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) {\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } else {\r
+ //\r
+ // generate a random port\r
+ //\r
+ Cycle = FALSE;\r
+\r
+ if (TCP_PORT_USER_RESERVED == *RandomPort) {\r
+ *RandomPort = TCP_PORT_KNOWN;\r
+ }\r
+\r
+ (*RandomPort)++;\r
+\r
+ while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) {\r
+ (*RandomPort)++;\r
+\r
+ if (*RandomPort <= TCP_PORT_KNOWN) {\r
+ if (Cycle) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpBind: no port can be allocated for this pcb\n")\r
+ );\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ *RandomPort = TCP_PORT_KNOWN + 1;\r
+\r
+ Cycle = TRUE;\r
+ }\r
+ }\r
+\r
+ *Port = *RandomPort;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Flush the Tcb add its associated protocols.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB to be flushed.\r
+\r
+**/\r
+VOID\r
+TcpFlushPcb (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ TCP_PROTO_DATA *TcpProto;\r
+\r
+ IpIoConfigIp (Tcb->IpInfo, NULL);\r
+\r
+ Sock = Tcb->Sk;\r
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+\r
+ if (SOCK_IS_CONFIGURED (Sock)) {\r
+ RemoveEntryList (&Tcb->List);\r
+\r
+ if (Sock->DevicePath != NULL) {\r
+ //\r
+ // Uninstall the device path protocl.\r
+ //\r
+ gBS->UninstallProtocolInterface (\r
+ Sock->SockHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Sock->DevicePath\r
+ );\r
+\r
+ FreePool (Sock->DevicePath);\r
+ Sock->DevicePath = NULL;\r
+ }\r
+\r
+ TcpSetVariableData (TcpProto->TcpService);\r
+ }\r
+\r
+ NetbufFreeList (&Tcb->SndQue);\r
+ NetbufFreeList (&Tcb->RcvQue);\r
+ Tcb->State = TCP_CLOSED;\r
+}\r
+\r
+/**\r
+ Attach a Pcb to the socket.\r
+\r
+ @param[in] Sk Pointer to the socket of this TCP instance.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpAttachPcb (\r
+ IN SOCKET *Sk\r
+ )\r
+{\r
+ TCP_CB *Tcb;\r
+ TCP_PROTO_DATA *ProtoData;\r
+ IP_IO *IpIo;\r
+\r
+ Tcb = AllocateZeroPool (sizeof (TCP_CB));\r
+\r
+ if (Tcb == NULL) {\r
+\r
+ DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n"));\r
+\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;\r
+ IpIo = ProtoData->TcpService->IpIo;\r
+\r
+ //\r
+ // Create an IpInfo for this Tcb.\r
+ //\r
+ Tcb->IpInfo = IpIoAddIp (IpIo);\r
+ if (Tcb->IpInfo == NULL) {\r
+\r
+ FreePool (Tcb);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ InitializeListHead (&Tcb->List);\r
+ InitializeListHead (&Tcb->SndQue);\r
+ InitializeListHead (&Tcb->RcvQue);\r
+\r
+ Tcb->State = TCP_CLOSED;\r
+ Tcb->Sk = Sk;\r
+ ProtoData->TcpPcb = Tcb;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Detach the Pcb of the socket.\r
+\r
+ @param[in, out] Sk Pointer to the socket of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpDetachPcb (\r
+ IN OUT SOCKET *Sk\r
+ )\r
+{\r
+ TCP_PROTO_DATA *ProtoData;\r
+ TCP_CB *Tcb;\r
+\r
+ ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;\r
+ Tcb = ProtoData->TcpPcb;\r
+\r
+ ASSERT (Tcb != NULL);\r
+\r
+ TcpFlushPcb (Tcb);\r
+\r
+ IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);\r
+\r
+ FreePool (Tcb);\r
+\r
+ ProtoData->TcpPcb = NULL;\r
+}\r
+\r
+/**\r
+ Configure the Pcb using CfgData.\r
+\r
+ @param[in] Sk Pointer to the socket of this TCP instance.\r
+ @param[in] CfgData Pointer to the TCP configuration data.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER A same access point has been configured in\r
+ another TCP instance.\r
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpConfigurePcb (\r
+ IN SOCKET *Sk,\r
+ IN TCP_CONFIG_DATA *CfgData\r
+ )\r
+{\r
+ IP_IO_IP_CONFIG_DATA IpCfgData;\r
+ EFI_STATUS Status;\r
+ EFI_TCP4_OPTION *Option;\r
+ TCP_PROTO_DATA *TcpProto;\r
+ TCP_CB *Tcb;\r
+ TCP_ACCESS_POINT *TcpAp;\r
+\r
+ ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL));\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved;\r
+ Tcb = TcpProto->TcpPcb;\r
+\r
+ ASSERT (Tcb != NULL);\r
+\r
+ if (Sk->IpVersion == IP_VERSION_4) {\r
+ //\r
+ // Add Ip for send pkt to the peer\r
+ //\r
+ CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA));\r
+ IpCfgData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;\r
+ IpCfgData.Ip4CfgData.TypeOfService = CfgData->Tcp4CfgData.TypeOfService;\r
+ IpCfgData.Ip4CfgData.TimeToLive = CfgData->Tcp4CfgData.TimeToLive;\r
+ IpCfgData.Ip4CfgData.UseDefaultAddress = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;\r
+ IpCfgData.Ip4CfgData.SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;\r
+ IpCfgData.Ip4CfgData.ReceiveTimeout = (UINT32) (-1);\r
+ CopyMem (\r
+ &IpCfgData.Ip4CfgData.StationAddress,\r
+ &CfgData->Tcp4CfgData.AccessPoint.StationAddress,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+\r
+ } else {\r
+ ASSERT (Sk->IpVersion == IP_VERSION_6);\r
+\r
+ CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA));\r
+ IpCfgData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;\r
+ IpCfgData.Ip6CfgData.TrafficClass = CfgData->Tcp6CfgData.TrafficClass;\r
+ IpCfgData.Ip6CfgData.HopLimit = CfgData->Tcp6CfgData.HopLimit;\r
+ IpCfgData.Ip6CfgData.ReceiveTimeout = (UINT32) (-1);\r
+ IP6_COPY_ADDRESS (\r
+ &IpCfgData.Ip6CfgData.StationAddress,\r
+ &CfgData->Tcp6CfgData.AccessPoint.StationAddress\r
+ );\r
+ IP6_COPY_ADDRESS (\r
+ &IpCfgData.Ip6CfgData.DestinationAddress,\r
+ &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress\r
+ );\r
+ }\r
+\r
+ //\r
+ // Configure the IP instance this Tcb consumes.\r
+ //\r
+ Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto OnExit;\r
+ }\r
+\r
+ if (Sk->IpVersion == IP_VERSION_4) {\r
+ //\r
+ // Get the default address information if the instance is configured to use default address.\r
+ //\r
+ CfgData->Tcp4CfgData.AccessPoint.StationAddress = IpCfgData.Ip4CfgData.StationAddress;\r
+ CfgData->Tcp4CfgData.AccessPoint.SubnetMask = IpCfgData.Ip4CfgData.SubnetMask;\r
+\r
+ TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint;\r
+ } else {\r
+ IP6_COPY_ADDRESS (\r
+ &CfgData->Tcp6CfgData.AccessPoint.StationAddress,\r
+ &IpCfgData.Ip6CfgData.StationAddress\r
+ );\r
+\r
+ TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint;\r
+ }\r
+\r
+ //\r
+ // check if we can bind this endpoint in CfgData\r
+ //\r
+ Status = TcpBind (TcpAp, Sk->IpVersion);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpConfigurePcb: Bind endpoint failed with %r\n",\r
+ Status)\r
+ );\r
+\r
+ goto OnExit;\r
+ }\r
+\r
+ //\r
+ // Initalize the operating information in this Tcb\r
+ //\r
+ ASSERT (Tcb->State == TCP_CLOSED &&\r
+ IsListEmpty (&Tcb->SndQue) &&\r
+ IsListEmpty (&Tcb->RcvQue));\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);\r
+ Tcb->State = TCP_CLOSED;\r
+\r
+ Tcb->SndMss = 536;\r
+ Tcb->RcvMss = TcpGetRcvMss (Sk);\r
+\r
+ Tcb->SRtt = 0;\r
+ Tcb->Rto = 3 * TCP_TICK_HZ;\r
+\r
+ Tcb->CWnd = Tcb->SndMss;\r
+ Tcb->Ssthresh = 0xffffffff;\r
+\r
+ Tcb->CongestState = TCP_CONGEST_OPEN;\r
+\r
+ Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN;\r
+ Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD;\r
+ Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE;\r
+ Tcb->MaxRexmit = TCP_MAX_LOSS;\r
+ Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME;\r
+ Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME;\r
+ Tcb->ConnectTimeout = TCP_CONNECT_TIME;\r
+\r
+ if (Sk->IpVersion == IP_VERSION_4) {\r
+ //\r
+ // initialize Tcb in the light of CfgData\r
+ //\r
+ Tcb->Ttl = CfgData->Tcp4CfgData.TimeToLive;\r
+ Tcb->Tos = CfgData->Tcp4CfgData.TypeOfService;\r
+\r
+ Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;\r
+\r
+ CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR));\r
+ Tcb->LocalEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort);\r
+ Tcb->SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;\r
+\r
+ CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR));\r
+ Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort);\r
+\r
+ Option = CfgData->Tcp4CfgData.ControlOption;\r
+ } else {\r
+ Tcb->Ttl = CfgData->Tcp6CfgData.HopLimit;\r
+ Tcb->Tos = CfgData->Tcp6CfgData.TrafficClass;\r
+\r
+ IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress);\r
+ Tcb->LocalEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort);\r
+\r
+ IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress);\r
+ Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort);\r
+\r
+ //\r
+ // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same.\r
+ //\r
+ Option = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption;\r
+ }\r
+\r
+ if (Option != NULL) {\r
+ SET_RCV_BUFFSIZE (\r
+ Sk,\r
+ (UINT32) (TCP_COMP_VAL (\r
+ TCP_RCV_BUF_SIZE_MIN,\r
+ TCP_RCV_BUF_SIZE,\r
+ TCP_RCV_BUF_SIZE,\r
+ Option->ReceiveBufferSize\r
+ )\r
+ )\r
+ );\r
+ SET_SND_BUFFSIZE (\r
+ Sk,\r
+ (UINT32) (TCP_COMP_VAL (\r
+ TCP_SND_BUF_SIZE_MIN,\r
+ TCP_SND_BUF_SIZE,\r
+ TCP_SND_BUF_SIZE,\r
+ Option->SendBufferSize\r
+ )\r
+ )\r
+ );\r
+\r
+ SET_BACKLOG (\r
+ Sk,\r
+ (UINT32) (TCP_COMP_VAL (\r
+ TCP_BACKLOG_MIN,\r
+ TCP_BACKLOG,\r
+ TCP_BACKLOG,\r
+ Option->MaxSynBackLog\r
+ )\r
+ )\r
+ );\r
+\r
+ Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL (\r
+ TCP_MAX_LOSS_MIN,\r
+ TCP_MAX_LOSS,\r
+ TCP_MAX_LOSS,\r
+ Option->DataRetries\r
+ );\r
+ Tcb->FinWait2Timeout = TCP_COMP_VAL (\r
+ TCP_FIN_WAIT2_TIME,\r
+ TCP_FIN_WAIT2_TIME_MAX,\r
+ TCP_FIN_WAIT2_TIME,\r
+ (UINT32) (Option->FinTimeout * TCP_TICK_HZ)\r
+ );\r
+\r
+ if (Option->TimeWaitTimeout != 0) {\r
+ Tcb->TimeWaitTimeout = TCP_COMP_VAL (\r
+ TCP_TIME_WAIT_TIME,\r
+ TCP_TIME_WAIT_TIME_MAX,\r
+ TCP_TIME_WAIT_TIME,\r
+ (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ)\r
+ );\r
+ } else {\r
+ Tcb->TimeWaitTimeout = 0;\r
+ }\r
+\r
+ if (Option->KeepAliveProbes != 0) {\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);\r
+\r
+ Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL (\r
+ TCP_MAX_KEEPALIVE_MIN,\r
+ TCP_MAX_KEEPALIVE,\r
+ TCP_MAX_KEEPALIVE,\r
+ Option->KeepAliveProbes\r
+ );\r
+ Tcb->KeepAliveIdle = TCP_COMP_VAL (\r
+ TCP_KEEPALIVE_IDLE_MIN,\r
+ TCP_KEEPALIVE_IDLE_MAX,\r
+ TCP_KEEPALIVE_IDLE_MIN,\r
+ (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ)\r
+ );\r
+ Tcb->KeepAlivePeriod = TCP_COMP_VAL (\r
+ TCP_KEEPALIVE_PERIOD_MIN,\r
+ TCP_KEEPALIVE_PERIOD,\r
+ TCP_KEEPALIVE_PERIOD,\r
+ (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ)\r
+ );\r
+ }\r
+\r
+ Tcb->ConnectTimeout = TCP_COMP_VAL (\r
+ TCP_CONNECT_TIME_MIN,\r
+ TCP_CONNECT_TIME,\r
+ TCP_CONNECT_TIME,\r
+ (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ)\r
+ );\r
+\r
+ if (!Option->EnableNagle) {\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);\r
+ }\r
+\r
+ if (!Option->EnableTimeStamp) {\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS);\r
+ }\r
+\r
+ if (!Option->EnableWindowScaling) {\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS);\r
+ }\r
+ }\r
+\r
+ //\r
+ // The socket is bound, the <SrcIp, SrcPort, DstIp, DstPort> is\r
+ // determined, construct the IP device path and install it.\r
+ //\r
+ Status = TcpInstallDevicePath (Sk);\r
+ if (EFI_ERROR (Status)) {\r
+ goto OnExit;\r
+ }\r
+\r
+ //\r
+ // update state of Tcb and socket\r
+ //\r
+ if (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) ||\r
+ ((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag)\r
+ ) {\r
+\r
+ TcpSetState (Tcb, TCP_LISTEN);\r
+ SockSetState (Sk, SO_LISTENING);\r
+\r
+ Sk->ConfigureState = SO_CONFIGURED_PASSIVE;\r
+ } else {\r
+\r
+ Sk->ConfigureState = SO_CONFIGURED_ACTIVE;\r
+ }\r
+\r
+ if (Sk->IpVersion == IP_VERSION_6) {\r
+ Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK;\r
+ }\r
+\r
+ TcpInsertTcb (Tcb);\r
+\r
+OnExit:\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The procotol handler provided to the socket layer, which is used to\r
+ dispatch the socket level requests by calling the corresponding\r
+ TCP layer functions.\r
+\r
+ @param[in] Sock Pointer to the socket of this TCP instance.\r
+ @param[in] Request The code of this operation request.\r
+ @param[in] Data Pointer to the operation specific data passed in\r
+ together with the operation request. This is an\r
+ optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The socket request completed successfully.\r
+ @retval other The error status returned by the corresponding TCP\r
+ layer function.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpDispatcher (\r
+ IN SOCKET *Sock,\r
+ IN UINT8 Request,\r
+ IN VOID *Data OPTIONAL\r
+ )\r
+{\r
+ TCP_CB *Tcb;\r
+ TCP_PROTO_DATA *ProtoData;\r
+\r
+ ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+ Tcb = ProtoData->TcpPcb;\r
+\r
+ switch (Request) {\r
+ case SOCK_POLL:\r
+ if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
+ ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4);\r
+ } else {\r
+ ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6);\r
+ }\r
+\r
+ break;\r
+\r
+ case SOCK_CONSUMED:\r
+ //\r
+ // After user received data from socket buffer, socket will\r
+ // notify TCP using this message to give it a chance to send out\r
+ // window update information\r
+ //\r
+ ASSERT (Tcb != NULL);\r
+ TcpOnAppConsume (Tcb);\r
+ break;\r
+\r
+ case SOCK_SND:\r
+\r
+ ASSERT (Tcb != NULL);\r
+ TcpOnAppSend (Tcb);\r
+ break;\r
+\r
+ case SOCK_CLOSE:\r
+\r
+ TcpOnAppClose (Tcb);\r
+\r
+ break;\r
+\r
+ case SOCK_ABORT:\r
+\r
+ TcpOnAppAbort (Tcb);\r
+\r
+ break;\r
+\r
+ case SOCK_SNDPUSH:\r
+ Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk);\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);\r
+\r
+ break;\r
+\r
+ case SOCK_SNDURG:\r
+ Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1;\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);\r
+\r
+ break;\r
+\r
+ case SOCK_CONNECT:\r
+\r
+ TcpOnAppConnect (Tcb);\r
+\r
+ break;\r
+\r
+ case SOCK_ATTACH:\r
+\r
+ return TcpAttachPcb (Sock);\r
+\r
+ break;\r
+\r
+ case SOCK_FLUSH:\r
+\r
+ TcpFlushPcb (Tcb);\r
+\r
+ break;\r
+\r
+ case SOCK_DETACH:\r
+\r
+ TcpDetachPcb (Sock);\r
+\r
+ break;\r
+\r
+ case SOCK_CONFIGURE:\r
+\r
+ return TcpConfigurePcb (\r
+ Sock,\r
+ (TCP_CONFIG_DATA *) Data\r
+ );\r
+\r
+ break;\r
+\r
+ case SOCK_MODE:\r
+\r
+ ASSERT ((Data != NULL) && (Tcb != NULL));\r
+\r
+ if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
+\r
+ return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data);\r
+ } else {\r
+\r
+ return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data);\r
+ }\r
+\r
+ break;\r
+\r
+ case SOCK_ROUTE:\r
+\r
+ ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4));\r
+\r
+ return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data);\r
+\r
+ default:\r
+\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ The driver binding and service binding protocol for the TCP driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+UINT16 mTcp4RandomPort;\r
+UINT16 mTcp6RandomPort;\r
+\r
+TCP_HEARTBEAT_TIMER mTcpTimer = {\r
+ NULL,\r
+ 0\r
+};\r
+\r
+EFI_TCP4_PROTOCOL gTcp4ProtocolTemplate = {\r
+ Tcp4GetModeData,\r
+ Tcp4Configure,\r
+ Tcp4Routes,\r
+ Tcp4Connect,\r
+ Tcp4Accept,\r
+ Tcp4Transmit,\r
+ Tcp4Receive,\r
+ Tcp4Close,\r
+ Tcp4Cancel,\r
+ Tcp4Poll\r
+};\r
+\r
+EFI_TCP6_PROTOCOL gTcp6ProtocolTemplate = {\r
+ Tcp6GetModeData,\r
+ Tcp6Configure,\r
+ Tcp6Connect,\r
+ Tcp6Accept,\r
+ Tcp6Transmit,\r
+ Tcp6Receive,\r
+ Tcp6Close,\r
+ Tcp6Cancel,\r
+ Tcp6Poll\r
+};\r
+\r
+SOCK_INIT_DATA mTcpDefaultSockData = {\r
+ SockStream,\r
+ SO_CLOSED,\r
+ NULL,\r
+ TCP_BACKLOG,\r
+ TCP_SND_BUF_SIZE,\r
+ TCP_RCV_BUF_SIZE,\r
+ IP_VERSION_4,\r
+ NULL,\r
+ TcpCreateSocketCallback,\r
+ TcpDestroySocketCallback,\r
+ NULL,\r
+ NULL,\r
+ 0,\r
+ TcpDispatcher,\r
+ NULL,\r
+};\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gTcpDriverBinding = {\r
+ TcpDriverBindingSupported,\r
+ TcpDriverBindingStart,\r
+ TcpDriverBindingStop,\r
+ 0xa,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL gTcpServiceBinding = {\r
+ TcpServiceBindingCreateChild,\r
+ TcpServiceBindingDestroyChild\r
+};\r
+\r
+\r
+/**\r
+ Create and start the heartbeat timer for the TCP driver.\r
+\r
+ @retval EFI_SUCCESS The timer was successfully created and started.\r
+ @retval other The timer was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateTimer (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (mTcpTimer.RefCnt == 0) {\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ TcpTicking,\r
+ NULL,\r
+ &mTcpTimer.TimerEvent\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ Status = gBS->SetTimer (\r
+ mTcpTimer.TimerEvent,\r
+ TimerPeriodic,\r
+ (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ)\r
+ );\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ mTcpTimer.RefCnt++;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Stop and destroy the heartbeat timer for TCP driver.\r
+\r
+**/\r
+VOID\r
+TcpDestroyTimer (\r
+ VOID\r
+ )\r
+{\r
+ ASSERT (mTcpTimer.RefCnt > 0);\r
+\r
+ mTcpTimer.RefCnt--;\r
+\r
+ if (mTcpTimer.RefCnt > 0) {\r
+ return;\r
+ }\r
+\r
+ gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0);\r
+ gBS->CloseEvent (mTcpTimer.TimerEvent);\r
+ mTcpTimer.TimerEvent = NULL;\r
+}\r
+\r
+/**\r
+ The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for this driver image.\r
+ @param[in] SystemTable Pointer to the EFI system table.\r
+\r
+ @retval EFI_SUCCESS The driver loaded.\r
+ @retval other The driver did not load.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 Seed;\r
+\r
+ //\r
+ // Install the TCP Driver Binding Protocol\r
+ //\r
+ Status = EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gTcpDriverBinding,\r
+ ImageHandle,\r
+ &gTcpComponentName,\r
+ &gTcpComponentName2\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Initialize ISS and random port.\r
+ //\r
+ Seed = NetRandomInitSeed ();\r
+ mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss;\r
+ mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN));\r
+ mTcp6RandomPort = mTcp4RandomPort;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create a new TCP4 or TCP6 driver service binding protocol\r
+\r
+ @param[in] Controller Controller handle of device to bind driver to.\r
+ @param[in] ImageHandle The TCP driver's image handle.\r
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.\r
+ @retval EFI_SUCCESS A new IP6 service binding private was created.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateService (\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE Image,\r
+ IN UINT8 IpVersion\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_GUID *IpServiceBindingGuid;\r
+ EFI_GUID *TcpServiceBindingGuid;\r
+ TCP_SERVICE_DATA *TcpServiceData;\r
+ IP_IO_OPEN_DATA OpenData;\r
+\r
+ if (IpVersion == IP_VERSION_4) {\r
+ IpServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid;\r
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+ } else {\r
+ IpServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid;\r
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ TcpServiceBindingGuid,\r
+ NULL,\r
+ Image,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ IpServiceBindingGuid,\r
+ NULL,\r
+ Image,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Create the TCP service data.\r
+ //\r
+ TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA));\r
+ if (TcpServiceData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TcpServiceData->Signature = TCP_DRIVER_SIGNATURE;\r
+ TcpServiceData->ControllerHandle = Controller;\r
+ TcpServiceData->DriverBindingHandle = Image;\r
+ TcpServiceData->IpVersion = IpVersion;\r
+ CopyMem (\r
+ &TcpServiceData->ServiceBinding,\r
+ &gTcpServiceBinding,\r
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)\r
+ );\r
+\r
+ TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);\r
+ if (TcpServiceData->IpIo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+\r
+ InitializeListHead (&TcpServiceData->SocketList);\r
+ ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));\r
+\r
+ if (IpVersion == IP_VERSION_4) {\r
+ CopyMem (\r
+ &OpenData.IpConfigData.Ip4CfgData,\r
+ &mIp4IoDefaultIpConfigData,\r
+ sizeof (EFI_IP4_CONFIG_DATA)\r
+ );\r
+ OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;\r
+ } else {\r
+ CopyMem (\r
+ &OpenData.IpConfigData.Ip6CfgData,\r
+ &mIp6IoDefaultIpConfigData,\r
+ sizeof (EFI_IP6_CONFIG_DATA)\r
+ );\r
+ OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;\r
+ }\r
+\r
+ OpenData.PktRcvdNotify = TcpRxCallback;\r
+ Status = IpIoOpen (TcpServiceData->IpIo, &OpenData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = TcpCreateTimer ();\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Controller,\r
+ TcpServiceBindingGuid,\r
+ &TcpServiceData->ServiceBinding,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ TcpDestroyTimer ();\r
+\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ TcpSetVariableData (TcpServiceData);\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ if (TcpServiceData->IpIo != NULL) {\r
+ IpIoDestroy (TcpServiceData->IpIo);\r
+ }\r
+\r
+ FreePool (TcpServiceData);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Destroy a TCP6 or TCP4 service binding instance. It will release all\r
+ the resources allocated by the instance.\r
+\r
+ @param[in] Controller Controller handle of device to bind driver to.\r
+ @param[in] ImageHandle The TCP driver's image handle.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number\r
+ of children is zero stop the entire bus driver.\r
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6\r
+\r
+ @retval EFI_SUCCESS The resources used by the instance were cleaned up.\r
+ @retval Others Failed to clean up some of the resources.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpDestroyService (\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN UINT8 IpVersion\r
+ )\r
+{\r
+ EFI_HANDLE NicHandle;\r
+ EFI_GUID *IpProtocolGuid;\r
+ EFI_GUID *ServiceBindingGuid;\r
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;\r
+ TCP_SERVICE_DATA *TcpServiceData;\r
+ EFI_STATUS Status;\r
+ SOCKET *Sock;\r
+\r
+ ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));\r
+\r
+ if (IpVersion == IP_VERSION_4) {\r
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;\r
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+ } else {\r
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;\r
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+ }\r
+\r
+ NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ NicHandle,\r
+ ServiceBindingGuid,\r
+ (VOID **) &ServiceBinding,\r
+ ImageHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding);\r
+\r
+ if (NumberOfChildren == 0) {\r
+ //\r
+ // Uninstall TCP servicebinding protocol\r
+ //\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ NicHandle,\r
+ ServiceBindingGuid,\r
+ ServiceBinding,\r
+ NULL\r
+ );\r
+\r
+ //\r
+ // Destroy the IpIO consumed by TCP driver\r
+ //\r
+ IpIoDestroy (TcpServiceData->IpIo);\r
+\r
+ //\r
+ // Destroy the heartbeat timer.\r
+ //\r
+ TcpDestroyTimer ();\r
+\r
+ //\r
+ // Clear the variable.\r
+ //\r
+ TcpClearVariableData (TcpServiceData);\r
+\r
+ //\r
+ // Release the TCP service data\r
+ //\r
+ FreePool (TcpServiceData);\r
+ } else {\r
+\r
+ while (!IsListEmpty (&TcpServiceData->SocketList)) {\r
+ Sock = NET_LIST_HEAD (&TcpServiceData->SocketList, SOCKET, Link);\r
+\r
+ ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle);\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to test.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific\r
+ child device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsTcp4Started;\r
+\r
+ //\r
+ // Test for the Tcp4ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiTcp4ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Test for the Ip4ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiIp4ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ IsTcp4Started = FALSE;\r
+ } else {\r
+ IsTcp4Started = TRUE;\r
+ }\r
+\r
+ //\r
+ // Check the Tcp6ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiTcp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Test for the Ip6ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ } else if (IsTcp4Started) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS The driver is added to ControllerHandle.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the\r
+ driver.\r
+ @retval other The driver cannot be added to ControllerHandle.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Tcp4Status;\r
+ EFI_STATUS Tcp6Status;\r
+\r
+ Tcp4Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4);\r
+ if ((Tcp4Status == EFI_ALREADY_STARTED) || (Tcp4Status == EFI_UNSUPPORTED)) {\r
+ Tcp4Status = EFI_SUCCESS;\r
+ }\r
+\r
+ Tcp6Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6);\r
+ if ((Tcp6Status == EFI_ALREADY_STARTED) || (Tcp6Status == EFI_UNSUPPORTED)) {\r
+ Tcp6Status = EFI_SUCCESS;\r
+ }\r
+\r
+ if (!EFI_ERROR (Tcp4Status) || !EFI_ERROR (Tcp6Status)) {\r
+ return EFI_SUCCESS;\r
+ } else if (EFI_ERROR (Tcp4Status)) {\r
+ return Tcp4Status;\r
+ } else {\r
+ return Tcp6Status;\r
+ }\r
+}\r
+\r
+/**\r
+ Stop this driver on ControllerHandle.\r
+\r
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must\r
+ support a bus specific I/O protocol for the driver\r
+ to use to stop the device.\r
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.\r
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL\r
+ if NumberOfChildren is 0.\r
+\r
+ @retval EFI_SUCCESS The device was stopped.\r
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Tcp4Status;\r
+ EFI_STATUS Tcp6Status;\r
+\r
+ Tcp4Status = TcpDestroyService (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ NumberOfChildren,\r
+ IP_VERSION_4\r
+ );\r
+\r
+ Tcp6Status = TcpDestroyService (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ NumberOfChildren,\r
+ IP_VERSION_6\r
+ );\r
+\r
+ if (EFI_ERROR (Tcp4Status) && EFI_ERROR (Tcp6Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ } else {\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ The Callback funtion called after the TCP socket was created.\r
+\r
+ @param[in] This Pointer to the socket just created\r
+ @param[in] Context Context of the socket\r
+\r
+ @retval EFI_SUCCESS This protocol installed successfully.\r
+ @retval other An error occured.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateSocketCallback (\r
+ IN SOCKET *This,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ TCP_SERVICE_DATA *TcpServiceData;\r
+ EFI_GUID *IpProtocolGuid;\r
+ VOID *Ip;\r
+\r
+ if (This->IpVersion == IP_VERSION_4) {\r
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;\r
+ } else {\r
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;\r
+ }\r
+\r
+ TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;\r
+\r
+ //\r
+ // Open the default IP protocol of IP_IO BY_DRIVER.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ TcpServiceData->IpIo->ChildHandle,\r
+ IpProtocolGuid,\r
+ &Ip,\r
+ TcpServiceData->DriverBindingHandle,\r
+ This->SockHandle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Open the device path on the handle where service binding resides on.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ TcpServiceData->ControllerHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **) &This->ParentDevicePath,\r
+ TcpServiceData->DriverBindingHandle,\r
+ This->SockHandle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->CloseProtocol (\r
+ TcpServiceData->IpIo->ChildHandle,\r
+ IpProtocolGuid,\r
+ TcpServiceData->DriverBindingHandle,\r
+ This->SockHandle\r
+ );\r
+ } else {\r
+ //\r
+ // Insert this socket into the SocketList.\r
+ //\r
+ InsertTailList (&TcpServiceData->SocketList, &This->Link);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The callback function called before the TCP socket was to be destroyed.\r
+\r
+ @param[in] This The TCP socket to be destroyed.\r
+ @param[in] Context The context of the socket.\r
+\r
+**/\r
+VOID\r
+TcpDestroySocketCallback (\r
+ IN SOCKET *This,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ TCP_SERVICE_DATA *TcpServiceData;\r
+ EFI_GUID *IpProtocolGuid;\r
+\r
+ if (This->IpVersion == IP_VERSION_4) {\r
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;\r
+ } else {\r
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;\r
+ }\r
+\r
+ TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;\r
+\r
+ //\r
+ // Remove this node from the list.\r
+ //\r
+ RemoveEntryList (&This->Link);\r
+\r
+ //\r
+ // Close the device path protocol\r
+ //\r
+ gBS->CloseProtocol (\r
+ TcpServiceData->ControllerHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ TcpServiceData->DriverBindingHandle,\r
+ This->SockHandle\r
+ );\r
+\r
+ //\r
+ // Close the IP protocol.\r
+ //\r
+ gBS->CloseProtocol (\r
+ TcpServiceData->IpIo->ChildHandle,\r
+ IpProtocolGuid,\r
+ TcpServiceData->DriverBindingHandle,\r
+ This->SockHandle\r
+ );\r
+}\r
+\r
+/**\r
+ Creates a child handle with a set of TCP services.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in, out] ChildHandle Pointer to the handle of the child to create.\r
+ If it is NULL, then a new handle is created.\r
+ If it is a pointer to an existing UEFI handle,\r
+ then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create\r
+ the child.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ TCP_SERVICE_DATA *TcpServiceData;\r
+ TCP_PROTO_DATA TcpProto;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (NULL == This || NULL == ChildHandle) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ Status = EFI_SUCCESS;\r
+ TcpServiceData = TCP_SERVICE_FROM_THIS (This);\r
+ TcpProto.TcpService = TcpServiceData;\r
+ TcpProto.TcpPcb = NULL;\r
+\r
+ //\r
+ // Create a tcp instance with defualt Tcp default\r
+ // sock init data and TcpProto\r
+ //\r
+ mTcpDefaultSockData.ProtoData = &TcpProto;\r
+ mTcpDefaultSockData.DataSize = sizeof (TCP_PROTO_DATA);\r
+ mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;\r
+ mTcpDefaultSockData.IpVersion = TcpServiceData->IpVersion;\r
+\r
+ if (TcpServiceData->IpVersion == IP_VERSION_4) {\r
+ mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate;\r
+ } else {\r
+ mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate;\r
+ }\r
+\r
+ Sock = SockCreateChild (&mTcpDefaultSockData);\r
+ if (NULL == Sock) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpDriverBindingCreateChild: No resource to create a Tcp Child\n")\r
+ );\r
+\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ } else {\r
+ *ChildHandle = Sock->SockHandle;\r
+ }\r
+\r
+ mTcpDefaultSockData.ProtoData = NULL;\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Destroys a child handle with a set of TCP services.\r
+\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param ChildHandle Handle of the child to be destroyed.\r
+\r
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.\r
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.\r
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle\r
+ because its services are being used.\r
+ @retval other The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *Tcp;\r
+ SOCKET *Sock;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (NULL == This || NULL == ChildHandle) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // retrieve the Tcp4 protocol from ChildHandle\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ChildHandle,\r
+ &gEfiTcp4ProtocolGuid,\r
+ &Tcp,\r
+ gTcpDriverBinding.DriverBindingHandle,\r
+ ChildHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // No Tcp4, try the Tcp6 protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ChildHandle,\r
+ &gEfiTcp6ProtocolGuid,\r
+ &Tcp,\r
+ gTcpDriverBinding.DriverBindingHandle,\r
+ ChildHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // destroy this sock and related Tcp protocol control\r
+ // block\r
+ //\r
+ Sock = SOCK_FROM_THIS (Tcp);\r
+\r
+ SockDestroyChild (Sock);\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+ The prototype of driver binding and service binding protocol for TCP driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _TCP_DRIVER_H_\r
+#define _TCP_DRIVER_H_\r
+\r
+#define TCP_DRIVER_SIGNATURE SIGNATURE_32 ('T', 'C', 'P', 'D')\r
+\r
+#define TCP_PORT_KNOWN 1024\r
+#define TCP_PORT_USER_RESERVED 65535\r
+\r
+typedef struct _TCP_HEARTBEAT_TIMER {\r
+ EFI_EVENT TimerEvent;\r
+ INTN RefCnt;\r
+} TCP_HEARTBEAT_TIMER;\r
+\r
+typedef struct _TCP_SERVICE_DATA {\r
+ UINT32 Signature;\r
+ EFI_HANDLE ControllerHandle;\r
+ EFI_HANDLE DriverBindingHandle;\r
+ UINT8 IpVersion;\r
+ IP_IO *IpIo;\r
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;\r
+ CHAR16 *MacString;\r
+ LIST_ENTRY SocketList;\r
+} TCP_SERVICE_DATA;\r
+\r
+typedef struct _TCP_PROTO_DATA {\r
+ TCP_SERVICE_DATA *TcpService;\r
+ TCP_CB *TcpPcb;\r
+} TCP_PROTO_DATA;\r
+\r
+#define TCP_SERVICE_FROM_THIS(a) \\r
+ CR ( \\r
+ (a), \\r
+ TCP_SERVICE_DATA, \\r
+ ServiceBinding, \\r
+ TCP_DRIVER_SIGNATURE \\r
+ )\r
+\r
+//\r
+// Function prototype for the driver's entry point\r
+//\r
+\r
+/**\r
+ The entry point for Tcp driver, used to install Tcp driver on the ImageHandle.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for this driver image.\r
+ @param[in] SystemTable Pointer to the EFI system table.\r
+\r
+ @retval EFI_SUCCESS The driver loaded.\r
+ @retval other The driver did not load.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ );\r
+\r
+//\r
+// Function prototypes for the Driver Binding Protocol\r
+//\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of the device to test.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific\r
+ child device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind driver to.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS The driver was added to ControllerHandle.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the\r
+ driver.\r
+ @retval other The driver cannot be added to ControllerHandle.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Stop this driver on ControllerHandle.\r
+\r
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must\r
+ support a bus specific I/O protocol for the driver\r
+ to use to stop the device.\r
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.\r
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL\r
+ if NumberOfChildren is 0.\r
+\r
+ @retval EFI_SUCCESS The device was stopped.\r
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ );\r
+\r
+/**\r
+ The Callback funtion called after the TCP socket is created.\r
+\r
+ @param[in] This Pointer to the socket just created.\r
+ @param[in] Context The context of the socket.\r
+\r
+ @retval EFI_SUCCESS This protocol is installed successfully.\r
+ @retval other An error occured.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpCreateSocketCallback (\r
+ IN SOCKET *This,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ The callback function called before the TCP socket is to be destroyed.\r
+\r
+ @param[in] This The TCP socket to be destroyed.\r
+ @param[in] Context The context of the socket.\r
+\r
+**/\r
+VOID\r
+TcpDestroySocketCallback (\r
+ IN SOCKET *This,\r
+ IN VOID *Context\r
+ );\r
+\r
+//\r
+// Function ptototypes for the ServiceBinding Prococol\r
+//\r
+\r
+/**\r
+ Creates a child handle with a set of TCP services.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in, out] ChildHandle Pointer to the handle of the child to create.\r
+ If it is NULL, then a new handle is created.\r
+ If it is a pointer to an existing UEFI handle,\r
+ then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create\r
+ the child.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ );\r
+\r
+/**\r
+ Destroys a child handle with a set of TCP services.\r
+\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.\r
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.\r
+ @retval EFI_INVALID_PARAMETER The child handle is not a valid UEFI Handle.\r
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle\r
+ because its services are being used.\r
+ @retval other The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TcpServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ );\r
+\r
+#endif\r
--- /dev/null
+## @file TcpDxe.inf\r
+# Component description file for Tcp module.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = TcpDxe\r
+ FILE_GUID = 1A7E4468-2F55-4a56-903C-01265EB7622B\r
+ MODULE_TYPE = UEFI_DRIVER\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = TcpDriverEntryPoint\r
+ UNLOAD_IMAGE = NetLibDefaultUnload\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+ TcpDriver.c\r
+ SockImpl.c\r
+ SockInterface.c\r
+ TcpDispatcher.c\r
+ TcpOutput.c\r
+ TcpMain.c\r
+ SockImpl.h\r
+ TcpMisc.c\r
+ TcpProto.h\r
+ TcpOption.c\r
+ TcpInput.c\r
+ TcpFunc.h\r
+ TcpOption.h\r
+ TcpTimer.c\r
+ TcpMain.h\r
+ Socket.h\r
+ ComponentName.c\r
+ TcpIo.c\r
+ TcpDriver.h\r
+\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+ BaseLib\r
+ BaseMemoryLib\r
+ DevicePathLib\r
+ DebugLib\r
+ MemoryAllocationLib\r
+ UefiLib\r
+ UefiBootServicesTableLib\r
+ UefiDriverEntryPoint\r
+ UefiRuntimeServicesTableLib\r
+ DpcLib\r
+ NetLib\r
+ IpIoLib\r
+\r
+\r
+[Protocols]\r
+ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiIp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiIp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiTcp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiTcp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+\r
--- /dev/null
+/** @file\r
+ Declaration of external functions shared in TCP driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _TCP_FUNC_H_\r
+#define _TCP_FUNC_H_\r
+\r
+#include "TcpOption.h"\r
+\r
+#define TCP_COMP_VAL(Min, Max, Default, Val) \\r
+ ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default))\r
+\r
+/**\r
+ Timeout handler prototype.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+typedef\r
+VOID\r
+(*TCP_TIMER_HANDLER) (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+//\r
+// Functions in TcpMisc.c\r
+//\r
+\r
+/**\r
+ Initialize the Tcb locally related members.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbLocal (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Initialize the peer related members.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seg Pointer to the segment that contains the peer's intial information.\r
+ @param[in] Opt Pointer to the options announced by the peer.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbPeer (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN TCP_SEG *Seg,\r
+ IN TCP_OPTION *Opt\r
+ );\r
+\r
+/**\r
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.\r
+\r
+ @param[in] Addr Pointer to the IP address needs to match.\r
+ @param[in] Port The port number needs to match.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack.\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+\r
+ @retval TRUE The Tcb which matches the <Addr Port> pairs exists.\r
+ @retval FALSE Otherwise\r
+\r
+**/\r
+BOOLEAN\r
+TcpFindTcbByPeer (\r
+ IN EFI_IP_ADDRESS *Addr,\r
+ IN TCP_PORTNO Port,\r
+ IN UINT8 Version\r
+ );\r
+\r
+/**\r
+ Locate the TCP_CB related to the socket pair.\r
+\r
+ @param[in] LocalPort The local port number.\r
+ @param[in] LocalIp The local IP address.\r
+ @param[in] RemotePort The remote port number.\r
+ @param[in] RemoteIp The remote IP address.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+ @param[in] Syn If TRUE, the listen sockets are searched.\r
+\r
+ @return Pointer to the related TCP_CB. If NULL, no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateTcb (\r
+ IN TCP_PORTNO LocalPort,\r
+ IN EFI_IP_ADDRESS *LocalIp,\r
+ IN TCP_PORTNO RemotePort,\r
+ IN EFI_IP_ADDRESS *RemoteIp,\r
+ IN UINT8 Version,\r
+ IN BOOLEAN Syn\r
+ );\r
+\r
+/**\r
+ Insert a Tcb into the proper queue.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB to be inserted.\r
+\r
+ @retval 0 The Tcb was inserted successfully.\r
+ @retval -1 An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpInsertTcb (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Clone a TCP_CB from Tcb.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB to be cloned.\r
+\r
+ @return Pointer to the new cloned TCP_CB. If NULL, an error condition occurred.\r
+\r
+**/\r
+TCP_CB *\r
+TcpCloneTcb (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Compute an ISS to be used by a new connection.\r
+\r
+ @return The result ISS.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetIss (\r
+ VOID\r
+ );\r
+\r
+/**\r
+ Get the local mss.\r
+\r
+ @param[in] Sock Pointer to the socket to get mss.\r
+\r
+ @return The mss size.\r
+\r
+**/\r
+UINT16\r
+TcpGetRcvMss (\r
+ IN SOCKET *Sock\r
+ );\r
+\r
+/**\r
+ Set the Tcb's state.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] State The state to be set.\r
+\r
+**/\r
+VOID\r
+TcpSetState (\r
+ IN TCP_CB *Tcb,\r
+ IN UINT8 State\r
+ );\r
+\r
+/**\r
+ Compute the TCP segment's checksum.\r
+\r
+ @param[in] Nbuf Pointer to the buffer that contains the TCP segment.\r
+ @param[in] HeadSum The checksum value of the fixed part of pseudo header.\r
+\r
+ @return The checksum value.\r
+\r
+**/\r
+UINT16\r
+TcpChecksum (\r
+ IN NET_BUF *Nbuf,\r
+ IN UINT16 HeadSum\r
+ );\r
+\r
+/**\r
+ Translate the information from the head of the received TCP\r
+ segment Nbuf contains, and fill it into a TCP_SEG structure.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.\r
+\r
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
+\r
+**/\r
+TCP_SEG *\r
+TcpFormatNetbuf (\r
+ IN TCP_CB *Tcb,\r
+ IN OUT NET_BUF *Nbuf\r
+ );\r
+\r
+/**\r
+ Initialize an active connection,\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a\r
+ connection.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConnect (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Application has consumed some data, check whether\r
+ to send a window update ack or a delayed ack.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConsume (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Initiate the connection close procedure, called when\r
+ applications want to close the connection.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppClose (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Check whether the application's newly delivered data can be sent out.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @retval 0 The data has been sent out successfully.\r
+ @retval -1 The Tcb is not in a state that data is permitted to\r
+ be sent out.\r
+\r
+**/\r
+INTN\r
+TcpOnAppSend (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Abort the connection by sending a reset segment: called\r
+ when the application wants to abort the connection.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of the TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppAbort (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Reset the connection related with Tcb.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.\r
+\r
+**/\r
+VOID\r
+TcpResetConnection (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Set the Tcp variable data.\r
+\r
+ @param[in] TcpService Tcp service data.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpSetVariableData (\r
+ IN TCP_SERVICE_DATA *TcpService\r
+ );\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param[in] TcpService Tcp service data.\r
+\r
+**/\r
+VOID\r
+TcpClearVariableData (\r
+ IN TCP_SERVICE_DATA *TcpService\r
+ );\r
+\r
+/**\r
+ Install the device path protocol on the TCP instance.\r
+\r
+ @param[in] Sock Pointer to the socket representing the TCP instance.\r
+\r
+ @retval EFI_SUCCESS The device path protocol installed.\r
+ @retval other Failed to install the device path protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpInstallDevicePath (\r
+ IN SOCKET *Sock\r
+ );\r
+\r
+\r
+//\r
+// Functions in TcpOutput.c\r
+//\r
+\r
+/**\r
+ Compute the sequence space left in the old receive window.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The sequence space left in the old receive window.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinOld (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Compute the current receive window.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The size of the current receive window, in bytes.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinNow (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Get the maximum SndNxt.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The sequence number of the maximum SndNxt.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetMaxSndNxt (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Compute how much data to send.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm\r
+ and send out data by force.\r
+\r
+ @return The length of the data that can be sent. If 0, no data can be sent.\r
+\r
+**/\r
+UINT32\r
+TcpDataToSend (\r
+ IN TCP_CB *Tcb,\r
+ IN INTN Force\r
+ );\r
+\r
+/**\r
+ Retransmit the segment from sequence Seq.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seq The sequence number of the segment to be retransmitted.\r
+\r
+ @retval 0 The retransmission succeeded.\r
+ @retval -1 An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpRetransmit (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_SEQNO Seq\r
+ );\r
+\r
+/**\r
+ Check whether to send data/SYN/FIN and piggyback an ACK.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm\r
+ and send out data by force.\r
+\r
+ @return The number of bytes sent.\r
+\r
+**/\r
+INTN\r
+TcpToSendData (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN INTN Force\r
+ );\r
+\r
+/**\r
+ Check whether to send an ACK or delayed ACK.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpToSendAck (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Send an ACK immediately.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSendAck (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Send a zero probe segment. It can be used by keepalive and zero window probe.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @retval 0 The zero probe segment was sent out successfully.\r
+ @retval other An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpSendZeroProbe (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Send a RESET segment in response to the segment received.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.\r
+ @param[in] Head TCP header of the segment that triggers the reset.\r
+ @param[in] Len Length of the segment that triggers the reset.\r
+ @param[in] Local Local IP address.\r
+ @param[in] Remote Remote peer's IP address.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+ @retval 0 A reset is sent or no need to send it.\r
+ @retval -1 No reset is sent.\r
+\r
+**/\r
+INTN\r
+TcpSendReset (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_HEAD *Head,\r
+ IN INT32 Len,\r
+ IN EFI_IP_ADDRESS *Local,\r
+ IN EFI_IP_ADDRESS *Remote,\r
+ IN UINT8 Version\r
+ );\r
+\r
+/**\r
+ Verify that the segment is in good shape.\r
+\r
+ @param[in] Nbuf Buffer that contains the segment to be checked.\r
+\r
+ @retval 0 The segment is broken.\r
+ @retval 1 The segment is in good shape.\r
+\r
+**/\r
+INTN\r
+TcpVerifySegment (\r
+ IN NET_BUF *Nbuf\r
+ );\r
+\r
+//\r
+// Functions from TcpInput.c\r
+//\r
+\r
+/**\r
+ Process the received ICMP error messages for TCP.\r
+\r
+ @param[in] Nbuf Buffer that contains part of the TCP segment without IP header\r
+ truncated from the ICMP error packet.\r
+ @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet.\r
+ @param[in] Src Source address of the ICMP error message.\r
+ @param[in] Dst Destination address of the ICMP error message.\r
+ @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates\r
+ IP6 stack.\r
+\r
+**/\r
+VOID\r
+TcpIcmpInput (\r
+ IN NET_BUF *Nbuf,\r
+ IN UINT8 IcmpErr,\r
+ IN EFI_IP_ADDRESS *Src,\r
+ IN EFI_IP_ADDRESS *Dst,\r
+ IN UINT8 Version\r
+ );\r
+\r
+/**\r
+ Process the received TCP segments.\r
+\r
+ @param[in] Nbuf Buffer that contains received TCP segment without an IP header.\r
+ @param[in] Src Source address of the segment, or the peer's IP address.\r
+ @param[in] Dst Destination address of the segment, or the local end's IP\r
+ address.\r
+ @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates\r
+ IP6 stack.\r
+\r
+ @retval 0 The segment processed successfully. It is either accepted or\r
+ discarded. But no connection is reset by the segment.\r
+ @retval -1 A connection is reset by the segment.\r
+\r
+**/\r
+INTN\r
+TcpInput (\r
+ IN NET_BUF *Nbuf,\r
+ IN EFI_IP_ADDRESS *Src,\r
+ IN EFI_IP_ADDRESS *Dst,\r
+ IN UINT8 Version\r
+ );\r
+\r
+//\r
+// Functions in TcpTimer.c\r
+//\r
+\r
+/**\r
+ Close the TCP connection.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClose (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Heart beat timer handler, queues the DPC at TPL_CALLBACK.\r
+\r
+ @param[in] Event Timer event signaled, ignored.\r
+ @param[in] Context Context of the timer event, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpTicking (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ Enable a TCP timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Timer The index of the timer to be enabled.\r
+ @param[in] TimeOut The timeout value of this timer.\r
+\r
+**/\r
+VOID\r
+TcpSetTimer (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN UINT16 Timer,\r
+ IN UINT32 TimeOut\r
+ );\r
+\r
+/**\r
+ Clear one TCP timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Timer The index of the timer to be cleared.\r
+\r
+**/\r
+VOID\r
+TcpClearTimer (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN UINT16 Timer\r
+ );\r
+\r
+/**\r
+ Clear all TCP timers.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClearAllTimer (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Enable the window prober timer and set the timeout value.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetProbeTimer (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Enable the keepalive timer and set the timeout value.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetKeepaliveTimer (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+//\r
+// Functions in TcpIo.c\r
+//\r
+\r
+/**\r
+ Packet receive callback function provided to IP_IO. Used to call\r
+ the proper function to handle the packet received by IP.\r
+\r
+ @param[in] Status Result of the receive request.\r
+ @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR.\r
+ @param[in] NetSession The IP session for the received packet.\r
+ @param[in] Pkt Packet received.\r
+ @param[in] Context The data provided by the user for the received packet when\r
+ the callback is registered in IP_IO_OPEN_DATA::RcvdContext.\r
+ This is an optional parameter that may be NULL.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpRxCallback (\r
+ IN EFI_STATUS Status,\r
+ IN UINT8 IcmpErr,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Pkt,\r
+ IN VOID *Context OPTIONAL\r
+ );\r
+\r
+/**\r
+ Send the segment to IP via IpIo function.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the TCP segment to be sent.\r
+ @param[in] Src Source address of the TCP segment.\r
+ @param[in] Dest Destination address of the TCP segment.\r
+ @param[in] Version IP_VERSION_4 or IP_VERSION_6\r
+\r
+ @retval 0 The segment was sent out successfully.\r
+ @retval -1 The segment failed to be sent.\r
+\r
+**/\r
+INTN\r
+TcpSendIpPacket (\r
+ IN TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf,\r
+ IN EFI_IP_ADDRESS *Src,\r
+ IN EFI_IP_ADDRESS *Dest,\r
+ IN UINT8 Version\r
+ );\r
+\r
+/**\r
+ Refresh the remote peer's Neighbor Cache State if already exists.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Neighbor Source address of the TCP segment.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain\r
+ in the neighbor cache. A value of zero means that\r
+ the entry is permanent. A value of non-zero means\r
+ that the entry is dynamic and will be deleted\r
+ after Timeout.\r
+\r
+ @retval EFI_SUCCESS Successfully updated the neighbor relationship.\r
+ @retval EFI_NOT_STARTED The IpIo is not configured.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.\r
+ @retval EFI_NOT_FOUND This entry is not in the neighbor table.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp6RefreshNeighbor (\r
+ IN TCP_CB *Tcb,\r
+ IN EFI_IP_ADDRESS *Neighbor,\r
+ IN UINT32 Timeout\r
+ );\r
+\r
+//\r
+// Functions in TcpDispatcher.c\r
+//\r
+\r
+/**\r
+ The procotol handler provided to the socket layer, used to\r
+ dispatch the socket level requests by calling the corresponding\r
+ TCP layer functions.\r
+\r
+ @param[in] Sock Pointer to the socket of this TCP instance.\r
+ @param[in] Request The code of this operation request.\r
+ @param[in] Data Pointer to the operation specific data passed in\r
+ together with the operation request. This is an\r
+ optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The socket request completed successfully.\r
+ @retval other The error status returned by the corresponding TCP\r
+ layer function.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpDispatcher (\r
+ IN SOCKET *Sock,\r
+ IN UINT8 Request,\r
+ IN VOID *Data OPTIONAL\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ TCP input process routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+ Check whether the sequence number of the incoming segment is acceptable.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seg Pointer to the incoming segment.\r
+\r
+ @retval 1 The sequence number is acceptable.\r
+ @retval 0 The sequence number is not acceptable.\r
+\r
+**/\r
+INTN\r
+TcpSeqAcceptable (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_SEG *Seg\r
+ )\r
+{\r
+ return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) &&\r
+ TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd));\r
+}\r
+\r
+/**\r
+ NewReno fast recovery defined in RFC3782.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seg Segment that triggers the fast recovery.\r
+\r
+**/\r
+VOID\r
+TcpFastRecover (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN TCP_SEG *Seg\r
+ )\r
+{\r
+ UINT32 FlightSize;\r
+ UINT32 Acked;\r
+\r
+ //\r
+ // Step 1: Three duplicate ACKs and not in fast recovery\r
+ //\r
+ if (Tcb->CongestState != TCP_CONGEST_RECOVER) {\r
+\r
+ //\r
+ // Step 1A: Invoking fast retransmission.\r
+ //\r
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
+\r
+ Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss));\r
+ Tcb->Recover = Tcb->SndNxt;\r
+\r
+ Tcb->CongestState = TCP_CONGEST_RECOVER;\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+\r
+ //\r
+ // Step 2: Entering fast retransmission\r
+ //\r
+ TcpRetransmit (Tcb, Tcb->SndUna);\r
+ Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss;\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpFastRecover: enter fast retransmission for TCB %p, recover point is %d\n",\r
+ Tcb,\r
+ Tcb->Recover)\r
+ );\r
+ return;\r
+ }\r
+\r
+ //\r
+ // During fast recovery, execute Step 3, 4, 5 of RFC3782\r
+ //\r
+ if (Seg->Ack == Tcb->SndUna) {\r
+\r
+ //\r
+ // Step 3: Fast Recovery,\r
+ // If this is a duplicated ACK, increse Cwnd by SMSS.\r
+ //\r
+\r
+ // Step 4 is skipped here only to be executed later\r
+ // by TcpToSendData\r
+ //\r
+ Tcb->CWnd += Tcb->SndMss;\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpFastRecover: received another duplicated ACK (%d) for TCB %p\n",\r
+ Seg->Ack,\r
+ Tcb)\r
+ );\r
+\r
+ } else {\r
+\r
+ //\r
+ // New data is ACKed, check whether it is a\r
+ // full ACK or partial ACK\r
+ //\r
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) {\r
+\r
+ //\r
+ // Step 5 - Full ACK:\r
+ // deflate the congestion window, and exit fast recovery\r
+ //\r
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
+\r
+ Tcb->CWnd = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss);\r
+\r
+ Tcb->CongestState = TCP_CONGEST_OPEN;\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpFastRecover: received a full ACK(%d) for TCB %p, exit fast recovery\n",\r
+ Seg->Ack,\r
+ Tcb)\r
+ );\r
+\r
+ } else {\r
+\r
+ //\r
+ // Step 5 - Partial ACK:\r
+ // fast retransmit the first unacknowledge field\r
+ // , then deflate the CWnd\r
+ //\r
+ TcpRetransmit (Tcb, Seg->Ack);\r
+ Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna);\r
+\r
+ //\r
+ // Deflate the CWnd by the amount of new data\r
+ // ACKed by SEG.ACK. If more than one SMSS data\r
+ // is ACKed, add back SMSS byte to CWnd after\r
+ //\r
+ if (Acked >= Tcb->SndMss) {\r
+ Acked -= Tcb->SndMss;\r
+\r
+ }\r
+\r
+ Tcb->CWnd -= Acked;\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpFastRecover: received a partial ACK(%d) for TCB %p\n",\r
+ Seg->Ack,\r
+ Tcb)\r
+ );\r
+\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ NewReno fast loss recovery defined in RFC3792.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seg Segment that triggers the fast loss recovery.\r
+\r
+**/\r
+VOID\r
+TcpFastLossRecover (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN TCP_SEG *Seg\r
+ )\r
+{\r
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+ //\r
+ // New data is ACKed, check whether it is a\r
+ // full ACK or partial ACK\r
+ //\r
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) {\r
+\r
+ //\r
+ // Full ACK: exit the loss recovery.\r
+ //\r
+ Tcb->LossTimes = 0;\r
+ Tcb->CongestState = TCP_CONGEST_OPEN;\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpFastLossRecover: received a full ACK(%d) for TCB %p\n",\r
+ Seg->Ack,\r
+ Tcb)\r
+ );\r
+\r
+ } else {\r
+\r
+ //\r
+ // Partial ACK:\r
+ // fast retransmit the first unacknowledge field.\r
+ //\r
+ TcpRetransmit (Tcb, Seg->Ack);\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpFastLossRecover: received a partial ACK(%d) for TCB %p\n",\r
+ Seg->Ack,\r
+ Tcb)\r
+ );\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Compute the RTT as specified in RFC2988.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Measure Currently measured RTT in heartbeats.\r
+\r
+**/\r
+VOID\r
+TcpComputeRtt (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN UINT32 Measure\r
+ )\r
+{\r
+ INT32 Var;\r
+\r
+ //\r
+ // Step 2.3: Compute the RTO for subsequent RTT measurement.\r
+ //\r
+ if (Tcb->SRtt != 0) {\r
+\r
+ Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT);\r
+\r
+ if (Var < 0) {\r
+ Var = -Var;\r
+ }\r
+\r
+ Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2;\r
+ Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure;\r
+\r
+ } else {\r
+ //\r
+ // Step 2.2: compute the first RTT measure\r
+ //\r
+ Tcb->SRtt = Measure << TCP_RTT_SHIFT;\r
+ Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1);\r
+ }\r
+\r
+ Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT;\r
+\r
+ //\r
+ // Step 2.4: Limit the RTO to at least 1 second\r
+ // Step 2.5: Limit the RTO to a maxium value that\r
+ // is at least 60 second\r
+ //\r
+ if (Tcb->Rto < TCP_RTO_MIN) {\r
+ Tcb->Rto = TCP_RTO_MIN;\r
+\r
+ } else if (Tcb->Rto > TCP_RTO_MAX) {\r
+ Tcb->Rto = TCP_RTO_MAX;\r
+\r
+ }\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpComputeRtt: new RTT for TCB %p computed SRTT: %d RTTVAR: %d RTO: %d\n",\r
+ Tcb,\r
+ Tcb->SRtt,\r
+ Tcb->RttVar,\r
+ Tcb->Rto)\r
+ );\r
+\r
+}\r
+\r
+/**\r
+ Trim the data; SYN and FIN to fit into the window defined by Left and Right.\r
+\r
+ @param[in] Nbuf The buffer that contains a received TCP segment without an IP header.\r
+ @param[in] Left The sequence number of the window's left edge.\r
+ @param[in] Right The sequence number of the window's right edge.\r
+\r
+**/\r
+VOID\r
+TcpTrimSegment (\r
+ IN NET_BUF *Nbuf,\r
+ IN TCP_SEQNO Left,\r
+ IN TCP_SEQNO Right\r
+ )\r
+{\r
+ TCP_SEG *Seg;\r
+ TCP_SEQNO Urg;\r
+ UINT32 Drop;\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+\r
+ //\r
+ // If the segment is completely out of window,\r
+ // truncate every thing, include SYN and FIN.\r
+ //\r
+ if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {\r
+\r
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);\r
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);\r
+\r
+ Seg->Seq = Seg->End;\r
+ NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Adjust the buffer header\r
+ //\r
+ if (TCP_SEQ_LT (Seg->Seq, Left)) {\r
+\r
+ Drop = TCP_SUB_SEQ (Left, Seg->Seq);\r
+ Urg = Seg->Seq + Seg->Urg;\r
+ Seg->Seq = Left;\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);\r
+ Drop--;\r
+ }\r
+\r
+ //\r
+ // Adjust the urgent point\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) {\r
+\r
+ if (TCP_SEQ_LT (Urg, Seg->Seq)) {\r
+\r
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);\r
+ } else {\r
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq);\r
+ }\r
+ }\r
+\r
+ if (Drop != 0) {\r
+ NetbufTrim (Nbuf, Drop, NET_BUF_HEAD);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Adjust the buffer tail\r
+ //\r
+ if (TCP_SEQ_GT (Seg->End, Right)) {\r
+\r
+ Drop = TCP_SUB_SEQ (Seg->End, Right);\r
+ Seg->End = Right;\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);\r
+ Drop--;\r
+ }\r
+\r
+ if (Drop != 0) {\r
+ NetbufTrim (Nbuf, Drop, NET_BUF_TAIL);\r
+ }\r
+ }\r
+\r
+ ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+}\r
+\r
+/**\r
+ Trim off the data outside the tcb's receive window.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the NET_BUF containing the received tcp segment.\r
+\r
+**/\r
+VOID\r
+TcpTrimInWnd (\r
+ IN TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf\r
+ )\r
+{\r
+ TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);\r
+}\r
+\r
+/**\r
+ Process the data and FIN flag, and check whether to deliver\r
+ data to the socket layer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @retval 0 No error occurred to deliver data.\r
+ @retval -1 An error condition occurred. The proper response is to reset the\r
+ connection.\r
+\r
+**/\r
+INTN\r
+TcpDeliverData (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ NET_BUF *Nbuf;\r
+ TCP_SEQNO Seq;\r
+ TCP_SEG *Seg;\r
+ UINT32 Urgent;\r
+\r
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+ //\r
+ // make sure there is some data queued,\r
+ // and TCP is in a proper state\r
+ //\r
+ if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) {\r
+\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // Deliver data to the socket layer\r
+ //\r
+ Entry = Tcb->RcvQue.ForwardLink;\r
+ Seq = Tcb->RcvNxt;\r
+\r
+ while (Entry != &Tcb->RcvQue) {\r
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+\r
+ ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+ ASSERT (Nbuf->Tcp == NULL);\r
+\r
+ if (TCP_SEQ_GT (Seg->Seq, Seq)) {\r
+ break;\r
+ }\r
+\r
+ Entry = Entry->ForwardLink;\r
+ Seq = Seg->End;\r
+ Tcb->RcvNxt = Seq;\r
+\r
+ RemoveEntryList (&Nbuf->List);\r
+\r
+ //\r
+ // RFC793 Eighth step: process FIN in sequence\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+\r
+ //\r
+ // The peer sends to us junky data after FIN,\r
+ // reset the connection.\r
+ //\r
+ if (!IsListEmpty (&Tcb->RcvQue)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpDeliverData: data received after FIN from peer of TCB %p, reset connection\n",\r
+ Tcb)\r
+ );\r
+\r
+ NetbufFree (Nbuf);\r
+ return -1;\r
+ }\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpDeliverData: processing FIN from peer of TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_SYN_RCVD:\r
+ case TCP_ESTABLISHED:\r
+\r
+ TcpSetState (Tcb, TCP_CLOSE_WAIT);\r
+ break;\r
+\r
+ case TCP_FIN_WAIT_1:\r
+\r
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+ TcpSetState (Tcb, TCP_CLOSING);\r
+ break;\r
+ }\r
+\r
+ //\r
+ // fall through\r
+ //\r
+ case TCP_FIN_WAIT_2:\r
+\r
+ TcpSetState (Tcb, TCP_TIME_WAIT);\r
+ TcpClearAllTimer (Tcb);\r
+\r
+ if (Tcb->TimeWaitTimeout != 0) {\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
+ } else {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "Connection closed immediately because app disables TIME_WAIT timer for %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpSendAck (Tcb);\r
+ TcpClose (Tcb);\r
+ }\r
+ break;\r
+\r
+ case TCP_CLOSE_WAIT:\r
+ case TCP_CLOSING:\r
+ case TCP_LAST_ACK:\r
+ case TCP_TIME_WAIT:\r
+ //\r
+ // The peer sends to us junk FIN byte. Discard\r
+ // the buffer then reset the connection\r
+ //\r
+ NetbufFree (Nbuf);\r
+ return -1;\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+\r
+ Seg->End--;\r
+ }\r
+\r
+ //\r
+ // Don't delay the ack if PUSH flag is on.\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) {\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+ }\r
+\r
+ if (Nbuf->TotalSize != 0) {\r
+ Urgent = 0;\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&\r
+ TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)\r
+ ) {\r
+\r
+ if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) {\r
+ Urgent = Nbuf->TotalSize;\r
+ } else {\r
+ Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1;\r
+ }\r
+ }\r
+\r
+ SockDataRcvd (Tcb->Sk, Nbuf, Urgent);\r
+ }\r
+\r
+ if (TCP_FIN_RCVD (Tcb->State)) {\r
+\r
+ SockNoMoreData (Tcb->Sk);\r
+ }\r
+\r
+ NetbufFree (Nbuf);\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Store the data into the reassemble queue.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the buffer containing the data to be queued.\r
+\r
+**/\r
+VOID\r
+TcpQueueData (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf\r
+ )\r
+{\r
+ TCP_SEG *Seg;\r
+ LIST_ENTRY *Head;\r
+ LIST_ENTRY *Prev;\r
+ LIST_ENTRY *Cur;\r
+ NET_BUF *Node;\r
+\r
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));\r
+\r
+ NET_GET_REF (Nbuf);\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+ Head = &Tcb->RcvQue;\r
+\r
+ //\r
+ // Fast path to process normal case. That is,\r
+ // no out-of-order segments are received.\r
+ //\r
+ if (IsListEmpty (Head)) {\r
+\r
+ InsertTailList (Head, &Nbuf->List);\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Find the point to insert the buffer\r
+ //\r
+ for (Prev = Head, Cur = Head->ForwardLink;\r
+ Cur != Head;\r
+ Prev = Cur, Cur = Cur->ForwardLink\r
+ ) {\r
+\r
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+\r
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check whether the current segment overlaps with the\r
+ // previous segment.\r
+ //\r
+ if (Prev != Head) {\r
+ Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
+\r
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) {\r
+\r
+ if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) {\r
+\r
+ NetbufFree (Nbuf);\r
+ return;\r
+ }\r
+\r
+ TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End);\r
+ }\r
+ }\r
+\r
+ InsertHeadList (Prev, &Nbuf->List);\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+\r
+ //\r
+ // Check the segments after the insert point.\r
+ //\r
+ while (Cur != Head) {\r
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+\r
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) {\r
+\r
+ Cur = Cur->ForwardLink;\r
+\r
+ RemoveEntryList (&Node->List);\r
+ NetbufFree (Node);\r
+ continue;\r
+ }\r
+\r
+ if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) {\r
+\r
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) {\r
+\r
+ RemoveEntryList (&Nbuf->List);\r
+ NetbufFree (Nbuf);\r
+ return;\r
+ }\r
+\r
+ TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq);\r
+ break;\r
+ }\r
+\r
+ Cur = Cur->ForwardLink;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Adjust the send queue or the retransmit queue.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Ack The acknowledge seuqence number of the received segment.\r
+\r
+**/\r
+VOID\r
+TcpAdjustSndQue (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_SEQNO Ack\r
+ )\r
+{\r
+ LIST_ENTRY *Head;\r
+ LIST_ENTRY *Cur;\r
+ NET_BUF *Node;\r
+ TCP_SEG *Seg;\r
+\r
+ Head = &Tcb->SndQue;\r
+ Cur = Head->ForwardLink;\r
+\r
+ while (Cur != Head) {\r
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+ Seg = TCPSEG_NETBUF (Node);\r
+\r
+ if (TCP_SEQ_GEQ (Seg->Seq, Ack)) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Remove completely ACKed segments\r
+ //\r
+ if (TCP_SEQ_LEQ (Seg->End, Ack)) {\r
+ Cur = Cur->ForwardLink;\r
+\r
+ RemoveEntryList (&Node->List);\r
+ NetbufFree (Node);\r
+ continue;\r
+ }\r
+\r
+ TcpTrimSegment (Node, Ack, Seg->End);\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ Process the received TCP segments.\r
+\r
+ @param[in] Nbuf Buffer that contains received a TCP segment without an IP header.\r
+ @param[in] Src Source address of the segment, or the peer's IP address.\r
+ @param[in] Dst Destination address of the segment, or the local end's IP\r
+ address.\r
+ @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates\r
+ IP6 stack.\r
+\r
+ @retval 0 Segment processed successfully. It is either accepted or\r
+ discarded. However, no connection is reset by the segment.\r
+ @retval -1 A connection is reset by the segment.\r
+\r
+**/\r
+INTN\r
+TcpInput (\r
+ IN NET_BUF *Nbuf,\r
+ IN EFI_IP_ADDRESS *Src,\r
+ IN EFI_IP_ADDRESS *Dst,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ TCP_CB *Tcb;\r
+ TCP_CB *Parent;\r
+ TCP_OPTION Option;\r
+ TCP_HEAD *Head;\r
+ INT32 Len;\r
+ TCP_SEG *Seg;\r
+ TCP_SEQNO Right;\r
+ TCP_SEQNO Urg;\r
+ UINT16 Checksum;\r
+\r
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
+\r
+ Parent = NULL;\r
+ Tcb = NULL;\r
+\r
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
+ ASSERT (Head != NULL);\r
+ Len = Nbuf->TotalSize - (Head->HeadLen << 2);\r
+\r
+ if ((Head->HeadLen < 5) || (Len < 0)) {\r
+\r
+ DEBUG ((EFI_D_INFO, "TcpInput: received an mal-formated packet\n"));\r
+ goto DISCARD;\r
+ }\r
+\r
+ if (Version == IP_VERSION_4) {\r
+ Checksum = NetPseudoHeadChecksum (Src->Addr[0], Dst->Addr[0], 6, 0);\r
+ } else {\r
+ Checksum = NetIp6PseudoHeadChecksum (&Src->v6, &Dst->v6, 6, 0);\r
+ }\r
+\r
+ Checksum = TcpChecksum (Nbuf, Checksum);\r
+\r
+ if (Checksum != 0) {\r
+ DEBUG ((EFI_D_ERROR, "TcpInput: received a checksum error packet\n"));\r
+ goto DISCARD;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) {\r
+ Len++;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) {\r
+ Len++;\r
+ }\r
+\r
+ Tcb = TcpLocateTcb (\r
+ Head->DstPort,\r
+ Dst,\r
+ Head->SrcPort,\r
+ Src,\r
+ Version,\r
+ (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)\r
+ );\r
+\r
+ if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) {\r
+ DEBUG ((EFI_D_INFO, "TcpInput: send reset because no TCB find\n"));\r
+\r
+ Tcb = NULL;\r
+ goto SEND_RESET;\r
+ }\r
+\r
+ Seg = TcpFormatNetbuf (Tcb, Nbuf);\r
+\r
+ //\r
+ // RFC1122 recommended reaction to illegal option\r
+ // (in fact, an illegal option length) is reset.\r
+ //\r
+ if (TcpParseOption (Nbuf->Tcp, &Option) == -1) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpInput: reset the peer because of mal-format option for Tcb %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto SEND_RESET;\r
+ }\r
+\r
+ //\r
+ // From now on, the segment is headless\r
+ //\r
+ NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD);\r
+ Nbuf->Tcp = NULL;\r
+\r
+ //\r
+ // Process the segment in LISTEN state.\r
+ //\r
+ if (Tcb->State == TCP_LISTEN) {\r
+ //\r
+ // First step: Check RST\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: discard a reset segment for TCB %p in listening\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto DISCARD;\r
+ }\r
+\r
+ //\r
+ // Second step: Check ACK.\r
+ // Any ACK sent to TCP in LISTEN is reseted.\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: send reset because of segment with ACK for TCB %p in listening\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto SEND_RESET;\r
+ }\r
+\r
+ //\r
+ // Third step: Check SYN\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+ //\r
+ // create a child TCB to handle the data\r
+ //\r
+ Parent = Tcb;\r
+\r
+ Tcb = TcpCloneTcb (Parent);\r
+ if (Tcb == NULL) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpInput: discard a segment because failed to clone a child for TCB%p\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto DISCARD;\r
+ }\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpInput: create a child for TCB %p in listening\n",\r
+ Tcb)\r
+ );\r
+\r
+ //\r
+ // init the TCB structure\r
+ //\r
+ IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, Dst);\r
+ IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, Src);\r
+ Tcb->LocalEnd.Port = Head->DstPort;\r
+ Tcb->RemoteEnd.Port = Head->SrcPort;\r
+\r
+ TcpInitTcbLocal (Tcb);\r
+ TcpInitTcbPeer (Tcb, Seg, &Option);\r
+\r
+ TcpSetState (Tcb, TCP_SYN_RCVD);\r
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
+ TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+ goto StepSix;\r
+ }\r
+\r
+ goto DISCARD;\r
+\r
+ } else if (Tcb->State == TCP_SYN_SENT) {\r
+ //\r
+ // First step: Check ACK bit\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: send reset because of wrong ACK received for TCB %p in SYN_SENT\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto SEND_RESET;\r
+ }\r
+\r
+ //\r
+ // Second step: Check RST bit\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: connection reset by peer for TCB %p in SYN_SENT\n",\r
+ Tcb)\r
+ );\r
+\r
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
+ goto DROP_CONNECTION;\r
+ } else {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: discard a reset segment because of no ACK for TCB %p in SYN_SENT\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto DISCARD;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Third step: Check security and precedence. Skipped\r
+ //\r
+\r
+ //\r
+ // Fourth step: Check SYN. Pay attention to sitimulatous open\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+\r
+ TcpInitTcbPeer (Tcb, Seg, &Option);\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+\r
+ Tcb->SndUna = Seg->Ack;\r
+ }\r
+\r
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
+\r
+ if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) {\r
+\r
+ TcpSetState (Tcb, TCP_ESTABLISHED);\r
+\r
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);\r
+ TcpDeliverData (Tcb);\r
+\r
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&\r
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)\r
+ ) {\r
+\r
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+ }\r
+\r
+ TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpInput: connection established for TCB %p in SYN_SENT\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto StepSix;\r
+ } else {\r
+ //\r
+ // Received a SYN segment without ACK, simultanous open.\r
+ //\r
+ TcpSetState (Tcb, TCP_SYN_RCVD);\r
+\r
+ ASSERT (Tcb->SndNxt == Tcb->Iss + 1);\r
+ TcpAdjustSndQue (Tcb, Tcb->SndNxt);\r
+\r
+ TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: simultanous open for TCB %p in SYN_SENT\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto StepSix;\r
+ }\r
+ }\r
+\r
+ goto DISCARD;\r
+ }\r
+\r
+ //\r
+ // Process segment in SYN_RCVD or TCP_CONNECTED states\r
+ //\r
+\r
+ //\r
+ // Clear probe timer since the RecvWindow is opened.\r
+ //\r
+ if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) {\r
+ TcpClearTimer (Tcb, TCP_TIMER_PROBE);\r
+ Tcb->ProbeTimerOn = FALSE;\r
+ }\r
+\r
+ //\r
+ // First step: Check whether SEG.SEQ is acceptable\r
+ //\r
+ if (TcpSeqAcceptable (Tcb, Seg) == 0) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: sequence acceptance test failed for segment of TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+ TcpSendAck (Tcb);\r
+ }\r
+\r
+ goto DISCARD;\r
+ }\r
+\r
+ if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) &&\r
+ (Tcb->RcvWl2 == Seg->End) &&\r
+ !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)\r
+ ) {\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+ }\r
+\r
+ //\r
+ // Second step: Check the RST\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
+\r
+ DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb));\r
+\r
+ if (Tcb->State == TCP_SYN_RCVD) {\r
+\r
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED);\r
+\r
+ //\r
+ // This TCB comes from either a LISTEN TCB,\r
+ // or active open TCB with simultanous open.\r
+ // Do NOT signal user CONNECTION refused\r
+ // if it comes from a LISTEN TCB.\r
+ //\r
+ } else if ((Tcb->State == TCP_ESTABLISHED) ||\r
+ (Tcb->State == TCP_FIN_WAIT_1) ||\r
+ (Tcb->State == TCP_FIN_WAIT_2) ||\r
+ (Tcb->State == TCP_CLOSE_WAIT)\r
+ ) {\r
+\r
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
+\r
+ } else {\r
+ }\r
+\r
+ goto DROP_CONNECTION;\r
+ }\r
+\r
+ //\r
+ // Trim the data and flags.\r
+ //\r
+ TcpTrimInWnd (Tcb, Nbuf);\r
+\r
+ //\r
+ // Third step: Check security and precedence, Ignored\r
+ //\r
+\r
+ //\r
+ // Fourth step: Check the SYN bit.\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: connection reset because received extra SYN for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
+ goto RESET_THEN_DROP;\r
+ }\r
+ //\r
+ // Fifth step: Check the ACK\r
+ //\r
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: segment discard because of no ACK for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto DISCARD;\r
+ } else {\r
+ if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick == 0) {\r
+ Tcp6RefreshNeighbor (Tcb, Src, TCP6_KEEP_NEIGHBOR_TIME * TICKS_PER_SECOND);\r
+ Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK;\r
+ }\r
+ }\r
+\r
+ if (Tcb->State == TCP_SYN_RCVD) {\r
+\r
+ if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) {\r
+\r
+ Tcb->SndWnd = Seg->Wnd;\r
+ Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);\r
+ Tcb->SndWl1 = Seg->Seq;\r
+ Tcb->SndWl2 = Seg->Ack;\r
+ TcpSetState (Tcb, TCP_ESTABLISHED);\r
+\r
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);\r
+ TcpDeliverData (Tcb);\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpInput: connection established for TCB %p in SYN_RCVD\n",\r
+ Tcb)\r
+ );\r
+\r
+ //\r
+ // Continue the process as ESTABLISHED state\r
+ //\r
+ } else {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: send reset because of wrong ACK for TCB %p in SYN_RCVD\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto SEND_RESET;\r
+ }\r
+ }\r
+\r
+ if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: ignore the out-of-data ACK for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto StepSix;\r
+\r
+ } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: discard segment for future ACK for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpSendAck (Tcb);\r
+ goto DISCARD;\r
+ }\r
+\r
+ //\r
+ // From now on: SND.UNA <= SEG.ACK <= SND.NXT.\r
+ //\r
+ if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) {\r
+ //\r
+ // update TsRecent as specified in page 16 RFC1323.\r
+ // RcvWl2 equals to the variable "LastAckSent"\r
+ // defined there.\r
+ //\r
+ if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) {\r
+\r
+ Tcb->TsRecent = Option.TSVal;\r
+ Tcb->TsRecentAge = mTcpTick;\r
+ }\r
+\r
+ TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr));\r
+\r
+ } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
+\r
+ ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN);\r
+\r
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+ }\r
+\r
+ if (Seg->Ack == Tcb->SndNxt) {\r
+\r
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
+ } else {\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
+ }\r
+\r
+ //\r
+ // Count duplicate acks.\r
+ //\r
+ if ((Seg->Ack == Tcb->SndUna) &&\r
+ (Tcb->SndUna != Tcb->SndNxt) &&\r
+ (Seg->Wnd == Tcb->SndWnd) &&\r
+ (0 == Len)\r
+ ) {\r
+\r
+ Tcb->DupAck++;\r
+ } else {\r
+\r
+ Tcb->DupAck = 0;\r
+ }\r
+\r
+ //\r
+ // Congestion avoidance, fast recovery and fast retransmission.\r
+ //\r
+ if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) ||\r
+ (Tcb->CongestState == TCP_CONGEST_LOSS)\r
+ ) {\r
+\r
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+ if (Tcb->CWnd < Tcb->Ssthresh) {\r
+\r
+ Tcb->CWnd += Tcb->SndMss;\r
+ } else {\r
+\r
+ Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1);\r
+ }\r
+\r
+ Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale);\r
+ }\r
+\r
+ if (Tcb->CongestState == TCP_CONGEST_LOSS) {\r
+ TcpFastLossRecover (Tcb, Seg);\r
+ }\r
+ } else {\r
+\r
+ TcpFastRecover (Tcb, Seg);\r
+ }\r
+\r
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
+\r
+ TcpAdjustSndQue (Tcb, Seg->Ack);\r
+ Tcb->SndUna = Seg->Ack;\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&\r
+ TCP_SEQ_LT (Tcb->SndUp, Seg->Ack)\r
+ ) {\r
+\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Update window info\r
+ //\r
+ if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) ||\r
+ ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))\r
+ ) {\r
+\r
+ Right = Seg->Ack + Seg->Wnd;\r
+\r
+ if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) {\r
+\r
+ if ((Tcb->SndWl1 == Seg->Seq) &&\r
+ (Tcb->SndWl2 == Seg->Ack) &&\r
+ (Len == 0)\r
+ ) {\r
+\r
+ goto NO_UPDATE;\r
+ }\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: peer shrinks the window for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && (TCP_SEQ_LT (Right, Tcb->Recover))) {\r
+\r
+ Tcb->Recover = Right;\r
+ }\r
+\r
+ if ((Tcb->CongestState == TCP_CONGEST_LOSS) && (TCP_SEQ_LT (Right, Tcb->LossRecover))) {\r
+\r
+ Tcb->LossRecover = Right;\r
+ }\r
+\r
+ if (TCP_SEQ_LT (Right, Tcb->SndNxt)) {\r
+\r
+ Tcb->SndNxt = Right;\r
+\r
+ if (Right == Tcb->SndUna) {\r
+\r
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
+ TcpSetProbeTimer (Tcb);\r
+ }\r
+ }\r
+ }\r
+\r
+ Tcb->SndWnd = Seg->Wnd;\r
+ Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);\r
+ Tcb->SndWl1 = Seg->Seq;\r
+ Tcb->SndWl2 = Seg->Ack;\r
+ }\r
+\r
+NO_UPDATE:\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && (Tcb->SndUna == Tcb->SndNxt)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpInput: local FIN is ACKed by peer for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED);\r
+ }\r
+\r
+ //\r
+ // Transit the state if proper.\r
+ //\r
+ switch (Tcb->State) {\r
+ case TCP_FIN_WAIT_1:\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+ TcpSetState (Tcb, TCP_FIN_WAIT_2);\r
+\r
+ TcpClearAllTimer (Tcb);\r
+ TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout);\r
+ }\r
+\r
+ case TCP_FIN_WAIT_2:\r
+\r
+ break;\r
+\r
+ case TCP_CLOSE_WAIT:\r
+ break;\r
+\r
+ case TCP_CLOSING:\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+ TcpSetState (Tcb, TCP_TIME_WAIT);\r
+\r
+ TcpClearAllTimer (Tcb);\r
+\r
+ if (Tcb->TimeWaitTimeout != 0) {\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
+ } else {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "Connection closed immediately because app disables TIME_WAIT timer for %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpClose (Tcb);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case TCP_LAST_ACK:\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
+\r
+ TcpSetState (Tcb, TCP_CLOSED);\r
+ }\r
+\r
+ break;\r
+\r
+ case TCP_TIME_WAIT:\r
+\r
+ TcpSendAck (Tcb);\r
+\r
+ if (Tcb->TimeWaitTimeout != 0) {\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
+ } else {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "Connection closed immediately because app disables TIME_WAIT timer for %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpClose (Tcb);\r
+ }\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ //\r
+ // Sixth step: Check the URG bit.update the Urg point\r
+ // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact.\r
+ //\r
+StepSix:\r
+\r
+ Tcb->Idle = 0;\r
+ TcpSetKeepaliveTimer (Tcb);\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && !TCP_FIN_RCVD (Tcb->State)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpInput: received urgent data from peer for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ Urg = Seg->Seq + Seg->Urg;\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && TCP_SEQ_GT (Urg, Tcb->RcvUp)) {\r
+\r
+ Tcb->RcvUp = Urg;\r
+ } else {\r
+\r
+ Tcb->RcvUp = Urg;\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG);\r
+ }\r
+ }\r
+ //\r
+ // Seventh step: Process the segment data\r
+ //\r
+ if (Seg->End != Seg->Seq) {\r
+\r
+ if (TCP_FIN_RCVD (Tcb->State)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: connection reset because data is lost for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto RESET_THEN_DROP;\r
+ }\r
+\r
+ if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpInput: connection reset because data is lost for connected TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto RESET_THEN_DROP;\r
+ }\r
+\r
+ TcpQueueData (Tcb, Nbuf);\r
+ if (TcpDeliverData (Tcb) == -1) {\r
+ goto RESET_THEN_DROP;\r
+ }\r
+\r
+ if (!IsListEmpty (&Tcb->RcvQue)) {\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Eighth step: check the FIN.\r
+ // This step is moved to TcpDeliverData. FIN will be\r
+ // processed in sequence there. Check the comments in\r
+ // the beginning of the file header for information.\r
+ //\r
+\r
+ //\r
+ // Tcb is a new child of the listening Parent,\r
+ // commit it.\r
+ //\r
+ if (Parent != NULL) {\r
+ Tcb->Parent = Parent;\r
+ TcpInsertTcb (Tcb);\r
+ }\r
+\r
+ if ((Tcb->State != TCP_CLOSED) &&\r
+ (TcpToSendData (Tcb, 0) == 0) &&\r
+ (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0))\r
+ ) {\r
+\r
+ TcpToSendAck (Tcb);\r
+ }\r
+\r
+ NetbufFree (Nbuf);\r
+ return 0;\r
+\r
+RESET_THEN_DROP:\r
+ TcpSendReset (Tcb, Head, Len, Dst, Src, Version);\r
+\r
+DROP_CONNECTION:\r
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+ NetbufFree (Nbuf);\r
+ TcpClose (Tcb);\r
+\r
+ return -1;\r
+\r
+SEND_RESET:\r
+\r
+ TcpSendReset (Tcb, Head, Len, Dst, Src, Version);\r
+\r
+DISCARD:\r
+\r
+ //\r
+ // Tcb is a child of Parent, and it doesn't survive\r
+ //\r
+ DEBUG ((EFI_D_WARN, "TcpInput: Discard a packet\n"));\r
+ NetbufFree (Nbuf);\r
+\r
+ if ((Parent != NULL) && (Tcb != NULL)) {\r
+\r
+ ASSERT (Tcb->Sk != NULL);\r
+ TcpClose (Tcb);\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Process the received ICMP error messages for TCP.\r
+\r
+ @param[in] Nbuf The buffer that contains part of the TCP segment without an IP header\r
+ truncated from the ICMP error packet.\r
+ @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet.\r
+ @param[in] Src Source address of the ICMP error message.\r
+ @param[in] Dst Destination address of the ICMP error message.\r
+ @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates\r
+ IP6 stack.\r
+\r
+**/\r
+VOID\r
+TcpIcmpInput (\r
+ IN NET_BUF *Nbuf,\r
+ IN UINT8 IcmpErr,\r
+ IN EFI_IP_ADDRESS *Src,\r
+ IN EFI_IP_ADDRESS *Dst,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ TCP_HEAD *Head;\r
+ TCP_CB *Tcb;\r
+ TCP_SEQNO Seq;\r
+ EFI_STATUS IcmpErrStatus;\r
+ BOOLEAN IcmpErrIsHard;\r
+ BOOLEAN IcmpErrNotify;\r
+\r
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
+ ASSERT (Head != NULL);\r
+\r
+ Tcb = TcpLocateTcb (\r
+ Head->DstPort,\r
+ Dst,\r
+ Head->SrcPort,\r
+ Src,\r
+ Version,\r
+ FALSE\r
+ );\r
+ if (Tcb == NULL || Tcb->State == TCP_CLOSED) {\r
+\r
+ goto CLEAN_EXIT;\r
+ }\r
+\r
+ //\r
+ // Validate the sequence number.\r
+ //\r
+ Seq = NTOHL (Head->Seq);\r
+ if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) {\r
+\r
+ goto CLEAN_EXIT;\r
+ }\r
+\r
+ IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, Tcb->Sk->IpVersion, &IcmpErrIsHard, &IcmpErrNotify);\r
+\r
+ if (IcmpErrNotify) {\r
+\r
+ SOCK_ERROR (Tcb->Sk, IcmpErrStatus);\r
+ }\r
+\r
+ if (IcmpErrIsHard) {\r
+\r
+ TcpClose (Tcb);\r
+ }\r
+\r
+CLEAN_EXIT:\r
+\r
+ NetbufFree (Nbuf);\r
+}\r
--- /dev/null
+/** @file\r
+ Implementation of I/O interfaces between TCP and IpIoLib.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+ Packet receive callback function provided to IP_IO, used to call\r
+ the proper function to handle the packet received by IP.\r
+\r
+ @param[in] Status Result of the receive request.\r
+ @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR.\r
+ @param[in] NetSession The IP session for the received packet.\r
+ @param[in] Pkt Packet received.\r
+ @param[in] Context The data provided by the user for the received packet when\r
+ the callback is registered in IP_IO_OPEN_DATA::RcvdContext.\r
+ This is an optional parameter that may be NULL.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpRxCallback (\r
+ IN EFI_STATUS Status,\r
+ IN UINT8 IcmpErr,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Pkt,\r
+ IN VOID *Context OPTIONAL\r
+ )\r
+{\r
+ if (EFI_SUCCESS == Status) {\r
+ TcpInput (Pkt, &NetSession->Source, &NetSession->Dest, NetSession->IpVersion);\r
+ } else {\r
+ TcpIcmpInput (\r
+ Pkt,\r
+ IcmpErr,\r
+ &NetSession->Source,\r
+ &NetSession->Dest,\r
+ NetSession->IpVersion\r
+ );\r
+ }\r
+}\r
+\r
+/**\r
+ Send the segment to IP via IpIo function.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the TCP segment to be sent.\r
+ @param[in] Src Source address of the TCP segment.\r
+ @param[in] Dest Destination address of the TCP segment.\r
+ @param[in] Version IP_VERSION_4 or IP_VERSION_6\r
+\r
+ @retval 0 The segment was sent out successfully.\r
+ @retval -1 The segment failed to send.\r
+\r
+**/\r
+INTN\r
+TcpSendIpPacket (\r
+ IN TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf,\r
+ IN EFI_IP_ADDRESS *Src,\r
+ IN EFI_IP_ADDRESS *Dest,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP_IO *IpIo;\r
+ IP_IO_OVERRIDE Override;\r
+ SOCKET *Sock;\r
+ VOID *IpSender;\r
+ TCP_PROTO_DATA *TcpProto;\r
+\r
+ if (NULL == Tcb) {\r
+\r
+ IpIo = NULL;\r
+ IpSender = IpIoFindSender (&IpIo, Version, Src);\r
+\r
+ if (IpSender == NULL) {\r
+ DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n"));\r
+ return -1;\r
+ }\r
+\r
+ if (Version == IP_VERSION_6) {\r
+ //\r
+ // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the\r
+ // destination address if the dest is already specified through the\r
+ // configuration data. Here we get the IpIo we need and use the default IP\r
+ // instance in this IpIo to send the packet. The dest address is configured\r
+ // to be the unspecified address for the default IP instance.\r
+ //\r
+ IpSender = NULL;\r
+ }\r
+ } else {\r
+\r
+ Sock = Tcb->Sk;\r
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+ IpIo = TcpProto->TcpService->IpIo;\r
+ IpSender = Tcb->IpInfo;\r
+\r
+ if (Version == IP_VERSION_6) {\r
+ //\r
+ // It's IPv6 and this TCP segment belongs to a solid TCB, in such case\r
+ // the destination address can't be overridden, so reset the Dest to NULL.\r
+ //\r
+ Dest = NULL;\r
+ }\r
+ }\r
+\r
+ ASSERT (Version == IpIo->IpVersion);\r
+\r
+ if (Version == IP_VERSION_4) {\r
+ Override.Ip4OverrideData.TypeOfService = 0;\r
+ Override.Ip4OverrideData.TimeToLive = 255;\r
+ Override.Ip4OverrideData.DoNotFragment = FALSE;\r
+ Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_TCP;\r
+ ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS));\r
+ } else {\r
+ Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_TCP;\r
+ Override.Ip6OverrideData.HopLimit = 255;\r
+ Override.Ip6OverrideData.FlowLabel = 0;\r
+ }\r
+\r
+ Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status));\r
+ return -1;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Refresh the remote peer's Neighbor Cache State if already exists.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Neighbor Source address of the TCP segment.\r
+ @param[in] Timeout Time in 100-ns units that this entry will remain\r
+ in the neighbor cache. A value of zero means that\r
+ the entry is permanent. A value of non-zero means\r
+ that the entry is dynamic and will be deleted\r
+ after Timeout.\r
+\r
+ @retval EFI_SUCCESS Successfully updated the neighbor relationship.\r
+ @retval EFI_NOT_STARTED The IpIo is not configured.\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.\r
+ @retval EFI_NOT_FOUND This entry is not in the neighbor table.\r
+\r
+**/\r
+EFI_STATUS\r
+Tcp6RefreshNeighbor (\r
+ IN TCP_CB *Tcb,\r
+ IN EFI_IP_ADDRESS *Neighbor,\r
+ IN UINT32 Timeout\r
+ )\r
+{\r
+ IP_IO *IpIo;\r
+ SOCKET *Sock;\r
+ TCP_PROTO_DATA *TcpProto;\r
+\r
+ if (NULL == Tcb) {\r
+ IpIo = NULL;\r
+ IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor);\r
+\r
+ if (IpIo == NULL) {\r
+ DEBUG ((EFI_D_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n"));\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ } else {\r
+ Sock = Tcb->Sk;\r
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+ IpIo = TcpProto->TcpService->IpIo;\r
+ }\r
+\r
+ return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout);\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+ Check the integrity of the data buffer.\r
+\r
+ @param[in] DataLen The total length of the data buffer.\r
+ @param[in] FragmentCount The fragment count of the fragment table.\r
+ @param[in] FragmentTable Pointer to the fragment table of the data\r
+ buffer.\r
+\r
+ @retval EFI_SUCCESS The integrity check passed.\r
+ @retval EFI_INVALID_PARAMETER The integrity check failed.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpChkDataBuf (\r
+ IN UINT32 DataLen,\r
+ IN UINT32 FragmentCount,\r
+ IN EFI_TCP4_FRAGMENT_DATA *FragmentTable\r
+ )\r
+{\r
+ UINT32 Index;\r
+\r
+ UINT32 Len;\r
+\r
+ for (Index = 0, Len = 0; Index < FragmentCount; Index++) {\r
+ Len = Len + FragmentTable[Index].FragmentLength;\r
+ }\r
+\r
+ if (DataLen != Len) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get the current operational status.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[out] Tcp4State Pointer to the buffer to receive the current TCP\r
+ state. Optional parameter that may be NULL.\r
+ @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP\r
+ configuration. Optional parameter that may be NULL.\r
+ @param[out] Ip4ModeData Pointer to the buffer to receive the current\r
+ IPv4 configuration. Optional parameter that may be NULL.\r
+ @param[out] MnpConfigData Pointer to the buffer to receive the current MNP\r
+ configuration data indirectly used by the TCPv4\r
+ Instance. Optional parameter that may be NULL.\r
+ @param[out] SnpModeData Pointer to the buffer to receive the current SNP\r
+ configuration data indirectly used by the TCPv4\r
+ Instance. Optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED No configuration data is available because this\r
+ instance hasn't been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4GetModeData (\r
+ IN CONST EFI_TCP4_PROTOCOL *This,\r
+ OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL,\r
+ OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL,\r
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ )\r
+{\r
+ TCP4_MODE_DATA TcpMode;\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ TcpMode.Tcp4State = Tcp4State;\r
+ TcpMode.Tcp4ConfigData = Tcp4ConfigData;\r
+ TcpMode.Ip4ModeData = Ip4ModeData;\r
+ TcpMode.MnpConfigData = MnpConfigData;\r
+ TcpMode.SnpModeData = SnpModeData;\r
+\r
+ return SockGetMode (Sock, &TcpMode);\r
+}\r
+\r
+/**\r
+ Initialize or brutally reset the operational parameters for\r
+ this EFI TCPv4 instance.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] TcpConfigData Pointer to the configure data to configure the\r
+ instance. Optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operational settings were set, changed, or\r
+ reset successfully.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (through DHCP, BOOTP, RARP, etc.) is not\r
+ finished.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already\r
+ configured.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+ @retval EFI_UNSUPPORTED One or more of the control options are not\r
+ supported in the implementation.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Configure (\r
+ IN EFI_TCP4_PROTOCOL * This,\r
+ IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL\r
+ )\r
+{\r
+ EFI_TCP4_OPTION *Option;\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+ IP4_ADDR Ip;\r
+ IP4_ADDR SubnetMask;\r
+\r
+ if (NULL == This) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Tcp protocol related parameter check will be conducted here\r
+ //\r
+ if (NULL != TcpConfigData) {\r
+\r
+ CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));\r
+ if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (TcpConfigData->AccessPoint.ActiveFlag && (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!TcpConfigData->AccessPoint.UseDefaultAddress) {\r
+\r
+ CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR));\r
+ CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR));\r
+ if (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (SubnetMask))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ Option = TcpConfigData->ControlOption;\r
+ if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ if (NULL == TcpConfigData) {\r
+ return SockFlush (Sock);\r
+ }\r
+\r
+ Status = SockConfigure (Sock, TcpConfigData);\r
+\r
+ if (EFI_NO_MAPPING == Status) {\r
+ Sock->ConfigureState = SO_NO_MAPPING;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Add or delete routing entries.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] DeleteRoute If TRUE, delete the specified route from routing\r
+ table; if FALSE, add the specified route to\r
+ routing table.\r
+ @param[in] SubnetAddress The destination network.\r
+ @param[in] SubnetMask The subnet mask for the destination network.\r
+ @param[in] GatewayAddress The gateway address for this route.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been\r
+ configured.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (through DHCP, BOOTP, RARP, etc.) is not\r
+ finished.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the\r
+ entry to the routing table.\r
+ @retval EFI_NOT_FOUND This route is not in the routing table.\r
+ @retval EFI_ACCESS_DENIED This route is already in the routing table.\r
+ @retval EFI_UNSUPPORTED The TCP driver does not support this operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Routes (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN BOOLEAN DeleteRoute,\r
+ IN EFI_IPv4_ADDRESS *SubnetAddress,\r
+ IN EFI_IPv4_ADDRESS *SubnetMask,\r
+ IN EFI_IPv4_ADDRESS *GatewayAddress\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ TCP4_ROUTE_INFO RouteInfo;\r
+\r
+ if (NULL == This) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ RouteInfo.DeleteRoute = DeleteRoute;\r
+ RouteInfo.SubnetAddress = SubnetAddress;\r
+ RouteInfo.SubnetMask = SubnetMask;\r
+ RouteInfo.GatewayAddress = GatewayAddress;\r
+\r
+ return SockRoute (Sock, &RouteInfo);\r
+}\r
+\r
+/**\r
+ Initiate a non-blocking TCP connection request for an active TCP instance.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] ConnectionToken Pointer to the connection token to return when\r
+ the TCP three way handshake finishes.\r
+\r
+ @retval EFI_SUCCESS The connection request successfully\r
+ initiated.\r
+ @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_ACCESS_DENIED The instance is not configured as an active one,\r
+ or it is not in Tcp4StateClosed state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to\r
+ initiate the active open.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Connect (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockConnect (Sock, ConnectionToken);\r
+}\r
+\r
+/**\r
+ Listen on the passive instance to accept an incoming connection request.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] ListenToken Pointer to the listen token to return when\r
+ operation finishes.\r
+\r
+ @retval EFI_SUCCESS The listen token was queued successfully.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not\r
+ in Tcp4StateListen state or a same listen token\r
+ has already existed in the listen token queue of\r
+ this TCP instance.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish\r
+ the operation.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Accept (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockAccept (Sock, ListenToken);\r
+}\r
+\r
+/**\r
+ Queues outgoing data into the transmit queue\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] Token Pointer to the completion token to queue to the\r
+ transmit queue.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:\r
+ * A transmit completion token with the same\r
+ Token-> CompletionToken.Event was already in the\r
+ transmission queue. * The current instance is in\r
+ Tcp4StateClosed state. * The current instance is\r
+ a passive one and it is in Tcp4StateListen\r
+ state. * User has called Close() to disconnect\r
+ this connection.\r
+ @retval EFI_NOT_READY The completion token could not be queued because\r
+ the transmit queue is full.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a\r
+ resource shortage.\r
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or\r
+ address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Transmit (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_IO_TOKEN *Token\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+\r
+ if (NULL == This ||\r
+ NULL == Token ||\r
+ NULL == Token->CompletionToken.Event ||\r
+ NULL == Token->Packet.TxData ||\r
+ 0 == Token->Packet.TxData->FragmentCount ||\r
+ 0 == Token->Packet.TxData->DataLength\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = TcpChkDataBuf (\r
+ Token->Packet.TxData->DataLength,\r
+ Token->Packet.TxData->FragmentCount,\r
+ Token->Packet.TxData->FragmentTable\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockSend (Sock, Token);\r
+}\r
+\r
+/**\r
+ Place an asynchronous receive request into the receiving queue.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that is associated with the\r
+ receive data descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token was cached\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued\r
+ due to a lack of system resources.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:\r
+ * A receive completion token with the same\r
+ Token->CompletionToken.Event was already in the\r
+ receive queue. * The current instance is in\r
+ Tcp4StateClosed state. * The current instance is\r
+ a passive one and it is in Tcp4StateListen\r
+ state. * User has called Close() to disconnect\r
+ this connection.\r
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection,\r
+ and there is no any buffered data in the receive\r
+ buffer of this instance.\r
+ @retval EFI_NOT_READY The receive request could not be queued because\r
+ the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Receive (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_IO_TOKEN *Token\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+\r
+ if (NULL == This ||\r
+ NULL == Token ||\r
+ NULL == Token->CompletionToken.Event ||\r
+ NULL == Token->Packet.RxData ||\r
+ 0 == Token->Packet.RxData->FragmentCount ||\r
+ 0 == Token->Packet.RxData->DataLength\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = TcpChkDataBuf (\r
+ Token->Packet.RxData->DataLength,\r
+ Token->Packet.RxData->FragmentCount,\r
+ Token->Packet.RxData->FragmentTable\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockRcv (Sock, Token);\r
+\r
+}\r
+\r
+/**\r
+ Disconnecting a TCP connection gracefully or reset a TCP connection.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] CloseToken Pointer to the close token to return when\r
+ operation finishes.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE: *\r
+ Configure() has been called with TcpConfigData\r
+ set to NULL, and this function has not returned.\r
+ * Previous Close() call on this instance has not\r
+ finished.\r
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the\r
+ operation.\r
+ @retval EFI_DEVICE_ERROR Any unexpected category error not belonging to those\r
+ listed above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Close (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);\r
+}\r
+\r
+/**\r
+ Abort an asynchronous connection, listen, transmission or receive request.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that has been issued by\r
+ Connect(), Accept(), Transmit() or Receive(). If\r
+ NULL, all pending tokens issued by the four\r
+ functions listed above will be aborted.\r
+\r
+ @retval EFI_UNSUPPORTED The operation is not supported in the current\r
+ implementation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Cancel (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Poll to receive incoming data and transmit outgoing segments.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or\r
+ receive queue. Consider increasing the polling\r
+ rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Poll (\r
+ IN EFI_TCP4_PROTOCOL *This\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+\r
+ if (NULL == This) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get the current operational status.\r
+\r
+ The GetModeData() function copies the current operational settings of this EFI TCPv6\r
+ Protocol instance into user-supplied buffers. This function can also be used to retrieve\r
+ the operational setting of underlying drivers such as IPv6, MNP, or SNP.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[out] Tcp6State The buffer in which the current TCP state is\r
+ returned. Optional parameter that may be NULL.\r
+ @param[out] Tcp6ConfigData The buffer in which the current TCP configuration\r
+ is returned. Optional parameter that may be NULL.\r
+ @param[out] Ip6ModeData The buffer in which the current IPv6 configuration\r
+ data used by the TCP instance is returned.\r
+ Optional parameter that may be NULL.\r
+ @param[out] MnpConfigData The buffer in which the current MNP configuration\r
+ data indirectly used by the TCP instance is returned.\r
+ Optional parameter that may be NULL.\r
+ @param[out] SnpModeData The buffer in which the current SNP mode data\r
+ indirectly used by the TCP instance is returned.\r
+ Optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't\r
+ been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6GetModeData (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL,\r
+ OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL,\r
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ )\r
+{\r
+ TCP6_MODE_DATA TcpMode;\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ TcpMode.Tcp6State = Tcp6State;\r
+ TcpMode.Tcp6ConfigData = Tcp6ConfigData;\r
+ TcpMode.Ip6ModeData = Ip6ModeData;\r
+ TcpMode.MnpConfigData = MnpConfigData;\r
+ TcpMode.SnpModeData = SnpModeData;\r
+\r
+ return SockGetMode (Sock, &TcpMode);\r
+}\r
+\r
+/**\r
+ Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.\r
+\r
+ The Configure() function does the following:\r
+ - Initialize this TCP instance, i.e., initialize the communication end settings and\r
+ specify active open or passive open for an instance.\r
+ - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush\r
+ transmission and receiving buffer directly without informing the communication peer.\r
+\r
+ No other TCPv6 Protocol operation except Poll() can be executed by this instance until\r
+ it is configured properly. For an active TCP instance, after a proper configuration it\r
+ may call Connect() to initiate a three-way handshake. For a passive TCP instance,\r
+ its state transits to Tcp6StateListen after configuration, and Accept() may be\r
+ called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,\r
+ the instance is reset. The resetting process will be done brutally, the state machine will\r
+ be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,\r
+ and no traffic is allowed through this instance.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance.\r
+ If Tcp6ConfigData is set to NULL, the instance is reset.\r
+\r
+ @retval EFI_SUCCESS The operational settings were set, changed, or reset\r
+ successfully.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for\r
+ use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:\r
+ - This is NULL.\r
+ - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor\r
+ one of the configured IP addresses in the underlying IPv6 driver.\r
+ - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast\r
+ IPv6 address.\r
+ - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or\r
+ Tcp6ConfigData->AccessPoint.RemotePort is zero when\r
+ Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.\r
+ - A same access point has been configured in other TCP\r
+ instance properly.\r
+ @retval EFI_ACCESS_DENIED Configuring a TCP instance when it is configured without\r
+ calling Configure() with NULL to reset it.\r
+ @retval EFI_UNSUPPORTED One or more of the control options are not supported in\r
+ the implementation.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when\r
+ executing Configure().\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Configure (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL\r
+ )\r
+{\r
+ EFI_TCP6_OPTION *Option;\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+ EFI_IPv6_ADDRESS *Ip;\r
+\r
+ if (NULL == This) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Tcp protocol related parameter check will be conducted here\r
+ //\r
+ if (NULL != Tcp6ConfigData) {\r
+\r
+ Ip = &Tcp6ConfigData->AccessPoint.RemoteAddress;\r
+ if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Tcp6ConfigData->AccessPoint.ActiveFlag &&\r
+ (0 == Tcp6ConfigData->AccessPoint.RemotePort || NetIp6IsUnspecifiedAddr (Ip))\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Ip = &Tcp6ConfigData->AccessPoint.StationAddress;\r
+ if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Option = Tcp6ConfigData->ControlOption;\r
+ if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ if (NULL == Tcp6ConfigData) {\r
+ return SockFlush (Sock);\r
+ }\r
+\r
+ Status = SockConfigure (Sock, Tcp6ConfigData);\r
+\r
+ if (EFI_NO_MAPPING == Status) {\r
+ Sock->ConfigureState = SO_NO_MAPPING;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Initiate a nonblocking TCP connection request for an active TCP instance.\r
+\r
+ The Connect() function will initiate an active open to the remote peer configured\r
+ in a current TCP instance if it is configured active. If the connection succeeds or\r
+ fails due to any error, the ConnectionToken->CompletionToken.Event will be signaled\r
+ and ConnectionToken->CompletionToken.Status will be updated accordingly. This\r
+ function can only be called for the TCP instance in the Tcp6StateClosed state. The\r
+ instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.\r
+ If a TCP three-way handshake succeeds, its state will become Tcp6StateEstablished.\r
+ Otherwise, the state will return to Tcp6StateClosed.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] ConnectionToken Pointer to the connection token to return when the TCP three\r
+ way handshake finishes.\r
+\r
+ @retval EFI_SUCCESS The connection request successfully initiated and the state of\r
+ this TCP instance has been changed to Tcp6StateSynSent.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:\r
+ - This instance is not configured as an active one.\r
+ - This instance is not in Tcp6StateClosed state.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - ConnectionToken is NULL.\r
+ - ConnectionToken->CompletionToken.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Connect (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockConnect (Sock, ConnectionToken);\r
+}\r
+\r
+/**\r
+ Listen on the passive instance to accept an incoming connection request. This is a\r
+ nonblocking operation.\r
+\r
+ The Accept() function initiates an asynchronous accept request to wait for an incoming\r
+ connection on the passive TCP instance. If a remote peer successfully establishes a\r
+ connection with this instance, a new TCP instance will be created and its handle will\r
+ be returned in ListenToken->NewChildHandle. The newly created instance is configured\r
+ by inheriting the passive instance's configuration and is ready for use upon return.\r
+ The new instance is in the Tcp6StateEstablished state.\r
+\r
+ The ListenToken->CompletionToken.Event will be signaled when a new connection is\r
+ accepted, when a user aborts the listen or when a connection is reset.\r
+\r
+ This function only can be called when a current TCP instance is in Tcp6StateListen state.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] ListenToken Pointer to the listen token to return when operation finishes.\r
+\r
+\r
+ @retval EFI_SUCCESS The listen token queued successfully.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:\r
+ - This instance is not a passive instance.\r
+ - This instance is not in Tcp6StateListen state.\r
+ - The same listen token has already existed in the listen\r
+ token queue of this TCP instance.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - ListenToken is NULL.\r
+ - ListentToken->CompletionToken.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation.\r
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to a category listed above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Accept (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_LISTEN_TOKEN *ListenToken\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockAccept (Sock, ListenToken);\r
+}\r
+\r
+/**\r
+ Queues outgoing data into the transmit queue.\r
+\r
+ The Transmit() function queues a sending request to this TCP instance along with the\r
+ user data. The status of the token is updated and the event in the token will be\r
+ signaled once the data is sent out or an error occurs.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the completion token to queue to the transmit queue.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a\r
+ source address for this instance, but no source address was\r
+ available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token->CompletionToken.Event is NULL.\r
+ - Token->Packet.TxData is NULL.\r
+ - Token->Packet.FragmentCount is zero.\r
+ - Token->Packet.DataLength is not equal to the sum of fragment lengths.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:\r
+ - A transmit completion token with the same Token->\r
+ CompletionToken.Event was already in the\r
+ transmission queue.\r
+ - The current instance is in Tcp6StateClosed state.\r
+ - The current instance is a passive one and it is in\r
+ Tcp6StateListen state.\r
+ - User has called Close() to disconnect this connection.\r
+ @retval EFI_NOT_READY The completion token could not be queued because the\r
+ transmit queue is full.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of resource\r
+ shortage.\r
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Transmit (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_IO_TOKEN *Token\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+\r
+ if (NULL == This ||\r
+ NULL == Token ||\r
+ NULL == Token->CompletionToken.Event ||\r
+ NULL == Token->Packet.TxData ||\r
+ 0 == Token->Packet.TxData->FragmentCount ||\r
+ 0 == Token->Packet.TxData->DataLength\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = TcpChkDataBuf (\r
+ Token->Packet.TxData->DataLength,\r
+ Token->Packet.TxData->FragmentCount,\r
+ (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.TxData->FragmentTable\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockSend (Sock, Token);\r
+}\r
+\r
+/**\r
+ Places an asynchronous receive request into the receiving queue.\r
+\r
+ The Receive() function places a completion token into the receive packet queue. This\r
+ function is always asynchronous. The caller must allocate the Token->CompletionToken.Event\r
+ and the FragmentBuffer used to receive data. The caller also must fill the DataLength that\r
+ represents the whole length of all FragmentBuffer. When the receive operation completes, the\r
+ EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData\r
+ fields, and the Token->CompletionToken.Event is signaled. If data obtained, the data and its length\r
+ will be copied into the FragmentTable; at the same time the full length of received data will\r
+ be recorded in the DataLength fields. Providing a proper notification function and context\r
+ for the event enables the user to receive the notification and receiving status. That\r
+ notification function is guaranteed to not be re-entered.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that is associated with the receive data\r
+ descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token was cached.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token->CompletionToken.Event is NULL.\r
+ - Token->Packet.RxData is NULL.\r
+ - Token->Packet.RxData->DataLength is 0.\r
+ - The Token->Packet.RxData->DataLength is not the\r
+ sum of all FragmentBuffer length in FragmentTable.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of\r
+ system resources (usually memory).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ The EFI TCPv6 Protocol instance has been reset to startup defaults.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:\r
+ - A receive completion token with the same Token->CompletionToken.Event\r
+ was already in the receive queue.\r
+ - The current instance is in Tcp6StateClosed state.\r
+ - The current instance is a passive one and it is in\r
+ Tcp6StateListen state.\r
+ - User has called Close() to disconnect this connection.\r
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection and there is no\r
+ buffered data in the receive buffer of this instance.\r
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Receive (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_IO_TOKEN *Token\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+\r
+ if (NULL == This ||\r
+ NULL == Token ||\r
+ NULL == Token->CompletionToken.Event ||\r
+ NULL == Token->Packet.RxData ||\r
+ 0 == Token->Packet.RxData->FragmentCount ||\r
+ 0 == Token->Packet.RxData->DataLength\r
+ ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = TcpChkDataBuf (\r
+ Token->Packet.RxData->DataLength,\r
+ Token->Packet.RxData->FragmentCount,\r
+ (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.RxData->FragmentTable\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockRcv (Sock, Token);\r
+}\r
+\r
+/**\r
+ Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a\r
+ nonblocking operation.\r
+\r
+ Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered\r
+ transmission data will be sent by the TCP driver, and the current instance will have a graceful close\r
+ working flow described as RFC 793 if AbortOnClose is set to FALSE. Otherwise, a rest packet\r
+ will be sent by TCP driver to fast disconnect this connection. When the close operation completes\r
+ successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous\r
+ operations are signaled, and any buffers used for TCP network traffic are flushed.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] CloseToken Pointer to the close token to return when operation finishes.\r
+\r
+ @retval EFI_SUCCESS The Close() was called successfully.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:\r
+ - CloseToken or CloseToken->CompletionToken.Event is already in use.\r
+ - Previous Close() call on this instance has not finished.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - CloseToken is NULL.\r
+ - CloseToken->CompletionToken.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation.\r
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to error categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Close (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_CLOSE_TOKEN *CloseToken\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+\r
+ if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);\r
+}\r
+\r
+/**\r
+ Abort an asynchronous connection, listen, transmission, or receive request.\r
+\r
+ The Cancel() function aborts a pending connection, listen, transmit, or\r
+ receive request.\r
+\r
+ If Token is not NULL and the token is in the connection, listen, transmission,\r
+ or receive queue when it is being cancelled, its Token->Status will be set\r
+ to EFI_ABORTED, and then Token->Event will be signaled.\r
+\r
+ If the token is not in one of the queues, which usually means that the\r
+ asynchronous operation has completed, EFI_NOT_FOUND is returned.\r
+\r
+ If Token is NULL all asynchronous token issued by Connect(), Accept(),\r
+ Transmit(), and Receive() will be aborted.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that has been issued by\r
+ EFI_TCP6_PROTOCOL.Connect(),\r
+ EFI_TCP6_PROTOCOL.Accept(),\r
+ EFI_TCP6_PROTOCOL.Transmit() or\r
+ EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending\r
+ tokens issued by above four functions will be aborted. Type\r
+ EFI_TCP6_COMPLETION_TOKEN is defined in\r
+ EFI_TCP_PROTOCOL.Connect().\r
+\r
+ @retval EFI_UNSUPPORTED The implementation does not support this function.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Cancel (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Poll to receive incoming data and transmit outgoing segments.\r
+\r
+ The Poll() function increases the rate that data is moved between the network\r
+ and application, and can be called when the TCP instance is created successfully.\r
+ Its use is optional.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_NOT_READY No incoming or outgoing data is processed.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue.\r
+ Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Poll (\r
+ IN EFI_TCP6_PROTOCOL *This\r
+ )\r
+{\r
+ SOCKET *Sock;\r
+ EFI_STATUS Status;\r
+\r
+ if (NULL == This) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Sock = SOCK_FROM_THIS (This);\r
+\r
+ Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Declaration of protocol interfaces in EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.\r
+ It is the common head file for all Tcp*.c in TCP driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _TCP_MAIN_H_\r
+#define _TCP_MAIN_H_\r
+\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+#include <Library/IpIoLib.h>\r
+#include <Library/DevicePathLib.h>\r
+\r
+#include "Socket.h"\r
+#include "TcpProto.h"\r
+#include "TcpDriver.h"\r
+#include "TcpFunc.h"\r
+\r
+extern UINT16 mTcp4RandomPort;\r
+extern UINT16 mTcp6RandomPort;\r
+extern CHAR16 *mTcpStateName[];\r
+extern EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2;\r
+\r
+extern LIST_ENTRY mTcpRunQue;\r
+extern LIST_ENTRY mTcpListenQue;\r
+extern TCP_SEQNO mTcpGlobalIss;\r
+extern UINT32 mTcpTick;\r
+\r
+///\r
+/// 30 seconds.\r
+///\r
+#define TCP6_KEEP_NEIGHBOR_TIME 30\r
+///\r
+/// 5 seconds, since 1 tick equals 200ms.\r
+///\r
+#define TCP6_REFRESH_NEIGHBOR_TICK 25\r
+\r
+#define TCP_EXPIRE_TIME 65535\r
+\r
+///\r
+/// The implementation selects the initial send sequence number and the unit to\r
+/// be added when it is increased.\r
+///\r
+#define TCP_BASE_ISS 0x4d7e980b\r
+#define TCP_ISS_INCREMENT_1 2048\r
+#define TCP_ISS_INCREMENT_2 100\r
+\r
+typedef union {\r
+ EFI_TCP4_CONFIG_DATA Tcp4CfgData;\r
+ EFI_TCP6_CONFIG_DATA Tcp6CfgData;\r
+} TCP_CONFIG_DATA;\r
+\r
+typedef union {\r
+ EFI_TCP4_ACCESS_POINT Tcp4Ap;\r
+ EFI_TCP6_ACCESS_POINT Tcp6Ap;\r
+} TCP_ACCESS_POINT;\r
+\r
+typedef struct _TCP4_MODE_DATA {\r
+ EFI_TCP4_CONNECTION_STATE *Tcp4State;\r
+ EFI_TCP4_CONFIG_DATA *Tcp4ConfigData;\r
+ EFI_IP4_MODE_DATA *Ip4ModeData;\r
+ EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;\r
+ EFI_SIMPLE_NETWORK_MODE *SnpModeData;\r
+} TCP4_MODE_DATA;\r
+\r
+typedef struct _TCP6_MODE_DATA {\r
+ EFI_TCP6_CONNECTION_STATE *Tcp6State;\r
+ EFI_TCP6_CONFIG_DATA *Tcp6ConfigData;\r
+ EFI_IP6_MODE_DATA *Ip6ModeData;\r
+ EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;\r
+ EFI_SIMPLE_NETWORK_MODE *SnpModeData;\r
+} TCP6_MODE_DATA;\r
+\r
+typedef struct _TCP4_ROUTE_INFO {\r
+ BOOLEAN DeleteRoute;\r
+ EFI_IPv4_ADDRESS *SubnetAddress;\r
+ EFI_IPv4_ADDRESS *SubnetMask;\r
+ EFI_IPv4_ADDRESS *GatewayAddress;\r
+} TCP4_ROUTE_INFO;\r
+\r
+//\r
+// EFI_TCP4_PROTOCOL definitions.\r
+//\r
+\r
+/**\r
+ Get the current operational status.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[out] Tcp4State Pointer to the buffer to receive the current TCP\r
+ state. Optional parameter that may be NULL.\r
+ @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP\r
+ configuration. Optional parameter that may be NULL.\r
+ @param[out] Ip4ModeData Pointer to the buffer to receive the current\r
+ IPv4 configuration. Optional parameter that may be NULL.\r
+ @param[out] MnpConfigData Pointer to the buffer to receive the current MNP\r
+ configuration data indirectly used by the TCPv4\r
+ Instance. Optional parameter that may be NULL.\r
+ @param[out] SnpModeData Pointer to the buffer to receive the current SNP\r
+ configuration data indirectly used by the TCPv4\r
+ Instance. Optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED No configuration data is available because this\r
+ instance hasn't been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4GetModeData (\r
+ IN CONST EFI_TCP4_PROTOCOL *This,\r
+ OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL,\r
+ OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL,\r
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Initialize or brutally reset the operational parameters for\r
+ this EFI TCPv4 instance.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] TcpConfigData Pointer to the configure data to configure the\r
+ instance. Optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operational settings are set, changed, or\r
+ reset successfully.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (through DHCP, BOOTP, RARP, etc.) is not\r
+ finished.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_ACCESS_DENIED Configuring the TCP instance when it is already\r
+ configured.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+ @retval EFI_UNSUPPORTED One or more of the control options are not\r
+ supported in the implementation.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Configure (\r
+ IN EFI_TCP4_PROTOCOL * This,\r
+ IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Add or delete routing entries.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] DeleteRoute If TRUE, delete the specified route from routing\r
+ table; if FALSE, add the specified route to\r
+ routing table.\r
+ @param[in] SubnetAddress The destination network.\r
+ @param[in] SubnetMask The subnet mask for the destination network.\r
+ @param[in] GatewayAddress The gateway address for this route.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been\r
+ configured.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (through DHCP, BOOTP, RARP, etc.) is not\r
+ finished.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the\r
+ entry to the routing table.\r
+ @retval EFI_NOT_FOUND This route is not in the routing table.\r
+ @retval EFI_ACCESS_DENIED This route is already in the routing table.\r
+ @retval EFI_UNSUPPORTED The TCP driver does not support this operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Routes (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN BOOLEAN DeleteRoute,\r
+ IN EFI_IPv4_ADDRESS *SubnetAddress,\r
+ IN EFI_IPv4_ADDRESS *SubnetMask,\r
+ IN EFI_IPv4_ADDRESS *GatewayAddress\r
+ );\r
+\r
+/**\r
+ Initiate a nonblocking TCP connection request for an active TCP instance.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] ConnectionToken Pointer to the connection token to return when\r
+ the TCP three way handshake finishes.\r
+\r
+ @retval EFI_SUCCESS The connection request is successfully\r
+ initiated.\r
+ @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_ACCESS_DENIED The instance is not configured as an active one\r
+ or it is not in Tcp4StateClosed state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to\r
+ initiate the active open.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Connect (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken\r
+ );\r
+\r
+/**\r
+ Listen on the passive instance to accept an incoming connection request.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] ListenToken Pointer to the listen token to return when\r
+ operation finishes.\r
+\r
+ @retval EFI_SUCCESS The listen token has been queued successfully.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not\r
+ in Tcp4StateListen state, or a same listen token\r
+ has already existed in the listen token queue of\r
+ this TCP instance.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish\r
+ the operation.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Accept (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken\r
+ );\r
+\r
+/**\r
+ Queues outgoing data into the transmit queue\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance\r
+ @param[in] Token Pointer to the completion token to queue to the\r
+ transmit queue\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:\r
+ * A transmit completion token with the same\r
+ Token-> CompletionToken.Event was already in the\r
+ transmission queue. * The current instance is in\r
+ Tcp4StateClosed state * The current instance is\r
+ a passive one and it is in Tcp4StateListen\r
+ state. * User has called Close() to disconnect\r
+ this connection.\r
+ @retval EFI_NOT_READY The completion token could not be queued because\r
+ the transmit queue is full.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a\r
+ resource shortage.\r
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or\r
+ address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Transmit (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_IO_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Place an asynchronous receive request into the receiving queue.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that is associated with the\r
+ receive data descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token was cached.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration\r
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued\r
+ due to a lack of system resources.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:\r
+ * A receive completion token with the same\r
+ Token->CompletionToken.Event was already in the\r
+ receive queue. * The current instance is in\r
+ Tcp4StateClosed state. * The current instance is\r
+ a passive one and it is in Tcp4StateListen\r
+ state. * User has called Close() to disconnect\r
+ this connection.\r
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection\r
+ and there is no buffered data in the receive\r
+ buffer of this instance.\r
+ @retval EFI_NOT_READY The receive request could not be queued because\r
+ the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Receive (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_IO_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Disconnecting a TCP connection gracefully or reset a TCP connection.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] CloseToken Pointer to the close token to return when\r
+ operation finishes.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been\r
+ configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE: *\r
+ Configure() has been called with TcpConfigData\r
+ set to NULL and this function has not returned.\r
+ * Previous Close() call on this instance has not\r
+ finished.\r
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the\r
+ operation.\r
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error\r
+ categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Close (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken\r
+ );\r
+\r
+/**\r
+ Abort an asynchronous connection, listen, transmission or receive request.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that has been issued by\r
+ Connect(), Accept(), Transmit() or Receive(). If\r
+ NULL, all pending tokens issued by the above four\r
+ functions will be aborted.\r
+\r
+ @retval EFI_UNSUPPORTED The operation is not supported in the current\r
+ implementation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Cancel (\r
+ IN EFI_TCP4_PROTOCOL *This,\r
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL\r
+ );\r
+\r
+/**\r
+ Poll to receive incoming data and transmit outgoing segments.\r
+\r
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or\r
+ receive queue. Consider increasing the polling\r
+ rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp4Poll (\r
+ IN EFI_TCP4_PROTOCOL *This\r
+ );\r
+\r
+//\r
+// EFI_TCP6_PROTOCOL definitions.\r
+//\r
+\r
+/**\r
+ Get the current operational status.\r
+\r
+ The GetModeData() function copies the current operational settings of this EFI TCPv6\r
+ Protocol instance into user-supplied buffers. This function can also be used to retrieve\r
+ the operational setting of underlying drivers such as IPv6, MNP, or SNP.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[out] Tcp6State The buffer in which the current TCP state is\r
+ returned. Optional parameter that may be NULL.\r
+ @param[out] Tcp6ConfigData The buffer in which the current TCP configuration\r
+ is returned. Optional parameter that may be NULL.\r
+ @param[out] Ip6ModeData The buffer in which the current IPv6 configuration\r
+ data used by the TCP instance is returned.\r
+ Optional parameter that may be NULL.\r
+ @param[out] MnpConfigData The buffer in which the current MNP configuration\r
+ data used indirectly by the TCP instance is returned.\r
+ Optional parameter that may be NULL.\r
+ @param[out] SnpModeData The buffer in which the current SNP mode data\r
+ used indirectly by the TCP instance is returned.\r
+ Optional parameter that may be NULL.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't\r
+ been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6GetModeData (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL,\r
+ OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL,\r
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.\r
+\r
+ The Configure() function does the following:\r
+ - Initialize this TCP instance, i.e., initialize the communication end settings and\r
+ specify active open or passive open for an instance.\r
+ - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush\r
+ transmission and receiving buffer directly without informing the communication peer.\r
+\r
+ No other TCPv6 Protocol operation except Poll() can be executed by this instance until\r
+ it is configured properly. For an active TCP instance, after a proper configuration it\r
+ may call Connect() to initiates the three-way handshake. For a passive TCP instance,\r
+ its state will transit to Tcp6StateListen after configuration, and Accept() may be\r
+ called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,\r
+ the instance is reset. Resetting process will be done brutally, the state machine will\r
+ be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,\r
+ and no traffic is allowed through this instance.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance.\r
+ If Tcp6ConfigData is set to NULL, the instance is reset.\r
+\r
+ @retval EFI_SUCCESS The operational settings were set, changed, or reset\r
+ successfully.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for\r
+ use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:\r
+ - This is NULL.\r
+ - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor\r
+ one of the configured IP addresses in the underlying IPv6 driver.\r
+ - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast\r
+ IPv6 address.\r
+ - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or\r
+ Tcp6ConfigData->AccessPoint.RemotePort is zero when\r
+ Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.\r
+ - A same access point has been configured in other TCP\r
+ instance properly.\r
+ @retval EFI_ACCESS_DENIED Configuring TCP instance when it is configured without\r
+ calling Configure() with NULL to reset it.\r
+ @retval EFI_UNSUPPORTED One or more of the control options are not supported in\r
+ the implementation.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when\r
+ executing Configure().\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Configure (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL\r
+ );\r
+\r
+/**\r
+ Initiate a nonblocking TCP connection request for an active TCP instance.\r
+\r
+ The Connect() function will initiate an active open to the remote peer configured\r
+ in current TCP instance if it is configured active. If the connection succeeds or\r
+ fails due to an error, the ConnectionToken->CompletionToken.Event will be signaled,\r
+ and ConnectionToken->CompletionToken.Status will be updated accordingly. This\r
+ function can only be called for the TCP instance in Tcp6StateClosed state. The\r
+ instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.\r
+ If TCP three-way handshake succeeds, its state will become Tcp6StateEstablished;\r
+ otherwise, the state will return to Tcp6StateClosed.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] ConnectionToken Pointer to the connection token to return when the TCP\r
+ three-way handshake finishes.\r
+\r
+ @retval EFI_SUCCESS The connection request successfully initiated and the state of\r
+ this TCP instance has been changed to Tcp6StateSynSent.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:\r
+ - This instance is not configured as an active instance.\r
+ - This instance is not in Tcp6StateClosed state.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - ConnectionToken is NULL.\r
+ - ConnectionToken->CompletionToken.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Connect (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken\r
+ );\r
+\r
+/**\r
+ Listen on the passive instance to accept an incoming connection request. This is a\r
+ nonblocking operation.\r
+\r
+ The Accept() function initiates an asynchronous accept request to wait for an incoming\r
+ connection on the passive TCP instance. If a remote peer successfully establishes a\r
+ connection with this instance, a new TCP instance will be created and its handle will\r
+ be returned in ListenToken->NewChildHandle. The newly created instance is configured\r
+ by inheriting the passive instance's configuration, and is ready for use upon return.\r
+ The new instance is in the Tcp6StateEstablished state.\r
+\r
+ The ListenToken->CompletionToken.Event will be signaled when a new connection is\r
+ accepted, user aborts the listen or connection is reset.\r
+\r
+ This function only can be called when the current TCP instance is in Tcp6StateListen state.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] ListenToken Pointer to the listen token to return when the operation finishes.\r
+\r
+\r
+ @retval EFI_SUCCESS The listen token was been queued successfully.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:\r
+ - This instance is not a passive instance.\r
+ - This instance is not in Tcp6StateListen state.\r
+ - The same listen token has already existed in the listen\r
+ token queue of this TCP instance.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - ListenToken is NULL.\r
+ - ListentToken->CompletionToken.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation.\r
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error\r
+ categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Accept (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_LISTEN_TOKEN *ListenToken\r
+ );\r
+\r
+/**\r
+ Queues outgoing data into the transmit queue.\r
+\r
+ The Transmit() function queues a sending request to this TCP instance along with the\r
+ user data. The status of the token is updated and the event in the token will be\r
+ signaled once the data is sent out or some error occurs.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the completion token to queue to the transmit queue.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a\r
+ source address for this instance, but no source address was\r
+ available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token->CompletionToken.Event is NULL.\r
+ - Token->Packet.TxData is NULL.\r
+ - Token->Packet.FragmentCount is zero.\r
+ - Token->Packet.DataLength is not equal to the sum of fragment lengths.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:\r
+ - A transmit completion token with the same Token->\r
+ CompletionToken.Event was already in the\r
+ transmission queue.\r
+ - The current instance is in Tcp6StateClosed state.\r
+ - The current instance is a passive one and it is in\r
+ Tcp6StateListen state.\r
+ - User has called Close() to disconnect this connection.\r
+ @retval EFI_NOT_READY The completion token could not be queued because the\r
+ transmit queue is full.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a resource\r
+ shortage.\r
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Transmit (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_IO_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Places an asynchronous receive request into the receiving queue.\r
+\r
+ The Receive() function places a completion token into the receive packet queue. This\r
+ function is always asynchronous. The caller must allocate the Token->CompletionToken.Event\r
+ and the FragmentBuffer used to receive data. The caller also must fill the DataLength, which\r
+ represents the whole length of all FragmentBuffer. When the receive operation completes, the\r
+ EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData\r
+ fields, and the Token->CompletionToken.Event is signaled. If data is obtained, the data and its length\r
+ will be copied into the FragmentTable. At the same time the full length of received data will\r
+ be recorded in the DataLength fields. Providing a proper notification function and context\r
+ for the event enables the user to receive the notification and receiving status. That\r
+ notification function is guaranteed to not be re-entered.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that is associated with the receive data\r
+ descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token was cached.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source\r
+ address for this instance, but no source address was available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ - This is NULL.\r
+ - Token is NULL.\r
+ - Token->CompletionToken.Event is NULL.\r
+ - Token->Packet.RxData is NULL.\r
+ - Token->Packet.RxData->DataLength is 0.\r
+ - The Token->Packet.RxData->DataLength is not the\r
+ sum of all FragmentBuffer length in FragmentTable.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of\r
+ system resources (usually memory).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ The EFI TCPv6 Protocol instance has been reset to startup defaults.\r
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:\r
+ - A receive completion token with the same Token->CompletionToken.Event\r
+ was already in the receive queue.\r
+ - The current instance is in Tcp6StateClosed state.\r
+ - The current instance is a passive one and it is in\r
+ Tcp6StateListen state.\r
+ - The user has called Close() to disconnect this connection.\r
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection, and there is no\r
+ buffered data in the receive buffer of this instance.\r
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Receive (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_IO_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a\r
+ nonblocking operation.\r
+\r
+ Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered\r
+ transmission data will be sent by the TCP driver, and the current instance will have a graceful close\r
+ working flow described as RFC 793 if AbortOnClose is set to FALSE, otherwise, a rest packet\r
+ will be sent by TCP driver to fast disconnect this connection. When the close operation completes\r
+ successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous\r
+ operations are signaled, and any buffers used for TCP network traffic are flushed.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] CloseToken Pointer to the close token to return when operation finishes.\r
+\r
+ @retval EFI_SUCCESS The Close() was called successfully.\r
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.\r
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:\r
+ - CloseToken or CloseToken->CompletionToken.Event is already in use.\r
+ - Previous Close() call on this instance has not finished.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ - This is NULL.\r
+ - CloseToken is NULL.\r
+ - CloseToken->CompletionToken.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation.\r
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error categories given above.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Close (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_CLOSE_TOKEN *CloseToken\r
+ );\r
+\r
+/**\r
+ Abort an asynchronous connection, listen, transmission or receive request.\r
+\r
+ The Cancel() function aborts a pending connection, listen, transmit or\r
+ receive request.\r
+\r
+ If Token is not NULL and the token is in the connection, listen, transmission\r
+ or receive queue when it is being cancelled, its Token->Status will be set\r
+ to EFI_ABORTED and then Token->Event will be signaled.\r
+\r
+ If the token is not in one of the queues, which usually means that the\r
+ asynchronous operation has completed, EFI_NOT_FOUND is returned.\r
+\r
+ If Token is NULL all asynchronous token issued by Connect(), Accept(),\r
+ Transmit() and Receive() will be aborted.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that has been issued by\r
+ EFI_TCP6_PROTOCOL.Connect(),\r
+ EFI_TCP6_PROTOCOL.Accept(),\r
+ EFI_TCP6_PROTOCOL.Transmit() or\r
+ EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending\r
+ tokens issued by above four functions will be aborted. Type\r
+ EFI_TCP6_COMPLETION_TOKEN is defined in\r
+ EFI_TCP_PROTOCOL.Connect().\r
+\r
+ @retval EFI_UNSUPPORTED The implementation does not support this function.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Cancel (\r
+ IN EFI_TCP6_PROTOCOL *This,\r
+ IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ );\r
+\r
+/**\r
+ Poll to receive incoming data and transmit outgoing segments.\r
+\r
+ The Poll() function increases the rate that data is moved between the network\r
+ and application and can be called when the TCP instance is created successfully.\r
+ Its use is optional.\r
+\r
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_NOT_READY No incoming or outgoing data is processed.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue.\r
+ Consider increasing the polling rate.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcp6Poll (\r
+ IN EFI_TCP6_PROTOCOL *This\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Misc support routines for TCP driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+LIST_ENTRY mTcpRunQue = {\r
+ &mTcpRunQue,\r
+ &mTcpRunQue\r
+};\r
+\r
+LIST_ENTRY mTcpListenQue = {\r
+ &mTcpListenQue,\r
+ &mTcpListenQue\r
+};\r
+\r
+TCP_SEQNO mTcpGlobalIss = TCP_BASE_ISS;\r
+\r
+CHAR16 *mTcpStateName[] = {\r
+ L"TCP_CLOSED",\r
+ L"TCP_LISTEN",\r
+ L"TCP_SYN_SENT",\r
+ L"TCP_SYN_RCVD",\r
+ L"TCP_ESTABLISHED",\r
+ L"TCP_FIN_WAIT_1",\r
+ L"TCP_FIN_WAIT_2",\r
+ L"TCP_CLOSING",\r
+ L"TCP_TIME_WAIT",\r
+ L"TCP_CLOSE_WAIT",\r
+ L"TCP_LAST_ACK"\r
+};\r
+\r
+\r
+/**\r
+ Initialize the Tcb local related members.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbLocal (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ //\r
+ // Compute the checksum of the fixed parts of pseudo header\r
+ //\r
+ if (Tcb->Sk->IpVersion == IP_VERSION_4) {\r
+ Tcb->HeadSum = NetPseudoHeadChecksum (\r
+ Tcb->LocalEnd.Ip.Addr[0],\r
+ Tcb->RemoteEnd.Ip.Addr[0],\r
+ 0x06,\r
+ 0\r
+ );\r
+ } else {\r
+ Tcb->HeadSum = NetIp6PseudoHeadChecksum (\r
+ &Tcb->LocalEnd.Ip.v6,\r
+ &Tcb->RemoteEnd.Ip.v6,\r
+ 0x06,\r
+ 0\r
+ );\r
+ }\r
+\r
+ Tcb->Iss = TcpGetIss ();\r
+ Tcb->SndUna = Tcb->Iss;\r
+ Tcb->SndNxt = Tcb->Iss;\r
+\r
+ Tcb->SndWl2 = Tcb->Iss;\r
+ Tcb->SndWnd = 536;\r
+\r
+ Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+\r
+ //\r
+ // First window size is never scaled\r
+ //\r
+ Tcb->RcvWndScale = 0;\r
+\r
+ Tcb->ProbeTimerOn = FALSE;\r
+}\r
+\r
+/**\r
+ Initialize the peer related members.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seg Pointer to the segment that contains the peer's intial info.\r
+ @param[in] Opt Pointer to the options announced by the peer.\r
+\r
+**/\r
+VOID\r
+TcpInitTcbPeer (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN TCP_SEG *Seg,\r
+ IN TCP_OPTION *Opt\r
+ )\r
+{\r
+ UINT16 RcvMss;\r
+\r
+ ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL));\r
+ ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));\r
+\r
+ Tcb->SndWnd = Seg->Wnd;\r
+ Tcb->SndWndMax = Tcb->SndWnd;\r
+ Tcb->SndWl1 = Seg->Seq;\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
+ Tcb->SndWl2 = Seg->Ack;\r
+ } else {\r
+ Tcb->SndWl2 = Tcb->Iss + 1;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {\r
+ Tcb->SndMss = (UINT16) MAX (64, Opt->Mss);\r
+\r
+ RcvMss = TcpGetRcvMss (Tcb->Sk);\r
+ if (Tcb->SndMss > RcvMss) {\r
+ Tcb->SndMss = RcvMss;\r
+ }\r
+\r
+ } else {\r
+ //\r
+ // One end doesn't support MSS option, use default.\r
+ //\r
+ Tcb->RcvMss = 536;\r
+ }\r
+\r
+ Tcb->CWnd = Tcb->SndMss;\r
+\r
+ Tcb->Irs = Seg->Seq;\r
+ Tcb->RcvNxt = Tcb->Irs + 1;\r
+\r
+ Tcb->RcvWl2 = Tcb->RcvNxt;\r
+\r
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {\r
+\r
+ Tcb->SndWndScale = Opt->WndScale;\r
+\r
+ Tcb->RcvWndScale = TcpComputeScale (Tcb);\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);\r
+\r
+ } else {\r
+ //\r
+ // One end doesn't support window scale option. use zero.\r
+ //\r
+ Tcb->RcvWndScale = 0;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);\r
+\r
+ //\r
+ // Compute the effective SndMss per RFC1122\r
+ // section 4.2.2.6. If timestamp option is\r
+ // enabled, it will always occupy 12 bytes.\r
+ //\r
+ Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;\r
+ }\r
+}\r
+\r
+/**\r
+ Check whether one IP address equals the other.\r
+\r
+ @param[in] Ip1 Pointer to IP address to be checked.\r
+ @param[in] Ip2 Pointer to IP address to be checked.\r
+ @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
+ IP_VERSION_6 indicates the IP address is an IPv6 address.\r
+\r
+ @retval TRUE Ip1 equals Ip2.\r
+ @retval FALSE Ip1 does not equal Ip2.\r
+\r
+**/\r
+BOOLEAN\r
+TcpIsIpEqual (\r
+ IN EFI_IP_ADDRESS *Ip1,\r
+ IN EFI_IP_ADDRESS *Ip2,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+ if (Version == IP_VERSION_4) {\r
+ return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]);\r
+ } else {\r
+ return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6);\r
+ }\r
+}\r
+\r
+/**\r
+ Check whether one IP address is filled with ZERO.\r
+\r
+ @param[in] Ip Pointer to the IP address to be checked.\r
+ @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,\r
+ IP_VERSION_6 indicates the IP address is an IPv6 address.\r
+\r
+ @retval TRUE Ip is all zero address.\r
+ @retval FALSE Ip is not all zero address.\r
+\r
+**/\r
+BOOLEAN\r
+TcpIsIpZero (\r
+ IN EFI_IP_ADDRESS *Ip,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));\r
+\r
+ if (Version == IP_VERSION_4) {\r
+ return (BOOLEAN) (Ip->Addr[0] == 0);\r
+ } else {\r
+ return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) &&\r
+ (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0));\r
+ }\r
+}\r
+\r
+/**\r
+ Locate a listen TCB that matchs the Local and Remote.\r
+\r
+ @param[in] Local Pointer to the local (IP, Port).\r
+ @param[in] Remote Pointer to the remote (IP, Port).\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+ @return Pointer to the TCP_CB with the least number of wildcards,\r
+ if NULL no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateListenTcb (\r
+ IN TCP_PEER *Local,\r
+ IN TCP_PEER *Remote,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *Node;\r
+ TCP_CB *Match;\r
+ INTN Last;\r
+ INTN Cur;\r
+\r
+ Last = 4;\r
+ Match = NULL;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version != Node->Sk->IpVersion) ||\r
+ (Local->Port != Node->LocalEnd.Port) ||\r
+ !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) ||\r
+ !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version)\r
+ ) {\r
+\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Compute the number of wildcard\r
+ //\r
+ Cur = 0;\r
+ if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) {\r
+ Cur++;\r
+ }\r
+\r
+ if (Node->RemoteEnd.Port == 0) {\r
+ Cur++;\r
+ }\r
+\r
+ if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) {\r
+ Cur++;\r
+ }\r
+\r
+ if (Cur < Last) {\r
+ if (Cur == 0) {\r
+ return Node;\r
+ }\r
+\r
+ Last = Cur;\r
+ Match = Node;\r
+ }\r
+ }\r
+\r
+ return Match;\r
+}\r
+\r
+/**\r
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.\r
+\r
+ @param[in] Addr Pointer to the IP address needs to match.\r
+ @param[in] Port The port number needs to match.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+\r
+ @retval TRUE The Tcb which matches the <Addr Port> pair exists.\r
+ @retval FALSE Otherwise\r
+\r
+**/\r
+BOOLEAN\r
+TcpFindTcbByPeer (\r
+ IN EFI_IP_ADDRESS *Addr,\r
+ IN TCP_PORTNO Port,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ TCP_PORTNO LocalPort;\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *Tcb;\r
+\r
+ ASSERT ((Addr != NULL) && (Port != 0));\r
+\r
+ LocalPort = HTONS (Port);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version == Tcb->Sk->IpVersion) &&\r
+ TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
+ (LocalPort == Tcb->LocalEnd.Port)\r
+ ) {\r
+\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version == Tcb->Sk->IpVersion) &&\r
+ TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&\r
+ (LocalPort == Tcb->LocalEnd.Port)\r
+ ) {\r
+\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Locate the TCP_CB related to the socket pair.\r
+\r
+ @param[in] LocalPort The local port number.\r
+ @param[in] LocalIp The local IP address.\r
+ @param[in] RemotePort The remote port number.\r
+ @param[in] RemoteIp The remote IP address.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+ @param[in] Syn If TRUE, the listen sockets are searched.\r
+\r
+ @return Pointer to the related TCP_CB. If NULL, no match is found.\r
+\r
+**/\r
+TCP_CB *\r
+TcpLocateTcb (\r
+ IN TCP_PORTNO LocalPort,\r
+ IN EFI_IP_ADDRESS *LocalIp,\r
+ IN TCP_PORTNO RemotePort,\r
+ IN EFI_IP_ADDRESS *RemoteIp,\r
+ IN UINT8 Version,\r
+ IN BOOLEAN Syn\r
+ )\r
+{\r
+ TCP_PEER Local;\r
+ TCP_PEER Remote;\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *Tcb;\r
+\r
+ Local.Port = LocalPort;\r
+ Remote.Port = RemotePort;\r
+\r
+ CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+ //\r
+ // First check for exact match.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if ((Version == Tcb->Sk->IpVersion) &&\r
+ TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) &&\r
+ TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version)\r
+ ) {\r
+\r
+ RemoveEntryList (&Tcb->List);\r
+ InsertHeadList (&mTcpRunQue, &Tcb->List);\r
+\r
+ return Tcb;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Only check the listen queue when the SYN flag is on.\r
+ //\r
+ if (Syn) {\r
+ return TcpLocateListenTcb (&Local, &Remote, Version);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Insert a Tcb into the proper queue.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB to be inserted.\r
+\r
+ @retval 0 The Tcb was inserted successfully.\r
+ @retval -1 Error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpInsertTcb (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Head;\r
+ TCP_CB *Node;\r
+ TCP_PROTO_DATA *TcpProto;\r
+\r
+ ASSERT (\r
+ (Tcb != NULL) &&\r
+ (\r
+ (Tcb->State == TCP_LISTEN) ||\r
+ (Tcb->State == TCP_SYN_SENT) ||\r
+ (Tcb->State == TCP_SYN_RCVD) ||\r
+ (Tcb->State == TCP_CLOSED)\r
+ )\r
+ );\r
+\r
+ if (Tcb->LocalEnd.Port == 0) {\r
+ return -1;\r
+ }\r
+\r
+ Head = &mTcpRunQue;\r
+\r
+ if (Tcb->State == TCP_LISTEN) {\r
+ Head = &mTcpListenQue;\r
+ }\r
+\r
+ //\r
+ // Check that the Tcb isn't already on the list.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, Head) {\r
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd, Tcb->Sk->IpVersion) &&\r
+ TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion)\r
+ ) {\r
+\r
+ return -1;\r
+ }\r
+ }\r
+\r
+ InsertHeadList (Head, &Tcb->List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) Tcb->Sk->ProtoReserved;\r
+ TcpSetVariableData (TcpProto->TcpService);\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Clone a TCP_CB from Tcb.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB to be cloned.\r
+\r
+ @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.\r
+\r
+**/\r
+TCP_CB *\r
+TcpCloneTcb (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ TCP_CB *Clone;\r
+\r
+ Clone = AllocateZeroPool (sizeof (TCP_CB));\r
+\r
+ if (Clone == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ CopyMem (Clone, Tcb, sizeof (TCP_CB));\r
+\r
+ //\r
+ // Increase the reference count of the shared IpInfo.\r
+ //\r
+ NET_GET_REF (Tcb->IpInfo);\r
+\r
+ InitializeListHead (&Clone->List);\r
+ InitializeListHead (&Clone->SndQue);\r
+ InitializeListHead (&Clone->RcvQue);\r
+\r
+ Clone->Sk = SockClone (Tcb->Sk);\r
+ if (Clone->Sk == NULL) {\r
+ DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n"));\r
+ FreePool (Clone);\r
+ return NULL;\r
+ }\r
+\r
+ ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;\r
+\r
+ return Clone;\r
+}\r
+\r
+/**\r
+ Compute an ISS to be used by a new connection.\r
+\r
+ @return The resulting ISS.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetIss (\r
+ VOID\r
+ )\r
+{\r
+ mTcpGlobalIss += TCP_ISS_INCREMENT_1;\r
+ return mTcpGlobalIss;\r
+}\r
+\r
+/**\r
+ Get the local mss.\r
+\r
+ @param[in] Sock Pointer to the socket to get mss.\r
+\r
+ @return The mss size.\r
+\r
+**/\r
+UINT16\r
+TcpGetRcvMss (\r
+ IN SOCKET *Sock\r
+ )\r
+{\r
+ EFI_IP4_MODE_DATA Ip4Mode;\r
+ EFI_IP6_MODE_DATA Ip6Mode;\r
+ EFI_IP4_PROTOCOL *Ip4;\r
+ EFI_IP6_PROTOCOL *Ip6;\r
+ TCP_PROTO_DATA *TcpProto;\r
+\r
+ ASSERT (Sock != NULL);\r
+\r
+ ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA));\r
+ ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA));\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+\r
+ if (Sock->IpVersion == IP_VERSION_4) {\r
+ Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4;\r
+ ASSERT (Ip4 != NULL);\r
+ Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL);\r
+\r
+ return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
+ } else {\r
+ Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6;\r
+ ASSERT (Ip6 != NULL);\r
+ Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL);\r
+\r
+ return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD));\r
+ }\r
+}\r
+\r
+/**\r
+ Set the Tcb's state.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] State The state to be set.\r
+\r
+**/\r
+VOID\r
+TcpSetState (\r
+ IN TCP_CB *Tcb,\r
+ IN UINT8 State\r
+ )\r
+{\r
+ ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
+ ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "Tcb (%p) state %s --> %s\n",\r
+ Tcb,\r
+ mTcpStateName[Tcb->State],\r
+ mTcpStateName[State])\r
+ );\r
+\r
+ Tcb->State = State;\r
+\r
+ switch (State) {\r
+ case TCP_ESTABLISHED:\r
+\r
+ SockConnEstablished (Tcb->Sk);\r
+\r
+ if (Tcb->Parent != NULL) {\r
+ //\r
+ // A new connection is accepted by a listening socket. Install\r
+ // the device path.\r
+ //\r
+ TcpInstallDevicePath (Tcb->Sk);\r
+ }\r
+\r
+ break;\r
+\r
+ case TCP_CLOSED:\r
+\r
+ SockConnClosed (Tcb->Sk);\r
+\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ Compute the TCP segment's checksum.\r
+\r
+ @param[in] Nbuf Pointer to the buffer that contains the TCP segment.\r
+ @param[in] HeadSum The checksum value of the fixed part of pseudo header.\r
+\r
+ @return The checksum value.\r
+\r
+**/\r
+UINT16\r
+TcpChecksum (\r
+ IN NET_BUF *Nbuf,\r
+ IN UINT16 HeadSum\r
+ )\r
+{\r
+ UINT16 Checksum;\r
+\r
+ Checksum = NetbufChecksum (Nbuf);\r
+ Checksum = NetAddChecksum (Checksum, HeadSum);\r
+\r
+ Checksum = NetAddChecksum (\r
+ Checksum,\r
+ HTONS ((UINT16) Nbuf->TotalSize)\r
+ );\r
+\r
+ return (UINT16) (~Checksum);\r
+}\r
+\r
+/**\r
+ Translate the information from the head of the received TCP\r
+ segment Nbuf contents and fill it into a TCP_SEG structure.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.\r
+\r
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
+\r
+**/\r
+TCP_SEG *\r
+TcpFormatNetbuf (\r
+ IN TCP_CB *Tcb,\r
+ IN OUT NET_BUF *Nbuf\r
+ )\r
+{\r
+ TCP_SEG *Seg;\r
+ TCP_HEAD *Head;\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
+ ASSERT (Head != NULL);\r
+\r
+ Nbuf->Tcp = Head;\r
+\r
+ Seg->Seq = NTOHL (Head->Seq);\r
+ Seg->Ack = NTOHL (Head->Ack);\r
+ Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));\r
+\r
+ Seg->Urg = NTOHS (Head->Urg);\r
+ Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale);\r
+ Seg->Flag = Head->Flag;\r
+\r
+ //\r
+ // SYN and FIN flag occupy one sequence space each.\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+ //\r
+ // RFC requires that the initial window not be scaled.\r
+ //\r
+ Seg->Wnd = NTOHS (Head->Wnd);\r
+ Seg->End++;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+ Seg->End++;\r
+ }\r
+\r
+ return Seg;\r
+}\r
+\r
+/**\r
+ Initialize an active connection.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a\r
+ connection.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConnect (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ TcpInitTcbLocal (Tcb);\r
+ TcpSetState (Tcb, TCP_SYN_SENT);\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
+ TcpToSendData (Tcb, 1);\r
+}\r
+\r
+/**\r
+ Initiate the connection close procedure, called when\r
+ applications want to close the connection.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppClose (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ ASSERT (Tcb != NULL);\r
+\r
+ if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpOnAppClose: connection reset because data is lost for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpResetConnection (Tcb);\r
+ TcpClose (Tcb);\r
+ return;\r
+ }\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_CLOSED:\r
+ case TCP_LISTEN:\r
+ case TCP_SYN_SENT:\r
+ TcpSetState (Tcb, TCP_CLOSED);\r
+ break;\r
+\r
+ case TCP_SYN_RCVD:\r
+ case TCP_ESTABLISHED:\r
+ TcpSetState (Tcb, TCP_FIN_WAIT_1);\r
+ break;\r
+\r
+ case TCP_CLOSE_WAIT:\r
+ TcpSetState (Tcb, TCP_LAST_ACK);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ TcpToSendData (Tcb, 1);\r
+}\r
+\r
+/**\r
+ Check whether the application's newly delivered data can be sent out.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @retval 0 The data has been sent out successfully.\r
+ @retval -1 The Tcb is not in a state that data is permitted to\r
+ be sent out.\r
+\r
+**/\r
+INTN\r
+TcpOnAppSend (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_CLOSED:\r
+ return -1;\r
+\r
+ case TCP_LISTEN:\r
+ return -1;\r
+\r
+ case TCP_SYN_SENT:\r
+ case TCP_SYN_RCVD:\r
+ return 0;\r
+\r
+ case TCP_ESTABLISHED:\r
+ case TCP_CLOSE_WAIT:\r
+ TcpToSendData (Tcb, 0);\r
+ return 0;\r
+\r
+ case TCP_FIN_WAIT_1:\r
+ case TCP_FIN_WAIT_2:\r
+ case TCP_CLOSING:\r
+ case TCP_LAST_ACK:\r
+ case TCP_TIME_WAIT:\r
+ return -1;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Application has consumed some data. Check whether\r
+ to send a window update ack or a delayed ack.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppConsume (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ UINT32 TcpOld;\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_ESTABLISHED:\r
+ TcpOld = TcpRcvWinOld (Tcb);\r
+ if (TcpRcvWinNow (Tcb) > TcpOld) {\r
+\r
+ if (TcpOld < Tcb->RcvMss) {\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpSendAck (Tcb);\r
+ } else if (Tcb->DelayedAck == 0) {\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ Tcb->DelayedAck = 1;\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ Abort the connection by sending a reset segment. Called\r
+ when the application wants to abort the connection.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of the TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpOnAppAbort (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpOnAppAbort: connection reset issued by application for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ switch (Tcb->State) {\r
+ case TCP_SYN_RCVD:\r
+ case TCP_ESTABLISHED:\r
+ case TCP_FIN_WAIT_1:\r
+ case TCP_FIN_WAIT_2:\r
+ case TCP_CLOSE_WAIT:\r
+ TcpResetConnection (Tcb);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ TcpSetState (Tcb, TCP_CLOSED);\r
+}\r
+\r
+/**\r
+ Reset the connection related with Tcb.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.\r
+\r
+**/\r
+VOID\r
+TcpResetConnection (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ TCP_HEAD *Nhead;\r
+\r
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+ if (Nbuf == NULL) {\r
+ return ;\r
+ }\r
+\r
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
+ Nbuf,\r
+ sizeof (TCP_HEAD),\r
+ NET_BUF_TAIL\r
+ );\r
+\r
+ ASSERT (Nhead != NULL);\r
+\r
+ Nbuf->Tcp = Nhead;\r
+\r
+ Nhead->Flag = TCP_FLG_RST;\r
+ Nhead->Seq = HTONL (Tcb->SndNxt);\r
+ Nhead->Ack = HTONL (Tcb->RcvNxt);\r
+ Nhead->SrcPort = Tcb->LocalEnd.Port;\r
+ Nhead->DstPort = Tcb->RemoteEnd.Port;\r
+ Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
+ Nhead->Res = 0;\r
+ Nhead->Wnd = HTONS (0xFFFF);\r
+ Nhead->Checksum = 0;\r
+ Nhead->Urg = 0;\r
+ Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
+\r
+ TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);\r
+\r
+ NetbufFree (Nbuf);\r
+}\r
+\r
+/**\r
+ Set the Tcp variable data.\r
+\r
+ @param[in] TcpService Tcp service data.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpSetVariableData (\r
+ IN TCP_SERVICE_DATA *TcpService\r
+ )\r
+{\r
+ EFI_GUID *ServiceBindingGuid;\r
+ UINT32 NumConfiguredInstance;\r
+ LIST_ENTRY *Entry;\r
+ TCP_CB *TcpPcb;\r
+ TCP_PROTO_DATA *TcpProto;\r
+ UINTN VariableDataSize;\r
+ EFI_TCP4_VARIABLE_DATA *Tcp4VariableData;\r
+ EFI_TCP4_SERVICE_POINT *Tcp4ServicePoint;\r
+ EFI_TCP6_VARIABLE_DATA *Tcp6VariableData;\r
+ EFI_TCP6_SERVICE_POINT *Tcp6ServicePoint;\r
+ VOID *VariableData;\r
+ CHAR16 *NewMacString;\r
+ EFI_STATUS Status;\r
+\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+ } else {\r
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+ }\r
+\r
+ NumConfiguredInstance = 0;\r
+ Tcp4VariableData = NULL;\r
+ Tcp6VariableData = NULL;\r
+\r
+ //\r
+ // Go through the running queue to count the instances.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ NumConfiguredInstance++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Go through the listening queue to count the instances.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ NumConfiguredInstance++;\r
+ }\r
+ }\r
+\r
+ Tcp4ServicePoint = NULL;\r
+ Tcp6ServicePoint = NULL;\r
+\r
+ //\r
+ // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,\r
+ // we should add extra buffers for the service points only if the number of configured\r
+ // children is more than one.\r
+ //\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);\r
+\r
+ if (NumConfiguredInstance > 1) {\r
+ VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+ }\r
+\r
+ Tcp4VariableData = AllocateZeroPool (VariableDataSize);\r
+ if (Tcp4VariableData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Tcp4VariableData->DriverHandle = TcpService->DriverBindingHandle;\r
+ Tcp4VariableData->ServiceCount = NumConfiguredInstance;\r
+\r
+ Tcp4ServicePoint = &Tcp4VariableData->Services[0];\r
+ VariableData = Tcp4VariableData;\r
+ } else {\r
+ VariableDataSize = sizeof (EFI_TCP6_VARIABLE_DATA);\r
+\r
+ if (NumConfiguredInstance > 1) {\r
+ VariableDataSize += sizeof (EFI_TCP6_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+ }\r
+\r
+ Tcp6VariableData = AllocateZeroPool (VariableDataSize);\r
+ if (Tcp6VariableData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Tcp6VariableData->DriverHandle = TcpService->DriverBindingHandle;\r
+ Tcp6VariableData->ServiceCount = NumConfiguredInstance;\r
+\r
+ Tcp6ServicePoint = &Tcp6VariableData->Services[0];\r
+ VariableData = Tcp6VariableData;\r
+ }\r
+\r
+ //\r
+ // Go through the running queue to fill the service points.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp4ServicePoint++;\r
+ } else {\r
+ Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);\r
+ Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);\r
+ Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp6ServicePoint++;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Go through the listening queue to fill the service points.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
+\r
+ if (TcpProto->TcpService == TcpService) {\r
+ //\r
+ // This tcp instance belongs to the TcpService.\r
+ //\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp4ServicePoint++;\r
+ } else {\r
+ Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);\r
+ Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);\r
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);\r
+ Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);\r
+\r
+ Tcp6ServicePoint++;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get the mac string.\r
+ //\r
+ Status = NetLibGetMacString (\r
+ TcpService->ControllerHandle,\r
+ TcpService->DriverBindingHandle,\r
+ &NewMacString\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ if (TcpService->MacString != NULL) {\r
+ //\r
+ // The variable is set already. We're going to update it.\r
+ //\r
+ if (StrCmp (TcpService->MacString, NewMacString) != 0) {\r
+ //\r
+ // The mac address is changed. Delete the previous variable first.\r
+ //\r
+ gRT->SetVariable (\r
+ TcpService->MacString,\r
+ ServiceBindingGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ FreePool (TcpService->MacString);\r
+ }\r
+\r
+ TcpService->MacString = NewMacString;\r
+\r
+ Status = gRT->SetVariable (\r
+ TcpService->MacString,\r
+ ServiceBindingGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ VariableDataSize,\r
+ VariableData\r
+ );\r
+\r
+ON_ERROR:\r
+\r
+ FreePool (VariableData);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param[in] TcpService Tcp service data.\r
+\r
+**/\r
+VOID\r
+TcpClearVariableData (\r
+ IN TCP_SERVICE_DATA *TcpService\r
+ )\r
+{\r
+ EFI_GUID *ServiceBindingGuid;\r
+\r
+ ASSERT (TcpService->MacString != NULL);\r
+\r
+ if (TcpService->IpVersion == IP_VERSION_4) {\r
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+ } else {\r
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+ }\r
+\r
+ gRT->SetVariable (\r
+ TcpService->MacString,\r
+ ServiceBindingGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ FreePool (TcpService->MacString);\r
+ TcpService->MacString = NULL;\r
+}\r
+\r
+/**\r
+ Install the device path protocol on the TCP instance.\r
+\r
+ @param[in] Sock Pointer to the socket representing the TCP instance.\r
+\r
+ @retval EFI_SUCCESS The device path protocol was installed.\r
+ @retval other Failed to install the device path protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+TcpInstallDevicePath (\r
+ IN SOCKET *Sock\r
+ )\r
+{\r
+ TCP_PROTO_DATA *TcpProto;\r
+ TCP_SERVICE_DATA *TcpService;\r
+ TCP_CB *Tcb;\r
+ IPv4_DEVICE_PATH Ip4DPathNode;\r
+ IPv6_DEVICE_PATH Ip6DPathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ EFI_STATUS Status;\r
+ TCP_PORTNO LocalPort;\r
+ TCP_PORTNO RemotePort;\r
+\r
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;\r
+ TcpService = TcpProto->TcpService;\r
+ Tcb = TcpProto->TcpPcb;\r
+\r
+ LocalPort = NTOHS (Tcb->LocalEnd.Port);\r
+ RemotePort = NTOHS (Tcb->RemoteEnd.Port);\r
+ if (Sock->IpVersion == IP_VERSION_4) {\r
+ NetLibCreateIPv4DPathNode (\r
+ &Ip4DPathNode,\r
+ TcpService->ControllerHandle,\r
+ Tcb->LocalEnd.Ip.Addr[0],\r
+ LocalPort,\r
+ Tcb->RemoteEnd.Ip.Addr[0],\r
+ RemotePort,\r
+ EFI_IP_PROTO_TCP,\r
+ Tcb->UseDefaultAddr\r
+ );\r
+\r
+ DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode;\r
+ } else {\r
+ NetLibCreateIPv6DPathNode (\r
+ &Ip6DPathNode,\r
+ TcpService->ControllerHandle,\r
+ &Tcb->LocalEnd.Ip.v6,\r
+ LocalPort,\r
+ &Tcb->RemoteEnd.Ip.v6,\r
+ RemotePort,\r
+ EFI_IP_PROTO_TCP\r
+ );\r
+\r
+ DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode;\r
+ }\r
+\r
+ Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath);\r
+ if (Sock->DevicePath == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = gBS->InstallProtocolInterface (\r
+ &Sock->SockHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ Sock->DevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Sock->DevicePath);\r
+ Sock->DevicePath = NULL;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Routines to process TCP option.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+/**\r
+ Get a UINT16 value from buffer.\r
+\r
+ @param[in] Buf Pointer to input buffer.\r
+\r
+ @return The UINT16 value obtained from the buffer.\r
+\r
+**/\r
+UINT16\r
+TcpGetUint16 (\r
+ IN UINT8 *Buf\r
+ )\r
+{\r
+ UINT16 Value;\r
+ CopyMem (&Value, Buf, sizeof (UINT16));\r
+ return NTOHS (Value);\r
+}\r
+\r
+/**\r
+ Get a UINT32 value from buffer.\r
+\r
+ @param[in] Buf Pointer to input buffer.\r
+\r
+ @return The UINT32 value obtained from the buffer.\r
+\r
+**/\r
+UINT32\r
+TcpGetUint32 (\r
+ IN UINT8 *Buf\r
+ )\r
+{\r
+ UINT32 Value;\r
+ CopyMem (&Value, Buf, sizeof (UINT32));\r
+ return NTOHL (Value);\r
+}\r
+\r
+/**\r
+ Put a UINT32 value in buffer.\r
+\r
+ @param[out] Buf Pointer to the buffer.\r
+ @param[in] Data The UINT32 Date to put in the buffer.\r
+\r
+**/\r
+VOID\r
+TcpPutUint32 (\r
+ OUT UINT8 *Buf,\r
+ IN UINT32 Data\r
+ )\r
+{\r
+ Data = HTONL (Data);\r
+ CopyMem (Buf, &Data, sizeof (UINT32));\r
+}\r
+\r
+/**\r
+ Compute the window scale value according to the given buffer size.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The scale value.\r
+\r
+**/\r
+UINT8\r
+TcpComputeScale (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ UINT8 Scale;\r
+ UINT32 BufSize;\r
+\r
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+ BufSize = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+\r
+ Scale = 0;\r
+ while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) {\r
+\r
+ Scale++;\r
+ }\r
+\r
+ return Scale;\r
+}\r
+\r
+/**\r
+ Build the TCP option in three-way handshake.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the buffer to store the options.\r
+\r
+ @return The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpSynBuildOption (\r
+ IN TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf\r
+ )\r
+{\r
+ UINT8 *Data;\r
+ UINT16 Len;\r
+\r
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));\r
+\r
+ Len = 0;\r
+\r
+ //\r
+ // Add a timestamp option if not disabled by the application\r
+ // and it is the first SYN segment, or the peer has sent\r
+ // us its timestamp.\r
+ //\r
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) &&\r
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||\r
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))\r
+ ) {\r
+\r
+ Data = NetbufAllocSpace (\r
+ Nbuf,\r
+ TCP_OPTION_TS_ALIGNED_LEN,\r
+ NET_BUF_HEAD\r
+ );\r
+\r
+ ASSERT (Data != NULL);\r
+ Len += TCP_OPTION_TS_ALIGNED_LEN;\r
+\r
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);\r
+ TcpPutUint32 (Data + 4, mTcpTick);\r
+ TcpPutUint32 (Data + 8, 0);\r
+ }\r
+\r
+ //\r
+ // Build window scale option, only when configured\r
+ // to send WS option, and either we are doing active\r
+ // open or we have received WS option from peer.\r
+ //\r
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) &&\r
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||\r
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))\r
+ ) {\r
+\r
+ Data = NetbufAllocSpace (\r
+ Nbuf,\r
+ TCP_OPTION_WS_ALIGNED_LEN,\r
+ NET_BUF_HEAD\r
+ );\r
+\r
+ ASSERT (Data != NULL);\r
+\r
+ Len += TCP_OPTION_WS_ALIGNED_LEN;\r
+ TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb));\r
+ }\r
+\r
+ //\r
+ // Build the MSS option.\r
+ //\r
+ Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1);\r
+ ASSERT (Data != NULL);\r
+\r
+ Len += TCP_OPTION_MSS_LEN;\r
+ TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss);\r
+\r
+ return Len;\r
+}\r
+\r
+/**\r
+ Build the TCP option in synchronized states.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the buffer to store the options.\r
+\r
+ @return The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpBuildOption (\r
+ IN TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf\r
+ )\r
+{\r
+ UINT8 *Data;\r
+ UINT16 Len;\r
+\r
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));\r
+ Len = 0;\r
+\r
+ //\r
+ // Build the Timestamp option.\r
+ //\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) &&\r
+ !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)\r
+ ) {\r
+\r
+ Data = NetbufAllocSpace (\r
+ Nbuf,\r
+ TCP_OPTION_TS_ALIGNED_LEN,\r
+ NET_BUF_HEAD\r
+ );\r
+\r
+ ASSERT (Data != NULL);\r
+ Len += TCP_OPTION_TS_ALIGNED_LEN;\r
+\r
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);\r
+ TcpPutUint32 (Data + 4, mTcpTick);\r
+ TcpPutUint32 (Data + 8, Tcb->TsRecent);\r
+ }\r
+\r
+ return Len;\r
+}\r
+\r
+/**\r
+ Parse the supported options.\r
+\r
+ @param[in] Tcp Pointer to the TCP_CB of this TCP instance.\r
+ @param[in, out] Option Pointer to the TCP_OPTION used to store the\r
+ successfully pasrsed options.\r
+\r
+ @retval 0 The options are successfully pasrsed.\r
+ @retval -1 Ilegal option was found.\r
+\r
+**/\r
+INTN\r
+TcpParseOption (\r
+ IN TCP_HEAD *Tcp,\r
+ IN OUT TCP_OPTION *Option\r
+ )\r
+{\r
+ UINT8 *Head;\r
+ UINT8 TotalLen;\r
+ UINT8 Cur;\r
+ UINT8 Type;\r
+ UINT8 Len;\r
+\r
+ ASSERT ((Tcp != NULL) && (Option != NULL));\r
+\r
+ Option->Flag = 0;\r
+\r
+ TotalLen = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD));\r
+ if (TotalLen <= 0) {\r
+ return 0;\r
+ }\r
+\r
+ Head = (UINT8 *) (Tcp + 1);\r
+\r
+ //\r
+ // Fast process of the timestamp option.\r
+ //\r
+ if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) {\r
+\r
+ Option->TSVal = TcpGetUint32 (Head + 4);\r
+ Option->TSEcr = TcpGetUint32 (Head + 8);\r
+ Option->Flag = TCP_OPTION_RCVD_TS;\r
+\r
+ return 0;\r
+ }\r
+ //\r
+ // Slow path to process the options.\r
+ //\r
+ Cur = 0;\r
+\r
+ while (Cur < TotalLen) {\r
+ Type = Head[Cur];\r
+\r
+ switch (Type) {\r
+ case TCP_OPTION_MSS:\r
+ Len = Head[Cur + 1];\r
+\r
+ if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) {\r
+\r
+ return -1;\r
+ }\r
+\r
+ Option->Mss = TcpGetUint16 (&Head[Cur + 2]);\r
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS);\r
+\r
+ Cur += TCP_OPTION_MSS_LEN;\r
+ break;\r
+\r
+ case TCP_OPTION_WS:\r
+ Len = Head[Cur + 1];\r
+\r
+ if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) {\r
+\r
+ return -1;\r
+ }\r
+\r
+ Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]);\r
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS);\r
+\r
+ Cur += TCP_OPTION_WS_LEN;\r
+ break;\r
+\r
+ case TCP_OPTION_TS:\r
+ Len = Head[Cur + 1];\r
+\r
+ if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) {\r
+\r
+ return -1;\r
+ }\r
+\r
+ Option->TSVal = TcpGetUint32 (&Head[Cur + 2]);\r
+ Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]);\r
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS);\r
+\r
+ Cur += TCP_OPTION_TS_LEN;\r
+ break;\r
+\r
+ case TCP_OPTION_NOP:\r
+ Cur++;\r
+ break;\r
+\r
+ case TCP_OPTION_EOP:\r
+ Cur = TotalLen;\r
+ break;\r
+\r
+ default:\r
+ Len = Head[Cur + 1];\r
+\r
+ if ((TotalLen - Cur) < Len || Len < 2) {\r
+ return -1;\r
+ }\r
+\r
+ Cur = (UINT8) (Cur + Len);\r
+ break;\r
+ }\r
+\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Check the segment against PAWS.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] TSVal The timestamp value.\r
+\r
+ @retval 1 The segment passed the PAWS check.\r
+ @retval 0 The segment failed to pass the PAWS check.\r
+\r
+**/\r
+UINT32\r
+TcpPawsOK (\r
+ IN TCP_CB *Tcb,\r
+ IN UINT32 TSVal\r
+ )\r
+{\r
+ //\r
+ // PAWS as defined in RFC1323, buggy...\r
+ //\r
+ if (TCP_TIME_LT (TSVal, Tcb->TsRecent) &&\r
+ TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)\r
+ ) {\r
+\r
+ return 0;\r
+\r
+ }\r
+\r
+ return 1;\r
+}\r
--- /dev/null
+/** @file\r
+ Tcp option's routine header file.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _TCP_OPTION_H_\r
+#define _TCP_OPTION_H_\r
+\r
+//\r
+// Supported TCP option types and their length.\r
+//\r
+#define TCP_OPTION_EOP 0 ///< End Of oPtion\r
+#define TCP_OPTION_NOP 1 ///< No-Option.\r
+#define TCP_OPTION_MSS 2 ///< Maximum Segment Size\r
+#define TCP_OPTION_WS 3 ///< Window scale\r
+#define TCP_OPTION_TS 8 ///< Timestamp\r
+#define TCP_OPTION_MSS_LEN 4 ///< Length of MSS option\r
+#define TCP_OPTION_WS_LEN 3 ///< Length of window scale option\r
+#define TCP_OPTION_TS_LEN 10 ///< Length of timestamp option\r
+#define TCP_OPTION_WS_ALIGNED_LEN 4 ///< Length of window scale option, aligned\r
+#define TCP_OPTION_TS_ALIGNED_LEN 12 ///< Length of timestamp option, aligned\r
+\r
+//\r
+// recommend format of timestamp window scale\r
+// option for fast process.\r
+//\r
+#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \\r
+ (TCP_OPTION_NOP << 16) | \\r
+ (TCP_OPTION_TS << 8) | \\r
+ (TCP_OPTION_TS_LEN))\r
+\r
+#define TCP_OPTION_WS_FAST ((TCP_OPTION_NOP << 24) | \\r
+ (TCP_OPTION_WS << 16) | \\r
+ (TCP_OPTION_WS_LEN << 8))\r
+\r
+#define TCP_OPTION_MSS_FAST ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16))\r
+\r
+//\r
+// Other misc definations\r
+//\r
+#define TCP_OPTION_RCVD_MSS 0x01\r
+#define TCP_OPTION_RCVD_WS 0x02\r
+#define TCP_OPTION_RCVD_TS 0x04\r
+#define TCP_OPTION_MAX_WS 14 ///< Maxium window scale value\r
+#define TCP_OPTION_MAX_WIN 0xffff ///< Max window size in TCP header\r
+\r
+///\r
+/// The structure to store the parse option value.\r
+/// ParseOption only parses the options, doesn't process them.\r
+///\r
+typedef struct _TCP_OPTION {\r
+ UINT8 Flag; ///< Flag such as TCP_OPTION_RCVD_MSS\r
+ UINT8 WndScale; ///< The WndScale received\r
+ UINT16 Mss; ///< The Mss received\r
+ UINT32 TSVal; ///< The TSVal field in a timestamp option\r
+ UINT32 TSEcr; ///< The TSEcr field in a timestamp option\r
+} TCP_OPTION;\r
+\r
+/**\r
+ Compute the window scale value according to the given buffer size.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The scale value.\r
+\r
+**/\r
+UINT8\r
+TcpComputeScale (\r
+ IN TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Build the TCP option in three-way handshake.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the buffer to store the options.\r
+\r
+ @return The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpSynBuildOption (\r
+ IN TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf\r
+ );\r
+\r
+/**\r
+ Build the TCP option in synchronized states.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the buffer to store the options.\r
+\r
+ @return The total length of the TCP option field.\r
+\r
+**/\r
+UINT16\r
+TcpBuildOption (\r
+ IN TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf\r
+ );\r
+\r
+/**\r
+ Parse the supported options.\r
+\r
+ @param[in] Tcp Pointer to the TCP_CB of this TCP instance.\r
+ @param[in, out] Option Pointer to the TCP_OPTION used to store the\r
+ successfully pasrsed options.\r
+\r
+ @retval 0 The options successfully pasrsed.\r
+ @retval -1 Ilegal option was found.\r
+\r
+**/\r
+INTN\r
+TcpParseOption (\r
+ IN TCP_HEAD *Tcp,\r
+ IN OUT TCP_OPTION *Option\r
+ );\r
+\r
+/**\r
+ Check the segment against PAWS.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] TSVal The timestamp value.\r
+\r
+ @retval 1 The segment passed the PAWS check.\r
+ @retval 0 The segment failed to pass the PAWS check.\r
+\r
+**/\r
+UINT32\r
+TcpPawsOK (\r
+ IN TCP_CB *Tcb,\r
+ IN UINT32 TSVal\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ TCP output process routines.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+UINT8 mTcpOutFlag[] = {\r
+ 0, // TCP_CLOSED\r
+ 0, // TCP_LISTEN\r
+ TCP_FLG_SYN, // TCP_SYN_SENT\r
+ TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD\r
+ TCP_FLG_ACK, // TCP_ESTABLISHED\r
+ TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1\r
+ TCP_FLG_ACK, // TCP_FIN_WAIT_2\r
+ TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING\r
+ TCP_FLG_ACK, // TCP_TIME_WAIT\r
+ TCP_FLG_ACK, // TCP_CLOSE_WAIT\r
+ TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK\r
+};\r
+\r
+/**\r
+ Compute the sequence space left in the old receive window.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The sequence space left in the old receive window.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinOld (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ UINT32 OldWin;\r
+\r
+ OldWin = 0;\r
+\r
+ if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {\r
+\r
+ OldWin = TCP_SUB_SEQ (\r
+ Tcb->RcvWl2 + Tcb->RcvWnd,\r
+ Tcb->RcvNxt\r
+ );\r
+ }\r
+\r
+ return OldWin;\r
+}\r
+\r
+/**\r
+ Compute the current receive window.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The size of the current receive window, in bytes.\r
+\r
+**/\r
+UINT32\r
+TcpRcvWinNow (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ SOCKET *Sk;\r
+ UINT32 Win;\r
+ UINT32 Increase;\r
+ UINT32 OldWin;\r
+\r
+ Sk = Tcb->Sk;\r
+ ASSERT (Sk != NULL);\r
+\r
+ OldWin = TcpRcvWinOld (Tcb);\r
+\r
+ Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF);\r
+\r
+ Increase = 0;\r
+ if (Win > OldWin) {\r
+ Increase = Win - OldWin;\r
+ }\r
+\r
+ //\r
+ // Receiver's SWS: don't advertise a bigger window\r
+ // unless it can be increased by at least one Mss or\r
+ // half of the receive buffer.\r
+ //\r
+ if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {\r
+\r
+ return Win;\r
+ }\r
+\r
+ return OldWin;\r
+}\r
+\r
+/**\r
+ Compute the value to fill in the window size field of the outgoing segment.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Syn The flag to indicate whether the outgoing segment\r
+ is a SYN segment.\r
+\r
+ @return The value of the local receive window size used to fill the outgoing segment.\r
+\r
+**/\r
+UINT16\r
+TcpComputeWnd (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN BOOLEAN Syn\r
+ )\r
+{\r
+ UINT32 Wnd;\r
+\r
+ //\r
+ // RFC requires that initial window not be scaled\r
+ //\r
+ if (Syn) {\r
+\r
+ Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
+ } else {\r
+\r
+ Wnd = TcpRcvWinNow (Tcb);\r
+\r
+ Tcb->RcvWnd = Wnd;\r
+ }\r
+\r
+ Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);\r
+ return NTOHS ((UINT16) Wnd);\r
+}\r
+\r
+/**\r
+ Get the maximum SndNxt.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @return The sequence number of the maximum SndNxt.\r
+\r
+**/\r
+TCP_SEQNO\r
+TcpGetMaxSndNxt (\r
+ IN TCP_CB *Tcb\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ NET_BUF *Nbuf;\r
+\r
+ if (IsListEmpty (&Tcb->SndQue)) {\r
+ return Tcb->SndNxt;\r
+ }\r
+\r
+ Entry = Tcb->SndQue.BackLink;\r
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+ ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));\r
+ return TCPSEG_NETBUF (Nbuf)->End;\r
+}\r
+\r
+/**\r
+ Compute how much data to send.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Force If TRUE, to ignore the sender's SWS avoidance algorithm and send\r
+ out data by force.\r
+\r
+ @return The length of the data can be sent. If 0, no data can be sent.\r
+\r
+**/\r
+UINT32\r
+TcpDataToSend (\r
+ IN TCP_CB *Tcb,\r
+ IN INTN Force\r
+ )\r
+{\r
+ SOCKET *Sk;\r
+ UINT32 Win;\r
+ UINT32 Len;\r
+ UINT32 Left;\r
+ UINT32 Limit;\r
+\r
+ Sk = Tcb->Sk;\r
+ ASSERT (Sk != NULL);\r
+\r
+ //\r
+ // TCP should NOT send data beyond the send window\r
+ // and congestion window. The right edge of send\r
+ // window is defined as SND.WL2 + SND.WND. The right\r
+ // edge of congestion window is defined as SND.UNA +\r
+ // CWND.\r
+ //\r
+ Win = 0;\r
+ Limit = Tcb->SndWl2 + Tcb->SndWnd;\r
+\r
+ if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {\r
+\r
+ Limit = Tcb->SndUna + Tcb->CWnd;\r
+ }\r
+\r
+ if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {\r
+ Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);\r
+ }\r
+\r
+ //\r
+ // The data to send contains two parts: the data on the\r
+ // socket send queue, and the data on the TCB's send\r
+ // buffer. The later can be non-zero if the peer shrinks\r
+ // its advertised window.\r
+ //\r
+ Left = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);\r
+\r
+ Len = MIN (Win, Left);\r
+\r
+ if (Len > Tcb->SndMss) {\r
+ Len = Tcb->SndMss;\r
+ }\r
+\r
+ if ((Force != 0)|| (Len == 0 && Left == 0)) {\r
+ return Len;\r
+ }\r
+\r
+ if (Len == 0 && Left != 0) {\r
+ goto SetPersistTimer;\r
+ }\r
+\r
+ //\r
+ // Sender's SWS avoidance: Don't send a small segment unless\r
+ // a)A full-sized segment can be sent,\r
+ // b)At least one-half of the maximum sized windows that\r
+ // the other end has ever advertised.\r
+ // c)It can send everything it has, and either it isn't\r
+ // expecting an ACK, or the Nagle algorithm is disabled.\r
+ //\r
+ if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {\r
+\r
+ return Len;\r
+ }\r
+\r
+ if ((Len == Left) &&\r
+ ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))\r
+ ) {\r
+\r
+ return Len;\r
+ }\r
+\r
+ //\r
+ // RFC1122 suggests to set a timer when SWSA forbids TCP\r
+ // sending more data, and combines it with a probe timer.\r
+ //\r
+SetPersistTimer:\r
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpDataToSend: enter persistent state for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ if (!Tcb->ProbeTimerOn) {\r
+ TcpSetProbeTimer (Tcb);\r
+ }\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Build the TCP header of the TCP segment and transmit the segment by IP.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Nbuf Pointer to the buffer containing the segment to be\r
+ sent out.\r
+\r
+ @retval 0 The segment was sent out successfully.\r
+ @retval -1 An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpTransmitSegment (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN NET_BUF *Nbuf\r
+ )\r
+{\r
+ UINT16 Len;\r
+ TCP_HEAD *Head;\r
+ TCP_SEG *Seg;\r
+ BOOLEAN Syn;\r
+ UINT32 DataLen;\r
+\r
+ ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0));\r
+\r
+ DataLen = Nbuf->TotalSize;\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+ Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);\r
+\r
+ if (Syn) {\r
+\r
+ Len = TcpSynBuildOption (Tcb, Nbuf);\r
+ } else {\r
+\r
+ Len = TcpBuildOption (Tcb, Nbuf);\r
+ }\r
+\r
+ ASSERT ((Len % 4 == 0) && (Len <= 40));\r
+\r
+ Len += sizeof (TCP_HEAD);\r
+\r
+ Head = (TCP_HEAD *) NetbufAllocSpace (\r
+ Nbuf,\r
+ sizeof (TCP_HEAD),\r
+ NET_BUF_HEAD\r
+ );\r
+\r
+ ASSERT (Head != NULL);\r
+\r
+ Nbuf->Tcp = Head;\r
+\r
+ Head->SrcPort = Tcb->LocalEnd.Port;\r
+ Head->DstPort = Tcb->RemoteEnd.Port;\r
+ Head->Seq = NTOHL (Seg->Seq);\r
+ Head->Ack = NTOHL (Tcb->RcvNxt);\r
+ Head->HeadLen = (UINT8) (Len >> 2);\r
+ Head->Res = 0;\r
+ Head->Wnd = TcpComputeWnd (Tcb, Syn);\r
+ Head->Checksum = 0;\r
+\r
+ //\r
+ // Check whether to set the PSH flag.\r
+ //\r
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);\r
+\r
+ if (DataLen != 0) {\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&\r
+ TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)\r
+ ) {\r
+\r
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);\r
+\r
+ } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) {\r
+\r
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check whether to set the URG flag and the urgent pointer.\r
+ //\r
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);\r
+\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {\r
+\r
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);\r
+\r
+ if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {\r
+\r
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);\r
+ } else {\r
+\r
+ Seg->Urg = (UINT16) MIN (\r
+ TCP_SUB_SEQ (Tcb->SndUp,\r
+ Seg->Seq),\r
+ 0xffff\r
+ );\r
+ }\r
+ }\r
+\r
+ Head->Flag = Seg->Flag;\r
+ Head->Urg = NTOHS (Seg->Urg);\r
+ Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
+\r
+ //\r
+ // Update the TCP session's control information.\r
+ //\r
+ Tcb->RcvWl2 = Tcb->RcvNxt;\r
+ if (Syn) {\r
+ Tcb->RcvWnd = NTOHS (Head->Wnd);\r
+ }\r
+\r
+ //\r
+ // Clear the delayedack flag.\r
+ //\r
+ Tcb->DelayedAck = 0;\r
+\r
+ return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);\r
+}\r
+\r
+/**\r
+ Get a segment from the Tcb's SndQue.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seq The sequence number of the segment.\r
+ @param[in] Len The maximum length of the segment.\r
+\r
+ @return Pointer to the segment. If NULL, some error occurred.\r
+\r
+**/\r
+NET_BUF *\r
+TcpGetSegmentSndQue (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_SEQNO Seq,\r
+ IN UINT32 Len\r
+ )\r
+{\r
+ LIST_ENTRY *Head;\r
+ LIST_ENTRY *Cur;\r
+ NET_BUF *Node;\r
+ TCP_SEG *Seg;\r
+ NET_BUF *Nbuf;\r
+ TCP_SEQNO End;\r
+ UINT8 *Data;\r
+ UINT8 Flag;\r
+ INT32 Offset;\r
+ INT32 CopyLen;\r
+\r
+ ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));\r
+\r
+ //\r
+ // Find the segment that contains the Seq.\r
+ //\r
+ Head = &Tcb->SndQue;\r
+\r
+ Node = NULL;\r
+ Seg = NULL;\r
+\r
+ NET_LIST_FOR_EACH (Cur, Head) {\r
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
+ Seg = TCPSEG_NETBUF (Node);\r
+\r
+ if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Return the buffer if it can be returned without\r
+ // adjustment:\r
+ //\r
+ if ((Seg->Seq == Seq) &&\r
+ TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&\r
+ !NET_BUF_SHARED (Node)\r
+ ) {\r
+\r
+ NET_GET_REF (Node);\r
+ return Node;\r
+ }\r
+\r
+ //\r
+ // Create a new buffer and copy data there.\r
+ //\r
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
+\r
+ if (Nbuf == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+ Flag = Seg->Flag;\r
+ End = Seg->End;\r
+\r
+ if (TCP_SEQ_LT (Seq + Len, Seg->End)) {\r
+ End = Seq + Len;\r
+ }\r
+\r
+ CopyLen = TCP_SUB_SEQ (End, Seq);\r
+ Offset = TCP_SUB_SEQ (Seq, Seg->Seq);\r
+\r
+ //\r
+ // If SYN is set and out of the range, clear the flag.\r
+ // Becuase the sequence of the first byte is SEG.SEQ+1,\r
+ // adjust Offset by -1. If SYN is in the range, copy\r
+ // one byte less.\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+\r
+ if (TCP_SEQ_LT (Seg->Seq, Seq)) {\r
+\r
+ TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);\r
+ Offset--;\r
+ } else {\r
+\r
+ CopyLen--;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If FIN is set and in the range, copy one byte less,\r
+ // and if it is out of the range, clear the flag.\r
+ //\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+\r
+ if (Seg->End == End) {\r
+\r
+ CopyLen--;\r
+ } else {\r
+\r
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
+ }\r
+ }\r
+\r
+ ASSERT (CopyLen >= 0);\r
+\r
+ //\r
+ // Copy data to the segment\r
+ //\r
+ if (CopyLen != 0) {\r
+ Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);\r
+ ASSERT (Data != NULL);\r
+\r
+ if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {\r
+ goto OnError;\r
+ }\r
+ }\r
+\r
+ CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));\r
+\r
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
+ TCPSEG_NETBUF (Nbuf)->End = End;\r
+ TCPSEG_NETBUF (Nbuf)->Flag = Flag;\r
+\r
+ return Nbuf;\r
+\r
+OnError:\r
+ NetbufFree (Nbuf);\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Get a segment from the Tcb's socket buffer.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seq The sequence number of the segment.\r
+ @param[in] Len The maximum length of the segment.\r
+\r
+ @return Pointer to the segment. If NULL, some error occurred.\r
+\r
+**/\r
+NET_BUF *\r
+TcpGetSegmentSock (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_SEQNO Seq,\r
+ IN UINT32 Len\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ UINT8 *Data;\r
+ UINT32 DataGet;\r
+\r
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
+\r
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
+\r
+ if (Nbuf == NULL) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ return NULL;\r
+ }\r
+\r
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+ DataGet = 0;\r
+\r
+ if (Len != 0) {\r
+ //\r
+ // copy data to the segment.\r
+ //\r
+ Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);\r
+ ASSERT (Data != NULL);\r
+\r
+ DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);\r
+ }\r
+\r
+ NET_GET_REF (Nbuf);\r
+\r
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
+ TCPSEG_NETBUF (Nbuf)->End = Seq + Len;\r
+\r
+ InsertTailList (&(Tcb->SndQue), &(Nbuf->List));\r
+\r
+ if (DataGet != 0) {\r
+\r
+ SockDataSent (Tcb->Sk, DataGet);\r
+ }\r
+\r
+ return Nbuf;\r
+}\r
+\r
+/**\r
+ Get a segment starting from sequence Seq of a maximum\r
+ length of Len.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seq The sequence number of the segment.\r
+ @param[in] Len The maximum length of the segment.\r
+\r
+ @return Pointer to the segment. If NULL, some error occurred.\r
+\r
+**/\r
+NET_BUF *\r
+TcpGetSegment (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_SEQNO Seq,\r
+ IN UINT32 Len\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+\r
+ ASSERT (Tcb != NULL);\r
+\r
+ //\r
+ // Compare the SndNxt with the max sequence number sent.\r
+ //\r
+ if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {\r
+\r
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
+ } else {\r
+\r
+ Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);\r
+ }\r
+\r
+ ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+ return Nbuf;\r
+}\r
+\r
+/**\r
+ Retransmit the segment from sequence Seq.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Seq The sequence number of the segment to be retransmitted.\r
+\r
+ @retval 0 Retransmission succeeded.\r
+ @retval -1 Error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpRetransmit (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_SEQNO Seq\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ UINT32 Len;\r
+\r
+ //\r
+ // Compute the maxium length of retransmission. It is\r
+ // limited by three factors:\r
+ // 1. Less than SndMss\r
+ // 2. Must in the current send window\r
+ // 3. Will not change the boundaries of queued segments.\r
+ //\r
+ if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ return 0;\r
+ }\r
+\r
+ Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);\r
+ Len = MIN (Len, Tcb->SndMss);\r
+\r
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
+ if (Nbuf == NULL) {\r
+ return -1;\r
+ }\r
+\r
+ ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+\r
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
+ goto OnError;\r
+ }\r
+\r
+ //\r
+ // The retransmitted buffer may be on the SndQue,\r
+ // trim TCP head because all the buffers on SndQue\r
+ // are headless.\r
+ //\r
+ ASSERT (Nbuf->Tcp != NULL);\r
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
+ Nbuf->Tcp = NULL;\r
+\r
+ NetbufFree (Nbuf);\r
+ return 0;\r
+\r
+OnError:\r
+ if (Nbuf != NULL) {\r
+ NetbufFree (Nbuf);\r
+ }\r
+\r
+ return -1;\r
+}\r
+\r
+/**\r
+ Verify that all the segments in SndQue are in good shape.\r
+\r
+ @param[in] Head Pointer to the head node of the SndQue.\r
+\r
+ @retval 0 At least one segment is broken.\r
+ @retval 1 All segments in the specific queue are in good shape.\r
+\r
+**/\r
+INTN\r
+TcpCheckSndQue (\r
+ IN LIST_ENTRY *Head\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ NET_BUF *Nbuf;\r
+ TCP_SEQNO Seq;\r
+\r
+ if (IsListEmpty (Head)) {\r
+ return 1;\r
+ }\r
+ //\r
+ // Initialize the Seq.\r
+ //\r
+ Entry = Head->ForwardLink;\r
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+ Seq = TCPSEG_NETBUF (Nbuf)->Seq;\r
+\r
+ NET_LIST_FOR_EACH (Entry, Head) {\r
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+ if (TcpVerifySegment (Nbuf) == 0) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // All the node in the SndQue should has:\r
+ // SEG.SEQ = LAST_SEG.END\r
+ //\r
+ if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {\r
+ return 0;\r
+ }\r
+\r
+ Seq = TCPSEG_NETBUF (Nbuf)->End;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+/**\r
+ Check whether to send data/SYN/FIN and piggyback an ACK.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm\r
+ and send out data by force.\r
+\r
+ @return The number of bytes sent.\r
+\r
+**/\r
+INTN\r
+TcpToSendData (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN INTN Force\r
+ )\r
+{\r
+ UINT32 Len;\r
+ INTN Sent;\r
+ UINT8 Flag;\r
+ NET_BUF *Nbuf;\r
+ TCP_SEG *Seg;\r
+ TCP_SEQNO Seq;\r
+ TCP_SEQNO End;\r
+\r
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));\r
+\r
+ Sent = 0;\r
+\r
+ if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {\r
+\r
+ return 0;\r
+ }\r
+\r
+ do {\r
+ //\r
+ // Compute how much data can be sent\r
+ //\r
+ Len = TcpDataToSend (Tcb, Force);\r
+ Seq = Tcb->SndNxt;\r
+\r
+ ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0])));\r
+ Flag = mTcpOutFlag[Tcb->State];\r
+\r
+ if ((Flag & TCP_FLG_SYN) != 0) {\r
+\r
+ Seq = Tcb->Iss;\r
+ Len = 0;\r
+ }\r
+\r
+ //\r
+ // Only send a segment without data if SYN or\r
+ // FIN is set.\r
+ //\r
+ if ((Len == 0) && ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {\r
+ return Sent;\r
+ }\r
+\r
+ Nbuf = TcpGetSegment (Tcb, Seq, Len);\r
+\r
+ if (Nbuf == NULL) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpToSendData: failed to get a segment for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ goto OnError;\r
+ }\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+\r
+ //\r
+ // Set the TcpSeg in Nbuf.\r
+ //\r
+ Len = Nbuf->TotalSize;\r
+ End = Seq + Len;\r
+ if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {\r
+ End++;\r
+ }\r
+\r
+ if ((Flag & TCP_FLG_FIN) != 0) {\r
+ //\r
+ // Send FIN if all data is sent, and FIN is\r
+ // in the window\r
+ //\r
+ if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&\r
+ (GET_SND_DATASIZE (Tcb->Sk) == 0) &&\r
+ TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)\r
+ ) {\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpToSendData: send FIN to peer for TCB %p in state %s\n",\r
+ Tcb,\r
+ mTcpStateName[Tcb->State])\r
+ );\r
+\r
+ End++;\r
+ } else {\r
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
+ }\r
+ }\r
+\r
+ Seg->Seq = Seq;\r
+ Seg->End = End;\r
+ Seg->Flag = Flag;\r
+\r
+ ASSERT (TcpVerifySegment (Nbuf) != 0);\r
+ ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);\r
+\r
+ //\r
+ // Don't send an empty segment here.\r
+ //\r
+ if (Seg->End == Seg->Seq) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpToSendData: created a empty segment for TCB %p, free it now\n",\r
+ Tcb)\r
+ );\r
+\r
+ NetbufFree (Nbuf);\r
+ return Sent;\r
+ }\r
+\r
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
+ Nbuf->Tcp = NULL;\r
+\r
+ if ((Flag & TCP_FLG_FIN) != 0) {\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
+ }\r
+\r
+ goto OnError;\r
+ }\r
+\r
+ Sent += TCP_SUB_SEQ (End, Seq);\r
+\r
+ //\r
+ // All the buffers in the SndQue are headless.\r
+ //\r
+ ASSERT (Nbuf->Tcp != NULL);\r
+\r
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
+ Nbuf->Tcp = NULL;\r
+\r
+ NetbufFree (Nbuf);\r
+\r
+ //\r
+ // Update the status in TCB.\r
+ //\r
+ Tcb->DelayedAck = 0;\r
+\r
+ if ((Flag & TCP_FLG_FIN) != 0) {\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
+ }\r
+\r
+ if (TCP_SEQ_GT (End, Tcb->SndNxt)) {\r
+ Tcb->SndNxt = End;\r
+ }\r
+\r
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
+ }\r
+\r
+ //\r
+ // Enable RTT measurement only if not in retransmit.\r
+ // Karn's algorithm requires not to update RTT when in loss.\r
+ //\r
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpToSendData: set RTT measure sequence %d for TCB %p\n",\r
+ Seq,\r
+ Tcb)\r
+ );\r
+\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+ Tcb->RttSeq = Seq;\r
+ Tcb->RttMeasure = 0;\r
+ }\r
+\r
+ } while (Len == Tcb->SndMss);\r
+\r
+ return Sent;\r
+\r
+OnError:\r
+ if (Nbuf != NULL) {\r
+ NetbufFree (Nbuf);\r
+ }\r
+\r
+ return Sent;\r
+}\r
+\r
+/**\r
+ Send an ACK immediately.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSendAck (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ TCP_SEG *Seg;\r
+\r
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+ if (Nbuf == NULL) {\r
+ return;\r
+ }\r
+\r
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+ Seg->Seq = Tcb->SndNxt;\r
+ Seg->End = Tcb->SndNxt;\r
+ Seg->Flag = TCP_FLG_ACK;\r
+\r
+ if (TcpTransmitSegment (Tcb, Nbuf) == 0) {\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
+ Tcb->DelayedAck = 0;\r
+ }\r
+\r
+ NetbufFree (Nbuf);\r
+}\r
+\r
+/**\r
+ Send a zero probe segment. It can be used by keepalive and zero window probe.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+ @retval 0 The zero probe segment was sent out successfully.\r
+ @retval other An error condition occurred.\r
+\r
+**/\r
+INTN\r
+TcpSendZeroProbe (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ TCP_SEG *Seg;\r
+ INTN Result;\r
+\r
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+ if (Nbuf == NULL) {\r
+ return -1;\r
+ }\r
+\r
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
+\r
+ //\r
+ // SndNxt-1 is out of window. The peer should respond\r
+ // with an ACK.\r
+ //\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+ Seg->Seq = Tcb->SndNxt - 1;\r
+ Seg->End = Tcb->SndNxt - 1;\r
+ Seg->Flag = TCP_FLG_ACK;\r
+\r
+ Result = TcpTransmitSegment (Tcb, Nbuf);\r
+ NetbufFree (Nbuf);\r
+\r
+ return Result;\r
+}\r
+\r
+/**\r
+ Check whether to send an ACK or delayed ACK.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpToSendAck (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ UINT32 TcpNow;\r
+\r
+ //\r
+ // Generally, TCP should send a delayed ACK unless:\r
+ // 1. ACK at least every other FULL sized segment received.\r
+ // 2. Packets received out of order.\r
+ // 3. Receiving window is open.\r
+ //\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Tcb->DelayedAck >= 1)) {\r
+ TcpSendAck (Tcb);\r
+ return;\r
+ }\r
+\r
+ TcpNow = TcpRcvWinNow (Tcb);\r
+\r
+ if (TcpNow > TcpRcvWinOld (Tcb)) {\r
+ TcpSendAck (Tcb);\r
+ return;\r
+ }\r
+\r
+ DEBUG (\r
+ (EFI_D_INFO,\r
+ "TcpToSendAck: scheduled a delayed ACK for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ //\r
+ // Schedule a delayed ACK.\r
+ //\r
+ Tcb->DelayedAck++;\r
+}\r
+\r
+/**\r
+ Send a RESET segment in response to the segment received.\r
+\r
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance. May be NULL.\r
+ @param[in] Head TCP header of the segment that triggers the reset.\r
+ @param[in] Len Length of the segment that triggers the reset.\r
+ @param[in] Local Local IP address.\r
+ @param[in] Remote Remote peer's IP address.\r
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
+ IP_VERSION_6 indicates TCP is running on IP6 stack.\r
+\r
+ @retval 0 A reset was sent or there is no need to send it.\r
+ @retval -1 No reset is sent.\r
+\r
+**/\r
+INTN\r
+TcpSendReset (\r
+ IN TCP_CB *Tcb,\r
+ IN TCP_HEAD *Head,\r
+ IN INT32 Len,\r
+ IN EFI_IP_ADDRESS *Local,\r
+ IN EFI_IP_ADDRESS *Remote,\r
+ IN UINT8 Version\r
+ )\r
+{\r
+ NET_BUF *Nbuf;\r
+ TCP_HEAD *Nhead;\r
+ UINT16 HeadSum;\r
+\r
+ //\r
+ // Don't respond to a Reset with reset.\r
+ //\r
+ if ((Head->Flag & TCP_FLG_RST) != 0) {\r
+ return 0;\r
+ }\r
+\r
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
+\r
+ if (Nbuf == NULL) {\r
+ return -1;\r
+ }\r
+\r
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
+ Nbuf,\r
+ sizeof (TCP_HEAD),\r
+ NET_BUF_TAIL\r
+ );\r
+\r
+ ASSERT (Nhead != NULL);\r
+\r
+ Nbuf->Tcp = Nhead;\r
+ Nhead->Flag = TCP_FLG_RST;\r
+\r
+ //\r
+ // Derive Seq/ACK from the segment if no TCB\r
+ // is associated with it, otherwise derive from the Tcb.\r
+ //\r
+ if (Tcb == NULL) {\r
+\r
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {\r
+ Nhead->Seq = Head->Ack;\r
+ Nhead->Ack = 0;\r
+ } else {\r
+ Nhead->Seq = 0;\r
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
+ Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);\r
+ }\r
+ } else {\r
+\r
+ Nhead->Seq = HTONL (Tcb->SndNxt);\r
+ Nhead->Ack = HTONL (Tcb->RcvNxt);\r
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
+ }\r
+\r
+ Nhead->SrcPort = Head->DstPort;\r
+ Nhead->DstPort = Head->SrcPort;\r
+ Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
+ Nhead->Res = 0;\r
+ Nhead->Wnd = HTONS (0xFFFF);\r
+ Nhead->Checksum = 0;\r
+ Nhead->Urg = 0;\r
+\r
+ if (Version == IP_VERSION_4) {\r
+ HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0);\r
+ } else {\r
+ HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0);\r
+ }\r
+\r
+ Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);\r
+\r
+ TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version);\r
+\r
+ NetbufFree (Nbuf);\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Verify that the segment is in good shape.\r
+\r
+ @param[in] Nbuf The buffer that contains the segment to be checked.\r
+\r
+ @retval 0 The segment is broken.\r
+ @retval 1 The segment is in good shape.\r
+\r
+**/\r
+INTN\r
+TcpVerifySegment (\r
+ IN NET_BUF *Nbuf\r
+ )\r
+{\r
+ TCP_HEAD *Head;\r
+ TCP_SEG *Seg;\r
+ UINT32 Len;\r
+\r
+ if (Nbuf == NULL) {\r
+ return 1;\r
+ }\r
+\r
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
+\r
+ Seg = TCPSEG_NETBUF (Nbuf);\r
+ Len = Nbuf->TotalSize;\r
+ Head = Nbuf->Tcp;\r
+\r
+ if (Head != NULL) {\r
+ if (Head->Flag != Seg->Flag) {\r
+ return 0;\r
+ }\r
+\r
+ Len -= (Head->HeadLen << 2);\r
+ }\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
+ Len++;\r
+ }\r
+\r
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
+ Len++;\r
+ }\r
+\r
+ if (Seg->Seq + Len != Seg->End) {\r
+ return 0;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ TCP protocol header file.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _TCP_PROTO_H_\r
+#define _TCP_PROTO_H_\r
+\r
+///\r
+/// Tcp states don't change their order. It is used as an\r
+/// index to mTcpOutFlag and other macros.\r
+///\r
+#define TCP_CLOSED 0\r
+#define TCP_LISTEN 1\r
+#define TCP_SYN_SENT 2\r
+#define TCP_SYN_RCVD 3\r
+#define TCP_ESTABLISHED 4\r
+#define TCP_FIN_WAIT_1 5\r
+#define TCP_FIN_WAIT_2 6\r
+#define TCP_CLOSING 7\r
+#define TCP_TIME_WAIT 8\r
+#define TCP_CLOSE_WAIT 9\r
+#define TCP_LAST_ACK 10\r
+\r
+\r
+///\r
+/// Flags in the TCP header\r
+///\r
+#define TCP_FLG_FIN 0x01\r
+#define TCP_FLG_SYN 0x02\r
+#define TCP_FLG_RST 0x04\r
+#define TCP_FLG_PSH 0x08\r
+#define TCP_FLG_ACK 0x10\r
+#define TCP_FLG_URG 0x20\r
+\r
+ //\r
+ // mask for all the flags\r
+ //\r
+#define TCP_FLG_FLAG 0x3F\r
+\r
+\r
+#define TCP_CONNECT_REFUSED (-1) ///< TCP error status\r
+#define TCP_CONNECT_RESET (-2) ///< TCP error status\r
+#define TCP_CONNECT_CLOSED (-3) ///< TCP error status\r
+\r
+//\r
+// Current congestion status as suggested by RFC3782.\r
+//\r
+#define TCP_CONGEST_RECOVER 1 ///< During the NewReno fast recovery.\r
+#define TCP_CONGEST_LOSS 2 ///< Retxmit because of retxmit time out.\r
+#define TCP_CONGEST_OPEN 3 ///< TCP is opening its congestion window.\r
+\r
+//\r
+// TCP control flags\r
+//\r
+#define TCP_CTRL_NO_NAGLE 0x0001 ///< Disable Nagle algorithm\r
+#define TCP_CTRL_NO_KEEPALIVE 0x0002 ///< Disable keepalive timer.\r
+#define TCP_CTRL_NO_WS 0x0004 ///< Disable window scale option.\r
+#define TCP_CTRL_RCVD_WS 0x0008 ///< Received a wnd scale option in syn.\r
+#define TCP_CTRL_NO_TS 0x0010 ///< Disable Timestamp option.\r
+#define TCP_CTRL_RCVD_TS 0x0020 ///< Received a Timestamp option in syn.\r
+#define TCP_CTRL_SND_TS 0x0040 ///< Send Timestamp option to remote.\r
+#define TCP_CTRL_SND_URG 0x0080 ///< In urgent send mode.\r
+#define TCP_CTRL_RCVD_URG 0x0100 ///< In urgent receive mode.\r
+#define TCP_CTRL_SND_PSH 0x0200 ///< In PUSH send mode.\r
+#define TCP_CTRL_FIN_SENT 0x0400 ///< FIN is sent.\r
+#define TCP_CTRL_FIN_ACKED 0x0800 ///< FIN is ACKed.\r
+#define TCP_CTRL_TIMER_ON 0x1000 ///< At least one of the timer is on.\r
+#define TCP_CTRL_RTT_ON 0x2000 ///< The RTT measurement is on.\r
+#define TCP_CTRL_ACK_NOW 0x4000 ///< Send the ACK now, don't delay.\r
+\r
+//\r
+// Timer related values\r
+//\r
+#define TCP_TIMER_CONNECT 0 ///< Connection establishment timer.\r
+#define TCP_TIMER_REXMIT 1 ///< Retransmit timer.\r
+#define TCP_TIMER_PROBE 2 ///< Window probe timer.\r
+#define TCP_TIMER_KEEPALIVE 3 ///< Keepalive timer.\r
+#define TCP_TIMER_FINWAIT2 4 ///< FIN_WAIT_2 timer.\r
+#define TCP_TIMER_2MSL 5 ///< TIME_WAIT timer.\r
+#define TCP_TIMER_NUMBER 6 ///< The total number of the TCP timer.\r
+#define TCP_TICK 200 ///< Every TCP tick is 200ms.\r
+#define TCP_TICK_HZ 5 ///< The frequence of TCP tick.\r
+#define TCP_RTT_SHIFT 3 ///< SRTT & RTTVAR scaled by 8.\r
+#define TCP_RTO_MIN TCP_TICK_HZ ///< The minium value of RTO.\r
+#define TCP_RTO_MAX (TCP_TICK_HZ * 60) ///< The maxium value of RTO.\r
+#define TCP_FOLD_RTT 4 ///< Timeout threshod to fold RTT.\r
+\r
+//\r
+// Default values for some timers\r
+//\r
+#define TCP_MAX_LOSS 12 ///< Default max times to retxmit.\r
+#define TCP_KEEPALIVE_IDLE_MIN (TCP_TICK_HZ * 60 * 60 * 2) ///< First keepalive.\r
+#define TCP_KEEPALIVE_PERIOD (TCP_TICK_HZ * 60)\r
+#define TCP_MAX_KEEPALIVE 8\r
+#define TCP_FIN_WAIT2_TIME (2 * TCP_TICK_HZ)\r
+#define TCP_TIME_WAIT_TIME (2 * TCP_TICK_HZ)\r
+#define TCP_PAWS_24DAY (24 * 24 * 60 * 60 * TCP_TICK_HZ)\r
+#define TCP_CONNECT_TIME (75 * TCP_TICK_HZ)\r
+\r
+//\r
+// The header space to be reserved before TCP data to accomodate :\r
+// 60byte IP head + 60byte TCP head + link layer head\r
+//\r
+#define TCP_MAX_HEAD 192\r
+\r
+//\r
+// Value ranges for some control option\r
+//\r
+#define TCP_RCV_BUF_SIZE (2 * 1024 * 1024)\r
+#define TCP_RCV_BUF_SIZE_MIN (8 * 1024)\r
+#define TCP_SND_BUF_SIZE (2 * 1024 * 1024)\r
+#define TCP_SND_BUF_SIZE_MIN (8 * 1024)\r
+#define TCP_BACKLOG 10\r
+#define TCP_BACKLOG_MIN 5\r
+#define TCP_MAX_LOSS_MIN 6\r
+#define TCP_CONNECT_TIME_MIN (60 * TCP_TICK_HZ)\r
+#define TCP_MAX_KEEPALIVE_MIN 4\r
+#define TCP_KEEPALIVE_IDLE_MAX (TCP_TICK_HZ * 60 * 60 * 4)\r
+#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30)\r
+#define TCP_FIN_WAIT2_TIME_MAX (4 * TCP_TICK_HZ)\r
+#define TCP_TIME_WAIT_TIME_MAX (60 * TCP_TICK_HZ)\r
+\r
+///\r
+/// TCP_CONNECTED: both ends have synchronized their ISN.\r
+///\r
+#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD)\r
+\r
+#define TCP_FIN_RCVD(State) \\r
+ ( \\r
+ ((State) == TCP_CLOSE_WAIT) || \\r
+ ((State) == TCP_LAST_ACK) || \\r
+ ((State) == TCP_CLOSING) || \\r
+ ((State) == TCP_TIME_WAIT) \\r
+ )\r
+\r
+#define TCP_LOCAL_CLOSED(State) \\r
+ ( \\r
+ ((State) == TCP_FIN_WAIT_1) || \\r
+ ((State) == TCP_FIN_WAIT_2) || \\r
+ ((State) == TCP_CLOSING) || \\r
+ ((State) == TCP_TIME_WAIT) || \\r
+ ((State) == TCP_LAST_ACK) \\r
+ )\r
+\r
+//\r
+// Get the TCP_SEG point from a net buffer's ProtoData.\r
+//\r
+#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData))\r
+\r
+//\r
+// Macros to compare sequence no\r
+//\r
+#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0)\r
+#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0)\r
+#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0)\r
+#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0)\r
+\r
+//\r
+// TCP_SEQ_BETWEEN return whether b <= m <= e\r
+//\r
+#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b))\r
+\r
+//\r
+// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2\r
+//\r
+#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2)))\r
+\r
+//\r
+// Check whether Flag is on\r
+//\r
+#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0))\r
+//\r
+// Set and Clear operation on a Flag\r
+//\r
+#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag))\r
+#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag))\r
+\r
+//\r
+// Test whether two peers are equal\r
+//\r
+#define TCP_PEER_EQUAL(Pa, Pb, Ver) \\r
+ (((Pa)->Port == (Pb)->Port) && TcpIsIpEqual(&((Pa)->Ip), &((Pb)->Ip), Ver))\r
+\r
+//\r
+// Test whether Pa matches Pb, or Pa is more specific\r
+// than pb. Zero means wildcard.\r
+//\r
+#define TCP_PEER_MATCH(Pa, Pb, Ver) \\r
+ ( \\r
+ (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)) && \\r
+ (TcpIsIpZero (&((Pb)->Ip), Ver) || TcpIsIpEqual (&((Pb)->Ip), &((Pa)->Ip), Ver)) \\r
+ )\r
+\r
+#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer)))\r
+#define TCP_SET_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) | (1 << (Timer))))\r
+#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) & (~(1 << (Timer)))))\r
+\r
+\r
+#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0)\r
+#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0)\r
+#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb)))\r
+\r
+#define TCP_MAX_WIN 0xFFFFU\r
+\r
+///\r
+/// TCP segmentation data.\r
+///\r
+typedef struct _TCP_SEG {\r
+ TCP_SEQNO Seq; ///< Starting sequence number.\r
+ TCP_SEQNO End; ///< The sequence of the last byte + 1, include SYN/FIN. End-Seq = SEG.LEN.\r
+ TCP_SEQNO Ack; ///< ACK field in the segment.\r
+ UINT8 Flag; ///< TCP header flags.\r
+ UINT16 Urg; ///< Valid if URG flag is set.\r
+ UINT32 Wnd; ///< TCP window size field.\r
+} TCP_SEG;\r
+\r
+///\r
+/// Network endpoint, IP plus Port structure.\r
+///\r
+typedef struct _TCP_PEER {\r
+ EFI_IP_ADDRESS Ip; ///< IP address, in network byte order.\r
+ TCP_PORTNO Port; ///< Port number, in network byte order.\r
+} TCP_PEER;\r
+\r
+typedef struct _TCP_CONTROL_BLOCK TCP_CB;\r
+\r
+///\r
+/// TCP control block: it includes various states.\r
+///\r
+struct _TCP_CONTROL_BLOCK {\r
+ LIST_ENTRY List; ///< Back and forward link entry\r
+ TCP_CB *Parent; ///< The parent TCP_CB structure\r
+\r
+ SOCKET *Sk; ///< The socket it controled.\r
+ TCP_PEER LocalEnd; ///< Local endpoint.\r
+ TCP_PEER RemoteEnd;///< Remote endpoint.\r
+\r
+ LIST_ENTRY SndQue; ///< Retxmission queue.\r
+ LIST_ENTRY RcvQue; ///< Reassemble queue.\r
+ UINT32 CtrlFlag; ///< Control flags, such as NO_NAGLE.\r
+ INT32 Error; ///< Soft error status, such as TCP_CONNECT_RESET.\r
+\r
+ //\r
+ // RFC793 and RFC1122 defined variables\r
+ //\r
+ UINT8 State; ///< TCP state, such as SYN_SENT, LISTEN.\r
+ UINT8 DelayedAck; ///< Number of delayed ACKs.\r
+ UINT16 HeadSum; ///< Checksum of the fixed parts of pesudo\r
+ ///< header: Src IP, Dst IP, 0, Protocol,\r
+ ///< do not include the TCP length.\r
+\r
+ TCP_SEQNO Iss; ///< Initial Sending Sequence.\r
+ TCP_SEQNO SndUna; ///< First unacknowledged data.\r
+ TCP_SEQNO SndNxt; ///< Next data sequence to send.\r
+ TCP_SEQNO SndPsh; ///< Send PUSH point.\r
+ TCP_SEQNO SndUp; ///< Send urgent point.\r
+ UINT32 SndWnd; ///< Window advertised by the remote peer.\r
+ UINT32 SndWndMax; ///< Max send window advertised by the peer.\r
+ TCP_SEQNO SndWl1; ///< Seq number used for last window update.\r
+ TCP_SEQNO SndWl2; ///< Ack no of last window update.\r
+ UINT16 SndMss; ///< Max send segment size.\r
+ TCP_SEQNO RcvNxt; ///< Next sequence no to receive.\r
+ UINT32 RcvWnd; ///< Window advertised by the local peer.\r
+ TCP_SEQNO RcvWl2; ///< The RcvNxt (or ACK) of last window update.\r
+ ///< It is necessary because of delayed ACK.\r
+\r
+ TCP_SEQNO RcvUp; ///< Urgent point;\r
+ TCP_SEQNO Irs; ///< Initial Receiving Sequence.\r
+ UINT16 RcvMss; ///< Max receive segment size.\r
+ UINT16 EnabledTimer; ///< Which timer is currently enabled.\r
+ UINT32 Timer[TCP_TIMER_NUMBER]; ///< When the timer will expire.\r
+ INT32 NextExpire; ///< Countdown offset for the nearest timer.\r
+ UINT32 Idle; ///< How long the connection is in idle.\r
+ UINT32 ProbeTime; ///< The time out value for current window prober.\r
+ BOOLEAN ProbeTimerOn;///< If TRUE, the probe time is on.\r
+\r
+ //\r
+ // RFC1323 defined variables, about window scale,\r
+ // timestamp and PAWS\r
+ //\r
+ UINT8 SndWndScale; ///< Wndscale received from the peer.\r
+ UINT8 RcvWndScale; ///< Wndscale used to scale local buffer.\r
+ UINT32 TsRecent; ///< TsRecent to echo to the remote peer.\r
+ UINT32 TsRecentAge; ///< When this TsRecent is updated.\r
+\r
+ //\r
+ // RFC2988 defined variables. about RTT measurement\r
+ //\r
+ TCP_SEQNO RttSeq; ///< The seq of measured segment now.\r
+ UINT32 RttMeasure; ///< Currently measured RTT in heartbeats.\r
+ UINT32 SRtt; ///< Smoothed RTT, scaled by 8.\r
+ UINT32 RttVar; ///< RTT variance, scaled by 8.\r
+ UINT32 Rto; ///< Current RTO, not scaled.\r
+\r
+ //\r
+ // RFC2581, and 3782 variables.\r
+ // Congestion control + NewReno fast recovery.\r
+ //\r
+ UINT32 CWnd; ///< Sender's congestion window.\r
+ UINT32 Ssthresh; ///< Slow start threshold.\r
+ TCP_SEQNO Recover; ///< Recover point for NewReno.\r
+ UINT16 DupAck; ///< Number of duplicate ACKs.\r
+ UINT8 CongestState; ///< The current congestion state(RFC3782).\r
+ UINT8 LossTimes; ///< Number of retxmit timeouts in a row.\r
+ TCP_SEQNO LossRecover; ///< Recover point for retxmit.\r
+\r
+ //\r
+ // configuration parameters, for EFI_TCP4_PROTOCOL specification\r
+ //\r
+ UINT32 KeepAliveIdle; ///< Idle time before sending first probe.\r
+ UINT32 KeepAlivePeriod; ///< Interval for subsequent keep alive probe.\r
+ UINT8 MaxKeepAlive; ///< Maxium keep alive probe times.\r
+ UINT8 KeepAliveProbes; ///< The number of keep alive probe.\r
+ UINT16 MaxRexmit; ///< The maxium number of retxmit before abort.\r
+ UINT32 FinWait2Timeout; ///< The FIN_WAIT_2 timeout.\r
+ UINT32 TimeWaitTimeout; ///< The TIME_WAIT timeout.\r
+ UINT32 ConnectTimeout; ///< The connect establishment timeout.\r
+\r
+ //\r
+ // configuration for tcp provided by user\r
+ //\r
+ BOOLEAN UseDefaultAddr;\r
+ UINT8 Tos;\r
+ UINT8 Ttl;\r
+ EFI_IPv4_ADDRESS SubnetMask;\r
+\r
+ IP_IO_IP_INFO *IpInfo; ///< Pointer reference to Ip used to send pkt\r
+ UINT32 Tick; ///< 1 tick = 200ms\r
+};\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ TCP timer related functions.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "TcpMain.h"\r
+\r
+UINT32 mTcpTick = 1000;\r
+\r
+/**\r
+ Connect timeout handler.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpConnectTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Timeout handler for TCP retransmission timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpRexmitTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Timeout handler for window probe timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpProbeTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Timeout handler for keepalive timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpKeepaliveTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Timeout handler for FIN_WAIT_2 timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpFinwait2Timeout (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+/**\r
+ Timeout handler for 2MSL timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+Tcp2MSLTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ );\r
+\r
+TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {\r
+ TcpConnectTimeout,\r
+ TcpRexmitTimeout,\r
+ TcpProbeTimeout,\r
+ TcpKeepaliveTimeout,\r
+ TcpFinwait2Timeout,\r
+ Tcp2MSLTimeout,\r
+};\r
+\r
+/**\r
+ Close the TCP connection.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClose (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ NetbufFreeList (&Tcb->SndQue);\r
+ NetbufFreeList (&Tcb->RcvQue);\r
+\r
+ TcpSetState (Tcb, TCP_CLOSED);\r
+}\r
+\r
+/**\r
+ Backoff the RTO.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpBackoffRto (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ //\r
+ // Fold the RTT estimate if too many times, the estimate\r
+ // may be wrong, fold it. So the next time a valid\r
+ // measurement is sampled, we can start fresh.\r
+ //\r
+ if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {\r
+ Tcb->RttVar += Tcb->SRtt >> 2;\r
+ Tcb->SRtt = 0;\r
+ }\r
+\r
+ Tcb->Rto <<= 1;\r
+\r
+ if (Tcb->Rto < TCP_RTO_MIN) {\r
+\r
+ Tcb->Rto = TCP_RTO_MIN;\r
+ } else if (Tcb->Rto > TCP_RTO_MAX) {\r
+\r
+ Tcb->Rto = TCP_RTO_MAX;\r
+ }\r
+}\r
+\r
+/**\r
+ Connect timeout handler.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpConnectTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ if (!TCP_CONNECTED (Tcb->State)) {\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ if (EFI_ABORTED == Tcb->Sk->SockError) {\r
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
+ }\r
+\r
+ if (TCP_SYN_RCVD == Tcb->State) {\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpResetConnection (Tcb);\r
+\r
+ }\r
+\r
+ TcpClose (Tcb);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Timeout handler for TCP retransmission timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpRexmitTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ UINT32 FlightSize;\r
+\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpRexmitTimeout: transmission timeout for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ //\r
+ // Set the congestion window. FlightSize is the\r
+ // amount of data that has been sent but not\r
+ // yet ACKed.\r
+ //\r
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
+ Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);\r
+\r
+ Tcb->CWnd = Tcb->SndMss;\r
+ Tcb->LossRecover = Tcb->SndNxt;\r
+\r
+ Tcb->LossTimes++;\r
+ if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {\r
+\r
+ DEBUG (\r
+ (EFI_D_ERROR,\r
+ "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ if (EFI_ABORTED == Tcb->Sk->SockError) {\r
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
+ }\r
+\r
+ TcpClose (Tcb);\r
+ return ;\r
+ }\r
+\r
+ TcpBackoffRto (Tcb);\r
+ TcpRetransmit (Tcb, Tcb->SndUna);\r
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
+\r
+ Tcb->CongestState = TCP_CONGEST_LOSS;\r
+\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
+}\r
+\r
+/**\r
+ Timeout handler for window probe timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpProbeTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ //\r
+ // This is the timer for sender's SWSA. RFC1122 requires\r
+ // a timer set for sender's SWSA, and suggest combine it\r
+ // with window probe timer. If data is sent, don't set\r
+ // the probe timer, since retransmit timer is on.\r
+ //\r
+ if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {\r
+\r
+ ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);\r
+ Tcb->ProbeTimerOn = FALSE;\r
+ return ;\r
+ }\r
+\r
+ TcpSendZeroProbe (Tcb);\r
+ TcpSetProbeTimer (Tcb);\r
+}\r
+\r
+/**\r
+ Timeout handler for keepalive timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpKeepaliveTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ Tcb->KeepAliveProbes++;\r
+\r
+ //\r
+ // Too many Keep-alive probes, drop the connection\r
+ //\r
+ if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {\r
+\r
+ if (EFI_ABORTED == Tcb->Sk->SockError) {\r
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
+ }\r
+\r
+ TcpClose (Tcb);\r
+ return ;\r
+ }\r
+\r
+ TcpSendZeroProbe (Tcb);\r
+ TcpSetKeepaliveTimer (Tcb);\r
+}\r
+\r
+/**\r
+ Timeout handler for FIN_WAIT_2 timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpFinwait2Timeout (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpClose (Tcb);\r
+}\r
+\r
+/**\r
+ Timeout handler for 2MSL timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+Tcp2MSLTimeout (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ DEBUG (\r
+ (EFI_D_WARN,\r
+ "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",\r
+ Tcb)\r
+ );\r
+\r
+ TcpClose (Tcb);\r
+}\r
+\r
+/**\r
+ Update the timer status and the next expire time according to the timers\r
+ to expire in a specific future time slot.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpUpdateTimer (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ UINT16 Index;\r
+\r
+ //\r
+ // Don't use a too large value to init NextExpire\r
+ // since mTcpTick wraps around as sequence no does.\r
+ //\r
+ Tcb->NextExpire = TCP_EXPIRE_TIME;\r
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
+\r
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
+\r
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&\r
+ TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)\r
+ ) {\r
+\r
+ Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);\r
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Enable a TCP timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Timer The index of the timer to be enabled.\r
+ @param[in] TimeOut The timeout value of this timer.\r
+\r
+**/\r
+VOID\r
+TcpSetTimer (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN UINT16 Timer,\r
+ IN UINT32 TimeOut\r
+ )\r
+{\r
+ TCP_SET_TIMER (Tcb->EnabledTimer, Timer);\r
+ Tcb->Timer[Timer] = mTcpTick + TimeOut;\r
+\r
+ TcpUpdateTimer (Tcb);\r
+}\r
+\r
+/**\r
+ Clear one TCP timer.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+ @param[in] Timer The index of the timer to be cleared.\r
+\r
+**/\r
+VOID\r
+TcpClearTimer (\r
+ IN OUT TCP_CB *Tcb,\r
+ IN UINT16 Timer\r
+ )\r
+{\r
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);\r
+ TcpUpdateTimer (Tcb);\r
+}\r
+\r
+/**\r
+ Clear all TCP timers.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpClearAllTimer (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ Tcb->EnabledTimer = 0;\r
+ TcpUpdateTimer (Tcb);\r
+}\r
+\r
+/**\r
+ Enable the window prober timer and set the timeout value.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetProbeTimer (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ if (!Tcb->ProbeTimerOn) {\r
+ Tcb->ProbeTime = Tcb->Rto;\r
+ Tcb->ProbeTimerOn = TRUE;\r
+\r
+ } else {\r
+ Tcb->ProbeTime <<= 1;\r
+ }\r
+\r
+ if (Tcb->ProbeTime < TCP_RTO_MIN) {\r
+\r
+ Tcb->ProbeTime = TCP_RTO_MIN;\r
+ } else if (Tcb->ProbeTime > TCP_RTO_MAX) {\r
+\r
+ Tcb->ProbeTime = TCP_RTO_MAX;\r
+ }\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);\r
+}\r
+\r
+/**\r
+ Enable the keepalive timer and set the timeout value.\r
+\r
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
+\r
+**/\r
+VOID\r
+TcpSetKeepaliveTimer (\r
+ IN OUT TCP_CB *Tcb\r
+ )\r
+{\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {\r
+ return ;\r
+\r
+ }\r
+\r
+ //\r
+ // Set the timer to KeepAliveIdle if either\r
+ // 1. the keepalive timer is off\r
+ // 2. The keepalive timer is on, but the idle\r
+ // is less than KeepAliveIdle, that means the\r
+ // connection is alive since our last probe.\r
+ //\r
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||\r
+ (Tcb->Idle < Tcb->KeepAliveIdle)\r
+ ) {\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);\r
+ Tcb->KeepAliveProbes = 0;\r
+\r
+ } else {\r
+\r
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);\r
+ }\r
+}\r
+\r
+/**\r
+ Heart beat timer handler.\r
+\r
+ @param[in] Context Context of the timer event, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpTickingDpc (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ TCP_CB *Tcb;\r
+ INT16 Index;\r
+\r
+ mTcpTick++;\r
+ mTcpGlobalIss += TCP_ISS_INCREMENT_2;\r
+\r
+ //\r
+ // Don't use LIST_FOR_EACH, which isn't delete safe.\r
+ //\r
+ for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {\r
+\r
+ Next = Entry->ForwardLink;\r
+\r
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
+\r
+ if (Tcb->State == TCP_CLOSED) {\r
+ continue;\r
+ }\r
+ //\r
+ // The connection is doing RTT measurement.\r
+ //\r
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
+ Tcb->RttMeasure++;\r
+ }\r
+\r
+ Tcb->Idle++;\r
+\r
+ if (Tcb->DelayedAck != 0) {\r
+ TcpSendAck (Tcb);\r
+ }\r
+\r
+ if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) {\r
+ Tcb->Tick--;\r
+ }\r
+\r
+ //\r
+ // No timer is active or no timer expired\r
+ //\r
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {\r
+\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Call the timeout handler for each expired timer.\r
+ //\r
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
+\r
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {\r
+ //\r
+ // disable the timer before calling the handler\r
+ // in case the handler enables it again.\r
+ //\r
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);\r
+ mTcpTimerHandler[Index](Tcb);\r
+\r
+ //\r
+ // The Tcb may have been deleted by the timer, or\r
+ // no other timer is set.\r
+ //\r
+ if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // If the Tcb still exist or some timer is set, update the timer\r
+ //\r
+ if (Index == TCP_TIMER_NUMBER) {\r
+ TcpUpdateTimer (Tcb);\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Heart beat timer handler, queues the DPC at TPL_CALLBACK.\r
+\r
+ @param[in] Event Timer event signaled, ignored.\r
+ @param[in] Context Context of the timer event, ignored.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TcpTicking (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ UEFI Component Name(2) protocol implementation for UDP6 driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+//\r
+// EFI Component Name Functions\r
+//\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName = {\r
+ Udp6ComponentNameGetDriverName,\r
+ Udp6ComponentNameGetControllerName,\r
+ "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Udp6ComponentNameGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Udp6ComponentNameGetControllerName,\r
+ "en"\r
+};\r
+\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdp6DriverNameTable[] = {\r
+ {\r
+ "eng;en",\r
+ L"UDP6 Network Service Driver"\r
+ },\r
+ {\r
+ NULL,\r
+ NULL\r
+ }\r
+};\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mUdp6DriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN) (This == &gUdp6ComponentName)\r
+ );\r
+}\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Driver Binding functions and Service Binding functions for the Network driver module.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gUdp6DriverBinding = {\r
+ Udp6DriverBindingSupported,\r
+ Udp6DriverBindingStart,\r
+ Udp6DriverBindingStop,\r
+ 0xa,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding = {\r
+ Udp6ServiceBindingCreateChild,\r
+ Udp6ServiceBindingDestroyChild\r
+};\r
+\r
+/**\r
+ Tests to see if this driver supports a given controller. If a child device is provided,\r
+ it further tests to see if this driver supports creating a handle for the specified child device.\r
+\r
+ This function checks to see if the driver specified by This supports the device specified by\r
+ ControllerHandle. Drivers will typically use the device path attached to\r
+ ControllerHandle and/or the services from the bus I/O abstraction attached to\r
+ ControllerHandle to determine if the driver supports ControllerHandle. This function\r
+ may be called many times during platform initialization. In order to reduce boot times, the tests\r
+ performed by this function must be very small, and take as little time as possible to execute. This\r
+ function must not change the state of any hardware devices, and this function must be aware that the\r
+ device specified by ControllerHandle may already be managed by the same driver or a\r
+ different driver. This function must match its calls to AllocatePages() with FreePages(),\r
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().\r
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is\r
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required\r
+ to guarantee the state of ControllerHandle is not modified by this function.\r
+\r
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param[in] ControllerHandle The handle of the controller to test. This handle\r
+ must support a protocol interface that supplies\r
+ an I/O abstraction to the driver.\r
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This\r
+ parameter is ignored by device drivers, and is optional for bus\r
+ drivers. For bus drivers, if this parameter is not NULL, then\r
+ the bus driver must determine if the bus controller specified\r
+ by ControllerHandle and the child controller specified\r
+ by RemainingDevicePath are both supported by this\r
+ bus driver.\r
+\r
+ @retval EFI_SUCCESS The device specified by ControllerHandle and\r
+ RemainingDevicePath is supported by the driver specified by This.\r
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and\r
+ RemainingDevicePath is already being managed by the driver\r
+ specified by This.\r
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and\r
+ RemainingDevicePath is already being managed by a different\r
+ driver or an application that requires exclusive access.\r
+ Currently not implemented.\r
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and\r
+ RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ //\r
+ // Test for the Udp6ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+ //\r
+ // Test for the Ip6ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ This service is called by the EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start() it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind the driver to.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCES This driver is added to ControllerHandle.\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+\r
+ //\r
+ // Allocate Private Context Data Structure.\r
+ //\r
+ Udp6Service = AllocateZeroPool (sizeof (UDP6_SERVICE_DATA));\r
+ if (Udp6Service == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+\r
+ Status = Udp6CreateService (Udp6Service, This->DriverBindingHandle, ControllerHandle);\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Install the Udp6ServiceBindingProtocol on the ControllerHandle.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &ControllerHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ &Udp6Service->ServiceBinding,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Udp6CleanService (Udp6Service);\r
+ goto EXIT;\r
+ } else {\r
+ Status = Udp6SetVariableData (Udp6Service);\r
+ }\r
+\r
+EXIT:\r
+ if (EFI_ERROR (Status)) {\r
+ if (Udp6Service != NULL) {\r
+ FreePool (Udp6Service);\r
+ }\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Stop this driver on ControllerHandle.\r
+\r
+ This service is called by the EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop(), it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop the driver on.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number\r
+ of children is zero stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional.\r
+\r
+ @retval EFI_SUCCES This driver is removed ControllerHandle.\r
+ @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID.\r
+ @retval other This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE NicHandle;\r
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+\r
+ //\r
+ // Find the NicHandle where UDP6 ServiceBinding Protocol is installed.\r
+ //\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Retrieve the UDP6 ServiceBinding Protocol.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ NicHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ (VOID **) &ServiceBinding,\r
+ This->DriverBindingHandle,\r
+ NicHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (ServiceBinding);\r
+\r
+ if (NumberOfChildren == 0) {\r
+\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ NicHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ &Udp6Service->ServiceBinding,\r
+ NULL\r
+ );\r
+\r
+ Udp6ClearVariableData (Udp6Service);\r
+\r
+ Udp6CleanService (Udp6Service);\r
+\r
+ FreePool (Udp6Service);\r
+ } else {\r
+\r
+ while (!IsListEmpty (&Udp6Service->ChildrenList)) {\r
+ Instance = NET_LIST_HEAD (&Udp6Service->ChildrenList, UDP6_INSTANCE_DATA, Link);\r
+\r
+ Status = ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Creates a child handle and installs a protocol.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+ then a new handle is created. If it is a pointer to an existing UEFI handle,\r
+ then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create\r
+ the child.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_TPL OldTpl;\r
+ VOID *Ip6;\r
+\r
+ if ((This == NULL) || (ChildHandle == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);\r
+\r
+ //\r
+ // Allocate the instance private data structure.\r
+ //\r
+ Instance = AllocateZeroPool (sizeof (UDP6_INSTANCE_DATA));\r
+ if (Instance == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Udp6InitInstance (Udp6Service, Instance);\r
+\r
+ //\r
+ // Add an IpInfo for this instance.\r
+ //\r
+ Instance->IpInfo = IpIoAddIp (Udp6Service->IpIo);\r
+ if (Instance->IpInfo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Install the Udp6Protocol for this instance.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ ChildHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ &Instance->Udp6Proto,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Instance->ChildHandle = *ChildHandle;\r
+\r
+ //\r
+ // Open the default Ip6 protocol in the IP_IO BY_CHILD.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Udp6Service->IpIo->ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ (VOID **) &Ip6,\r
+ gUdp6DriverBinding.DriverBindingHandle,\r
+ Instance->ChildHandle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Link this instance into the service context data and increase the ChildrenNumber.\r
+ //\r
+ InsertTailList (&Udp6Service->ChildrenList, &Instance->Link);\r
+ Udp6Service->ChildrenNumber++;\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ if (Instance->ChildHandle != NULL) {\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Instance->ChildHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ &Instance->Udp6Proto,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ if (Instance->IpInfo != NULL) {\r
+ IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);\r
+ }\r
+\r
+ Udp6CleanInstance (Instance);\r
+\r
+ FreePool (Instance);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Destroys a child handle with a set of I/O services.\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The I/O services were removed from the child\r
+ handle.\r
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services\r
+ that are being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.\r
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because\r
+ its I/O services are being used.\r
+ @retval other The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+ EFI_UDP6_PROTOCOL *Udp6Proto;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_TPL OldTpl;\r
+\r
+ if ((This == NULL) || (ChildHandle == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);\r
+\r
+ //\r
+ // Try to get the Udp6 protocol from the ChildHandle.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ChildHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ (VOID **) &Udp6Proto,\r
+ gUdp6DriverBinding.DriverBindingHandle,\r
+ ChildHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (Udp6Proto);\r
+\r
+ if (Instance->Destroyed) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Use the Destroyed flag to avoid the re-entering of the following code.\r
+ //\r
+ Instance->Destroyed = TRUE;\r
+\r
+ //\r
+ // Close the Ip6 protocol.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Udp6Service->IpIo->ChildHandle,\r
+ &gEfiIp6ProtocolGuid,\r
+ gUdp6DriverBinding.DriverBindingHandle,\r
+ Instance->ChildHandle\r
+ );\r
+\r
+ //\r
+ // Uninstall the Udp6Protocol previously installed on the ChildHandle.\r
+ //\r
+ Status = gBS->UninstallMultipleProtocolInterfaces (\r
+ ChildHandle,\r
+ &gEfiUdp6ProtocolGuid,\r
+ (VOID *) &Instance->Udp6Proto,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Instance->Destroyed = FALSE;\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Reset the configuration in case the instance's consumer forgets to do this.\r
+ //\r
+ Udp6Proto->Configure (Udp6Proto, NULL);\r
+\r
+ //\r
+ // Remove the IpInfo this instance consumes.\r
+ //\r
+ IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Remove this instance from the service context data's ChildrenList.\r
+ //\r
+ RemoveEntryList (&Instance->Link);\r
+ Udp6Service->ChildrenNumber--;\r
+\r
+ //\r
+ // Clean the instance.\r
+ //\r
+ Udp6CleanInstance (Instance);\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ FreePool (Instance);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This is the declaration of an EFI image entry point. This entry point is\r
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including\r
+ both device drivers and bus drivers.\r
+\r
+ The entry point for Udp6 driver that installs the driver binding\r
+ and component name protocol on its ImageHandle.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Install the Udp6DriverBinding and Udp6ComponentName protocols.\r
+ //\r
+\r
+ Status = EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gUdp6DriverBinding,\r
+ ImageHandle,\r
+ &gUdp6ComponentName,\r
+ &gUdp6ComponentName2\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Initialize the UDP random port.\r
+ //\r
+ mUdp6RandomPort = (UINT16)(\r
+ ((UINT16) NetRandomInitSeed ()) %\r
+ UDP6_PORT_KNOWN +\r
+ UDP6_PORT_KNOWN\r
+ );\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
--- /dev/null
+/** @file\r
+ Driver Binding functions and Service Binding functions for the Network driver module.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _UDP6_DRIVER_H_\r
+#define _UDP6_DRIVER_H_\r
+\r
+#include <Protocol/DriverBinding.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DevicePath.h>\r
+\r
+/**\r
+ Tests to see if this driver supports a given controller. If a child device is provided,\r
+ it further tests to see if this driver supports creating a handle for the specified child device.\r
+\r
+ This function checks to see if the driver specified by This supports the device specified by\r
+ ControllerHandle. Drivers typically use the device path attached to\r
+ ControllerHandle and/or the services from the bus I/O abstraction attached to\r
+ ControllerHandle to determine if the driver supports ControllerHandle. This function\r
+ may be called many times during platform initialization. In order to reduce boot times, the tests\r
+ performed by this function must be very small, and take as little time as possible to execute. This\r
+ function must not change the state of any hardware devices, and this function must be aware that the\r
+ device specified by ControllerHandle may already be managed by the same driver or a\r
+ different driver. This function must match its calls to AllocatePages() with FreePages(),\r
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().\r
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is\r
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required\r
+ to guarantee the state of ControllerHandle is not modified by this function.\r
+\r
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+ @param[in] ControllerHandle The handle of the controller to test. This handle\r
+ must support a protocol interface that supplies\r
+ an I/O abstraction to the driver.\r
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This\r
+ parameter is ignored by device drivers, and is optional for bus\r
+ drivers. For bus drivers, if this parameter is not NULL, then\r
+ the bus driver must determine if the bus controller specified\r
+ by ControllerHandle and the child controller specified\r
+ by RemainingDevicePath are both supported by this\r
+ bus driver.\r
+\r
+ @retval EFI_SUCCESS The device specified by ControllerHandle and\r
+ RemainingDevicePath is supported by the driver specified by This.\r
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and\r
+ RemainingDevicePath is already being managed by the driver\r
+ specified by This.\r
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and\r
+ RemainingDevicePath is already being managed by a different\r
+ driver or an application that requires exclusive access.\r
+ Currently not implemented.\r
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and\r
+ RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ This service is called by the EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start() it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to bind a driver to.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCES This driver is added to ControllerHandle.\r
+ @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+/**\r
+ Stop this driver on ControllerHandle.\r
+\r
+ This service is called by the EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop(), it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop the driver on.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number\r
+ of children is zero, stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional.\r
+\r
+ @retval EFI_SUCCESS This driver removed ControllerHandle.\r
+ @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID.\r
+ @retval other This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6DriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
+ );\r
+\r
+/**\r
+ Creates a child handle and installs a protocol.\r
+\r
+ The CreateChild() function installs a protocol on ChildHandle.\r
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.\r
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.\r
+\r
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.\r
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,\r
+ then a new handle is created. If it is a pointer to an existing UEFI handle,\r
+ then the protocol is added to the existing UEFI handle.\r
+\r
+ @retval EFI_SUCCES The protocol was added to ChildHandle.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create\r
+ the child.\r
+ @retval other The child handle was not created.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingCreateChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ );\r
+\r
+/**\r
+ Destroys a child handle with a set of I/O services.\r
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol\r
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the\r
+ last protocol on ChildHandle, then ChildHandle is destroyed.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ChildHandle Handle of the child to destroy.\r
+\r
+ @retval EFI_SUCCES The I/O services were removed from the child\r
+ handle.\r
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services\r
+ that are being removed.\r
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.\r
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because\r
+ its I/O services are being used.\r
+ @retval other The child handle was not destroyed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6ServiceBindingDestroyChild (\r
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ChildHandle\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+## @file Udp6Dxe.inf\r
+# Component description file for Udp6 module.\r
+#\r
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = Udp6Dxe\r
+ FILE_GUID = D912C7BC-F098-4367-92BA-E911083C7B0E\r
+ MODULE_TYPE = UEFI_DRIVER\r
+ VERSION_STRING = 1.0\r
+\r
+ ENTRY_POINT = Udp6DriverEntryPoint\r
+ UNLOAD_IMAGE = NetLibDefaultUnload\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC\r
+#\r
+\r
+[Sources]\r
+ Udp6Driver.h\r
+ Udp6Driver.c\r
+ Udp6Impl.c\r
+ Udp6Impl.h\r
+ ComponentName.c\r
+ Udp6Main.c\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+ BaseLib\r
+ BaseMemoryLib\r
+ MemoryAllocationLib\r
+ UefiBootServicesTableLib\r
+ UefiDriverEntryPoint\r
+ UefiRuntimeServicesTableLib\r
+ UefiLib\r
+ DebugLib\r
+ IpIoLib\r
+ NetLib\r
+ DpcLib\r
+\r
+\r
+[Protocols]\r
+ gEfiIp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiIp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiUdp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+ gEfiUdp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED\r
+\r
--- /dev/null
+/** @file\r
+ Udp6 driver's whole implementation.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+UINT16 mUdp6RandomPort;\r
+\r
+/**\r
+ This function checks and timeouts the I/O datagrams holding by the corresponding\r
+ service context.\r
+\r
+ @param[in] Event The event this function is registered to.\r
+ @param[in] Context The context data registered during the creation of\r
+ the Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6CheckTimeout (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ This function finds the udp instance by the specified <Address, Port> pair.\r
+\r
+ @param[in] InstanceList Pointer to the head of the list linking the udp\r
+ instances.\r
+ @param[in] Address Pointer to the specified IPv6 address.\r
+ @param[in] Port The udp port number.\r
+\r
+ @retval TRUE The specified <Address, Port> pair is found.\r
+ @retval FALSE Otherwise.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6FindInstanceByPort (\r
+ IN LIST_ENTRY *InstanceList,\r
+ IN EFI_IPv6_ADDRESS *Address,\r
+ IN UINT16 Port\r
+ );\r
+\r
+/**\r
+ This function is the packet transmitting notify function registered to the IpIo\r
+ interface. It's called to signal the udp TxToken when the IpIo layer completes\r
+ transmitting of the udp datagram.\r
+\r
+ @param[in] Status The completion status of the output udp datagram.\r
+ @param[in] Context Pointer to the context data.\r
+ @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending.\r
+ @param[in] NotifyData Pointer to the notify data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramSent (\r
+ IN EFI_STATUS Status,\r
+ IN VOID *Context,\r
+ IN IP_IO_IP_PROTOCOL Sender,\r
+ IN VOID *NotifyData\r
+ );\r
+\r
+/**\r
+ This function processes the received datagram passed up by the IpIo layer.\r
+\r
+ @param[in] Status The status of this udp datagram.\r
+ @param[in] IcmpError The IcmpError code, only available when Status is\r
+ EFI_ICMP_ERROR.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA.\r
+ @param[in] Packet Pointer to the NET_BUF containing the received udp\r
+ datagram.\r
+ @param[in] Context Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramRcvd (\r
+ IN EFI_STATUS Status,\r
+ IN UINT8 IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ This function cancle the token specified by Arg in the Map.\r
+\r
+ @param[in] Map Pointer to the NET_MAP.\r
+ @param[in] Item Pointer to the NET_MAP_ITEM.\r
+ @param[in] Arg Pointer to the token to be cancelled, if NULL, all\r
+ the tokens in this Map will be cancelled.\r
+ This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token\r
+ is not the same as that in the Item if Arg is not\r
+ NULL.\r
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is\r
+ cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6CancelTokens (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function check if the received udp datagram matches with the Instance.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+ @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted\r
+ from the received udp datagram.\r
+\r
+ @retval TRUE The udp datagram matches the receiving requirements of the Instance.\r
+ @retval FALSE The udp datagram doe not match the receiving requirements of the Instance.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6MatchDgram (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP6_SESSION_DATA *Udp6Session\r
+ );\r
+\r
+/**\r
+ This function removes the Wrap specified by Context and releases relevant resources.\r
+\r
+ @param[in] Event The Event this notify function is registered to.\r
+ @param[in] Context Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6RecycleRxDataWrap (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ This function wraps the Packet into RxData.\r
+\r
+ @param[in] Instance Pointer to the instance context data.\r
+ @param[in] Packet Pointer to the buffer containing the received\r
+ datagram.\r
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+ datagram.\r
+\r
+ @return Pointer to the structure wrapping the RxData and the Packet.\r
+\r
+**/\r
+UDP6_RXDATA_WRAP *\r
+Udp6WrapRxData (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP6_RECEIVE_DATA *RxData\r
+ );\r
+\r
+/**\r
+ This function enqueues the received datagram into the instances' receiving queues.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+ @param[in] Packet Pointer to the buffer containing the received\r
+ datagram.\r
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+ datagram.\r
+\r
+ @return The times this datagram is enqueued.\r
+\r
+**/\r
+UINTN\r
+Udp6EnqueueDgram (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP6_RECEIVE_DATA *RxData\r
+ );\r
+\r
+/**\r
+ This function delivers the datagrams enqueued in the instances.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+\r
+**/\r
+VOID\r
+Udp6DeliverDgram (\r
+ IN UDP6_SERVICE_DATA *Udp6Service\r
+ );\r
+\r
+/**\r
+ This function demultiplexes the received udp datagram to the apropriate instances.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from\r
+ the received datagram.\r
+ @param[in] Packet Pointer to the buffer containing the received udp\r
+ datagram.\r
+\r
+**/\r
+VOID\r
+Udp6Demultiplex (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ This function handles the received Icmp Error message and demultiplexes it to the\r
+ instance.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+ @param[in] IcmpError The icmp error code.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted\r
+ from the received Icmp Error packet.\r
+ @param[in, out] Packet Pointer to the Icmp Error packet.\r
+\r
+**/\r
+VOID\r
+Udp6IcmpHandler (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN UINT8 IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN OUT NET_BUF *Packet\r
+ );\r
+\r
+/**\r
+ This function builds and sends out a icmp port unreachable message.\r
+\r
+ @param[in] IpIo Pointer to the IP_IO instance.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet\r
+ causes this icmp error message.\r
+ @param[in] Udp6Header Pointer to the udp header of the datagram causes\r
+ this icmp error message.\r
+\r
+**/\r
+VOID\r
+Udp6SendPortUnreach (\r
+ IN IP_IO *IpIo,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN VOID *Udp6Header\r
+ );\r
+\r
+/**\r
+ Find the key in the netmap\r
+\r
+ @param[in] Map The netmap to search within.\r
+ @param[in] Key The key to search.\r
+\r
+ @return The point to the item contains the Key, or NULL if Key isn't in the map.\r
+\r
+**/\r
+NET_MAP_ITEM *\r
+Udp6MapMultiCastAddr (\r
+ IN NET_MAP *Map,\r
+ IN VOID *Key\r
+ );\r
+\r
+/**\r
+ Create the Udp service context data.\r
+\r
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.\r
+ @param[in] ImageHandle The image handle of this udp6 driver.\r
+ @param[in] ControllerHandle The controller handle this udp6 driver binds on.\r
+\r
+ @retval EFI_SUCCESS The udp6 service context data was created and\r
+ initialized.\r
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.\r
+ @retval Others An error condition occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6CreateService (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_HANDLE ControllerHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IP_IO_OPEN_DATA OpenData;\r
+\r
+ ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA));\r
+\r
+ Udp6Service->Signature = UDP6_SERVICE_DATA_SIGNATURE;\r
+ Udp6Service->ServiceBinding = mUdp6ServiceBinding;\r
+ Udp6Service->ImageHandle = ImageHandle;\r
+ Udp6Service->ControllerHandle = ControllerHandle;\r
+ Udp6Service->ChildrenNumber = 0;\r
+\r
+ InitializeListHead (&Udp6Service->ChildrenList);\r
+\r
+ //\r
+ // Create the IpIo for this service context.\r
+ //\r
+ Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6);\r
+ if (Udp6Service->IpIo == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Set the OpenData used to open the IpIo.\r
+ //\r
+ CopyMem (\r
+ &OpenData.IpConfigData.Ip6CfgData,\r
+ &mIp6IoDefaultIpConfigData,\r
+ sizeof (EFI_IP6_CONFIG_DATA)\r
+ );\r
+ OpenData.RcvdContext = (VOID *) Udp6Service;\r
+ OpenData.SndContext = NULL;\r
+ OpenData.PktRcvdNotify = Udp6DgramRcvd;\r
+ OpenData.PktSentNotify = Udp6DgramSent;\r
+\r
+ //\r
+ // Configure and start the IpIo.\r
+ //\r
+ Status = IpIoOpen (Udp6Service->IpIo, &OpenData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create the event for Udp timeout checking.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ Udp6CheckTimeout,\r
+ Udp6Service,\r
+ &Udp6Service->TimeoutEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Start the timeout timer event.\r
+ //\r
+ Status = gBS->SetTimer (\r
+ Udp6Service->TimeoutEvent,\r
+ TimerPeriodic,\r
+ UDP6_TIMEOUT_INTERVAL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+ if (Udp6Service->TimeoutEvent != NULL) {\r
+ gBS->CloseEvent (Udp6Service->TimeoutEvent);\r
+ }\r
+\r
+ IpIoDestroy (Udp6Service->IpIo);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Clean the Udp service context data.\r
+\r
+ @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6CleanService (\r
+ IN OUT UDP6_SERVICE_DATA *Udp6Service\r
+ )\r
+{\r
+ //\r
+ // Close the TimeoutEvent timer.\r
+ //\r
+ gBS->CloseEvent (Udp6Service->TimeoutEvent);\r
+\r
+ //\r
+ // Destroy the IpIo.\r
+ //\r
+ IpIoDestroy (Udp6Service->IpIo);\r
+}\r
+\r
+\r
+/**\r
+ This function checks and times out the I/O datagrams listed in the\r
+ UDP6_SERVICE_DATA which is specified by the input parameter Context.\r
+\r
+\r
+ @param[in] Event The event this function registered to.\r
+ @param[in] Context The context data registered during the creation of\r
+ the Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6CheckTimeout (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+ LIST_ENTRY *Entry;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ LIST_ENTRY *WrapEntry;\r
+ LIST_ENTRY *NextEntry;\r
+ UDP6_RXDATA_WRAP *Wrap;\r
+\r
+ Udp6Service = (UDP6_SERVICE_DATA *) Context;\r
+ NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+ //\r
+ // Iterate all the instances belonging to this service context.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+ NET_CHECK_SIGNATURE (Instance, UDP6_INSTANCE_DATA_SIGNATURE);\r
+\r
+ if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {\r
+ //\r
+ // Skip this instance if it's not configured or no receive timeout.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {\r
+ //\r
+ // Iterate all the rxdatas belonging to this udp instance.\r
+ //\r
+ Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP6_RXDATA_WRAP, Link);\r
+\r
+ if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) {\r
+ //\r
+ // Remove this RxData if it timeouts.\r
+ //\r
+ Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);\r
+ } else {\r
+ Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function intializes the new created udp instance.\r
+\r
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.\r
+ @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6InitInstance (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN OUT UDP6_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ //\r
+ // Set the signature.\r
+ //\r
+ Instance->Signature = UDP6_INSTANCE_DATA_SIGNATURE;\r
+\r
+ //\r
+ // Init the lists.\r
+ //\r
+ InitializeListHead (&Instance->Link);\r
+ InitializeListHead (&Instance->RcvdDgramQue);\r
+ InitializeListHead (&Instance->DeliveredDgramQue);\r
+\r
+ //\r
+ // Init the NET_MAPs.\r
+ //\r
+ NetMapInit (&Instance->TxTokens);\r
+ NetMapInit (&Instance->RxTokens);\r
+ NetMapInit (&Instance->McastIps);\r
+\r
+ //\r
+ // Save the pointer to the UDP6_SERVICE_DATA, and initialize other members.\r
+ //\r
+ Instance->Udp6Service = Udp6Service;\r
+ CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL));\r
+ Instance->IcmpError = EFI_SUCCESS;\r
+ Instance->Configured = FALSE;\r
+ Instance->IsNoMapping = FALSE;\r
+ Instance->Destroyed = FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This function cleans the udp instance.\r
+\r
+ @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean.\r
+\r
+**/\r
+VOID\r
+Udp6CleanInstance (\r
+ IN OUT UDP6_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ NetMapClean (&Instance->McastIps);\r
+ NetMapClean (&Instance->RxTokens);\r
+ NetMapClean (&Instance->TxTokens);\r
+}\r
+\r
+\r
+/**\r
+ This function finds the udp instance by the specified <Address, Port> pair.\r
+\r
+ @param[in] InstanceList Pointer to the head of the list linking the udp\r
+ instances.\r
+ @param[in] Address Pointer to the specified IPv6 address.\r
+ @param[in] Port The udp port number.\r
+\r
+ @retval TRUE The specified <Address, Port> pair is found.\r
+ @retval FALSE Otherwise.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6FindInstanceByPort (\r
+ IN LIST_ENTRY *InstanceList,\r
+ IN EFI_IPv6_ADDRESS *Address,\r
+ IN UINT16 Port\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_UDP6_CONFIG_DATA *ConfigData;\r
+\r
+ NET_LIST_FOR_EACH (Entry, InstanceList) {\r
+ //\r
+ // Iterate all the udp instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+ ConfigData = &Instance->ConfigData;\r
+\r
+ if (!Instance->Configured || ConfigData->AcceptAnyPort) {\r
+ //\r
+ // If the instance is not configured, or the configdata of the instance indicates\r
+ // this instance accepts any port, skip it.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (EFI_IP6_EQUAL (&ConfigData->StationAddress, Address) &&\r
+ (ConfigData->StationPort == Port)\r
+ ) {\r
+ //\r
+ // If both the address and the port are the same, return TRUE.\r
+ //\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Return FALSE when matching fails.\r
+ //\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This function tries to bind the udp instance according to the configured port\r
+ allocation stragety.\r
+\r
+ @param[in] InstanceList Pointer to the head of the list linking the udp\r
+ instances.\r
+ @param[in] ConfigData Pointer to the ConfigData of the instance to be\r
+ bound.\r
+\r
+ @retval EFI_SUCCESS The bound operation completed successfully.\r
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is\r
+ already used by other instance.\r
+ @retval EFI_OUT_OF_RESOURCES No available port resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6Bind (\r
+ IN LIST_ENTRY *InstanceList,\r
+ IN EFI_UDP6_CONFIG_DATA *ConfigData\r
+ )\r
+{\r
+ EFI_IPv6_ADDRESS *StationAddress;\r
+ UINT16 StartPort;\r
+\r
+ if (ConfigData->AcceptAnyPort) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ StationAddress = &ConfigData->StationAddress;\r
+\r
+ if (ConfigData->StationPort != 0) {\r
+\r
+ if (!ConfigData->AllowDuplicatePort &&\r
+ Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)\r
+ ) {\r
+ //\r
+ // Do not allow duplicate ports and the port is already used by other instance.\r
+ //\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+ } else {\r
+ //\r
+ // Select a random port for this instance.\r
+ //\r
+ if (ConfigData->AllowDuplicatePort) {\r
+ //\r
+ // Just pick up the random port if the instance allows duplicate port.\r
+ //\r
+ ConfigData->StationPort = mUdp6RandomPort;\r
+ } else {\r
+\r
+ StartPort = mUdp6RandomPort;\r
+\r
+ while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) {\r
+\r
+ mUdp6RandomPort++;\r
+ if (mUdp6RandomPort == 0) {\r
+ mUdp6RandomPort = UDP6_PORT_KNOWN;\r
+ }\r
+\r
+ if (mUdp6RandomPort == StartPort) {\r
+ //\r
+ // No available port.\r
+ //\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ ConfigData->StationPort = mUdp6RandomPort;\r
+ }\r
+\r
+ mUdp6RandomPort++;\r
+ if (mUdp6RandomPort == 0) {\r
+ mUdp6RandomPort = UDP6_PORT_KNOWN;\r
+ }\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function is used to check whether the NewConfigData has any un-reconfigurable\r
+ parameters changed compared to the OldConfigData.\r
+\r
+ @param[in] OldConfigData Pointer to the current ConfigData the udp instance\r
+ uses.\r
+ @param[in] NewConfigData Pointer to the new ConfigData.\r
+\r
+ @retval TRUE The instance is reconfigurable according to the NewConfigData.\r
+ @retval FALSE Otherwise.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6IsReconfigurable (\r
+ IN EFI_UDP6_CONFIG_DATA *OldConfigData,\r
+ IN EFI_UDP6_CONFIG_DATA *NewConfigData\r
+ )\r
+{\r
+ if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||\r
+ (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||\r
+ (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)\r
+ ) {\r
+ //\r
+ // The receiving filter parameters cannot be changed.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if ((!NewConfigData->AcceptAnyPort) &&\r
+ (NewConfigData->StationPort != OldConfigData->StationPort)\r
+ ) {\r
+ //\r
+ // The port is not changeable.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) {\r
+ //\r
+ // The StationAddress is not the same.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+\r
+ if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) {\r
+ //\r
+ // The remoteaddress is not the same.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) &&\r
+ (NewConfigData->RemotePort != OldConfigData->RemotePort)\r
+ ) {\r
+ //\r
+ // The RemotePort differs if it's designated in the configdata.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // All checks pass, return TRUE.\r
+ //\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ This function builds the Ip6 configdata from the Udp6ConfigData.\r
+\r
+ @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA.\r
+ @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6BuildIp6ConfigData (\r
+ IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData,\r
+ IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData\r
+ )\r
+{\r
+ CopyMem (\r
+ Ip6ConfigData,\r
+ &mIp6IoDefaultIpConfigData,\r
+ sizeof (EFI_IP6_CONFIG_DATA)\r
+ );\r
+ Ip6ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP;\r
+ Ip6ConfigData->AcceptPromiscuous = Udp6ConfigData->AcceptPromiscuous;\r
+ IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress);\r
+ IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress);\r
+ //\r
+ // Use the -1 magic number to disable the receiving process of the ip instance.\r
+ //\r
+ Ip6ConfigData->ReceiveTimeout = (UINT32) (-1);\r
+}\r
+\r
+\r
+/**\r
+ This function validates the TxToken. It returns the error code according to the spec.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+ @param[in] TxToken Pointer to the token to be checked.\r
+\r
+ @retval EFI_SUCCESS The TxToken is valid.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ Token.Event is NULL;\r
+ Token.Packet.TxData is NULL;\r
+ Token.Packet.TxData.FragmentCount is zero;\r
+ Token.Packet.TxData.DataLength is not equal to the\r
+ sum of fragment lengths;\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[].FragmentLength\r
+ fields is zero;\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[].FragmentBuffer\r
+ fields is NULL;\r
+ UdpSessionData.DestinationAddress are not valid\r
+ unicast IPv6 addresses if the UdpSessionData is\r
+ not NULL;\r
+ UdpSessionData.DestinationPort and\r
+ ConfigData.RemotePort are all zero if the\r
+ UdpSessionData is not NULL.\r
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP\r
+ packet size.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6ValidateTxToken (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *TxToken\r
+ )\r
+{\r
+ EFI_UDP6_TRANSMIT_DATA *TxData;\r
+ UINT32 Index;\r
+ UINT32 TotalLen;\r
+ EFI_UDP6_CONFIG_DATA *ConfigData;\r
+ EFI_UDP6_SESSION_DATA *UdpSessionData;\r
+\r
+\r
+ if (TxToken->Event == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TxData = TxToken->Packet.TxData;\r
+\r
+ if ((TxData == NULL) || (TxData->FragmentCount == 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TotalLen = 0;\r
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
+\r
+ if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||\r
+ (TxData->FragmentTable[Index].FragmentLength == 0)\r
+ ) {\r
+ //\r
+ // If the FragmentBuffer is NULL, or the FragmentLeng is zero.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ TotalLen += TxData->FragmentTable[Index].FragmentLength;\r
+ }\r
+\r
+ if (TotalLen != TxData->DataLength) {\r
+ //\r
+ // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the\r
+ // DataLength.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ConfigData = &Instance->ConfigData;\r
+ UdpSessionData = TxData->UdpSessionData;\r
+\r
+ if (UdpSessionData != NULL) {\r
+\r
+ if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {\r
+ //\r
+ // Ambiguous; no avalaible DestinationPort for this token.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&\r
+ NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)\r
+ ) {\r
+ //\r
+ // The DestinationAddress is not specificed.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&\r
+ !NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)\r
+ ) {\r
+ //\r
+ // The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress\r
+ // is not zero too.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) {\r
+ //\r
+ // The configured RemoteAddress is all zero, and the user doesn't override the\r
+ // destination address.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (TxData->DataLength > UDP6_MAX_DATA_SIZE) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function checks whether the specified Token duplicates the one in the Map.\r
+\r
+ @param[in] Map Pointer to the NET_MAP.\r
+ @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to\r
+ the Token.\r
+ @param[in] Context Pointer to the Token to be checked.\r
+\r
+ @retval EFI_SUCCESS The Token specified by Context differs from the\r
+ one in the Item.\r
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6TokenExist (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_UDP6_COMPLETION_TOKEN *Token;\r
+ EFI_UDP6_COMPLETION_TOKEN *TokenInItem;\r
+\r
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) Context;\r
+ TokenInItem = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;\r
+\r
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {\r
+ //\r
+ // The Token duplicates with the TokenInItem in case either the two pointers are the\r
+ // same, or the Events of these two tokens are the same.\r
+ //\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function calculates the checksum for the Packet, utilizing the pre-calculated\r
+ pseudo HeadSum to reduce some overhead.\r
+\r
+ @param[in] Packet Pointer to the NET_BUF contains the udp datagram.\r
+ @param[in] HeadSum Checksum of the pseudo header, execpt the length\r
+ field.\r
+\r
+ @return The 16-bit checksum of this udp datagram.\r
+\r
+**/\r
+UINT16\r
+Udp6Checksum (\r
+ IN NET_BUF *Packet,\r
+ IN UINT16 HeadSum\r
+ )\r
+{\r
+ UINT16 Checksum;\r
+\r
+ Checksum = NetbufChecksum (Packet);\r
+ Checksum = NetAddChecksum (Checksum, HeadSum);\r
+\r
+ Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));\r
+ Checksum = (UINT16) (~Checksum);\r
+ return Checksum;\r
+}\r
+\r
+\r
+/**\r
+ This function removes the specified Token from the TokenMap.\r
+\r
+ @param[in] TokenMap Pointer to the NET_MAP containing the tokens.\r
+ @param[in] Token Pointer to the Token to be removed.\r
+\r
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.\r
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6RemoveToken (\r
+ IN NET_MAP *TokenMap,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+\r
+ //\r
+ // Find the Token first.\r
+ //\r
+ Item = NetMapFindKey (TokenMap, (VOID *) Token);\r
+\r
+ if (Item != NULL) {\r
+ //\r
+ // Remove the token if it's found in the map.\r
+ //\r
+ NetMapRemoveItem (TokenMap, Item, NULL);\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+ This function is the packet transmitting notify function registered to the IpIo\r
+ interface. It's called to signal the udp TxToken when IpIo layer completes the\r
+ transmitting of the udp datagram.\r
+\r
+ @param[in] Status The completion status of the output udp datagram.\r
+ @param[in] Context Pointer to the context data.\r
+ @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending.\r
+ @param[in] NotifyData Pointer to the notify data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramSent (\r
+ IN EFI_STATUS Status,\r
+ IN VOID *Context,\r
+ IN IP_IO_IP_PROTOCOL Sender,\r
+ IN VOID *NotifyData\r
+ )\r
+{\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_UDP6_COMPLETION_TOKEN *Token;\r
+\r
+ Instance = (UDP6_INSTANCE_DATA *) Context;\r
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) NotifyData;\r
+\r
+ if (Udp6RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {\r
+ //\r
+ // The token may be cancelled. Only signal it if the remove operation succeeds.\r
+ //\r
+ Token->Status = Status;\r
+ gBS->SignalEvent (Token->Event);\r
+ DispatchDpc ();\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function processes the received datagram passed up by the IpIo layer.\r
+\r
+ @param[in] Status The status of this udp datagram.\r
+ @param[in] IcmpError The IcmpError code, only available when Status is\r
+ EFI_ICMP_ERROR.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA.\r
+ @param[in] Packet Pointer to the NET_BUF containing the received udp\r
+ datagram.\r
+ @param[in] Context Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6DgramRcvd (\r
+ IN EFI_STATUS Status,\r
+ IN UINT8 IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);\r
+\r
+ //\r
+ // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.\r
+ //\r
+ if (Status == EFI_SUCCESS) {\r
+\r
+ //\r
+ // Demultiplex the received datagram.\r
+ //\r
+ Udp6Demultiplex ((UDP6_SERVICE_DATA *) Context, NetSession, Packet);\r
+ } else {\r
+ //\r
+ // Handle the ICMP6 Error packet.\r
+ //\r
+ Udp6IcmpHandler ((UDP6_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);\r
+ }\r
+\r
+ //\r
+ // Dispatch the DPC queued by the NotifyFunction of the rx token's events\r
+ // that are signaled with received data.\r
+ //\r
+ DispatchDpc ();\r
+}\r
+\r
+\r
+/**\r
+ This function removes the multicast group specified by Arg from the Map.\r
+\r
+ @param[in] Map Pointer to the NET_MAP.\r
+ @param[in] Item Pointer to the NET_MAP_ITEM.\r
+ @param[in] Arg Pointer to the Arg, it's the pointer to a\r
+ multicast IPv6 Address. This parameter is\r
+ optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The multicast address is removed.\r
+ @retval EFI_ABORTED The specified multicast address is removed, and the\r
+ Arg is not NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6LeaveGroup (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg OPTIONAL\r
+ )\r
+{\r
+ EFI_IPv6_ADDRESS *McastIp;\r
+\r
+ McastIp = Arg;\r
+\r
+ if ((McastIp != NULL) &&\r
+ !EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key))\r
+ ) {\r
+ //\r
+ // McastIp is not NULL and the multicast address contained in the Item\r
+ // is not the same as McastIp.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ FreePool (Item->Key);\r
+\r
+ //\r
+ // Remove this Item.\r
+ //\r
+ NetMapRemoveItem (Map, Item, NULL);\r
+\r
+ if (McastIp != NULL) {\r
+ //\r
+ // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.\r
+ //\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function cancle the token specified by Arg in the Map.\r
+\r
+ @param[in] Map Pointer to the NET_MAP.\r
+ @param[in] Item Pointer to the NET_MAP_ITEM.\r
+ @param[in] Arg Pointer to the token to be cancelled. If NULL, all\r
+ the tokens in this Map will be cancelled.\r
+ This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token\r
+ is not the same as that in the Item, if Arg is not\r
+ NULL.\r
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is\r
+ cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6CancelTokens (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg OPTIONAL\r
+ )\r
+{\r
+ EFI_UDP6_COMPLETION_TOKEN *TokenToCancel;\r
+ NET_BUF *Packet;\r
+ IP_IO *IpIo;\r
+\r
+ if ((Arg != NULL) && (Item->Key != Arg)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (Item->Value != NULL) {\r
+ //\r
+ // If the token is a transmit token, the corresponding Packet is recorded in\r
+ // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken\r
+ // will invoke Udp6DgramSent, the token will be signaled and this Item will\r
+ // be removed from the Map there.\r
+ //\r
+ Packet = (NET_BUF *) (Item->Value);\r
+ IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));\r
+\r
+ IpIoCancelTxToken (IpIo, Packet);\r
+ } else {\r
+ //\r
+ // The token is a receive token. Abort it and remove it from the Map.\r
+ //\r
+ TokenToCancel = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;\r
+ NetMapRemoveItem (Map, Item, NULL);\r
+\r
+ TokenToCancel->Status = EFI_ABORTED;\r
+ gBS->SignalEvent (TokenToCancel->Event);\r
+ }\r
+\r
+ if (Arg != NULL) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function removes all the Wrap datas in the RcvdDgramQue.\r
+\r
+ @param[in] Instance Pointer to the Udp6 Instance.\r
+\r
+**/\r
+VOID\r
+Udp6FlushRcvdDgram (\r
+ IN UDP6_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ UDP6_RXDATA_WRAP *Wrap;\r
+\r
+ while (!IsListEmpty (&Instance->RcvdDgramQue)) {\r
+ //\r
+ // Iterate all the Wraps in the RcvdDgramQue.\r
+ //\r
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);\r
+\r
+ //\r
+ // The Wrap will be removed from the RcvdDgramQue by this function call.\r
+ //\r
+ Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);\r
+ }\r
+}\r
+\r
+\r
+\r
+/**\r
+ Cancel Udp6 tokens from the Udp6 instance.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+ @param[in] Token Pointer to the token to be canceled. If NULL, all\r
+ tokens in this instance will be cancelled.\r
+ This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The Token is cancelled.\r
+ @retval EFI_NOT_FOUND The Token is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6InstanceCancelToken (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Cancel this token from the TxTokens map.\r
+ //\r
+ Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token);\r
+\r
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {\r
+ //\r
+ // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from\r
+ // the TxTokens and returns success.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Try to cancel this token from the RxTokens map in condition either the Token\r
+ // is NULL or the specified Token is not in TxTokens.\r
+ //\r
+ Status = NetMapIterate (&Instance->RxTokens, Udp6CancelTokens, Token);\r
+\r
+ if ((Token != NULL) && (Status == EFI_SUCCESS)) {\r
+ //\r
+ // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the\r
+ // TxTokens nor the RxTokens, or say, it's not found.\r
+ //\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ ASSERT ((Token != NULL) ||\r
+ ((0 == NetMapGetCount (&Instance->TxTokens)) &&\r
+ (0 == NetMapGetCount (&Instance->RxTokens)))\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This function checks if the received udp datagram matches with the Instance.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+ @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted\r
+ from the received udp datagram.\r
+\r
+ @retval TRUE The udp datagram matches the receiving requirements of the Instance.\r
+ @retval FALSE The udp datagram does not matche the receiving requirements of the Instance.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6MatchDgram (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP6_SESSION_DATA *Udp6Session\r
+ )\r
+{\r
+ EFI_UDP6_CONFIG_DATA *ConfigData;\r
+ EFI_IPv6_ADDRESS Destination;\r
+\r
+ ConfigData = &Instance->ConfigData;\r
+\r
+ if (ConfigData->AcceptPromiscuous) {\r
+ //\r
+ // Always matches if this instance is in the promiscuous state.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) ||\r
+ ((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort))\r
+ ) {\r
+ //\r
+ // The local port or the remote port doesn't match.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) &&\r
+ !EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress)\r
+ ) {\r
+ //\r
+ // This datagram doesn't come from the instance's specified sender.\r
+ //\r
+ return FALSE;\r
+ }\r
+\r
+ if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) ||\r
+ EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress)\r
+ ) {\r
+ //\r
+ // The instance is configured to receive datagrams destinated to any station IP or\r
+ // the destination address of this datagram matches the configured station IP.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress);\r
+\r
+ if (IP6_IS_MULTICAST (&Destination) &&\r
+ (NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination))\r
+ ) {\r
+ //\r
+ // It's a multicast packet and the multicast address is accepted by this instance.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This function removes the Wrap specified by Context and release relevant resources.\r
+\r
+ @param[in] Event The Event this notify function registered to.\r
+ @param[in] Context Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6RecycleRxDataWrap (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UDP6_RXDATA_WRAP *Wrap;\r
+\r
+ Wrap = (UDP6_RXDATA_WRAP *) Context;\r
+\r
+ //\r
+ // Remove the Wrap from the list it belongs to.\r
+ //\r
+ RemoveEntryList (&Wrap->Link);\r
+\r
+ //\r
+ // Free the Packet associated with this Wrap.\r
+ //\r
+ NetbufFree (Wrap->Packet);\r
+\r
+ //\r
+ // Close the event.\r
+ //\r
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);\r
+\r
+ FreePool (Wrap);\r
+}\r
+\r
+\r
+/**\r
+ This function wraps the Packet into RxData.\r
+\r
+ @param[in] Instance Pointer to the instance context data.\r
+ @param[in] Packet Pointer to the buffer containing the received\r
+ datagram.\r
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+ datagram.\r
+\r
+ @return Pointer to the structure wrapping the RxData and the Packet.\r
+\r
+**/\r
+UDP6_RXDATA_WRAP *\r
+Udp6WrapRxData (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP6_RECEIVE_DATA *RxData\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_RXDATA_WRAP *Wrap;\r
+\r
+ //\r
+ // Allocate buffer for the Wrap.\r
+ //\r
+ Wrap = AllocateZeroPool (sizeof (UDP6_RXDATA_WRAP) +\r
+ (Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA));\r
+ if (Wrap == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ InitializeListHead (&Wrap->Link);\r
+\r
+ CopyMem (&Wrap->RxData, RxData, sizeof(EFI_UDP6_RECEIVE_DATA));\r
+ //\r
+ // Create the Recycle event.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ Udp6RecycleRxDataWrap,\r
+ Wrap,\r
+ &Wrap->RxData.RecycleSignal\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Wrap);\r
+ return NULL;\r
+ }\r
+\r
+ Wrap->Packet = Packet;\r
+ Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;\r
+\r
+ return Wrap;\r
+}\r
+\r
+\r
+/**\r
+ This function enqueues the received datagram into the instances' receiving queues.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+ @param[in] Packet Pointer to the buffer containing the received\r
+ datagram.\r
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this\r
+ datagram.\r
+\r
+ @return The times this datagram is enqueued.\r
+\r
+**/\r
+UINTN\r
+Udp6EnqueueDgram (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN NET_BUF *Packet,\r
+ IN EFI_UDP6_RECEIVE_DATA *RxData\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ UDP6_RXDATA_WRAP *Wrap;\r
+ UINTN Enqueued;\r
+\r
+ Enqueued = 0;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+ //\r
+ // Iterate the instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+\r
+ if (!Instance->Configured) {\r
+ continue;\r
+ }\r
+\r
+ if (Udp6MatchDgram (Instance, &RxData->UdpSession)) {\r
+ //\r
+ // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.\r
+ //\r
+ Wrap = Udp6WrapRxData (Instance, Packet, RxData);\r
+ if (Wrap == NULL) {\r
+ continue;\r
+ }\r
+\r
+ NET_GET_REF (Packet);\r
+\r
+ InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link);\r
+\r
+ Enqueued++;\r
+ }\r
+ }\r
+\r
+ return Enqueued;\r
+}\r
+\r
+\r
+/**\r
+ This function delivers the received datagrams to the specified instance.\r
+\r
+ @param[in] Instance Pointer to the instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6InstanceDeliverDgram (\r
+ IN UDP6_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ UDP6_RXDATA_WRAP *Wrap;\r
+ EFI_UDP6_COMPLETION_TOKEN *Token;\r
+ NET_BUF *Dup;\r
+ EFI_UDP6_RECEIVE_DATA *RxData;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (!IsListEmpty (&Instance->RcvdDgramQue) &&\r
+ !NetMapIsEmpty (&Instance->RxTokens)\r
+ ) {\r
+\r
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);\r
+\r
+ if (NET_BUF_SHARED (Wrap->Packet)) {\r
+ //\r
+ // Duplicate the Packet if it is shared between instances.\r
+ //\r
+ Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);\r
+ if (Dup == NULL) {\r
+ return;\r
+ }\r
+\r
+ NetbufFree (Wrap->Packet);\r
+\r
+ Wrap->Packet = Dup;\r
+ }\r
+\r
+ NetListRemoveHead (&Instance->RcvdDgramQue);\r
+\r
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);\r
+\r
+ //\r
+ // Build the FragmentTable and set the FragmentCount in RxData.\r
+ //\r
+ RxData = &Wrap->RxData;\r
+ RxData->FragmentCount = Wrap->Packet->BlockOpNum;\r
+\r
+ NetbufBuildExt (\r
+ Wrap->Packet,\r
+ (NET_FRAGMENT *) RxData->FragmentTable,\r
+ &RxData->FragmentCount\r
+ );\r
+\r
+ Token->Status = EFI_SUCCESS;\r
+ Token->Packet.RxData = &Wrap->RxData;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function delivers the datagrams enqueued in the instances.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+\r
+**/\r
+VOID\r
+Udp6DeliverDgram (\r
+ IN UDP6_SERVICE_DATA *Udp6Service\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+ //\r
+ // Iterate the instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+\r
+ if (!Instance->Configured) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Deliver the datagrams of this instance.\r
+ //\r
+ Udp6InstanceDeliverDgram (Instance);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function demultiplexes the received udp datagram to the appropriate instances.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from\r
+ the received datagram.\r
+ @param[in] Packet Pointer to the buffer containing the received udp\r
+ datagram.\r
+\r
+**/\r
+VOID\r
+Udp6Demultiplex (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN NET_BUF *Packet\r
+ )\r
+{\r
+ EFI_UDP_HEADER *Udp6Header;\r
+ UINT16 HeadSum;\r
+ EFI_UDP6_RECEIVE_DATA RxData;\r
+ EFI_UDP6_SESSION_DATA *Udp6Session;\r
+ UINTN Enqueued;\r
+\r
+ //\r
+ // Get the datagram header from the packet buffer.\r
+ //\r
+ Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+ ASSERT (Udp6Header != NULL);\r
+\r
+ if (Udp6Header->Checksum != 0) {\r
+ //\r
+ // check the checksum.\r
+ //\r
+ HeadSum = NetIp6PseudoHeadChecksum (\r
+ &NetSession->Source.v6,\r
+ &NetSession->Dest.v6,\r
+ EFI_IP_PROTO_UDP,\r
+ 0\r
+ );\r
+\r
+ if (Udp6Checksum (Packet, HeadSum) != 0) {\r
+ //\r
+ // Wrong checksum.\r
+ //\r
+ return;\r
+ }\r
+ }\r
+\r
+ gRT->GetTime (&RxData.TimeStamp, NULL);\r
+\r
+ Udp6Session = &RxData.UdpSession;\r
+ Udp6Session->SourcePort = NTOHS (Udp6Header->SrcPort);\r
+ Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort);\r
+\r
+ IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source);\r
+ IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest);\r
+\r
+ //\r
+ // Trim the UDP header.\r
+ //\r
+ NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE);\r
+\r
+ RxData.DataLength = (UINT32) Packet->TotalSize;\r
+\r
+ //\r
+ // Try to enqueue this datagram into the instances.\r
+ //\r
+ Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData);\r
+\r
+ if (Enqueued == 0) {\r
+ //\r
+ // Send the port unreachable ICMP packet before we free this NET_BUF\r
+ //\r
+ Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header);\r
+ }\r
+\r
+ //\r
+ // Try to free the packet before deliver it.\r
+ //\r
+ NetbufFree (Packet);\r
+\r
+ if (Enqueued > 0) {\r
+ //\r
+ // Deliver the datagram.\r
+ //\r
+ Udp6DeliverDgram (Udp6Service);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function builds and sends out a icmp port unreachable message.\r
+\r
+ @param[in] IpIo Pointer to the IP_IO instance.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet\r
+ causes this icmp error message.\r
+ @param[in] Udp6Header Pointer to the udp header of the datagram causes\r
+ this icmp error message.\r
+\r
+**/\r
+VOID\r
+Udp6SendPortUnreach (\r
+ IN IP_IO *IpIo,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN VOID *Udp6Header\r
+ )\r
+{\r
+ NET_BUF *Packet;\r
+ UINT32 Len;\r
+ IP6_ICMP_ERROR_HEAD *IcmpErrHdr;\r
+ UINT8 *Ptr;\r
+ IP_IO_OVERRIDE Override;\r
+ IP_IO_IP_INFO *IpSender;\r
+ EFI_IP6_MODE_DATA *Ip6ModeData;\r
+ EFI_STATUS Status;\r
+ EFI_IP6_PROTOCOL *Ip6Protocol;\r
+\r
+ Ip6ModeData = NULL;\r
+\r
+ //\r
+ // An ICMPv6 error message MUST NOT be originated as A packet destined to\r
+ // 1) an IPv6 multicast address 2) The IPv6 Unspecified Address\r
+ //\r
+ if (NetSession->IpVersion == IP_VERSION_6) {\r
+ if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) ||\r
+ IP6_IS_MULTICAST (&NetSession->Dest.v6)\r
+ ) {\r
+ goto EXIT;\r
+ }\r
+ }\r
+\r
+\r
+ IpSender = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest);\r
+\r
+ //\r
+ // Get the Ipv6 Mode Data.\r
+ //\r
+ Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA));\r
+ ASSERT (Ip6ModeData != NULL);\r
+\r
+ //\r
+ // If not finding the related IpSender use the default IpIo to send out\r
+ // the port unreachable ICMP message.\r
+ //\r
+ if (IpSender == NULL) {\r
+ Ip6Protocol = IpIo->Ip.Ip6;\r
+ } else {\r
+ Ip6Protocol = IpSender->Ip.Ip6;\r
+ }\r
+\r
+ Status = Ip6Protocol->GetModeData (\r
+ Ip6Protocol,\r
+ Ip6ModeData,\r
+ NULL,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+ //\r
+ // The ICMP6 packet length, includes whole invoking packet and ICMP6 error header.\r
+ //\r
+ Len = NetSession->IpHdrLen +\r
+ NTOHS(((EFI_UDP_HEADER *) Udp6Header)->Length) +\r
+ sizeof (IP6_ICMP_ERROR_HEAD);\r
+\r
+ //\r
+ // If the ICMP6 packet length larger than IP MTU, adjust its length to MTU.\r
+ //\r
+ if (Ip6ModeData->MaxPacketSize < Len) {\r
+ Len = Ip6ModeData->MaxPacketSize;\r
+ }\r
+\r
+ //\r
+ // Allocate buffer for the icmp error message.\r
+ //\r
+ Packet = NetbufAlloc (Len);\r
+ if (Packet == NULL) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Allocate space for the IP6_ICMP_ERROR_HEAD.\r
+ //\r
+ IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);\r
+ ASSERT (IcmpErrHdr != NULL);\r
+\r
+ //\r
+ // Set the required fields for the icmp port unreachable message.\r
+ //\r
+ IcmpErrHdr->Head.Type = ICMP_V6_DEST_UNREACHABLE;\r
+ IcmpErrHdr->Head.Code = ICMP_V6_PORT_UNREACHABLE;\r
+ IcmpErrHdr->Head.Checksum = 0;\r
+ IcmpErrHdr->Fourth = 0;\r
+\r
+ //\r
+ // Copy as much of invoking Packet as possible without the ICMPv6 packet\r
+ // exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains\r
+ // the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD\r
+ // for pointer movement that fact should be considered.\r
+ //\r
+ Ptr = (VOID *) &IcmpErrHdr->Head;\r
+ Ptr = (UINT8 *) (UINTN) ((UINTN) Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER));\r
+ CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen);\r
+ CopyMem (\r
+ Ptr + NetSession->IpHdrLen,\r
+ Udp6Header,\r
+ Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER)\r
+ );\r
+\r
+ //\r
+ // Set the checksum as zero, and IP6 driver will calcuate it with pseudo header.\r
+ //\r
+ IcmpErrHdr->Head.Checksum = 0;\r
+\r
+ //\r
+ // Fill the override data.\r
+ //\r
+ Override.Ip6OverrideData.FlowLabel = 0;\r
+ Override.Ip6OverrideData.HopLimit = 255;\r
+ Override.Ip6OverrideData.Protocol = IP6_ICMP;\r
+\r
+ //\r
+ // Send out this icmp packet.\r
+ //\r
+ IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override);\r
+\r
+ NetbufFree (Packet);\r
+\r
+EXIT:\r
+ if (Ip6ModeData != NULL) {\r
+ FreePool (Ip6ModeData);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function handles the received Icmp Error message and de-multiplexes it to the\r
+ instance.\r
+\r
+ @param[in] Udp6Service Pointer to the udp service context data.\r
+ @param[in] IcmpError The icmp error code.\r
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted\r
+ from the received Icmp Error packet.\r
+ @param[in, out] Packet Pointer to the Icmp Error packet.\r
+\r
+**/\r
+VOID\r
+Udp6IcmpHandler (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN UINT8 IcmpError,\r
+ IN EFI_NET_SESSION_DATA *NetSession,\r
+ IN OUT NET_BUF *Packet\r
+ )\r
+{\r
+ EFI_UDP_HEADER *Udp6Header;\r
+ EFI_UDP6_SESSION_DATA Udp6Session;\r
+ LIST_ENTRY *Entry;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+\r
+ Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);\r
+ ASSERT (Udp6Header != NULL);\r
+\r
+ IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source);\r
+ IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest);\r
+\r
+ Udp6Session.SourcePort = NTOHS (Udp6Header->DstPort);\r
+ Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+ //\r
+ // Iterate all the instances.\r
+ //\r
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);\r
+\r
+ if (!Instance->Configured) {\r
+ continue;\r
+ }\r
+\r
+ if (Udp6MatchDgram (Instance, &Udp6Session)) {\r
+ //\r
+ // Translate the Icmp Error code according to the udp spec.\r
+ //\r
+ Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL);\r
+\r
+ if (IcmpError > ICMP_ERR_UNREACH_PORT) {\r
+ Instance->IcmpError = EFI_ICMP_ERROR;\r
+ }\r
+\r
+ //\r
+ // Notify the instance with the received Icmp Error.\r
+ //\r
+ Udp6ReportIcmpError (Instance);\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ NetbufFree (Packet);\r
+}\r
+\r
+\r
+/**\r
+ This function reports the received ICMP error.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6ReportIcmpError (\r
+ IN UDP6_INSTANCE_DATA *Instance\r
+ )\r
+{\r
+ EFI_UDP6_COMPLETION_TOKEN *Token;\r
+\r
+ if (NetMapIsEmpty (&Instance->RxTokens)) {\r
+ //\r
+ // There are no receive tokens to deliver the ICMP error.\r
+ //\r
+ return;\r
+ }\r
+\r
+ if (EFI_ERROR (Instance->IcmpError)) {\r
+ //\r
+ // Try to get a RxToken from the RxTokens map.\r
+ //\r
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);\r
+\r
+ if (Token != NULL) {\r
+ //\r
+ // Report the error through the Token.\r
+ //\r
+ Token->Status = Instance->IcmpError;\r
+ gBS->SignalEvent (Token->Event);\r
+\r
+ //\r
+ // Clear the IcmpError.\r
+ //\r
+ Instance->IcmpError = EFI_SUCCESS;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is a dummy ext-free function for the NET_BUF created for the output\r
+ udp datagram.\r
+\r
+ @param[in] Context Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6NetVectorExtFree (\r
+ IN VOID *Context\r
+ )\r
+{\r
+}\r
+\r
+\r
+/**\r
+ Set the Udp6 variable data.\r
+\r
+ @param[in] Udp6Service Udp6 service data.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the\r
+ variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6SetVariableData (\r
+ IN UDP6_SERVICE_DATA *Udp6Service\r
+ )\r
+{\r
+ UINT32 NumConfiguredInstance;\r
+ LIST_ENTRY *Entry;\r
+ UINTN VariableDataSize;\r
+ EFI_UDP6_VARIABLE_DATA *Udp6VariableData;\r
+ EFI_UDP6_SERVICE_POINT *Udp6ServicePoint;\r
+ UDP6_INSTANCE_DATA *Udp6Instance;\r
+ CHAR16 *NewMacString;\r
+ EFI_STATUS Status;\r
+\r
+ NumConfiguredInstance = 0;\r
+\r
+ //\r
+ // Go through the children list to count the configured children.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+ Udp6Instance = NET_LIST_USER_STRUCT_S (\r
+ Entry,\r
+ UDP6_INSTANCE_DATA,\r
+ Link,\r
+ UDP6_INSTANCE_DATA_SIGNATURE\r
+ );\r
+\r
+ if (Udp6Instance->Configured) {\r
+ NumConfiguredInstance++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Calculate the size of the Udp6VariableData. As there may be no Udp6 child,\r
+ // we should add extra buffer for the service points only if the number of configured\r
+ // children is more than 1.\r
+ //\r
+ VariableDataSize = sizeof (EFI_UDP6_VARIABLE_DATA);\r
+\r
+ if (NumConfiguredInstance > 1) {\r
+ VariableDataSize += sizeof (EFI_UDP6_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
+ }\r
+\r
+ Udp6VariableData = AllocateZeroPool (VariableDataSize);\r
+ if (Udp6VariableData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Udp6VariableData->DriverHandle = Udp6Service->ImageHandle;\r
+ Udp6VariableData->ServiceCount = NumConfiguredInstance;\r
+\r
+ Udp6ServicePoint = &Udp6VariableData->Services[0];\r
+\r
+ //\r
+ // Go through the children list to fill the configured children's address pairs.\r
+ //\r
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {\r
+ Udp6Instance = NET_LIST_USER_STRUCT_S (\r
+ Entry,\r
+ UDP6_INSTANCE_DATA,\r
+ Link,\r
+ UDP6_INSTANCE_DATA_SIGNATURE\r
+ );\r
+\r
+ if (Udp6Instance->Configured) {\r
+ Udp6ServicePoint->InstanceHandle = Udp6Instance->ChildHandle;\r
+ Udp6ServicePoint->LocalPort = Udp6Instance->ConfigData.StationPort;\r
+ Udp6ServicePoint->RemotePort = Udp6Instance->ConfigData.RemotePort;\r
+\r
+ IP6_COPY_ADDRESS (\r
+ &Udp6ServicePoint->LocalAddress,\r
+ &Udp6Instance->ConfigData.StationAddress\r
+ );\r
+ IP6_COPY_ADDRESS (\r
+ &Udp6ServicePoint->RemoteAddress,\r
+ &Udp6Instance->ConfigData.RemoteAddress\r
+ );\r
+ Udp6ServicePoint++;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get the MAC string.\r
+ //\r
+ Status = NetLibGetMacString (\r
+ Udp6Service->ControllerHandle,\r
+ Udp6Service->ImageHandle,\r
+ &NewMacString\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ if (Udp6Service->MacString != NULL) {\r
+ //\r
+ // The variable is set already, we're going to update it.\r
+ //\r
+ if (StrCmp (Udp6Service->MacString, NewMacString) != 0) {\r
+ //\r
+ // The MAC address is changed, delete the previous variable first.\r
+ //\r
+ gRT->SetVariable (\r
+ Udp6Service->MacString,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ FreePool (Udp6Service->MacString);\r
+ }\r
+\r
+ Udp6Service->MacString = NewMacString;\r
+\r
+ Status = gRT->SetVariable (\r
+ Udp6Service->MacString,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ VariableDataSize,\r
+ (VOID *) Udp6VariableData\r
+ );\r
+\r
+EXIT:\r
+\r
+ FreePool (Udp6VariableData);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param[in, out] Udp6Service Udp6 service data.\r
+\r
+**/\r
+VOID\r
+Udp6ClearVariableData (\r
+ IN OUT UDP6_SERVICE_DATA *Udp6Service\r
+ )\r
+{\r
+ ASSERT (Udp6Service->MacString != NULL);\r
+\r
+ gRT->SetVariable (\r
+ Udp6Service->MacString,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ FreePool (Udp6Service->MacString);\r
+ Udp6Service->MacString = NULL;\r
+}\r
+\r
+\r
+/**\r
+ Find the key in the netmap.\r
+\r
+ @param[in] Map The netmap to search within.\r
+ @param[in] Key The key to search.\r
+\r
+ @return The point to the item contains the Key, or NULL, if Key isn't in the map.\r
+\r
+**/\r
+NET_MAP_ITEM *\r
+Udp6MapMultiCastAddr (\r
+ IN NET_MAP *Map,\r
+ IN VOID *Key\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ NET_MAP_ITEM *Item;\r
+ EFI_IPv6_ADDRESS *Addr;\r
+\r
+ ASSERT (Map != NULL);\r
+ NET_LIST_FOR_EACH (Entry, &Map->Used) {\r
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
+ Addr = (EFI_IPv6_ADDRESS *) Item->Key;\r
+ if (EFI_IP6_EQUAL (Addr, Key)) {\r
+ return Item;\r
+ }\r
+ }\r
+ return NULL;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Udp6 driver's whole implementation and internal data structures.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _UDP6_IMPL_H_\r
+#define _UDP6_IMPL_H_\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Udp6.h>\r
+\r
+#include <Library/IpIoLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DpcLib.h>\r
+\r
+#include "Udp6Driver.h"\r
+\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2;\r
+extern EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName;\r
+extern EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding;\r
+extern EFI_UDP6_PROTOCOL mUdp6Protocol;\r
+extern UINT16 mUdp6RandomPort;\r
+\r
+//\r
+// Define time out 50 milliseconds\r
+//\r
+#define UDP6_TIMEOUT_INTERVAL (50 * TICKS_PER_MS)\r
+#define UDP6_HEADER_SIZE sizeof (EFI_UDP_HEADER)\r
+#define UDP6_MAX_DATA_SIZE 65507\r
+#define UDP6_PORT_KNOWN 1024\r
+\r
+#define UDP6_SERVICE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', '6')\r
+#define UDP6_INSTANCE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', 'S')\r
+\r
+#define UDP6_SERVICE_DATA_FROM_THIS(a) \\r
+ CR ( \\r
+ (a), \\r
+ UDP6_SERVICE_DATA, \\r
+ ServiceBinding, \\r
+ UDP6_SERVICE_DATA_SIGNATURE \\r
+ )\r
+\r
+#define UDP6_INSTANCE_DATA_FROM_THIS(a) \\r
+ CR ( \\r
+ (a), \\r
+ UDP6_INSTANCE_DATA, \\r
+ Udp6Proto, \\r
+ UDP6_INSTANCE_DATA_SIGNATURE \\r
+ )\r
+//\r
+// Udp6 service contest data\r
+//\r
+typedef struct _UDP6_SERVICE_DATA {\r
+ UINT32 Signature;\r
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;\r
+ EFI_HANDLE ImageHandle;\r
+ EFI_HANDLE ControllerHandle;\r
+ LIST_ENTRY ChildrenList;\r
+ UINTN ChildrenNumber;\r
+ IP_IO *IpIo;\r
+ EFI_EVENT TimeoutEvent;\r
+ CHAR16 *MacString;\r
+} UDP6_SERVICE_DATA;\r
+\r
+typedef struct _UDP6_INSTANCE_DATA {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+ EFI_UDP6_PROTOCOL Udp6Proto;\r
+ EFI_UDP6_CONFIG_DATA ConfigData;\r
+ EFI_HANDLE ChildHandle;\r
+ BOOLEAN Configured;\r
+ BOOLEAN IsNoMapping;\r
+ NET_MAP TxTokens;\r
+ NET_MAP RxTokens;\r
+ NET_MAP McastIps;\r
+ LIST_ENTRY RcvdDgramQue;\r
+ LIST_ENTRY DeliveredDgramQue;\r
+ UINT16 HeadSum;\r
+ EFI_STATUS IcmpError;\r
+ IP_IO_IP_INFO *IpInfo;\r
+ BOOLEAN Destroyed;\r
+} UDP6_INSTANCE_DATA;\r
+\r
+typedef struct _UDP6_RXDATA_WRAP {\r
+ LIST_ENTRY Link;\r
+ NET_BUF *Packet;\r
+ UINT32 TimeoutTick;\r
+ EFI_UDP6_RECEIVE_DATA RxData;\r
+} UDP6_RXDATA_WRAP;\r
+\r
+/**\r
+ Clean the Udp service context data.\r
+\r
+ @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6CleanService (\r
+ IN OUT UDP6_SERVICE_DATA *Udp6Service\r
+ );\r
+\r
+/**\r
+ Create the Udp service context data.\r
+\r
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.\r
+ @param[in] ImageHandle The image handle of this udp6 driver.\r
+ @param[in] ControllerHandle The controller handle this udp6 driver binds on.\r
+\r
+ @retval EFI_SUCCESS The udp6 service context data was created and\r
+ initialized.\r
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.\r
+ @retval Others An error condition occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6CreateService (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_HANDLE ControllerHandle\r
+ );\r
+\r
+/**\r
+ Set the Udp6 variable data.\r
+\r
+ @param[in] Udp6Service Udp6 service data.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the\r
+ variable.\r
+ @retval other Set variable failed.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6SetVariableData (\r
+ IN UDP6_SERVICE_DATA *Udp6Service\r
+ );\r
+\r
+/**\r
+ This function cleans the udp instance.\r
+\r
+ @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean.\r
+\r
+**/\r
+VOID\r
+Udp6CleanInstance (\r
+ IN OUT UDP6_INSTANCE_DATA *Instance\r
+ );\r
+\r
+/**\r
+ Clear the variable and free the resource.\r
+\r
+ @param[in, out] Udp6Service Udp6 service data.\r
+\r
+**/\r
+VOID\r
+Udp6ClearVariableData (\r
+ IN OUT UDP6_SERVICE_DATA *Udp6Service\r
+ );\r
+\r
+/**\r
+ This function intializes the new created udp instance.\r
+\r
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.\r
+ @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6InitInstance (\r
+ IN UDP6_SERVICE_DATA *Udp6Service,\r
+ IN OUT UDP6_INSTANCE_DATA *Instance\r
+ );\r
+\r
+/**\r
+ This function reports the received ICMP error.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6ReportIcmpError (\r
+ IN UDP6_INSTANCE_DATA *Instance\r
+ );\r
+\r
+/**\r
+ This function copies the current operational settings of this EFI UDPv6 Protocol\r
+ instance into user-supplied buffers. This function is used optionally to retrieve\r
+ the operational mode data of underlying networks or drivers.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[out] Udp6ConfigData The buffer in which the current UDP configuration\r
+ data is returned. This parameter is optional and\r
+ may be NULL.\r
+ @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol\r
+ mode data is returned. This parameter is optional\r
+ and may be NULL.\r
+ @param[out] MnpConfigData The buffer in which the current managed network\r
+ configuration data is returned. This parameter\r
+ is optional and may be NULL.\r
+ @param[out] SnpModeData The buffer in which the simple network mode data\r
+ is returned. This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration\r
+ data is available because this instance has not\r
+ been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6GetModeData (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL,\r
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function is used to do the following:\r
+ Initialize and start this instance of the EFI UDPv6 Protocol.\r
+ Change the filtering rules and operational parameters.\r
+ Reset this instance of the EFI UDPv6 Protocol.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] UdpConfigData Pointer to the buffer to set the configuration\r
+ data. This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The configuration settings were set, changed, or\r
+ reset successfully.\r
+ @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set\r
+ to true and there is no address available for IP6\r
+ driver to binding source address to this\r
+ instance.\r
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:\r
+ This is NULL.\r
+ UdpConfigData.StationAddress is not a valid\r
+ unicast IPv6 address.\r
+ UdpConfigData.RemoteAddress is not a valid unicast\r
+ IPv6 address, if it is not zero.\r
+ @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already\r
+ started/configured and must be stopped/reset\r
+ before it can be reconfigured. Only TrafficClass,\r
+ HopLimit, ReceiveTimeout, and TransmitTimeout can\r
+ be reconfigured without stopping the current\r
+ instance of the EFI UDPv6 Protocol.\r
+ @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE, and\r
+ UdpConfigData.StationPort is already used by another\r
+ instance.\r
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate\r
+ memory for this EFI UDPv6 Protocol instance.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and\r
+ this instance was not opened.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Configure (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function places a sending request to this instance of the EFI UDPv6 Protocol,\r
+ alongside the transmit data that was filled by the user.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the completion token that will be\r
+ placed into the transmit queue.\r
+\r
+ @retval EFI_SUCCESS The data has been queued for transmission.\r
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been\r
+ started.\r
+ @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for\r
+ choosing a source address for this instance, but\r
+ no source address was available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ This is NULL. Token is NULL. Token.Event is NULL.\r
+ Token.Packet.TxData is NULL.\r
+ Token.Packet.TxData.FragmentCount is zero.\r
+ Token.Packet.TxData.DataLength is not equal to the\r
+ sum of fragment lengths.\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[]\r
+ .FragmentLength fields is zero.\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[]\r
+ .FragmentBuffer fields is NULL.\r
+ One or more of the\r
+ Token.Packet.TxData.UdpSessionData.\r
+ DestinationAddres are not valid unicast IPv6\r
+ addresses, if the UdpSessionData is not NULL.\r
+ Token.Packet.TxData.UdpSessionData.\r
+ DestinationAddres is NULL\r
+ Token.Packet.TxData.UdpSessionData.\r
+ DestinatioPort is zero.\r
+ Token.Packet.TxData.UdpSessionData is\r
+ NULL and this instance's\r
+ UdpConfigData.RemoteAddress is unspecified.\r
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same\r
+ Token.Event is already in the transmit queue.\r
+ @retval EFI_NOT_READY The completion token could not be queued because\r
+ the transmit queue is full.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.\r
+ @retval EFI_NOT_FOUND There is no route to the destination network or\r
+ address.\r
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP\r
+ packet size. Or the length of the IP header + UDP\r
+ header + data length is greater than MTU if\r
+ DoNotFragment is TRUE.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Transmit (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ This function places a completion token into the receive packet queue. This function\r
+ is always asynchronous.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that is associated with the\r
+ receive data descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token is cached.\r
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been\r
+ started.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,\r
+ BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL.\r
+ Token is NULL.\r
+ Token.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued\r
+ due to a lack of system resources (usually\r
+ memory).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ The EFI UDPv6 Protocol instance has been reset to\r
+ startup defaults.\r
+ @retval EFI_ACCESS_DENIED A receive completion token with the same\r
+ Token.Event is already in the receive queue.\r
+ @retval EFI_NOT_READY The receive request could not be queued because\r
+ the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Receive (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ This function is used to abort a pending transmit or receive request.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that has been issued by\r
+ EFI_UDP6_PROTOCOL.Transmit() or\r
+ EFI_UDP6_PROTOCOL.Receive(). This parameter is\r
+ optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and\r
+ Token.Event is signaled. When Token is NULL, all\r
+ pending requests are aborted and their events are\r
+ signaled.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_NO_MAPPING When using the default address, configuration\r
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O\r
+ request is not found in the transmit or receive\r
+ queue. It either completed or was not issued by\r
+ Transmit() or Receive().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Cancel (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function can be used by network drivers and applications to increase the rate that\r
+ data packets are moved between the communications device and the transmit/receive queues.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or\r
+ receive queue.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Poll (\r
+ IN EFI_UDP6_PROTOCOL *This\r
+ );\r
+\r
+/**\r
+ This function is used to enable and disable the multicast group filtering.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] JoinFlag Set to TRUE to join a multicast group. Set to\r
+ FALSE to leave one or all multicast groups.\r
+ @param[in] MulticastAddress Pointer to multicast group address to join or\r
+ leave. This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been\r
+ started.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL. JoinFlag is TRUE and\r
+ MulticastAddress is NULL. JoinFlag is TRUE and\r
+ *MulticastAddress is not a valid multicast\r
+ address.\r
+ @retval EFI_ALREADY_STARTED The group address is already in the group table\r
+ (when JoinFlag is TRUE).\r
+ @retval EFI_NOT_FOUND The group address is not in the group table (when\r
+ JoinFlag is FALSE).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Groups (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN BOOLEAN JoinFlag,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function tries to bind the udp instance according to the configured port\r
+ allocation stragety.\r
+\r
+ @param[in] InstanceList Pointer to the head of the list linking the udp\r
+ instances.\r
+ @param[in] ConfigData Pointer to the ConfigData of the instance to be\r
+ bound.\r
+\r
+ @retval EFI_SUCCESS The bound operation completed successfully.\r
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is\r
+ already used by another instance.\r
+ @retval EFI_OUT_OF_RESOURCES No available port resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6Bind (\r
+ IN LIST_ENTRY *InstanceList,\r
+ IN EFI_UDP6_CONFIG_DATA *ConfigData\r
+ );\r
+\r
+/**\r
+ This function builds the Ip6 configdata from the Udp6ConfigData.\r
+\r
+ @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA.\r
+ @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA.\r
+\r
+**/\r
+VOID\r
+Udp6BuildIp6ConfigData (\r
+ IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData,\r
+ IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData\r
+ );\r
+\r
+/**\r
+ This function checks whether the specified Token duplicates with the one in the Map.\r
+\r
+ @param[in] Map Pointer to the NET_MAP.\r
+ @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to\r
+ the Token.\r
+ @param[in] Context Pointer to the Token to be checked.\r
+\r
+ @retval EFI_SUCCESS The Token specified by Context differs from the\r
+ one in the Item.\r
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6TokenExist (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ This function removes the specified Token from the TokenMap.\r
+\r
+ @param[in] TokenMap Pointer to the NET_MAP containing the tokens.\r
+ @param[in] Token Pointer to the Token to be removed.\r
+\r
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.\r
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6RemoveToken (\r
+ IN NET_MAP *TokenMap,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token\r
+ );\r
+\r
+/**\r
+ This function is used to check whether the NewConfigData has any un-reconfigurable\r
+ parameters changed compared to the OldConfigData.\r
+\r
+ @param[in] OldConfigData Pointer to the current ConfigData the udp instance\r
+ uses.\r
+ @param[in] NewConfigData Pointer to the new ConfigData.\r
+\r
+ @retval TRUE The instance is reconfigurable according to NewConfigData.\r
+ @retval FALSE The instance is not reconfigurable according to NewConfigData.\r
+\r
+**/\r
+BOOLEAN\r
+Udp6IsReconfigurable (\r
+ IN EFI_UDP6_CONFIG_DATA *OldConfigData,\r
+ IN EFI_UDP6_CONFIG_DATA *NewConfigData\r
+ );\r
+\r
+/**\r
+ This function removes the multicast group specified by Arg from the Map.\r
+\r
+ @param[in] Map Pointer to the NET_MAP.\r
+ @param[in] Item Pointer to the NET_MAP_ITEM.\r
+ @param[in] Arg Pointer to the Arg. It is the pointer to a\r
+ multicast IPv6 Address. This parameter is\r
+ optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The multicast address is removed.\r
+ @retval EFI_ABORTED The specified multicast address is removed, and the\r
+ Arg is not NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6LeaveGroup (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item,\r
+ IN VOID *Arg OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function validates the TxToken, it returns the error code according to the spec.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+ @param[in] TxToken Pointer to the token to be checked.\r
+\r
+ @retval EFI_SUCCESS The TxToken is valid.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ Token.Event is NULL.\r
+ Token.Packet.TxData is NULL.\r
+ Token.Packet.TxData.FragmentCount is zero.\r
+ Token.Packet.TxData.DataLength is not equal to the\r
+ sum of fragment lengths.\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[].FragmentLength\r
+ fields is zero.\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[].FragmentBuffer\r
+ fields is NULL.\r
+ UdpSessionData.DestinationAddress are not valid\r
+ unicast IPv6 addresses if the UdpSessionData is\r
+ not NULL.\r
+ UdpSessionData.DestinationPort and\r
+ ConfigData.RemotePort are all zero if the\r
+ UdpSessionData is not NULL.\r
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP\r
+ packet size.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6ValidateTxToken (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *TxToken\r
+ );\r
+\r
+/**\r
+ This function is a dummy ext-free function for the NET_BUF created for the output\r
+ udp datagram.\r
+\r
+ @param[in] Context Pointer to the context data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Udp6NetVectorExtFree (\r
+ IN VOID *Context\r
+ );\r
+\r
+/**\r
+ This function calculates the checksum for the Packet, utilizing the pre-calculated\r
+ pseudo header to reduce overhead.\r
+\r
+ @param[in] Packet Pointer to the NET_BUF contains the udp datagram.\r
+ @param[in] HeadSum Checksum of the pseudo header execpt the length\r
+ field.\r
+\r
+ @return The 16-bit checksum of this udp datagram.\r
+\r
+**/\r
+UINT16\r
+Udp6Checksum (\r
+ IN NET_BUF *Packet,\r
+ IN UINT16 HeadSum\r
+ );\r
+\r
+/**\r
+ This function delivers the received datagrams to the specified instance.\r
+\r
+ @param[in] Instance Pointer to the instance context data.\r
+\r
+**/\r
+VOID\r
+Udp6InstanceDeliverDgram (\r
+ IN UDP6_INSTANCE_DATA *Instance\r
+ );\r
+\r
+/**\r
+ Cancel Udp6 tokens from the Udp6 instance.\r
+\r
+ @param[in] Instance Pointer to the udp instance context data.\r
+ @param[in] Token Pointer to the token to be canceled. If NULL, all\r
+ tokens in this instance will be cancelled.\r
+ This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The Token is cancelled.\r
+ @retval EFI_NOT_FOUND The Token is not found.\r
+\r
+**/\r
+EFI_STATUS\r
+Udp6InstanceCancelToken (\r
+ IN UDP6_INSTANCE_DATA *Instance,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ );\r
+\r
+/**\r
+ This function removes all the Wrap datas in the RcvdDgramQue.\r
+\r
+ @param[in] Instance Pointer to the Udp6 Instance.\r
+\r
+**/\r
+VOID\r
+Udp6FlushRcvdDgram (\r
+ IN UDP6_INSTANCE_DATA *Instance\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ Contains all EFI_UDP6_PROTOCOL interfaces.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Udp6Impl.h"\r
+\r
+EFI_UDP6_PROTOCOL mUdp6Protocol = {\r
+ Udp6GetModeData,\r
+ Udp6Configure,\r
+ Udp6Groups,\r
+ Udp6Transmit,\r
+ Udp6Receive,\r
+ Udp6Cancel,\r
+ Udp6Poll\r
+};\r
+\r
+\r
+/**\r
+ This function copies the current operational settings of this EFI UDPv6 Protocol\r
+ instance into user-supplied buffers. This function is used optionally to retrieve\r
+ the operational mode data of underlying networks or drivers.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[out] Udp6ConfigData The buffer in which the current UDP configuration\r
+ data is returned. This parameter is optional and\r
+ may be NULL.\r
+ @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol\r
+ mode data is returned. This parameter is optional\r
+ and may be NULL.\r
+ @param[out] MnpConfigData The buffer in which the current managed network\r
+ configuration data is returned. This parameter is\r
+ optional and may be NULL.\r
+ @param[out] SnpModeData The buffer in which the simple network mode data\r
+ is returned. This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The mode data was read.\r
+ @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration\r
+ data is available because this instance has not\r
+ been started.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6GetModeData (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL,\r
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,\r
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,\r
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL\r
+ )\r
+{\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_IP6_PROTOCOL *Ip;\r
+ EFI_TPL OldTpl;\r
+ EFI_STATUS Status;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+ if (!Instance->Configured && (Udp6ConfigData != NULL)) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (Udp6ConfigData != NULL) {\r
+ //\r
+ // Set the Udp6ConfigData.\r
+ //\r
+ CopyMem (Udp6ConfigData, &Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA));\r
+ }\r
+\r
+ Ip = Instance->IpInfo->Ip.Ip6;\r
+\r
+ //\r
+ // Get the underlying Ip6ModeData, MnpConfigData and SnpModeData.\r
+ //\r
+ Status = Ip->GetModeData (Ip, Ip6ModeData, MnpConfigData, SnpModeData);\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is used to do the following:\r
+ Initialize and start this instance of the EFI UDPv6 Protocol.\r
+ Change the filtering rules and operational parameters.\r
+ Reset this instance of the EFI UDPv6 Protocol.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] UdpConfigData Pointer to the buffer to set the configuration\r
+ data. This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The configuration settings were set, changed, or\r
+ reset successfully.\r
+ @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set\r
+ to true and there is no address available for the IP6\r
+ driver to bind a source address to this instance.\r
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:\r
+ This is NULL.\r
+ UdpConfigData.StationAddress is not a valid\r
+ unicast IPv6 address.\r
+ UdpConfigData.RemoteAddress is not a valid unicast\r
+ IPv6 address if it is not zero.\r
+ @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already\r
+ started/configured and must be stopped/reset\r
+ before it can be reconfigured. Only TrafficClass,\r
+ HopLimit, ReceiveTimeout, and TransmitTimeout can\r
+ be reconfigured without stopping the current\r
+ instance of the EFI UDPv6 Protocol.\r
+ @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE and\r
+ UdpConfigData.StationPort is already used by another\r
+ instance.\r
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate\r
+ memory for this EFI UDPv6 Protocol instance.\r
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and\r
+ this instance was not opened.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Configure (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+ EFI_TPL OldTpl;\r
+ EFI_IPv6_ADDRESS StationAddress;\r
+ EFI_IPv6_ADDRESS RemoteAddress;\r
+ EFI_IP6_CONFIG_DATA Ip6ConfigData;\r
+ EFI_IPv6_ADDRESS LocalAddr;\r
+ EFI_IPv6_ADDRESS RemoteAddr;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+ if (!Instance->Configured && (UdpConfigData == NULL)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Udp6Service = Instance->Udp6Service;\r
+ Status = EFI_SUCCESS;\r
+ ASSERT (Udp6Service != NULL);\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (UdpConfigData != NULL) {\r
+\r
+ IP6_COPY_ADDRESS (&StationAddress, &UdpConfigData->StationAddress);\r
+ IP6_COPY_ADDRESS (&RemoteAddress, &UdpConfigData->RemoteAddress);\r
+\r
+ if ((!NetIp6IsUnspecifiedAddr (&StationAddress) && !NetIp6IsValidUnicast (&StationAddress)) ||\r
+ (!NetIp6IsUnspecifiedAddr (&RemoteAddress) && !NetIp6IsValidUnicast (&RemoteAddress))\r
+ ){\r
+ //\r
+ // If not use default address, and StationAddress is not a valid unicast\r
+ // if it is not IPv6 address or RemoteAddress is not a valid unicast IPv6\r
+ // address if it is not 0.\r
+ //\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (Instance->Configured) {\r
+ //\r
+ // The instance is already configured, try to do the re-configuration.\r
+ //\r
+ if (!Udp6IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {\r
+ //\r
+ // If the new configuration data wants to change some unreconfigurable\r
+ // settings, return EFI_ALREADY_STARTED.\r
+ //\r
+ Status = EFI_ALREADY_STARTED;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Save the reconfigurable parameters.\r
+ //\r
+ Instance->ConfigData.TrafficClass = UdpConfigData->TrafficClass;\r
+ Instance->ConfigData.HopLimit = UdpConfigData->HopLimit;\r
+ Instance->ConfigData.ReceiveTimeout = UdpConfigData->ReceiveTimeout;\r
+ Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;\r
+ } else {\r
+ //\r
+ // Construct the Ip configuration data from the UdpConfigData.\r
+ //\r
+ Udp6BuildIp6ConfigData (UdpConfigData, &Ip6ConfigData);\r
+\r
+ //\r
+ // Configure the Ip instance wrapped in the IpInfo.\r
+ //\r
+ Status = IpIoConfigIp (Instance->IpInfo, &Ip6ConfigData);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_NO_MAPPING) {\r
+ Instance->IsNoMapping = TRUE;\r
+ }\r
+\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Instance->IsNoMapping = FALSE;\r
+\r
+ //\r
+ // Save the configuration data.\r
+ //\r
+ CopyMem (\r
+ &Instance->ConfigData,\r
+ UdpConfigData,\r
+ sizeof (EFI_UDP6_CONFIG_DATA)\r
+ );\r
+ IP6_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip6ConfigData.StationAddress);\r
+ //\r
+ // Try to allocate the required port resource.\r
+ //\r
+ Status = Udp6Bind (&Udp6Service->ChildrenList, &Instance->ConfigData);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Reset the ip instance if bind fails.\r
+ //\r
+ IpIoConfigIp (Instance->IpInfo, NULL);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Pre calculate the checksum for the pseudo head, ignore the UDP length first.\r
+ //\r
+ IP6_COPY_ADDRESS (&LocalAddr, &Instance->ConfigData.StationAddress);\r
+ IP6_COPY_ADDRESS (&RemoteAddr, &Instance->ConfigData.RemoteAddress);\r
+\r
+ Instance->HeadSum = NetIp6PseudoHeadChecksum (\r
+ &LocalAddr,\r
+ &RemoteAddr,\r
+ EFI_IP_PROTO_UDP,\r
+ 0\r
+ );\r
+\r
+ Instance->Configured = TRUE;\r
+ }\r
+ } else {\r
+ //\r
+ // UdpConfigData is NULL, reset the instance.\r
+ //\r
+ Instance->Configured = FALSE;\r
+ Instance->IsNoMapping = FALSE;\r
+\r
+ //\r
+ // Reset the Ip instance wrapped in the IpInfo.\r
+ //\r
+ IpIoConfigIp (Instance->IpInfo, NULL);\r
+\r
+ //\r
+ // Cancel all the user tokens.\r
+ //\r
+ Status = Instance->Udp6Proto.Cancel (&Instance->Udp6Proto, NULL);\r
+\r
+ //\r
+ // Remove the buffered RxData for this instance.\r
+ //\r
+ Udp6FlushRcvdDgram (Instance);\r
+\r
+ ASSERT (IsListEmpty (&Instance->DeliveredDgramQue));\r
+ }\r
+\r
+ Status = Udp6SetVariableData (Instance->Udp6Service);\r
+\r
+ON_EXIT:\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is used to enable and disable the multicast group filtering.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] JoinFlag Set to TRUE to join a multicast group. Set to\r
+ FALSE to leave one or all multicast groups.\r
+ @param[in] MulticastAddress Pointer to multicast group address to join or\r
+ leave. This parameter is optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been\r
+ started.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL.\r
+ JoinFlag is TRUE and MulticastAddress is NULL.\r
+ JoinFlag is TRUE and *MulticastAddress is not a\r
+ valid multicast address.\r
+ @retval EFI_ALREADY_STARTED The group address is already in the group table\r
+ (when JoinFlag is TRUE).\r
+ @retval EFI_NOT_FOUND The group address is not in the group table (when\r
+ JoinFlag is FALSE).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Groups (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN BOOLEAN JoinFlag,\r
+ IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_IP6_PROTOCOL *Ip;\r
+ EFI_TPL OldTpl;\r
+ EFI_IPv6_ADDRESS *McastIp;\r
+\r
+ if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ McastIp = NULL;\r
+\r
+ if (JoinFlag) {\r
+ if (!IP6_IS_MULTICAST (MulticastAddress)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ McastIp = AllocateCopyPool (sizeof (EFI_IPv6_ADDRESS), MulticastAddress);\r
+ if (McastIp == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+ if (!Instance->Configured) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ Ip = Instance->IpInfo->Ip.Ip6;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Invoke the Ip instance the Udp6 instance consumes to do the group operation.\r
+ //\r
+ Status = Ip->Groups (Ip, JoinFlag, MulticastAddress);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Keep a local copy of the configured multicast IPs because IpIo receives\r
+ // datagrams from the 0 station address IP instance and then UDP delivers to\r
+ // the matched instance. This copy of multicast IPs is used to avoid receive\r
+ // the mutlicast datagrams destinated to multicast IPs the other instances configured.\r
+ //\r
+ if (JoinFlag) {\r
+\r
+ Status = NetMapInsertTail (&Instance->McastIps, (VOID *) McastIp, NULL);\r
+ } else {\r
+\r
+ NetMapIterate (&Instance->McastIps, Udp6LeaveGroup, MulticastAddress);\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ if (McastIp != NULL) {\r
+ FreePool (McastIp);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+ This function places a sending request to this instance of the EFI UDPv6 Protocol,\r
+ alongside the transmit data that was filled by the user.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to the completion token that will be\r
+ placed into the transmit queue.\r
+\r
+ @retval EFI_SUCCESS The data was queued for transmission.\r
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been\r
+ started.\r
+ @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for\r
+ choosing a source address for this instance, but\r
+ no source address was available for use.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:\r
+ This is NULL.\r
+ Token is NULL. Token.Event is NULL.\r
+ Token.Packet.TxData is NULL.\r
+ Token.Packet.TxData.FragmentCount is zero.\r
+ Token.Packet.TxData.DataLength is not equal to the\r
+ sum of fragment lengths.\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[].FragmentLength\r
+ fields is zero.\r
+ One or more of the\r
+ Token.Packet.TxData.FragmentTable[].FragmentBuffer\r
+ fields is NULL. One or more of the\r
+ Token.Packet.TxData.UdpSessionData.DestinationAddres\r
+ are not valid unicast IPv6\r
+ addresses if the UdpSessionData is not NULL.\r
+ Token.Packet.TxData.UdpSessionData.\r
+ DestinationAddress is NULL\r
+ Token.Packet.TxData.UdpSessionData.\r
+ DestinatioPort\r
+ is zero.\r
+ Token.Packet.TxData.UdpSessionData is NULL and this\r
+ instance's UdpConfigData.RemoteAddress is unspecified.\r
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same\r
+ Token.Event is already in the transmit queue.\r
+ @retval EFI_NOT_READY The completion token could not be queued because\r
+ the transmit queue is full.\r
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.\r
+ @retval EFI_NOT_FOUND There is no route to the destination network or\r
+ address.\r
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP\r
+ packet size. Or, the length of the IP header + UDP\r
+ header + data length is greater than MTU if\r
+ DoNotFragment is TRUE.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Transmit (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_TPL OldTpl;\r
+ NET_BUF *Packet;\r
+ EFI_UDP_HEADER *Udp6Header;\r
+ EFI_UDP6_CONFIG_DATA *ConfigData;\r
+ EFI_IPv6_ADDRESS Source;\r
+ EFI_IPv6_ADDRESS Destination;\r
+ EFI_UDP6_TRANSMIT_DATA *TxData;\r
+ EFI_UDP6_SESSION_DATA *UdpSessionData;\r
+ UDP6_SERVICE_DATA *Udp6Service;\r
+ IP_IO_OVERRIDE Override;\r
+ UINT16 HeadSum;\r
+ EFI_IP_ADDRESS IpDestAddr;\r
+\r
+ if ((This == NULL) || (Token == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+ if (!Instance->Configured) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Validate the Token, if the token is invalid return the error code.\r
+ //\r
+ Status = Udp6ValidateTxToken (Instance, Token);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) ||\r
+ EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token))\r
+ ){\r
+ //\r
+ // Try to find a duplicate token in the two token maps, if found, return\r
+ // EFI_ACCESS_DENIED.\r
+ //\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ TxData = Token->Packet.TxData;\r
+\r
+ //\r
+ // Create a net buffer to hold the user buffer and the udp header.\r
+ //\r
+ Packet = NetbufFromExt (\r
+ (NET_FRAGMENT *) TxData->FragmentTable,\r
+ TxData->FragmentCount,\r
+ UDP6_HEADER_SIZE,\r
+ 0,\r
+ Udp6NetVectorExtFree,\r
+ NULL\r
+ );\r
+ if (Packet == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Store the IpIo in ProtoData.\r
+ //\r
+ Udp6Service = Instance->Udp6Service;\r
+ *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp6Service->IpIo);\r
+\r
+ Udp6Header = (EFI_UDP_HEADER *) NetbufAllocSpace (Packet, UDP6_HEADER_SIZE, TRUE);\r
+ ASSERT (Udp6Header != NULL);\r
+ ConfigData = &Instance->ConfigData;\r
+\r
+ //\r
+ // Fill the udp header.\r
+ //\r
+ Udp6Header->SrcPort = HTONS (ConfigData->StationPort);\r
+ Udp6Header->DstPort = HTONS (ConfigData->RemotePort);\r
+ Udp6Header->Length = HTONS ((UINT16) Packet->TotalSize);\r
+ Udp6Header->Checksum = 0;\r
+ //\r
+ // Set the UDP Header in NET_BUF, this UDP header is for IP6 can fast get the\r
+ // Udp header for pseudoHeadCheckSum.\r
+ //\r
+ Packet->Udp = Udp6Header;\r
+ UdpSessionData = TxData->UdpSessionData;\r
+\r
+ if (UdpSessionData != NULL) {\r
+ //\r
+ // Set the Destination according to the specified\r
+ // UdpSessionData.\r
+ //\r
+\r
+ if (UdpSessionData->DestinationPort != 0) {\r
+ Udp6Header->DstPort = HTONS (UdpSessionData->DestinationPort);\r
+ }\r
+\r
+ IP6_COPY_ADDRESS (&Source, &ConfigData->StationAddress);\r
+ if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress)) {\r
+ IP6_COPY_ADDRESS (&Destination, &UdpSessionData->DestinationAddress);\r
+ } else {\r
+ IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);\r
+ }\r
+\r
+ //\r
+ //Calculate the pseudo head checksum using the overridden parameters.\r
+ //\r
+ if (!NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress)) {\r
+ HeadSum = NetIp6PseudoHeadChecksum (\r
+ &Source,\r
+ &Destination,\r
+ EFI_IP_PROTO_UDP,\r
+ 0\r
+ );\r
+\r
+ //\r
+ // calculate the checksum.\r
+ //\r
+ Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);\r
+ if (Udp6Header->Checksum == 0) {\r
+ //\r
+ // If the calculated checksum is 0, fill the Checksum field with all ones.\r
+ //\r
+ Udp6Header->Checksum = 0XFFFF;\r
+ }\r
+ } else {\r
+ //\r
+ // Set the checksum is zero if the ConfigData->StationAddress is unspcified\r
+ // and the Ipv6 will fill the correct value of this checksum.\r
+ //\r
+ Udp6Header->Checksum = 0;\r
+\r
+ }\r
+ } else {\r
+ //\r
+ // UdpSessionData is NULL, use the address and port information previously configured.\r
+ //\r
+ IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);\r
+\r
+ HeadSum = Instance->HeadSum;\r
+ //\r
+ // calculate the checksum.\r
+ //\r
+ Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);\r
+ if (Udp6Header->Checksum == 0) {\r
+ //\r
+ // If the calculated checksum is 0, fill the Checksum field with all ones.\r
+ //\r
+ Udp6Header->Checksum = 0xffff;\r
+ }\r
+ }\r
+\r
+\r
+\r
+ //\r
+ // Fill the IpIo Override data.\r
+ //\r
+ Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_UDP;\r
+ Override.Ip6OverrideData.HopLimit = ConfigData->HopLimit;\r
+ Override.Ip6OverrideData.FlowLabel = 0;\r
+\r
+ //\r
+ // Save the token into the TxToken map.\r
+ //\r
+ Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FREE_PACKET;\r
+ }\r
+\r
+ //\r
+ // Send out this datagram through IpIo.\r
+ //\r
+ if (UdpSessionData != NULL){\r
+ IP6_COPY_ADDRESS (&(IpDestAddr.v6), &Destination);\r
+ } else {\r
+ ZeroMem (&IpDestAddr.v6, sizeof (EFI_IPv6_ADDRESS));\r
+ }\r
+\r
+ Status = IpIoSend (\r
+ Udp6Service->IpIo,\r
+ Packet,\r
+ Instance->IpInfo,\r
+ Instance,\r
+ Token,\r
+ &IpDestAddr,\r
+ &Override\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Remove this token from the TxTokens.\r
+ //\r
+ Udp6RemoveToken (&Instance->TxTokens, Token);\r
+ }\r
+\r
+FREE_PACKET:\r
+\r
+ NetbufFree (Packet);\r
+\r
+ON_EXIT:\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function places a completion token into the receive packet queue. This function\r
+ is always asynchronous.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that is associated with the\r
+ receive data descriptor.\r
+\r
+ @retval EFI_SUCCESS The receive completion token was cached.\r
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been\r
+ started.\r
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,\r
+ BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
+ This is NULL. Token is NULL. Token.Event is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued\r
+ due to a lack of system resources (usually\r
+ memory).\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ The EFI UDPv6 Protocol instance has been reset to\r
+ startup defaults.\r
+ @retval EFI_ACCESS_DENIED A receive completion token with the same\r
+ Token.Event is already in the receive queue.\r
+ @retval EFI_NOT_READY The receive request could not be queued because\r
+ the receive queue is full.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Receive (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_TPL OldTpl;\r
+\r
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+ if (!Instance->Configured) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) ||\r
+ EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token))\r
+ ){\r
+ //\r
+ // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or\r
+ // RxTokens map.\r
+ //\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Token->Packet.RxData = NULL;\r
+\r
+ //\r
+ // Save the token into the RxTokens map.\r
+ //\r
+ Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_NOT_READY;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // If there is an icmp error, report it.\r
+ //\r
+ Udp6ReportIcmpError (Instance);\r
+\r
+ //\r
+ // Try to delivered the received datagrams.\r
+ //\r
+ Udp6InstanceDeliverDgram (Instance);\r
+\r
+ //\r
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.\r
+ //\r
+ DispatchDpc ();\r
+\r
+ON_EXIT:\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is used to abort a pending transmit or receive request.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+ @param[in] Token Pointer to a token that has been issued by\r
+ EFI_UDP6_PROTOCOL.Transmit() or\r
+ EFI_UDP6_PROTOCOL.Receive(). This parameter is\r
+ optional and may be NULL.\r
+\r
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted, and\r
+ Token.Event was signaled. When Token is NULL, all\r
+ pending requests are aborted and their events are\r
+ signaled.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_NOT_STARTED This instance has not been started.\r
+ @retval EFI_NO_MAPPING When using the default address, configuration\r
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.\r
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O\r
+ request is not found in the transmit or receive\r
+ queue. It is either completed or not issued by\r
+ Transmit() or Receive().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Cancel (\r
+ IN EFI_UDP6_PROTOCOL *This,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_TPL OldTpl;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+\r
+ if (!Instance->Configured) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ //\r
+ // Cancle the tokens specified by Token for this instance.\r
+ //\r
+ Status = Udp6InstanceCancelToken (Instance, Token);\r
+\r
+ //\r
+ // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.\r
+ //\r
+ DispatchDpc ();\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function can be used by network drivers and applications to increase the rate that\r
+ data packets are moved between the communications device and the transmit/receive queues.\r
+\r
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.\r
+ @retval EFI_INVALID_PARAMETER This is NULL.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or\r
+ receive queue.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Udp6Poll (\r
+ IN EFI_UDP6_PROTOCOL *This\r
+ )\r
+{\r
+ UDP6_INSTANCE_DATA *Instance;\r
+ EFI_IP6_PROTOCOL *Ip;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);\r
+ Ip = Instance->IpInfo->Ip.Ip6;\r
+\r
+ //\r
+ // Invode the Ip instance consumed by the udp instance to do the poll operation.\r
+ //\r
+ return Ip->Poll (Ip);\r
+}\r
--- /dev/null
+/** @file\r
+ UEFI Component Name(2) protocol implementation for UefiPxeBc driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r
+\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = {\r
+ PxeBcComponentNameGetDriverName,\r
+ PxeBcComponentNameGetControllerName,\r
+ "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName,\r
+ "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = {\r
+ {\r
+ "eng;en",\r
+ L"UEFI PXE Base Code Driver"\r
+ },\r
+ {\r
+ NULL,\r
+ NULL\r
+ }\r
+};\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the driver.\r
+\r
+ This function retrieves the user-readable name of a driver in the form of a\r
+ Unicode string. If the driver specified by This has a user-readable name in\r
+ the language specified by Language, then a pointer to the driver name is\r
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+ by This does not support the language specified by Language,\r
+ then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified\r
+ in RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] DriverName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ driver specified by This in the language\r
+ specified by Language.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by\r
+ This and the language specified by Language was\r
+ returned in DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2(\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mPxeBcDriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN)(This == &gPxeBcComponentName)\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ Retrieves a Unicode string that is the user-readable name of the controller\r
+ that is being managed by a driver.\r
+\r
+ This function retrieves the user-readable name of the controller specified by\r
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+ driver specified by This has a user-readable name in the language specified by\r
+ Language, then a pointer to the controller name is returned in ControllerName,\r
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently\r
+ managing the controller specified by ControllerHandle and ChildHandle,\r
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not\r
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+ EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+ @param[in] ControllerHandle The handle of a controller that the driver\r
+ specified by This is managing. This handle\r
+ specifies the controller whose name is to be\r
+ returned.\r
+\r
+ @param[in] ChildHandle The handle of the child controller to retrieve\r
+ the name of. This is an optional parameter that\r
+ may be NULL. It will be NULL for device\r
+ drivers. It will also be NULL for a bus drivers\r
+ that wish to retrieve the name of the bus\r
+ controller. It will not be NULL for a bus\r
+ driver that wishes to retrieve the name of a\r
+ child controller.\r
+\r
+ @param[in] Language A pointer to a Null-terminated ASCII string\r
+ array indicating the language. This is the\r
+ language of the driver name that the caller is\r
+ requesting, and it must match one of the\r
+ languages specified in SupportedLanguages. The\r
+ number of languages supported by a driver is up\r
+ to the driver writer. Language is specified in\r
+ RFC 4646 or ISO 639-2 language code format.\r
+\r
+ @param[out] ControllerName A pointer to the Unicode string to return.\r
+ This Unicode string is the name of the\r
+ controller specified by ControllerHandle and\r
+ ChildHandle in the language specified by\r
+ Language from the point of view of the driver\r
+ specified by This.\r
+\r
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in\r
+ the language specified by Language for the\r
+ driver specified by This was returned in\r
+ DriverName.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid\r
+ EFI_HANDLE.\r
+\r
+ @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently\r
+ managing the controller specified by\r
+ ControllerHandle and ChildHandle.\r
+\r
+ @retval EFI_UNSUPPORTED The driver specified by This does not support\r
+ the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcComponentNameGetControllerName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_HANDLE ChildHandle OPTIONAL,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Boot functions implementation for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+/**\r
+ Display the string of the boot item.\r
+\r
+ If the length of the boot item string beyond 70 Char, just display 70 Char.\r
+\r
+ @param[in] Str The pointer to the string.\r
+ @param[in] Len The length of the string.\r
+\r
+**/\r
+VOID\r
+PxeBcDisplayBootItem (\r
+ IN UINT8 *Str,\r
+ IN UINT8 Len\r
+ )\r
+{\r
+ UINT8 Tmp;\r
+\r
+ //\r
+ // Cut off the chars behind 70th.\r
+ //\r
+ Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);\r
+ Tmp = Str[Len];\r
+ Str[Len] = 0;\r
+ AsciiPrint ("%a \n", Str);\r
+\r
+ //\r
+ // Restore the original 70th char.\r
+ //\r
+ Str[Len] = Tmp;\r
+}\r
+\r
+\r
+/**\r
+ Select and maintain the boot prompt if needed.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+\r
+ @retval EFI_SUCCESS Selected boot prompt done.\r
+ @retval EFI_TIMEOUT Selected boot prompt timed out.\r
+ @retval EFI_NOT_FOUND The proxy offer is not Pxe10.\r
+ @retval EFI_ABORTED User cancelled the operation.\r
+ @retval EFI_NOT_READY Reading the input key from the keyboard has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcSelectBootPrompt (\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ PXEBC_DHCP_PACKET_CACHE *Cache;\r
+ PXEBC_VENDOR_OPTION *VendorOpt;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_EVENT TimeoutEvent;\r
+ EFI_EVENT DescendEvent;\r
+ EFI_INPUT_KEY InputKey;\r
+ EFI_STATUS Status;\r
+ UINT32 OfferType;\r
+ UINT8 Timeout;\r
+ UINT8 *Prompt;\r
+ UINT8 PromptLen;\r
+ INT32 SecCol;\r
+ INT32 SecRow;\r
+\r
+ TimeoutEvent = NULL;\r
+ DescendEvent = NULL;\r
+ Mode = Private->PxeBc.Mode;\r
+ Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;\r
+ OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;\r
+\r
+ //\r
+ // Only ProxyPxe10 offer needs boot prompt.\r
+ //\r
+ if (OfferType != PxeOfferTypeProxyPxe10) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.\r
+ //\r
+ ASSERT (!Mode->UsingIpv6);\r
+\r
+ VendorOpt = &Cache->Dhcp4.VendorOpt;\r
+ if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Timeout = VendorOpt->MenuPrompt->Timeout;\r
+ Prompt = VendorOpt->MenuPrompt->Prompt;\r
+ PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);\r
+\r
+ //\r
+ // The valid scope of Timeout refers to PXE2.1 spec.\r
+ //\r
+ if (Timeout == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ if (Timeout == 255) {\r
+ return EFI_TIMEOUT;\r
+ }\r
+\r
+ //\r
+ // Create and start a timer as timeout event.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &TimeoutEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->SetTimer (\r
+ TimeoutEvent,\r
+ TimerRelative,\r
+ Timeout * TICKS_PER_SECOND\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Create and start a periodic timer as descend event by second.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &DescendEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = gBS->SetTimer (\r
+ DescendEvent,\r
+ TimerPeriodic,\r
+ TICKS_PER_SECOND\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Display the boot item and cursor on the screen.\r
+ //\r
+ SecCol = gST->ConOut->Mode->CursorColumn;\r
+ SecRow = gST->ConOut->Mode->CursorRow;\r
+\r
+ PxeBcDisplayBootItem (Prompt, PromptLen);\r
+\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);\r
+ AsciiPrint ("(%d) ", Timeout--);\r
+\r
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+ if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);\r
+ AsciiPrint ("(%d) ", Timeout--);\r
+ }\r
+ if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {\r
+ gBS->Stall (10 * TICKS_PER_MS);\r
+ continue;\r
+ }\r
+ //\r
+ // Parse the input key by user.\r
+ //\r
+ if (InputKey.ScanCode == 0) {\r
+\r
+ switch (InputKey.UnicodeChar) {\r
+\r
+ case CTRL ('c'):\r
+ Status = EFI_ABORTED;\r
+ break;\r
+\r
+ case CTRL ('m'):\r
+ case 'm':\r
+ case 'M':\r
+ Status = EFI_TIMEOUT;\r
+ break;\r
+\r
+ default:\r
+ continue;\r
+ }\r
+\r
+ } else {\r
+\r
+ switch (InputKey.ScanCode) {\r
+\r
+ case SCAN_F8:\r
+ Status = EFI_TIMEOUT;\r
+ break;\r
+\r
+ case SCAN_ESC:\r
+ Status = EFI_ABORTED;\r
+ break;\r
+\r
+ default:\r
+ continue;\r
+ }\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Reset the cursor on the screen.\r
+ //\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);\r
+\r
+ON_EXIT:\r
+ if (DescendEvent != NULL) {\r
+ gBS->CloseEvent (DescendEvent);\r
+ }\r
+ if (TimeoutEvent != NULL) {\r
+ gBS->CloseEvent (TimeoutEvent);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Select the boot menu by user's input.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[out] Type The type of the menu.\r
+ @param[in] UseDefaultItem Use default item or not.\r
+\r
+ @retval EFI_ABORTED User cancel operation.\r
+ @retval EFI_SUCCESS Select the boot menu success.\r
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcSelectBootMenu (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ OUT UINT16 *Type,\r
+ IN BOOLEAN UseDefaultItem\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ PXEBC_DHCP_PACKET_CACHE *Cache;\r
+ PXEBC_VENDOR_OPTION *VendorOpt;\r
+ EFI_INPUT_KEY InputKey;\r
+ UINT32 OfferType;\r
+ UINT8 MenuSize;\r
+ UINT8 MenuNum;\r
+ INT32 TopRow;\r
+ UINT16 Select;\r
+ UINT16 LastSelect;\r
+ UINT8 Index;\r
+ BOOLEAN Finish;\r
+ CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE];\r
+ PXEBC_BOOT_MENU_ENTRY *MenuItem;\r
+ PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM];\r
+\r
+ Finish = FALSE;\r
+ Select = 1;\r
+ Index = 0;\r
+ *Type = 0;\r
+ Mode = Private->PxeBc.Mode;\r
+ Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;\r
+ OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;\r
+\r
+ //\r
+ // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.\r
+ //\r
+ ASSERT (!Mode->UsingIpv6);\r
+ ASSERT (OfferType == PxeOfferTypeProxyPxe10);\r
+\r
+ VendorOpt = &Cache->Dhcp4.VendorOpt;\r
+ if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Display the boot menu on the screen.\r
+ //\r
+ SetMem (Blank, sizeof(Blank), ' ');\r
+\r
+ MenuSize = VendorOpt->BootMenuLen;\r
+ MenuItem = VendorOpt->BootMenu;\r
+\r
+ if (MenuSize == 0) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {\r
+ ASSERT (MenuItem != NULL);\r
+ MenuArray[Index] = MenuItem;\r
+ MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));\r
+ MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);\r
+ Index++;\r
+ }\r
+\r
+ if (UseDefaultItem) {\r
+ ASSERT (MenuArray[0] != NULL);\r
+ CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));\r
+ *Type = NTOHS (*Type);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ MenuNum = Index;\r
+\r
+ for (Index = 0; Index < MenuNum; Index++) {\r
+ ASSERT (MenuArray[Index] != NULL);\r
+ PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);\r
+ }\r
+\r
+ TopRow = gST->ConOut->Mode->CursorRow - MenuNum;\r
+\r
+ //\r
+ // Select the boot item by user in the boot menu.\r
+ //\r
+ do {\r
+ //\r
+ // Highlight selected row.\r
+ //\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);\r
+ ASSERT (Select < PXEBC_MENU_MAX_NUM);\r
+ ASSERT (MenuArray[Select] != NULL);\r
+ Blank[MenuArray[Select]->DescLen] = 0;\r
+ AsciiPrint ("%a\r", Blank);\r
+ PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);\r
+ LastSelect = Select;\r
+\r
+ while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {\r
+ gBS->Stall (10 * TICKS_PER_MS);\r
+ }\r
+\r
+ if (InputKey.ScanCode != 0) {\r
+ switch (InputKey.UnicodeChar) {\r
+ case CTRL ('c'):\r
+ InputKey.ScanCode = SCAN_ESC;\r
+ break;\r
+\r
+ case CTRL ('j'): /* linefeed */\r
+ case CTRL ('m'): /* return */\r
+ Finish = TRUE;\r
+ break;\r
+\r
+ case CTRL ('i'): /* tab */\r
+ case ' ':\r
+ case 'd':\r
+ case 'D':\r
+ InputKey.ScanCode = SCAN_DOWN;\r
+ break;\r
+\r
+ case CTRL ('h'): /* backspace */\r
+ case 'u':\r
+ case 'U':\r
+ InputKey.ScanCode = SCAN_UP;\r
+ break;\r
+\r
+ default:\r
+ InputKey.ScanCode = 0;\r
+ }\r
+ }\r
+\r
+ switch (InputKey.ScanCode) {\r
+ case SCAN_LEFT:\r
+ case SCAN_UP:\r
+ if (Select != 0) {\r
+ Select--;\r
+ }\r
+ break;\r
+\r
+ case SCAN_DOWN:\r
+ case SCAN_RIGHT:\r
+ if (++Select == MenuNum) {\r
+ Select--;\r
+ }\r
+ break;\r
+\r
+ case SCAN_PAGE_UP:\r
+ case SCAN_HOME:\r
+ Select = 0;\r
+ break;\r
+\r
+ case SCAN_PAGE_DOWN:\r
+ case SCAN_END:\r
+ Select = (UINT16) (MenuNum - 1);\r
+ break;\r
+\r
+ case SCAN_ESC:\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ //\r
+ // Unhighlight the last selected row.\r
+ //\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);\r
+ ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);\r
+ ASSERT (MenuArray[LastSelect] != NULL);\r
+ Blank[MenuArray[LastSelect]->DescLen] = 0;\r
+ AsciiPrint ("%a\r", Blank);\r
+ PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);\r
+ } while (!Finish);\r
+\r
+ //\r
+ // Swap the byte order.\r
+ //\r
+ ASSERT (Select < PXEBC_MENU_MAX_NUM);\r
+ ASSERT (MenuArray[Select] != NULL);\r
+ CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));\r
+ *Type = NTOHS (*Type);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Parse out the boot information from the last Dhcp4 reply packet.\r
+\r
+ @param[in, out] Private Pointer to PxeBc private data.\r
+ @param[out] BufferSize Size of the boot file to be downloaded.\r
+\r
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
+ @retval Others Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4BootInfo (\r
+ IN OUT PXEBC_PRIVATE_DATA *Private,\r
+ OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_STATUS Status;\r
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;\r
+ UINT16 Value;\r
+\r
+ PxeBc = &Private->PxeBc;\r
+ Mode = PxeBc->Mode;\r
+ Status = EFI_SUCCESS;\r
+ *BufferSize = 0;\r
+\r
+ //\r
+ // Get the last received Dhcp4 reply packet.\r
+ //\r
+ if (Mode->PxeReplyReceived) {\r
+ Cache4 = &Private->PxeReply.Dhcp4;\r
+ } else if (Mode->ProxyOfferReceived) {\r
+ Cache4 = &Private->ProxyOffer.Dhcp4;\r
+ } else {\r
+ Cache4 = &Private->DhcpAck.Dhcp4;\r
+ }\r
+\r
+ //\r
+ // Parse the boot server Ipv4 address by next server address.\r
+ // If this field isn't available, use option 54 instead.\r
+ //\r
+ CopyMem (\r
+ &Private->ServerIp,\r
+ &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+\r
+ if (Private->ServerIp.Addr[0] == 0) {\r
+ CopyMem (\r
+ &Private->ServerIp,\r
+ Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+ }\r
+\r
+ //\r
+ // Parse the boot file name by option.\r
+ //\r
+ ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);\r
+ Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;\r
+\r
+ if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {\r
+ //\r
+ // Parse the boot file size by option.\r
+ //\r
+ CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));\r
+ Value = NTOHS (Value);\r
+ //\r
+ // The field of boot file size is 512 bytes in unit.\r
+ //\r
+ *BufferSize = 512 * Value;\r
+ } else {\r
+ //\r
+ // Get the bootfile size by tftp command if no option available.\r
+ //\r
+ Status = PxeBc->Mtftp (\r
+ PxeBc,\r
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
+ NULL,\r
+ FALSE,\r
+ BufferSize,\r
+ &Private->BlockSize,\r
+ &Private->ServerIp,\r
+ Private->BootFileName,\r
+ NULL,\r
+ FALSE\r
+ );\r
+ }\r
+\r
+ //\r
+ // Save the value of boot file size.\r
+ //\r
+ Private->BootFileSize = (UINTN) *BufferSize;\r
+\r
+ //\r
+ // Display all the information: boot server address, boot file name and boot file size.\r
+ //\r
+ AsciiPrint ("\n Server IP address is ");\r
+ PxeBcShowIp4Addr (&Private->ServerIp.v4);\r
+ AsciiPrint ("\n NBP filename is %a", Private->BootFileName);\r
+ AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Parse out the boot information from the last Dhcp6 reply packet.\r
+\r
+ @param[in, out] Private Pointer to PxeBc private data.\r
+ @param[out] BufferSize Size of the boot file to be downloaded.\r
+\r
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
+ @retval EFI_BUFFER_TOO_SMALL\r
+ @retval Others Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6BootInfo (\r
+ IN OUT PXEBC_PRIVATE_DATA *Private,\r
+ OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_STATUS Status;\r
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
+ UINT16 Value;\r
+\r
+ PxeBc = &Private->PxeBc;\r
+ Mode = PxeBc->Mode;\r
+ Status = EFI_SUCCESS;\r
+ *BufferSize = 0;\r
+\r
+ //\r
+ // Get the last received Dhcp6 reply packet.\r
+ //\r
+ if (Mode->PxeReplyReceived) {\r
+ Cache6 = &Private->PxeReply.Dhcp6;\r
+ } else if (Mode->ProxyOfferReceived) {\r
+ Cache6 = &Private->ProxyOffer.Dhcp6;\r
+ } else {\r
+ Cache6 = &Private->DhcpAck.Dhcp6;\r
+ }\r
+\r
+ ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+\r
+ //\r
+ // Parse (m)tftp server ip address and bootfile name.\r
+ //\r
+ Status = PxeBcExtractBootFileUrl (\r
+ &Private->BootFileName,\r
+ &Private->ServerIp.v6,\r
+ (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
+ NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Parse the value of boot file size.\r
+ //\r
+ if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {\r
+ //\r
+ // Parse it out if have the boot file parameter option.\r
+ //\r
+ Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ //\r
+ // The field of boot file size is 512 bytes in unit.\r
+ //\r
+ *BufferSize = 512 * Value;\r
+ } else {\r
+ //\r
+ // Send get file size command by tftp if option unavailable.\r
+ //\r
+ Status = PxeBc->Mtftp (\r
+ PxeBc,\r
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
+ NULL,\r
+ FALSE,\r
+ BufferSize,\r
+ &Private->BlockSize,\r
+ &Private->ServerIp,\r
+ Private->BootFileName,\r
+ NULL,\r
+ FALSE\r
+ );\r
+ }\r
+\r
+ //\r
+ // Save the value of boot file size.\r
+ //\r
+ Private->BootFileSize = (UINTN) *BufferSize;\r
+\r
+ //\r
+ // Display all the information: boot server address, boot file name and boot file size.\r
+ //\r
+ AsciiPrint ("\n Server IP address is ");\r
+ PxeBcShowIp6Addr (&Private->ServerIp.v6);\r
+ AsciiPrint ("\n NBP filename is %a", Private->BootFileName);\r
+ AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Extract the discover information and boot server entry from the\r
+ cached packets if unspecified.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Type The type of bootstrap to perform.\r
+ @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.\r
+ @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.\r
+ @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+ @retval EFI_SUCCESS Successfully extracted the information.\r
+ @retval EFI_DEVICE_ERROR Failed to extract the information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractDiscoverInfo (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info,\r
+ OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,\r
+ OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;\r
+ PXEBC_VENDOR_OPTION *VendorOpt;\r
+ PXEBC_BOOT_SVR_ENTRY *Entry;\r
+ BOOLEAN IsFound;\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ if (Mode->UsingIpv6) {\r
+ Info->IpCnt = 1;\r
+ Info->UseUCast = TRUE;\r
+\r
+ Info->SrvList[0].Type = Type;\r
+ Info->SrvList[0].AcceptAnyResponse = FALSE;\r
+\r
+ //\r
+ // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.\r
+ //\r
+ CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+ *SrvList = Info->SrvList;\r
+ } else {\r
+ Entry = NULL;\r
+ IsFound = FALSE;\r
+ Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;\r
+ VendorOpt = &Cache4->VendorOpt;\r
+\r
+ if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {\r
+ //\r
+ // Address is not acquired or no discovery options.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Parse the boot server entry from the vendor option in the last cached packet.\r
+ //\r
+ Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);\r
+ Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);\r
+ Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);\r
+ Info->UseUCast = Info->MustUseList;\r
+\r
+ if (Info->UseMCast) {\r
+ //\r
+ // Get the multicast discover ip address from vendor option if has.\r
+ //\r
+ CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));\r
+ }\r
+\r
+ Info->IpCnt = 0;\r
+\r
+ if (Info->MustUseList) {\r
+ Entry = VendorOpt->BootSvr;\r
+\r
+ while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {\r
+ if (Entry->Type == HTONS (Type)) {\r
+ IsFound = TRUE;\r
+ break;\r
+ }\r
+ Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);\r
+ }\r
+\r
+ if (!IsFound) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Info->IpCnt = Entry->IpCnt;\r
+ }\r
+\r
+ *BootEntry = Entry;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Build the discover packet and send out for boot server.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Type PxeBc option boot item type.\r
+ @param[in] Layer Pointer to option boot item layer.\r
+ @param[in] UseBis Use BIS or not.\r
+ @param[in] DestIp Pointer to the destination address.\r
+ @param[in] IpCount The count of the server address.\r
+ @param[in] SrvList Pointer to the server address list.\r
+\r
+ @retval EFI_SUCCESS Successfully discovered boot file.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.\r
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
+ @retval Others Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDiscoverBootServer (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN UINT16 *Layer,\r
+ IN BOOLEAN UseBis,\r
+ IN EFI_IP_ADDRESS *DestIp,\r
+ IN UINT16 IpCount,\r
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList\r
+ )\r
+{\r
+ if (Private->PxeBc.Mode->UsingIpv6) {\r
+ return PxeBcDhcp6Discover (\r
+ Private,\r
+ Type,\r
+ Layer,\r
+ UseBis,\r
+ DestIp\r
+ );\r
+ } else {\r
+ return PxeBcDhcp4Discover (\r
+ Private,\r
+ Type,\r
+ Layer,\r
+ UseBis,\r
+ DestIp,\r
+ IpCount,\r
+ SrvList\r
+ );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Discover all the boot information for boot file.\r
+\r
+ @param[in, out] Private Pointer to PxeBc private data.\r
+ @param[out] BufferSize Size of the boot file to be downloaded.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained all the boot information .\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
+ @retval EFI_ABORTED User cancel current operation.\r
+ @retval Others Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDiscoverBootFile (\r
+ IN OUT PXEBC_PRIVATE_DATA *Private,\r
+ OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_STATUS Status;\r
+ UINT16 Type;\r
+ UINT16 Layer;\r
+ BOOLEAN UseBis;\r
+\r
+ PxeBc = &Private->PxeBc;\r
+ Mode = PxeBc->Mode;\r
+ Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;\r
+ Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;\r
+\r
+ //\r
+ // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and\r
+ // other pxe boot information.\r
+ //\r
+ Status = PxeBc->Dhcp (PxeBc, TRUE);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Select a boot server from boot server list.\r
+ //\r
+ Status = PxeBcSelectBootPrompt (Private);\r
+\r
+ if (Status == EFI_SUCCESS) {\r
+ //\r
+ // Choose by user's input.\r
+ //\r
+ Status = PxeBcSelectBootMenu (Private, &Type, TRUE);\r
+ } else if (Status == EFI_TIMEOUT) {\r
+ //\r
+ // Choose by default item.\r
+ //\r
+ Status = PxeBcSelectBootMenu (Private, &Type, FALSE);\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {\r
+ //\r
+ // Local boot(PXE bootstrap server) need abort\r
+ //\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ //\r
+ // Start to discover the boot server to get (m)tftp server ip address, bootfile\r
+ // name and bootfile size.\r
+ //\r
+ UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);\r
+ Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Parse the boot information.\r
+ //\r
+ if (Mode->UsingIpv6) {\r
+ Status = PxeBcDhcp6BootInfo (Private, BufferSize);\r
+ } else {\r
+ Status = PxeBcDhcp4BootInfo (Private, BufferSize);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Install PxeBaseCodeCallbackProtocol if not installed before.\r
+\r
+ @param[in, out] Private Pointer to PxeBc private data.\r
+ @param[out] NewMakeCallback If TRUE, it is a new callback.\r
+ Otherwise, it is not new callback.\r
+ @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully.\r
+ @retval Others Failed to install PxeBaseCodeCallbackProtocol.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcInstallCallback (\r
+ IN OUT PXEBC_PRIVATE_DATA *Private,\r
+ OUT BOOLEAN *NewMakeCallback\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Check whether PxeBaseCodeCallbackProtocol already installed.\r
+ //\r
+ PxeBc = &Private->PxeBc;\r
+ Status = gBS->HandleProtocol (\r
+ Private->Controller,\r
+ &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+ (VOID **) &Private->PxeBcCallback\r
+ );\r
+ if (Status == EFI_UNSUPPORTED) {\r
+\r
+ CopyMem (\r
+ &Private->LoadFileCallback,\r
+ &gPxeBcCallBackTemplate,\r
+ sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)\r
+ );\r
+\r
+ //\r
+ // Install a default callback if user didn't offer one.\r
+ //\r
+ Status = gBS->InstallProtocolInterface (\r
+ &Private->Controller,\r
+ &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &Private->LoadFileCallback\r
+ );\r
+\r
+ (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);\r
+\r
+ Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);\r
+ if (EFI_ERROR (Status)) {\r
+ PxeBc->Stop (PxeBc);\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Uninstall PxeBaseCodeCallbackProtocol.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] NewMakeCallback If TRUE, it is a new callback.\r
+ Otherwise, it is not new callback.\r
+\r
+**/\r
+VOID\r
+PxeBcUninstallCallback (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN BOOLEAN NewMakeCallback\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+\r
+ PxeBc = &Private->PxeBc;\r
+\r
+ if (NewMakeCallback) {\r
+\r
+ NewMakeCallback = FALSE;\r
+\r
+ PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);\r
+\r
+ gBS->UninstallProtocolInterface (\r
+ Private->Controller,\r
+ &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+ &Private->LoadFileCallback\r
+ );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Download one of boot file in the list, and it's special for IPv6.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in, out] BufferSize Size of user buffer for input;\r
+ required buffer size for output.\r
+ @param[in] Buffer Pointer to user buffer.\r
+\r
+ @retval EFI_SUCCESS Read one of boot file in the list successfully.\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
+ @retval EFI_NOT_FOUND There is no proper boot file available.\r
+ @retval Others Failed to download boot file in the list.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcReadBootFileList (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN VOID *Buffer OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+\r
+ PxeBc = &Private->PxeBc;\r
+\r
+ //\r
+ // Try to download the boot file if everything is ready.\r
+ //\r
+ if (Buffer != NULL) {\r
+ Status = PxeBc->Mtftp (\r
+ PxeBc,\r
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
+ Buffer,\r
+ FALSE,\r
+ BufferSize,\r
+ &Private->BlockSize,\r
+ &Private->ServerIp,\r
+ Private->BootFileName,\r
+ NULL,\r
+ FALSE\r
+ );\r
+\r
+\r
+ } else {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Load boot file into user buffer.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in, out] BufferSize Size of user buffer for input;\r
+ required buffer size for output.\r
+ @param[in] Buffer Pointer to user buffer.\r
+\r
+ @retval EFI_SUCCESS Get all the boot information successfully.\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
+ @retval EFI_ABORTED User cancelled the current operation.\r
+ @retval Others Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcLoadBootFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN OUT UINTN *BufferSize,\r
+ IN VOID *Buffer OPTIONAL\r
+ )\r
+{\r
+ BOOLEAN NewMakeCallback;\r
+ UINT64 RequiredSize;\r
+ UINT64 CurrentSize;\r
+ EFI_STATUS Status;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;\r
+\r
+ NewMakeCallback = FALSE;\r
+ PxeBc = &Private->PxeBc;\r
+ PxeBcMode = &Private->Mode;\r
+ CurrentSize = *BufferSize;\r
+ RequiredSize = 0;\r
+\r
+ //\r
+ // Install pxebc callback protocol if hasn't been installed yet.\r
+ //\r
+ Status = PxeBcInstallCallback (Private, &NewMakeCallback);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Private->BootFileSize == 0) {\r
+ //\r
+ // Discover the boot information about the bootfile if hasn't.\r
+ //\r
+ Status = PxeBcDiscoverBootFile (Private, &RequiredSize);\r
+\r
+ if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {\r
+ //\r
+ // It's error if the required buffer size is beyond the system scope.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto ON_EXIT;\r
+ } else if (RequiredSize > 0) {\r
+ //\r
+ // Get the right buffer size of the bootfile required.\r
+ //\r
+ if (CurrentSize < RequiredSize || Buffer == NULL) {\r
+ //\r
+ // It's buffer too small if the size of user buffer is smaller than the required.\r
+ //\r
+ CurrentSize = RequiredSize;\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto ON_EXIT;\r
+ }\r
+ CurrentSize = RequiredSize;\r
+ } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {\r
+ //\r
+ // Try to download another bootfile in list if failed to get the filesize of the last one.\r
+ // It's special for the case of IPv6.\r
+ //\r
+ Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);\r
+ goto ON_EXIT;\r
+ }\r
+ } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {\r
+ //\r
+ // It's buffer too small if the size of user buffer is smaller than the required.\r
+ //\r
+ CurrentSize = Private->BootFileSize;\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Begin to download the bootfile if everything is ready.\r
+ //\r
+ AsciiPrint ("\n Downloading NBP file...\n");\r
+ if (PxeBcMode->UsingIpv6) {\r
+ Status = PxeBcReadBootFileList (\r
+ Private,\r
+ &CurrentSize,\r
+ Buffer\r
+ );\r
+ } else {\r
+ Status = PxeBc->Mtftp (\r
+ PxeBc,\r
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
+ Buffer,\r
+ FALSE,\r
+ &CurrentSize,\r
+ &Private->BlockSize,\r
+ &Private->ServerIp,\r
+ Private->BootFileName,\r
+ NULL,\r
+ FALSE\r
+ );\r
+ }\r
+\r
+ON_EXIT:\r
+ *BufferSize = (UINTN) CurrentSize;\r
+ PxeBcUninstallCallback(Private, NewMakeCallback);\r
+\r
+ if (Status == EFI_SUCCESS) {\r
+ AsciiPrint ("\n Succeed to download NBP file.\n");\r
+ return EFI_SUCCESS;\r
+ } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {\r
+ AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ AsciiPrint ("\n PXE-E07: Network device error.\n");\r
+ } else if (Status == EFI_OUT_OF_RESOURCES) {\r
+ AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");\r
+ } else if (Status == EFI_NO_MEDIA) {\r
+ AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");\r
+ } else if (Status == EFI_NO_RESPONSE) {\r
+ AsciiPrint ("\n PXE-E16: No offer received.\n");\r
+ } else if (Status == EFI_TIMEOUT) {\r
+ AsciiPrint ("\n PXE-E18: Server response timeout.\n");\r
+ } else if (Status == EFI_ABORTED) {\r
+ AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");\r
+ } else if (Status == EFI_ICMP_ERROR) {\r
+ AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");\r
+ } else if (Status == EFI_TFTP_ERROR) {\r
+ AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");\r
+ } else if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ AsciiPrint ("\n PXE-E99: Unexpected network error.\n");\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Boot functions declaration for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_BOOT_H__\r
+#define __EFI_PXEBC_BOOT_H__\r
+\r
+#define PXEBC_DISPLAY_MAX_LINE 70\r
+#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE 8\r
+#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE 4\r
+\r
+#define PXEBC_IS_SIZE_OVERFLOWED(x) ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF))\r
+\r
+\r
+/**\r
+ Extract the discover information and boot server entry from the\r
+ cached packets if unspecified.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Type The type of bootstrap to perform.\r
+ @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.\r
+ @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.\r
+ @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+ @retval EFI_SUCCESS Successfully extracted the information.\r
+ @retval EFI_DEVICE_ERROR Failed to extract the information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractDiscoverInfo (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info,\r
+ OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,\r
+ OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList\r
+ );\r
+\r
+\r
+/**\r
+ Build the discover packet and send out for boot.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Type PxeBc option boot item type.\r
+ @param[in] Layer Pointer to option boot item layer.\r
+ @param[in] UseBis Use BIS or not.\r
+ @param[in] DestIp Pointer to the server address.\r
+ @param[in] IpCount The total count of the server address.\r
+ @param[in] SrvList Pointer to the server address list.\r
+\r
+ @retval EFI_SUCCESS Successfully discovered boot file.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
+ @retval Others Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDiscoverBootServer (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN UINT16 *Layer,\r
+ IN BOOLEAN UseBis,\r
+ IN EFI_IP_ADDRESS *DestIp,\r
+ IN UINT16 IpCount,\r
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList\r
+ );\r
+\r
+\r
+/**\r
+ Load boot file into user buffer.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in, out] BufferSize Size of user buffer for input;\r
+ required buffer size for output.\r
+ @param[in] Buffer Pointer to user buffer.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained all the boot information.\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.\r
+ @retval EFI_ABORTED User cancelled the current operation.\r
+ @retval Others Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcLoadBootFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN OUT UINTN *BufferSize,\r
+ IN VOID *Buffer OPTIONAL\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Functions implementation related with DHCPv4 for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+//\r
+// This is a map from the interested DHCP4 option tags' index to the tag value.\r
+//\r
+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {\r
+ PXEBC_DHCP4_TAG_BOOTFILE_LEN,\r
+ PXEBC_DHCP4_TAG_VENDOR,\r
+ PXEBC_DHCP4_TAG_OVERLOAD,\r
+ PXEBC_DHCP4_TAG_MSG_TYPE,\r
+ PXEBC_DHCP4_TAG_SERVER_ID,\r
+ PXEBC_DHCP4_TAG_CLASS_ID,\r
+ PXEBC_DHCP4_TAG_BOOTFILE\r
+};\r
+\r
+//\r
+// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec.\r
+//\r
+UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32};\r
+\r
+\r
+/**\r
+ Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.\r
+\r
+ @param[in] Buffer Pointer to the option buffer.\r
+ @param[in] Length Length of the option buffer.\r
+ @param[in] OptTag Tag of the required option.\r
+\r
+ @retval NULL Failed to find the required option.\r
+ @retval Others The position of the required option.\r
+\r
+**/\r
+EFI_DHCP4_PACKET_OPTION *\r
+PxeBcParseDhcp4Options (\r
+ IN UINT8 *Buffer,\r
+ IN UINT32 Length,\r
+ IN UINT8 OptTag\r
+ )\r
+{\r
+ EFI_DHCP4_PACKET_OPTION *Option;\r
+ UINT32 Offset;\r
+\r
+ Option = (EFI_DHCP4_PACKET_OPTION *) Buffer;\r
+ Offset = 0;\r
+\r
+ while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) {\r
+\r
+ if (Option->OpCode == OptTag) {\r
+ //\r
+ // Found the required option.\r
+ //\r
+ return Option;\r
+ }\r
+\r
+ //\r
+ // Skip the current option to the next.\r
+ //\r
+ if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) {\r
+ Offset++;\r
+ } else {\r
+ Offset += Option->Length + 2;\r
+ }\r
+\r
+ Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+\r
+/**\r
+ Parse the PXE vender options and extract the information from them.\r
+\r
+ @param[in] Dhcp4Option Pointer to vendor options in buffer.\r
+ @param[in] VendorOption Pointer to structure to store information in vendor options.\r
+\r
+**/\r
+VOID\r
+PxeBcParseVendorOptions (\r
+ IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option,\r
+ IN PXEBC_VENDOR_OPTION *VendorOption\r
+ )\r
+{\r
+ UINT32 *BitMap;\r
+ UINT8 VendorOptionLen;\r
+ EFI_DHCP4_PACKET_OPTION *PxeOption;\r
+ UINT8 Offset;\r
+\r
+ BitMap = VendorOption->BitMap;\r
+ VendorOptionLen = Dhcp4Option->Length;\r
+ PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];\r
+ Offset = 0;\r
+\r
+ ASSERT (PxeOption != NULL);\r
+\r
+ while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) {\r
+ //\r
+ // Parse all the interesting PXE vendor options one by one.\r
+ //\r
+ switch (PxeOption->OpCode) {\r
+\r
+ case PXEBC_VENDOR_TAG_MTFTP_IP:\r
+\r
+ CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_MTFTP_CPORT:\r
+\r
+ CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_MTFTP_SPORT:\r
+\r
+ CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:\r
+\r
+ VendorOption->MtftpTimeout = *PxeOption->Data;\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_MTFTP_DELAY:\r
+\r
+ VendorOption->MtftpDelay = *PxeOption->Data;\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_DISCOVER_CTRL:\r
+\r
+ VendorOption->DiscoverCtrl = *PxeOption->Data;\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_DISCOVER_MCAST:\r
+\r
+ CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_BOOT_SERVERS:\r
+\r
+ VendorOption->BootSvrLen = PxeOption->Length;\r
+ VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_BOOT_MENU:\r
+\r
+ VendorOption->BootMenuLen = PxeOption->Length;\r
+ VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_MENU_PROMPT:\r
+\r
+ VendorOption->MenuPromptLen = PxeOption->Length;\r
+ VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data;\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_MCAST_ALLOC:\r
+\r
+ CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));\r
+ CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:\r
+\r
+ VendorOption->CredTypeLen = PxeOption->Length;\r
+ VendorOption->CredType = (UINT32 *) PxeOption->Data;\r
+ break;\r
+\r
+ case PXEBC_VENDOR_TAG_BOOT_ITEM:\r
+\r
+ CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));\r
+ CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Not interesting PXE vendor options.\r
+ //\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Set the bit map for the special PXE options.\r
+ //\r
+ SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);\r
+\r
+ //\r
+ // Continue to the next option.\r
+ //\r
+ if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) {\r
+ Offset++;\r
+ } else {\r
+ Offset = (UINT8) (Offset + PxeOption->Length + 2);\r
+ }\r
+\r
+ PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Build the options buffer for the DHCPv4 request packet.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[out] OptList Pointer to the option pointer array.\r
+ @param[in] Buffer Pointer to the buffer to contain the option list.\r
+ @param[in] NeedMsgType If TRUE, it is necessary to include the Msg type option.\r
+ Otherwise, it is not necessary.\r
+\r
+ @return Index The count of the built-in options.\r
+\r
+**/\r
+UINT32\r
+PxeBcBuildDhcp4Options (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ OUT EFI_DHCP4_PACKET_OPTION **OptList,\r
+ IN UINT8 *Buffer,\r
+ IN BOOLEAN NeedMsgType\r
+ )\r
+{\r
+ UINT32 Index;\r
+ PXEBC_DHCP4_OPTION_ENTRY OptEnt;\r
+ UINT16 Value;\r
+\r
+ Index = 0;\r
+ OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;\r
+\r
+ if (NeedMsgType) {\r
+ //\r
+ // Append message type.\r
+ //\r
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MSG_TYPE;\r
+ OptList[Index]->Length = 1;\r
+ OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;\r
+ OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST;\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+ //\r
+ // Append max message size.\r
+ //\r
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MAXMSG;\r
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);\r
+ OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;\r
+ Value = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);\r
+ CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+ }\r
+\r
+ //\r
+ // Append parameter request list option.\r
+ //\r
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_PARA_LIST;\r
+ OptList[Index]->Length = 35;\r
+ OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;\r
+ OptEnt.Para->ParaList[0] = PXEBC_DHCP4_TAG_NETMASK;\r
+ OptEnt.Para->ParaList[1] = PXEBC_DHCP4_TAG_TIME_OFFSET;\r
+ OptEnt.Para->ParaList[2] = PXEBC_DHCP4_TAG_ROUTER;\r
+ OptEnt.Para->ParaList[3] = PXEBC_DHCP4_TAG_TIME_SERVER;\r
+ OptEnt.Para->ParaList[4] = PXEBC_DHCP4_TAG_NAME_SERVER;\r
+ OptEnt.Para->ParaList[5] = PXEBC_DHCP4_TAG_DNS_SERVER;\r
+ OptEnt.Para->ParaList[6] = PXEBC_DHCP4_TAG_HOSTNAME;\r
+ OptEnt.Para->ParaList[7] = PXEBC_DHCP4_TAG_BOOTFILE_LEN;\r
+ OptEnt.Para->ParaList[8] = PXEBC_DHCP4_TAG_DOMAINNAME;\r
+ OptEnt.Para->ParaList[9] = PXEBC_DHCP4_TAG_ROOTPATH;\r
+ OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;\r
+ OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;\r
+ OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;\r
+ OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;\r
+ OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;\r
+ OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;\r
+ OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;\r
+ OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;\r
+ OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;\r
+ OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;\r
+ OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;\r
+ OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;\r
+ OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;\r
+ OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;\r
+ OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;\r
+ OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;\r
+ OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID;\r
+ OptEnt.Para->ParaList[27] = 0x80;\r
+ OptEnt.Para->ParaList[28] = 0x81;\r
+ OptEnt.Para->ParaList[29] = 0x82;\r
+ OptEnt.Para->ParaList[30] = 0x83;\r
+ OptEnt.Para->ParaList[31] = 0x84;\r
+ OptEnt.Para->ParaList[32] = 0x85;\r
+ OptEnt.Para->ParaList[33] = 0x86;\r
+ OptEnt.Para->ParaList[34] = 0x87;\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+ //\r
+ // Append UUID/Guid-based client identifier option\r
+ //\r
+ OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UUID;\r
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);\r
+ OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;\r
+ OptEnt.Uuid->Type = 0;\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+ if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {\r
+ //\r
+ // Zero the Guid to indicate NOT programable if failed to get system Guid.\r
+ //\r
+ ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));\r
+ }\r
+\r
+ //\r
+ // Append client network device interface option\r
+ //\r
+ OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UNDI;\r
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);\r
+ OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;\r
+\r
+ if (Private->Nii != NULL) {\r
+ OptEnt.Undi->Type = Private->Nii->Type;\r
+ OptEnt.Undi->MajorVer = Private->Nii->MajorVer;\r
+ OptEnt.Undi->MinorVer = Private->Nii->MinorVer;\r
+ } else {\r
+ OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;\r
+ OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;\r
+ OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;\r
+ }\r
+\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+ //\r
+ // Append client system architecture option\r
+ //\r
+ OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_ARCH;\r
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);\r
+ OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;\r
+ Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);\r
+ CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);\r
+\r
+ //\r
+ // Append vendor class identify option\r
+ //\r
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_CLASS_ID;\r
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);\r
+ OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;\r
+ CopyMem (\r
+ OptEnt.Clid,\r
+ DEFAULT_CLASS_ID_DATA,\r
+ sizeof (PXEBC_DHCP4_OPTION_CLID)\r
+ );\r
+ PxeBcUintnToAscDecWithFormat (\r
+ EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,\r
+ OptEnt.Clid->ArchitectureType,\r
+ sizeof (OptEnt.Clid->ArchitectureType)\r
+ );\r
+\r
+ if (Private->Nii != NULL) {\r
+ CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));\r
+ PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));\r
+ PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));\r
+ }\r
+\r
+ Index++;\r
+\r
+ return Index;\r
+}\r
+\r
+\r
+/**\r
+ Create a template DHCPv4 packet as a seed.\r
+\r
+ @param[out] Seed Pointer to the seed packet.\r
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.\r
+\r
+**/\r
+VOID\r
+PxeBcSeedDhcp4Packet (\r
+ OUT EFI_DHCP4_PACKET *Seed,\r
+ IN EFI_UDP4_PROTOCOL *Udp4\r
+ )\r
+{\r
+ EFI_SIMPLE_NETWORK_MODE Mode;\r
+ EFI_DHCP4_HEADER *Header;\r
+\r
+ //\r
+ // Get IfType and HwAddressSize from SNP mode data.\r
+ //\r
+ Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);\r
+\r
+ Seed->Size = sizeof (EFI_DHCP4_PACKET);\r
+ Seed->Length = sizeof (Seed->Dhcp4);\r
+ Header = &Seed->Dhcp4.Header;\r
+ ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));\r
+ Header->OpCode = PXEBC_DHCP4_OPCODE_REQUEST;\r
+ Header->HwType = Mode.IfType;\r
+ Header->HwAddrLen = (UINT8) Mode.HwAddressSize;\r
+ CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);\r
+\r
+ Seed->Dhcp4.Magik = PXEBC_DHCP4_MAGIC;\r
+ Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP;\r
+}\r
+\r
+\r
+/**\r
+ Cache the DHCPv4 packet.\r
+\r
+ @param[in] Dst Pointer to the cache buffer for DHCPv4 packet.\r
+ @param[in] Src Pointer to the DHCPv4 packet to be cached.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp4Packet (\r
+ IN EFI_DHCP4_PACKET *Dst,\r
+ IN EFI_DHCP4_PACKET *Src\r
+ )\r
+{\r
+ ASSERT (Dst->Size >= Src->Length);\r
+\r
+ CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);\r
+ Dst->Length = Src->Length;\r
+}\r
+\r
+\r
+/**\r
+ Parse the cached DHCPv4 packet, including all the options.\r
+\r
+ @param[in] Cache4 Pointer to cached DHCPv4 packet.\r
+\r
+ @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully.\r
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp4Packet (\r
+ IN PXEBC_DHCP4_PACKET_CACHE *Cache4\r
+ )\r
+{\r
+ EFI_DHCP4_PACKET *Offer;\r
+ EFI_DHCP4_PACKET_OPTION **Options;\r
+ EFI_DHCP4_PACKET_OPTION *Option;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+ UINTN Index;\r
+ BOOLEAN IsProxyOffer;\r
+ BOOLEAN IsPxeOffer;\r
+ UINT8 *Ptr8;\r
+\r
+ IsProxyOffer = FALSE;\r
+ IsPxeOffer = FALSE;\r
+\r
+ ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));\r
+ ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt));\r
+\r
+ Offer = &Cache4->Packet.Offer;\r
+ Options = Cache4->OptList;\r
+\r
+ //\r
+ // Parse DHCPv4 options in this offer, and store the pointers.\r
+ //\r
+ for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {\r
+ Options[Index] = PxeBcParseDhcp4Options (\r
+ Offer->Dhcp4.Option,\r
+ GET_OPTION_BUFFER_LEN (Offer),\r
+ mInterestedDhcp4Tags[Index]\r
+ );\r
+ }\r
+\r
+ //\r
+ // The offer with "yiaddr" is a proxy offer.\r
+ //\r
+ if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {\r
+ IsProxyOffer = TRUE;\r
+ }\r
+\r
+ //\r
+ // The offer with "PXEClient" is a PXE offer.\r
+ //\r
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];\r
+ if ((Option != NULL) && (Option->Length >= 9) &&\r
+ (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {\r
+ IsPxeOffer = TRUE;\r
+ }\r
+\r
+ //\r
+ // Parse PXE vendor options in this offer, and store the contents/pointers.\r
+ //\r
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];\r
+ if (IsPxeOffer && Option != NULL) {\r
+ PxeBcParseVendorOptions (Option, &Cache4->VendorOpt);\r
+ }\r
+\r
+ //\r
+ // Check whether bootfilename and serverhostname overloaded, refers to rfc-2132 in details.\r
+ // If overloaded, parse the buffer as nested DHCPv4 options, or else just parse as bootfilename\r
+ // and serverhostname option.\r
+ //\r
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];\r
+ if (Option != NULL && (Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {\r
+\r
+ Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = PxeBcParseDhcp4Options (\r
+ (UINT8 *) Offer->Dhcp4.Header.BootFileName,\r
+ sizeof (Offer->Dhcp4.Header.BootFileName),\r
+ PXEBC_DHCP4_TAG_BOOTFILE\r
+ );\r
+ //\r
+ // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null\r
+ // terminated string. So force to append null terminated character at the end of string.\r
+ //\r
+ if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {\r
+ Ptr8 = (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];\r
+ Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length;\r
+ *Ptr8 = '\0';\r
+ }\r
+\r
+ } else if ((Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) &&\r
+ (Offer->Dhcp4.Header.BootFileName[0] != 0)) {\r
+ //\r
+ // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.\r
+ // Do not count dhcp option header here, or else will destory the serverhostname.\r
+ //\r
+ Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)\r
+ (&Offer->Dhcp4.Header.BootFileName[0] -\r
+ OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));\r
+\r
+ }\r
+\r
+ //\r
+ // Determine offer type of the DHCPv4 packet.\r
+ //\r
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];\r
+ if (Option == NULL || Option->Data[0] == 0) {\r
+ //\r
+ // It's a Bootp offer.\r
+ //\r
+ OfferType = PxeOfferTypeBootp;\r
+\r
+ Option = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];\r
+ if (Option == NULL) {\r
+ //\r
+ // If the Bootp offer without bootfilename, discard it.\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ } else {\r
+\r
+ if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {\r
+ //\r
+ // It's a PXE10 offer with PXEClient and discover vendor option.\r
+ //\r
+ OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10;\r
+ } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {\r
+ //\r
+ // It's a WFM11a offer with PXEClient and mtftp vendor option.\r
+ // But multi-cast download is not supported currently, so discard it.\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (IsPxeOffer) {\r
+ //\r
+ // It's a BINL offer only with PXEClient.\r
+ //\r
+ OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;\r
+ } else {\r
+ //\r
+ // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet.\r
+ //\r
+ OfferType = PxeOfferTypeDhcpOnly;\r
+ }\r
+ }\r
+\r
+ Cache4->OfferType = OfferType;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Cache the DHCPv4 ack packet, and parse it on demand.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Ack Pointer to the DHCPv4 ack packet.\r
+ @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyDhcp4Ack (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP4_PACKET *Ack,\r
+ IN BOOLEAN Verified\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack);\r
+\r
+ if (Verified) {\r
+ //\r
+ // Parse the ack packet and store it into mode data if needed.\r
+ //\r
+ PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4);\r
+ CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length);\r
+ Mode->DhcpAckReceived = TRUE;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Cache the DHCPv4 proxy offer packet according to the received order.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] OfferIndex The received order of offer packets.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyProxyOffer (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT32 OfferIndex\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_DHCP4_PACKET *Offer;\r
+\r
+ ASSERT (OfferIndex < Private->OfferNum);\r
+ ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+ Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer;\r
+\r
+ //\r
+ // Cache the proxy offer packet and parse it.\r
+ //\r
+ PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer);\r
+ PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4);\r
+\r
+ //\r
+ // Store this packet into mode data.\r
+ //\r
+ CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length);\r
+ Mode->ProxyOfferReceived = TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Retry to request bootfile name by the BINL offer.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Index The received order of offer packets.\r
+\r
+ @retval EFI_SUCCESS Successfully retried to request bootfile name.\r
+ @retval EFI_DEVICE_ERROR Failed to retry bootfile name.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRetryBinlOffer (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT32 Index\r
+ )\r
+{\r
+ EFI_DHCP4_PACKET *Offer;\r
+ EFI_IP_ADDRESS ServerIp;\r
+ EFI_STATUS Status;\r
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;\r
+ EFI_DHCP4_PACKET *Reply;\r
+\r
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+ ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl ||\r
+ Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl);\r
+\r
+ Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;\r
+\r
+ //\r
+ // Prefer to siaddr in header as next server address. If it's zero, then use option 54.\r
+ //\r
+ if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) {\r
+ CopyMem (\r
+ &ServerIp.Addr[0],\r
+ Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+ } else {\r
+ CopyMem (\r
+ &ServerIp.Addr[0],\r
+ &Offer->Dhcp4.Header.ServerAddr,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+ }\r
+\r
+ Private->IsDoDiscover = FALSE;\r
+ Cache4 = &Private->ProxyOffer.Dhcp4;\r
+ Reply = &Cache4->Packet.Offer;\r
+\r
+ //\r
+ // Send another request packet for bootfile name.\r
+ //\r
+ Status = PxeBcDhcp4Discover (\r
+ Private,\r
+ 0,\r
+ NULL,\r
+ FALSE,\r
+ &ServerIp,\r
+ 0,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Parse the reply for the last request packet.\r
+ //\r
+ Status = PxeBcParseDhcp4Packet (Cache4);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Cache4->OfferType != PxeOfferTypeProxyPxe10 &&\r
+ Cache4->OfferType != PxeOfferTypeProxyWfm11a &&\r
+ Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {\r
+ //\r
+ // This BINL ack doesn't have discovery option set or multicast option set\r
+ // or bootfile name specified.\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Store the reply into mode data.\r
+ //\r
+ Private->PxeBc.Mode->ProxyOfferReceived = TRUE;\r
+ CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] RcvdOffer Pointer to the received offer packet.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp4Offer (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP4_PACKET *RcvdOffer\r
+ )\r
+{\r
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;\r
+ EFI_DHCP4_PACKET *Offer;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+\r
+ ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM);\r
+ Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;\r
+ Offer = &Cache4->Packet.Offer;\r
+\r
+ //\r
+ // Cache the content of DHCPv4 packet firstly.\r
+ //\r
+ PxeBcCacheDhcp4Packet (Offer, RcvdOffer);\r
+\r
+ //\r
+ // Validate the DHCPv4 packet, and parse the options and offer type.\r
+ //\r
+ if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
+ //\r
+ OfferType = Cache4->OfferType;\r
+ ASSERT (OfferType < PxeOfferTypeMax);\r
+\r
+ if (OfferType == PxeOfferTypeBootp) {\r
+ //\r
+ // It's a Bootp offer, only cache the first one, and discard the others.\r
+ //\r
+ if (Private->OfferCount[OfferType] == 0) {\r
+ Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
+ Private->OfferCount[OfferType] = 1;\r
+ } else {\r
+ return;\r
+ }\r
+ } else {\r
+ ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);\r
+ if (IS_PROXY_DHCP_OFFER (Offer)) {\r
+ //\r
+ // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.\r
+ //\r
+ Private->IsProxyRecved = TRUE;\r
+\r
+ if (OfferType == PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Cache all proxy BINL offers.\r
+ //\r
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+ Private->OfferCount[OfferType]++;\r
+ } else if (Private->OfferCount[OfferType] > 0) {\r
+ //\r
+ // Only cache the first PXE10/WFM11a offer, and discard the others.\r
+ //\r
+ Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
+ Private->OfferCount[OfferType] = 1;\r
+ } else {\r
+ return ;\r
+ }\r
+ } else {\r
+ //\r
+ // It's a DHCPv4 offer with yiaddr, and cache them all.\r
+ //\r
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+ Private->OfferCount[OfferType]++;\r
+ }\r
+ }\r
+\r
+ Private->OfferNum++;\r
+}\r
+\r
+\r
+/**\r
+ Select an DHCPv4 offer, and record SelectIndex and SelectProxyType.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+\r
+**/\r
+VOID\r
+PxeBcSelectDhcp4Offer (\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT32 OfferIndex;\r
+ EFI_DHCP4_PACKET *Offer;\r
+\r
+ Private->SelectIndex = 0;\r
+\r
+ if (Private->IsOfferSorted) {\r
+ //\r
+ // Select offer by default policy.\r
+ //\r
+ if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {\r
+ //\r
+ // 1. DhcpPxe10 offer\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {\r
+ //\r
+ // 2. DhcpWfm11a offer\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+ Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {\r
+ //\r
+ // 3. DhcpOnly offer and ProxyPxe10 offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+ Private->SelectProxyType = PxeOfferTypeProxyPxe10;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+ Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {\r
+ //\r
+ // 4. DhcpOnly offer and ProxyWfm11a offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+ Private->SelectProxyType = PxeOfferTypeProxyWfm11a;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {\r
+ //\r
+ // 5. DhcpBinl offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+ Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {\r
+ //\r
+ // 6. DhcpOnly offer and ProxyBinl offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+ Private->SelectProxyType = PxeOfferTypeProxyBinl;\r
+\r
+ } else {\r
+ //\r
+ // 7. DhcpOnly offer with bootfilename.\r
+ //\r
+ for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {\r
+ OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];\r
+ if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {\r
+ Private->SelectIndex = OfferIndex + 1;\r
+ break;\r
+ }\r
+ }\r
+ //\r
+ // 8. Bootp offer with bootfilename.\r
+ //\r
+ OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0];\r
+ if (Private->SelectIndex == 0 &&\r
+ Private->OfferCount[PxeOfferTypeBootp] > 0 &&\r
+ Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {\r
+ Private->SelectIndex = OfferIndex + 1;\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // Select offer by received order.\r
+ //\r
+ for (Index = 0; Index < Private->OfferNum; Index++) {\r
+\r
+ Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;\r
+\r
+ if (IS_PROXY_DHCP_OFFER (Offer)) {\r
+ //\r
+ // Skip proxy offers\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (!Private->IsProxyRecved &&\r
+ Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly &&\r
+ Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {\r
+ //\r
+ // Skip if DhcpOnly offer without any other proxy offers or bootfilename.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Record the index of the select offer.\r
+ //\r
+ Private->SelectIndex = Index + 1;\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Handle the DHCPv4 offer packet.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+\r
+ @retval EFI_SUCCESS Handled the DHCPv4 offer packet successfully.\r
+ @retval EFI_NO_RESPONSE No response to the following request packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcHandleDhcp4Offer (\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;\r
+ EFI_DHCP4_PACKET_OPTION **Options;\r
+ UINT32 Index;\r
+ EFI_DHCP4_PACKET *Offer;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+ UINT32 ProxyIndex;\r
+ UINT32 SelectIndex;\r
+ EFI_STATUS Status;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_DHCP4_PACKET *Ack;\r
+\r
+ ASSERT (Private->SelectIndex > 0);\r
+ SelectIndex = (UINT32) (Private->SelectIndex - 1);\r
+ ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);\r
+ Cache4 = &Private->OfferBuffer[SelectIndex].Dhcp4;\r
+ Options = Cache4->OptList;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (Cache4->OfferType == PxeOfferTypeDhcpBinl) {\r
+ //\r
+ // DhcpBinl offer is selected, so need try to request bootfilename by this offer.\r
+ //\r
+ if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) {\r
+ Status = EFI_NO_RESPONSE;\r
+ }\r
+ } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) {\r
+\r
+ if (Private->IsProxyRecved) {\r
+ //\r
+ // DhcpOnly offer is selected, so need try to request bootfile name.\r
+ //\r
+ ProxyIndex = 0;\r
+ if (Private->IsOfferSorted) {\r
+ //\r
+ // The proxy offer should be determined if select by default policy.\r
+ // IsOfferSorted means all offers are labeled by OfferIndex.\r
+ //\r
+ ASSERT (Private->SelectProxyType < PxeOfferTypeMax);\r
+ ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);\r
+\r
+ if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Try all the cached ProxyBinl offer one by one to request bootfile name.\r
+ //\r
+ for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {\r
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];\r
+ if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) {\r
+ break;\r
+ }\r
+ }\r
+ if (Index == Private->OfferCount[Private->SelectProxyType]) {\r
+ Status = EFI_NO_RESPONSE;\r
+ }\r
+ } else {\r
+ //\r
+ // For other proxy offers, only one is buffered.\r
+ //\r
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
+ }\r
+ } else {\r
+ //\r
+ // The proxy offer should not be determined if select by received order.\r
+ //\r
+ Status = EFI_NO_RESPONSE;\r
+\r
+ for (Index = 0; Index < Private->OfferNum; Index++) {\r
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+ Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;\r
+ OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType;\r
+ if (!IS_PROXY_DHCP_OFFER (Offer)) {\r
+ //\r
+ // Skip non proxy DHCPv4 offers.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (OfferType == PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Try all the cached ProxyBinl offer one by one to request bootfile name.\r
+ //\r
+ if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) {\r
+ continue;\r
+ }\r
+ }\r
+\r
+ Private->SelectProxyType = OfferType;\r
+ ProxyIndex = Index;\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.\r
+ //\r
+ PxeBcCopyProxyOffer (Private, ProxyIndex);\r
+ }\r
+ } else {\r
+ //\r
+ // Othewise, the bootfile name must be included in DhcpOnly offer.\r
+ //\r
+ ASSERT (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // All PXE boot information is ready by now.\r
+ //\r
+ Mode = Private->PxeBc.Mode;\r
+ Offer = &Cache4->Packet.Offer;\r
+ Ack = &Private->DhcpAck.Dhcp4.Packet.Ack;\r
+ if (Cache4->OfferType == PxeOfferTypeBootp) {\r
+ //\r
+ // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply\r
+ // should be taken as ack.\r
+ //\r
+ Ack = Offer;\r
+ }\r
+\r
+ PxeBcCopyDhcp4Ack (Private, Ack, TRUE);\r
+ Mode->DhcpDiscoverValid = TRUE;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver\r
+ to intercept events that occurred in the configuration process.\r
+\r
+ @param[in] This Pointer to the EFI DHCPv4 Protocol.\r
+ @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().\r
+ @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver.\r
+ @param[in] Dhcp4Event The event that occurs in the current state, which usually means a\r
+ state transition.\r
+ @param[in] Packet The DHCPv4 packet that is going to be sent or already received.\r
+ @param[out] NewPacket The packet that is used to replace the above Packet.\r
+\r
+ @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.\r
+ @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol\r
+ driver will continue to wait for more DHCPOFFER packets until the\r
+ retry timeout expires.\r
+ @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process\r
+ and return to the Dhcp4Init or Dhcp4InitReboot state.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDhcp4CallBack (\r
+ IN EFI_DHCP4_PROTOCOL *This,\r
+ IN VOID *Context,\r
+ IN EFI_DHCP4_STATE CurrentState,\r
+ IN EFI_DHCP4_EVENT Dhcp4Event,\r
+ IN EFI_DHCP4_PACKET *Packet OPTIONAL,\r
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
+ EFI_DHCP4_PACKET_OPTION *MaxMsgSize;\r
+ UINT16 Value;\r
+ EFI_STATUS Status;\r
+ BOOLEAN Received;\r
+\r
+ if ((Dhcp4Event != Dhcp4RcvdOffer) &&\r
+ (Dhcp4Event != Dhcp4SelectOffer) &&\r
+ (Dhcp4Event != Dhcp4SendDiscover) &&\r
+ (Dhcp4Event != Dhcp4RcvdAck)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Private = (PXEBC_PRIVATE_DATA *) Context;\r
+ Mode = Private->PxeBc.Mode;\r
+ Callback = Private->PxeBcCallback;\r
+\r
+ //\r
+ // Override the Maximum DHCP Message Size.\r
+ //\r
+ MaxMsgSize = PxeBcParseDhcp4Options (\r
+ Packet->Dhcp4.Option,\r
+ GET_OPTION_BUFFER_LEN (Packet),\r
+ PXEBC_DHCP4_TAG_MAXMSG\r
+ );\r
+ if (MaxMsgSize != NULL) {\r
+ Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);\r
+ CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));\r
+ }\r
+\r
+ //\r
+ // Callback to user if any packets sent or received.\r
+ //\r
+ if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) {\r
+ Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck);\r
+ Status = Callback->Callback (\r
+ Callback,\r
+ Private->Function,\r
+ Received,\r
+ Packet->Length,\r
+ (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4\r
+ );\r
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ switch (Dhcp4Event) {\r
+\r
+ case Dhcp4SendDiscover:\r
+ //\r
+ // Cache the DHCPv4 discover packet to mode data directly.\r
+ // It need to check SendGuid as well as Dhcp4SendRequest.\r
+ //\r
+ CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length);\r
+\r
+ case Dhcp4SendRequest:\r
+ if (Mode->SendGUID) {\r
+ //\r
+ // Send the system Guid instead of the MAC address as the hardware address if required.\r
+ //\r
+ if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) {\r
+ //\r
+ // Zero the Guid to indicate NOT programable if failed to get system Guid.\r
+ //\r
+ ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));\r
+ }\r
+ Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);\r
+ }\r
+ break;\r
+\r
+ case Dhcp4RcvdOffer:\r
+ Status = EFI_NOT_READY;\r
+ if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {\r
+ //\r
+ // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record\r
+ // the OfferIndex and OfferCount.\r
+ //\r
+ PxeBcCacheDhcp4Offer (Private, Packet);\r
+ }\r
+ break;\r
+\r
+ case Dhcp4SelectOffer:\r
+ //\r
+ // Select offer by the default policy or by order, and record the SelectIndex\r
+ // and SelectProxyType.\r
+ //\r
+ PxeBcSelectDhcp4Offer (Private);\r
+\r
+ if (Private->SelectIndex == 0) {\r
+ Status = EFI_ABORTED;\r
+ } else {\r
+ *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;\r
+ }\r
+ break;\r
+\r
+ case Dhcp4RcvdAck:\r
+ //\r
+ // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data\r
+ // without verification.\r
+ //\r
+ ASSERT (Private->SelectIndex != 0);\r
+\r
+ PxeBcCopyDhcp4Ack (Private, Packet, FALSE);\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Type PxeBc option boot item type.\r
+ @param[in] Layer Pointer to option boot item layer.\r
+ @param[in] UseBis Use BIS or not.\r
+ @param[in] DestIp Pointer to the server address.\r
+ @param[in] IpCount The total count of the server address.\r
+ @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+ @retval EFI_SUCCESS Successfully discovered boot file.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.\r
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
+ @retval Others Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Discover (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN UINT16 *Layer,\r
+ IN BOOLEAN UseBis,\r
+ IN EFI_IP_ADDRESS *DestIp,\r
+ IN UINT16 IpCount,\r
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_UDP_PORT Sport;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_DHCP4_PROTOCOL *Dhcp4;\r
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token;\r
+ BOOLEAN IsBCast;\r
+ EFI_STATUS Status;\r
+ UINT16 RepIndex;\r
+ UINT16 SrvIndex;\r
+ UINT16 TryIndex;\r
+ EFI_DHCP4_LISTEN_POINT ListenPoint;\r
+ EFI_DHCP4_PACKET *Response;\r
+ UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];\r
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];\r
+ UINT32 OptCount;\r
+ EFI_DHCP4_PACKET_OPTION *PxeOpt;\r
+ PXEBC_OPTION_BOOT_ITEM *PxeBootItem;\r
+ UINT8 VendorOptLen;\r
+ UINT32 Xid;\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+ Dhcp4 = Private->Dhcp4;\r
+ Status = EFI_SUCCESS;\r
+\r
+ ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));\r
+\r
+ //\r
+ // Use broadcast if destination address not specified.\r
+ //\r
+ if (DestIp == NULL) {\r
+ Sport = PXEBC_DHCP4_S_PORT;\r
+ IsBCast = TRUE;\r
+ } else {\r
+ Sport = PXEBC_BS_DISCOVER_PORT;\r
+ IsBCast = FALSE;\r
+ }\r
+\r
+ if (!UseBis && Layer != NULL) {\r
+ *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;\r
+ }\r
+\r
+ //\r
+ // Build all the options for the request packet.\r
+ //\r
+ OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE);\r
+\r
+ if (Private->IsDoDiscover) {\r
+ //\r
+ // Add vendor option of PXE_BOOT_ITEM\r
+ //\r
+ VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);\r
+ OptList[OptCount] = AllocateZeroPool (VendorOptLen);\r
+ if (OptList[OptCount] == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ OptList[OptCount]->OpCode = PXEBC_DHCP4_TAG_VENDOR;\r
+ OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2);\r
+ PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;\r
+ PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM;\r
+ PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);\r
+ PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;\r
+ PxeBootItem->Type = HTONS (Type);\r
+ PxeOpt->Data[PxeOpt->Length] = PXEBC_DHCP4_TAG_EOP;\r
+\r
+ if (Layer != NULL) {\r
+ PxeBootItem->Layer = HTONS (*Layer);\r
+ }\r
+\r
+ OptCount++;\r
+ }\r
+\r
+ //\r
+ // Build the request packet with seed packet and option list.\r
+ //\r
+ Status = Dhcp4->Build (\r
+ Dhcp4,\r
+ &Private->SeedPacket,\r
+ 0,\r
+ NULL,\r
+ OptCount,\r
+ OptList,\r
+ &Token.Packet\r
+ );\r
+ //\r
+ // Free the vendor option of PXE_BOOT_ITEM.\r
+ //\r
+ if (Private->IsDoDiscover) {\r
+ FreePool (OptList[OptCount - 1]);\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Mode->SendGUID) {\r
+ if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) {\r
+ //\r
+ // Zero the Guid to indicate NOT programable if failed to get system Guid.\r
+ //\r
+ ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));\r
+ }\r
+ Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);\r
+ }\r
+\r
+ //\r
+ // Set fields of the token for the request packet.\r
+ //\r
+ Xid = NET_RANDOM (NetRandomInitSeed ());\r
+ Token.Packet->Dhcp4.Header.Xid = HTONL (Xid);\r
+ Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0));\r
+ CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ Token.RemotePort = Sport;\r
+\r
+ if (IsBCast) {\r
+ SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);\r
+ } else {\r
+ CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));\r
+ }\r
+\r
+ CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ if (!IsBCast) {\r
+ Token.ListenPointCount = 1;\r
+ Token.ListenPoints = &ListenPoint;\r
+ Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT;\r
+ CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));\r
+ CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));\r
+ }\r
+\r
+ //\r
+ // Send out the request packet to discover the bootfile.\r
+ //\r
+ for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {\r
+\r
+ Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);\r
+ Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));\r
+\r
+ Status = Dhcp4->TransmitReceive (Dhcp4, &Token);\r
+ if (Token.Status != EFI_TIMEOUT) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {\r
+ //\r
+ // No server response our PXE request\r
+ //\r
+ Status = EFI_TIMEOUT;\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ RepIndex = 0;\r
+ SrvIndex = 0;\r
+ Response = Token.ResponseList;\r
+ //\r
+ // Find the right PXE Reply according to server address.\r
+ //\r
+ while (RepIndex < Token.ResponseCount) {\r
+\r
+ while (SrvIndex < IpCount) {\r
+ if (SrvList[SrvIndex].AcceptAnyResponse) {\r
+ break;\r
+ }\r
+ if ((SrvList[SrvIndex].Type == Type) &&\r
+ EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &Private->ServerIp)) {\r
+ break;\r
+ }\r
+ SrvIndex++;\r
+ }\r
+\r
+ if ((IpCount != SrvIndex) || (IpCount == 0)) {\r
+ break;\r
+ }\r
+\r
+ SrvIndex = 0;\r
+ RepIndex++;\r
+\r
+ Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);\r
+ }\r
+\r
+ if (RepIndex < Token.ResponseCount) {\r
+ //\r
+ // Cache the right PXE reply packet here, set valid flag later.\r
+ // Especially for PXE discover packet, store it into mode data here.\r
+ //\r
+ if (Private->IsDoDiscover) {\r
+ PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response);\r
+ CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length);\r
+ } else {\r
+ PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response);\r
+ }\r
+ } else {\r
+ //\r
+ // Not found the right PXE reply packet.\r
+ //\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+ if (Token.ResponseList != NULL) {\r
+ FreePool (Token.ResponseList);\r
+ }\r
+ }\r
+\r
+ FreePool (Token.Packet);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL\r
+\r
+ @retval EFI_SUCCESS The D.O.R.A process successfully finished.\r
+ @retval Others Failed to finish the D.O.R.A process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Dora (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP4_PROTOCOL *Dhcp4\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *PxeMode;\r
+ EFI_DHCP4_CONFIG_DATA Config;\r
+ EFI_DHCP4_MODE_DATA Mode;\r
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];\r
+ UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];\r
+ UINT32 OptCount;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (Dhcp4 != NULL);\r
+\r
+ Status = EFI_SUCCESS;\r
+ PxeMode = Private->PxeBc.Mode;\r
+\r
+ //\r
+ // Build option list for the request packet.\r
+ //\r
+ OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE);\r
+ ASSERT (OptCount> 0);\r
+\r
+ ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA));\r
+ ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+\r
+ Config.OptionCount = OptCount;\r
+ Config.OptionList = OptList;\r
+ Config.Dhcp4Callback = PxeBcDhcp4CallBack;\r
+ Config.CallbackContext = Private;\r
+ Config.DiscoverTryCount = PXEBC_DHCP_RETRIES;\r
+ Config.DiscoverTimeout = mPxeDhcpTimeout;\r
+\r
+ //\r
+ // Configure the DHCPv4 instance for PXE boot.\r
+ //\r
+ Status = Dhcp4->Configure (Dhcp4, &Config);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Initialize the record fields for DHCPv4 offer in private data.\r
+ //\r
+ Private->IsProxyRecved = FALSE;\r
+ Private->OfferNum = 0;\r
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
+\r
+ //\r
+ // Start DHCPv4 D.O.R.A. process to acquire IPv4 address.\r
+ //\r
+ Status = Dhcp4->Start (Dhcp4, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_ICMP_ERROR) {\r
+ PxeMode->IcmpErrorReceived = TRUE;\r
+ }\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Get the acquired IPv4 address and store them.\r
+ //\r
+ Status = Dhcp4->GetModeData (Dhcp4, &Mode);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ ASSERT (Mode.State == Dhcp4Bound);\r
+\r
+ CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ Status = PxeBcFlushStaionIp (Private, &Private->StationIp, &Private->SubnetMask);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Check the selected offer whether BINL retry is needed.\r
+ //\r
+ Status = PxeBcHandleDhcp4Offer (Private);\r
+\r
+ AsciiPrint ("\n Station IP address is ");\r
+\r
+ PxeBcShowIp4Addr (&Private->StationIp.v4);\r
+\r
+ON_EXIT:\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp4->Stop (Dhcp4);\r
+ Dhcp4->Configure (Dhcp4, NULL);\r
+ } else {\r
+ ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+ Dhcp4->Configure (Dhcp4, &Config);\r
+ Private->IsAddressOk = TRUE;\r
+ }\r
+\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+ Functions declaration related with DHCPv4 for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_DHCP4_H__\r
+#define __EFI_PXEBC_DHCP4_H__\r
+\r
+#define PXEBC_DHCP4_OPTION_MAX_NUM 16\r
+#define PXEBC_DHCP4_OPTION_MAX_SIZE 312\r
+#define PXEBC_DHCP4_PACKET_MAX_SIZE 1472\r
+#define PXEBC_DHCP4_S_PORT 67\r
+#define PXEBC_DHCP4_C_PORT 68\r
+#define PXEBC_BS_DOWNLOAD_PORT 69\r
+#define PXEBC_BS_DISCOVER_PORT 4011\r
+#define PXEBC_DHCP4_OPCODE_REQUEST 1\r
+#define PXEBC_DHCP4_OPCODE_REPLY 2\r
+#define PXEBC_DHCP4_MSG_TYPE_REQUEST 3\r
+#define PXEBC_DHCP4_MAGIC 0x63538263 // network byte order\r
+\r
+//\r
+// Dhcp Options\r
+//\r
+#define PXEBC_DHCP4_TAG_PAD 0 // Pad Option\r
+#define PXEBC_DHCP4_TAG_EOP 255 // End Option\r
+#define PXEBC_DHCP4_TAG_NETMASK 1 // Subnet Mask\r
+#define PXEBC_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC\r
+#define PXEBC_DHCP4_TAG_ROUTER 3 // Router option,\r
+#define PXEBC_DHCP4_TAG_TIME_SERVER 4 // Time Server\r
+#define PXEBC_DHCP4_TAG_NAME_SERVER 5 // Name Server\r
+#define PXEBC_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server\r
+#define PXEBC_DHCP4_TAG_HOSTNAME 12 // Host Name\r
+#define PXEBC_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size\r
+#define PXEBC_DHCP4_TAG_DUMP 14 // Merit Dump File\r
+#define PXEBC_DHCP4_TAG_DOMAINNAME 15 // Domain Name\r
+#define PXEBC_DHCP4_TAG_ROOTPATH 17 // Root path\r
+#define PXEBC_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path\r
+#define PXEBC_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size\r
+#define PXEBC_DHCP4_TAG_TTL 23 // Default IP Time-to-live\r
+#define PXEBC_DHCP4_TAG_BROADCAST 28 // Broadcast Address\r
+#define PXEBC_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain\r
+#define PXEBC_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers\r
+#define PXEBC_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers\r
+#define PXEBC_DHCP4_TAG_VENDOR 43 // Vendor Specific Information\r
+#define PXEBC_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address\r
+#define PXEBC_DHCP4_TAG_LEASE 51 // IP Address Lease Time\r
+#define PXEBC_DHCP4_TAG_OVERLOAD 52 // Option Overload\r
+#define PXEBC_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type\r
+#define PXEBC_DHCP4_TAG_SERVER_ID 54 // Server Identifier\r
+#define PXEBC_DHCP4_TAG_PARA_LIST 55 // Parameter Request List\r
+#define PXEBC_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size\r
+#define PXEBC_DHCP4_TAG_T1 58 // Renewal (T1) Time Value\r
+#define PXEBC_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value\r
+#define PXEBC_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier\r
+#define PXEBC_DHCP4_TAG_CLIENT_ID 61 // Client-identifier\r
+#define PXEBC_DHCP4_TAG_TFTP 66 // TFTP server name\r
+#define PXEBC_DHCP4_TAG_BOOTFILE 67 // Bootfile name\r
+#define PXEBC_PXE_DHCP4_TAG_ARCH 93\r
+#define PXEBC_PXE_DHCP4_TAG_UNDI 94\r
+#define PXEBC_PXE_DHCP4_TAG_UUID 97\r
+//\r
+// Sub-Options in Dhcp Vendor Option\r
+//\r
+#define PXEBC_VENDOR_TAG_MTFTP_IP 1\r
+#define PXEBC_VENDOR_TAG_MTFTP_CPORT 2\r
+#define PXEBC_VENDOR_TAG_MTFTP_SPORT 3\r
+#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT 4\r
+#define PXEBC_VENDOR_TAG_MTFTP_DELAY 5\r
+#define PXEBC_VENDOR_TAG_DISCOVER_CTRL 6\r
+#define PXEBC_VENDOR_TAG_DISCOVER_MCAST 7\r
+#define PXEBC_VENDOR_TAG_BOOT_SERVERS 8\r
+#define PXEBC_VENDOR_TAG_BOOT_MENU 9\r
+#define PXEBC_VENDOR_TAG_MENU_PROMPT 10\r
+#define PXEBC_VENDOR_TAG_MCAST_ALLOC 11\r
+#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES 12\r
+#define PXEBC_VENDOR_TAG_BOOT_ITEM 71\r
+\r
+#define PXEBC_BOOT_REQUEST_TIMEOUT 1\r
+#define PXEBC_BOOT_REQUEST_RETRIES 4\r
+\r
+#define PXEBC_DHCP4_OVERLOAD_FILE 1\r
+#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME 2\r
+\r
+\r
+//\r
+// The array index of the DHCP4 option tag interested\r
+//\r
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0\r
+#define PXEBC_DHCP4_TAG_INDEX_VENDOR 1\r
+#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD 2\r
+#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE 3\r
+#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID 4\r
+#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID 5\r
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE 6\r
+#define PXEBC_DHCP4_TAG_INDEX_MAX 7\r
+\r
+//\r
+// Dhcp4 and Dhcp6 share this definition, and corresponding\r
+// relatioinship is as follows:\r
+//\r
+// Dhcp4Discover <> Dhcp6Solicit\r
+// Dhcp4Offer <> Dhcp6Advertise\r
+// Dhcp4Request <> Dhcp6Request\r
+// Dhcp4Ack <> DHcp6Reply\r
+//\r
+typedef enum {\r
+ PxeOfferTypeDhcpOnly,\r
+ PxeOfferTypeDhcpPxe10,\r
+ PxeOfferTypeDhcpWfm11a,\r
+ PxeOfferTypeDhcpBinl,\r
+ PxeOfferTypeProxyPxe10,\r
+ PxeOfferTypeProxyWfm11a,\r
+ PxeOfferTypeProxyBinl,\r
+ PxeOfferTypeBootp,\r
+ PxeOfferTypeMax\r
+} PXEBC_OFFER_TYPE;\r
+\r
+#define BIT(x) (1 << x)\r
+#define CTRL(x) (0x1F & (x))\r
+#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:?????:????:??????"\r
+#define DEFAULT_UNDI_TYPE 1\r
+#define DEFAULT_UNDI_MAJOR 3\r
+#define DEFAULT_UNDI_MINOR 0\r
+\r
+#define MTFTP_VENDOR_OPTION_BIT_MAP \\r
+ (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \\r
+ BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \\r
+ BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \\r
+ BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \\r
+ BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY))\r
+\r
+#define DISCOVER_VENDOR_OPTION_BIT_MAP \\r
+ (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \\r
+ BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \\r
+ BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \\r
+ BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \\r
+ BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))\r
+\r
+#define IS_VALID_BOOT_PROMPT(x) \\r
+ ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \\r
+ == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))\r
+\r
+#define IS_VALID_BOOT_MENU(x) \\r
+ ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \\r
+ == BIT (PXEBC_VENDOR_TAG_BOOT_MENU))\r
+\r
+#define IS_VALID_MTFTP_VENDOR_OPTION(x) \\r
+ (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \\r
+ == MTFTP_VENDOR_OPTION_BIT_MAP)\r
+\r
+#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \\r
+ (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0)\r
+\r
+#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \\r
+ (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \\r
+ == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES))\r
+\r
+#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \\r
+ (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \\r
+ BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \\r
+ == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32))\r
+\r
+#define SET_VENDOR_OPTION_BIT_MAP(x, y) \\r
+ (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32)))\r
+\r
+#define GET_NEXT_DHCP_OPTION(Opt) \\r
+ (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \\r
+ sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)\r
+\r
+#define GET_OPTION_BUFFER_LEN(Pkt) \\r
+ ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)\r
+\r
+#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \\r
+ (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \\r
+ ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS))\r
+\r
+#define IS_PROXY_DHCP_OFFER(Offer) \\r
+ EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr)\r
+\r
+#define IS_DISABLE_BCAST_DISCOVER(x) \\r
+ (((x) & BIT (0)) == BIT (0))\r
+\r
+#define IS_DISABLE_MCAST_DISCOVER(x) \\r
+ (((x) & BIT (1)) == BIT (1))\r
+\r
+#define IS_ENABLE_USE_SERVER_LIST(x) \\r
+ (((x) & BIT (2)) == BIT (2))\r
+\r
+#define IS_ENABLE_BOOT_FILE_NAME(x) \\r
+ (((x) & BIT (3)) == BIT (3))\r
+\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+ UINT8 ParaList[135];\r
+} PXEBC_DHCP4_OPTION_PARA;\r
+\r
+typedef struct {\r
+ UINT16 Size;\r
+} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE;\r
+\r
+typedef struct {\r
+ UINT8 Type;\r
+ UINT8 MajorVer;\r
+ UINT8 MinorVer;\r
+} PXEBC_DHCP4_OPTION_UNDI;\r
+\r
+typedef struct {\r
+ UINT8 Type;\r
+} PXEBC_DHCP4_OPTION_MESG;\r
+\r
+typedef struct {\r
+ UINT16 Type;\r
+} PXEBC_DHCP4_OPTION_ARCH;\r
+\r
+typedef struct {\r
+ UINT8 ClassIdentifier[10];\r
+ UINT8 ArchitecturePrefix[5];\r
+ UINT8 ArchitectureType[5];\r
+ UINT8 Lit3[1];\r
+ UINT8 InterfaceName[4];\r
+ UINT8 Lit4[1];\r
+ UINT8 UndiMajor[3];\r
+ UINT8 UndiMinor[3];\r
+} PXEBC_DHCP4_OPTION_CLID;\r
+\r
+typedef struct {\r
+ UINT8 Type;\r
+ UINT8 Guid[16];\r
+} PXEBC_DHCP4_OPTION_UUID;\r
+\r
+typedef struct {\r
+ UINT16 Type;\r
+ UINT16 Layer;\r
+} PXEBC_OPTION_BOOT_ITEM;\r
+\r
+#pragma pack()\r
+\r
+typedef union {\r
+ PXEBC_DHCP4_OPTION_PARA *Para;\r
+ PXEBC_DHCP4_OPTION_UNDI *Undi;\r
+ PXEBC_DHCP4_OPTION_ARCH *Arch;\r
+ PXEBC_DHCP4_OPTION_CLID *Clid;\r
+ PXEBC_DHCP4_OPTION_UUID *Uuid;\r
+ PXEBC_DHCP4_OPTION_MESG *Mesg;\r
+ PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize;\r
+} PXEBC_DHCP4_OPTION_ENTRY;\r
+\r
+typedef struct {\r
+ UINT16 Type;\r
+ UINT8 IpCnt;\r
+ EFI_IPv4_ADDRESS IpAddr[1];\r
+} PXEBC_BOOT_SVR_ENTRY;\r
+\r
+typedef struct {\r
+ UINT16 Type;\r
+ UINT8 DescLen;\r
+ UINT8 DescStr[1];\r
+} PXEBC_BOOT_MENU_ENTRY;\r
+\r
+typedef struct {\r
+ UINT8 Timeout;\r
+ UINT8 Prompt[1];\r
+} PXEBC_MENU_PROMPT;\r
+\r
+typedef struct {\r
+ UINT32 BitMap[8];\r
+ EFI_IPv4_ADDRESS MtftpIp;\r
+ UINT16 MtftpCPort;\r
+ UINT16 MtftpSPort;\r
+ UINT8 MtftpTimeout;\r
+ UINT8 MtftpDelay;\r
+ UINT8 DiscoverCtrl;\r
+ EFI_IPv4_ADDRESS DiscoverMcastIp;\r
+ EFI_IPv4_ADDRESS McastIpBase;\r
+ UINT16 McastIpBlock;\r
+ UINT16 McastIpRange;\r
+ UINT16 BootSrvType;\r
+ UINT16 BootSrvLayer;\r
+ PXEBC_BOOT_SVR_ENTRY *BootSvr;\r
+ UINT8 BootSvrLen;\r
+ PXEBC_BOOT_MENU_ENTRY *BootMenu;\r
+ UINT8 BootMenuLen;\r
+ PXEBC_MENU_PROMPT *MenuPrompt;\r
+ UINT8 MenuPromptLen;\r
+ UINT32 *CredType;\r
+ UINT8 CredTypeLen;\r
+} PXEBC_VENDOR_OPTION;\r
+\r
+typedef union {\r
+ EFI_DHCP4_PACKET Offer;\r
+ EFI_DHCP4_PACKET Ack;\r
+ UINT8 Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE];\r
+} PXEBC_DHCP4_PACKET;\r
+\r
+typedef struct {\r
+ PXEBC_DHCP4_PACKET Packet;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX];\r
+ PXEBC_VENDOR_OPTION VendorOpt;\r
+} PXEBC_DHCP4_PACKET_CACHE;\r
+\r
+\r
+/**\r
+ Create a template DHCPv4 packet as a seed.\r
+\r
+ @param[out] Seed Pointer to the seed packet.\r
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.\r
+\r
+**/\r
+VOID\r
+PxeBcSeedDhcp4Packet (\r
+ OUT EFI_DHCP4_PACKET *Seed,\r
+ IN EFI_UDP4_PROTOCOL *Udp4\r
+ );\r
+\r
+\r
+/**\r
+ Parse the cached DHCPv4 packet, including all the options.\r
+\r
+ @param[in] Cache4 Pointer to cached DHCPv4 packet.\r
+\r
+ @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully.\r
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp4Packet (\r
+ IN PXEBC_DHCP4_PACKET_CACHE *Cache4\r
+ );\r
+\r
+\r
+/**\r
+ Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Type PxeBc option boot item type.\r
+ @param[in] Layer Pointer to option boot item layer.\r
+ @param[in] UseBis Use BIS or not.\r
+ @param[in] DestIp Pointer to the server address.\r
+ @param[in] IpCount The total count of the server address.\r
+ @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.\r
+\r
+ @retval EFI_SUCCESS Successfully discovered boot file.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.\r
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
+ @retval Others Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Discover (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN UINT16 *Layer,\r
+ IN BOOLEAN UseBis,\r
+ IN EFI_IP_ADDRESS *DestIp,\r
+ IN UINT16 IpCount,\r
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList\r
+ );\r
+\r
+\r
+/**\r
+ Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL\r
+\r
+ @retval EFI_SUCCESS The D.O.R.A process successfully finished.\r
+ @retval Others Failed to finish the D.O.R.A process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp4Dora (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP4_PROTOCOL *Dhcp4\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+/**\r
+ Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
+\r
+ @param[in] Buffer The pointer to the option buffer.\r
+ @param[in] Length Length of the option buffer.\r
+ @param[in] OptTag The required option tag.\r
+\r
+ @retval NULL Failed to parse the required option.\r
+ @retval Others The postion of the required option in buffer.\r
+\r
+**/\r
+EFI_DHCP6_PACKET_OPTION *\r
+PxeBcParseDhcp6Options (\r
+ IN UINT8 *Buffer,\r
+ IN UINT32 Length,\r
+ IN UINT16 OptTag\r
+ )\r
+{\r
+ EFI_DHCP6_PACKET_OPTION *Option;\r
+ UINT32 Offset;\r
+\r
+ Option = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
+ Offset = 0;\r
+\r
+ //\r
+ // OpLen and OpCode here are both stored in network order.\r
+ //\r
+ while (Offset < Length) {\r
+\r
+ if (NTOHS (Option->OpCode) == OptTag) {\r
+\r
+ return Option;\r
+ }\r
+\r
+ Offset += (NTOHS(Option->OpLen) + 4);\r
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+\r
+/**\r
+ Build the options buffer for the DHCPv6 request packet.\r
+\r
+ @param[in] Private The pointer to PxeBc private data.\r
+ @param[out] OptList The pointer to the option pointer array.\r
+ @param[in] Buffer The pointer to the buffer to contain the option list.\r
+\r
+ @return Index The count of the built-in options.\r
+\r
+**/\r
+UINT32\r
+PxeBcBuildDhcp6Options (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ OUT EFI_DHCP6_PACKET_OPTION **OptList,\r
+ IN UINT8 *Buffer\r
+ )\r
+{\r
+ PXEBC_DHCP6_OPTION_ENTRY OptEnt;\r
+ UINT32 Index;\r
+ UINT16 Value;\r
+\r
+ Index = 0;\r
+ OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
+\r
+ //\r
+ // Append client option request option\r
+ //\r
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO);\r
+ OptList[Index]->OpLen = HTONS (4);\r
+ OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;\r
+ OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);\r
+ OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+ //\r
+ // Append client network device interface option\r
+ //\r
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI);\r
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_UNDI));\r
+ OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
+\r
+ if (Private->Nii != NULL) {\r
+ OptEnt.Undi->Type = Private->Nii->Type;\r
+ OptEnt.Undi->MajorVer = Private->Nii->MajorVer;\r
+ OptEnt.Undi->MinorVer = Private->Nii->MinorVer;\r
+ } else {\r
+ OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;\r
+ OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;\r
+ OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;\r
+ }\r
+\r
+ OptEnt.Undi->Reserved = 0;\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+ //\r
+ // Append client system architecture option\r
+ //\r
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH);\r
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));\r
+ OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;\r
+ Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);\r
+ CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));\r
+ Index++;\r
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
+\r
+ //\r
+ // Append vendor class option to store the PXE class identifier.\r
+ //\r
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);\r
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));\r
+ OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;\r
+ OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);\r
+ OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));\r
+ CopyMem (\r
+ &OptEnt.VendorClass->ClassId,\r
+ DEFAULT_CLASS_ID_DATA,\r
+ sizeof (PXEBC_CLASS_ID)\r
+ );\r
+ PxeBcUintnToAscDecWithFormat (\r
+ EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,\r
+ OptEnt.VendorClass->ClassId.ArchitectureType,\r
+ sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)\r
+ );\r
+\r
+ if (Private->Nii != NULL) {\r
+ CopyMem (\r
+ OptEnt.VendorClass->ClassId.InterfaceName,\r
+ Private->Nii->StringId,\r
+ sizeof (OptEnt.VendorClass->ClassId.InterfaceName)\r
+ );\r
+ PxeBcUintnToAscDecWithFormat (\r
+ Private->Nii->MajorVer,\r
+ OptEnt.VendorClass->ClassId.UndiMajor,\r
+ sizeof (OptEnt.VendorClass->ClassId.UndiMajor)\r
+ );\r
+ PxeBcUintnToAscDecWithFormat (\r
+ Private->Nii->MinorVer,\r
+ OptEnt.VendorClass->ClassId.UndiMinor,\r
+ sizeof (OptEnt.VendorClass->ClassId.UndiMinor)\r
+ );\r
+ }\r
+\r
+ Index++;\r
+\r
+ return Index;\r
+}\r
+\r
+\r
+/**\r
+ Cache the DHCPv6 packet.\r
+\r
+ @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.\r
+ @param[in] Src The pointer to the DHCPv6 packet to be cached.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp6Packet (\r
+ IN EFI_DHCP6_PACKET *Dst,\r
+ IN EFI_DHCP6_PACKET *Src\r
+ )\r
+{\r
+ ASSERT (Dst->Size >= Src->Length);\r
+\r
+ CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);\r
+ Dst->Length = Src->Length;\r
+}\r
+\r
+\r
+/**\r
+ Free all the nodes in the list for boot file.\r
+\r
+ @param[in] Head The pointer to the head of list.\r
+\r
+**/\r
+VOID\r
+PxeBcFreeBootFileOption (\r
+ IN LIST_ENTRY *Head\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ PXEBC_DHCP6_OPTION_NODE *Node;\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {\r
+ Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);\r
+ RemoveEntryList (Entry);\r
+ FreePool (Node);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Parse the Boot File URL option.\r
+\r
+ @param[out] FileName The pointer to the boot file name.\r
+ @param[in, out] SrvAddr The pointer to the boot server address.\r
+ @param[in] BootFile The pointer to the boot file URL option data.\r
+ @param[in] Length The length of the boot file URL option data.\r
+\r
+ @retval EFI_ABORTED User cancel operation.\r
+ @retval EFI_SUCCESS Selected the boot menu successfully.\r
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileUrl (\r
+ OUT UINT8 **FileName,\r
+ IN OUT EFI_IPv6_ADDRESS *SrvAddr,\r
+ IN CHAR8 *BootFile,\r
+ IN UINT16 Length\r
+ )\r
+{\r
+ UINT16 PrefixLen;\r
+ UINT8 *BootFileNamePtr;\r
+ UINT8 *BootFileName;\r
+ UINT16 BootFileNameLen;\r
+ CHAR8 *TmpStr;\r
+ CHAR8 *ServerAddressOption;\r
+ CHAR8 *ServerAddress;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // The format of the Boot File URL option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPT_BOOTFILE_URL | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // . bootfile-url (variable length) .\r
+ // | |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
+\r
+ //\r
+ // Based upon RFC 5970 and UEFI errata that will appear in chapter 21.3 of UEFI 2.3\r
+ // specification after 2.3 errata B and future UEFI Specifications after 2.3.\r
+ // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME\r
+ // As an example where the BOOTFILE_NAME is the EFI loader and\r
+ // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.\r
+ //\r
+ PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);\r
+\r
+ if (Length <= PrefixLen ||\r
+ CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ BootFile = BootFile + PrefixLen;\r
+ Length = (UINT16) (Length - PrefixLen);\r
+\r
+ TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);\r
+ if (TmpStr == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (TmpStr, BootFile, Length);\r
+ TmpStr[Length] = '\0';\r
+\r
+ //\r
+ // Get the part of SERVER_ADDRESS string.\r
+ //\r
+ ServerAddressOption = TmpStr;\r
+ if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {\r
+ FreePool (TmpStr);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ServerAddressOption ++;\r
+ ServerAddress = ServerAddressOption;\r
+ while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
+ ServerAddress++;\r
+ }\r
+\r
+ if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
+ FreePool (TmpStr);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *ServerAddress = '\0';\r
+\r
+ //\r
+ // Convert the string of server address to Ipv6 address format and store it.\r
+ //\r
+ Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (TmpStr);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Get the part of BOOTFILE_NAME string.\r
+ //\r
+ BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1);\r
+ if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
+ FreePool (TmpStr);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ++BootFileNamePtr;\r
+ BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);\r
+ if (BootFileNameLen != 0 || FileName != NULL) {\r
+ BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen);\r
+ if (BootFileName == NULL) {\r
+ FreePool (TmpStr);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (BootFileName, BootFileNamePtr, BootFileNameLen);\r
+ BootFileName[BootFileNameLen - 1] = '\0';\r
+ *FileName = BootFileName;\r
+ }\r
+\r
+\r
+ FreePool (TmpStr);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Parse the Boot File Parameter option.\r
+\r
+ @param[in] BootFilePara The pointer to boot file parameter option data.\r
+ @param[out] BootFileSize The pointer to the parsed boot file size.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.\r
+ @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileParam (\r
+ IN CHAR8 *BootFilePara,\r
+ OUT UINT16 *BootFileSize\r
+ )\r
+{\r
+ UINT16 Length;\r
+ UINT8 Index;\r
+ UINT8 Digit;\r
+ UINT32 Size;\r
+\r
+ CopyMem (&Length, BootFilePara, sizeof (UINT16));\r
+ Length = NTOHS (Length);\r
+\r
+ //\r
+ // The BootFile Size should be 1~5 byte ASCII strings\r
+ //\r
+ if (Length < 1 || Length > 5) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Extract the value of BootFile Size.\r
+ //\r
+ BootFilePara = BootFilePara + sizeof (UINT16);\r
+ Size = 0;\r
+ for (Index = 0; Index < Length; Index++) {\r
+ if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Size = (Size + Digit) * 10;\r
+ }\r
+\r
+ Size = Size / 10;\r
+ if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ *BootFileSize = (UINT16) Size;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Parse the cached DHCPv6 packet, including all the options.\r
+\r
+ @param[in] Cache6 The pointer to a cached DHCPv6 packet.\r
+\r
+ @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.\r
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp6Packet (\r
+ IN PXEBC_DHCP6_PACKET_CACHE *Cache6\r
+ )\r
+{\r
+ EFI_DHCP6_PACKET *Offer;\r
+ EFI_DHCP6_PACKET_OPTION **Options;\r
+ EFI_DHCP6_PACKET_OPTION *Option;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+ BOOLEAN IsProxyOffer;\r
+ BOOLEAN IsPxeOffer;\r
+ UINT32 Offset;\r
+ UINT32 Length;\r
+ UINT32 EnterpriseNum;\r
+\r
+ IsProxyOffer = TRUE;\r
+ IsPxeOffer = FALSE;\r
+ Offer = &Cache6->Packet.Offer;\r
+ Options = Cache6->OptList;\r
+\r
+ ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));\r
+\r
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);\r
+ Offset = 0;\r
+ Length = GET_DHCP6_OPTION_SIZE (Offer);\r
+\r
+ //\r
+ // OpLen and OpCode here are both stored in network order, since they are from original packet.\r
+ //\r
+ while (Offset < Length) {\r
+\r
+ if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {\r
+ Options[PXEBC_DHCP6_IDX_IA_NA] = Option;\r
+ } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {\r
+ //\r
+ // The server sends this option to inform the client about an URL to a boot file.\r
+ //\r
+ Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;\r
+ } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {\r
+ Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;\r
+ } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {\r
+ Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;\r
+ }\r
+\r
+ Offset += (NTOHS (Option->OpLen) + 4);\r
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);\r
+ }\r
+\r
+ //\r
+ // The offer with assigned client address is a proxy offer.\r
+ // An ia_na option, embeded with valid ia_addr option and a status_code of success.\r
+ //\r
+ Option = Options[PXEBC_DHCP6_IDX_IA_NA];\r
+ if (Option != NULL && NTOHS(Option->OpLen) >= 12) {\r
+ Option = PxeBcParseDhcp6Options (\r
+ Option->Data + 12,\r
+ NTOHS (Option->OpLen),\r
+ PXEBC_DHCP6_OPT_STATUS_CODE\r
+ );\r
+ if (Option != NULL && Option->Data[0] == 0) {\r
+ IsProxyOffer = FALSE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // The offer with "PXEClient" is a pxe offer.\r
+ //\r
+ Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];\r
+ EnterpriseNum = PXEBC_DHCP6_ENTERPRISE_NUM;\r
+ if (Option != NULL &&\r
+ NTOHS(Option->OpLen) >= 13 &&\r
+ CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&\r
+ CompareMem (&Option->Data[4], DEFAULT_CLASS_ID_DATA, 9) == 0) {\r
+ IsPxeOffer = TRUE;\r
+ }\r
+\r
+ //\r
+ // Determine offer type of the dhcp6 packet.\r
+ //\r
+ if (IsPxeOffer) {\r
+ //\r
+ // It's a binl offer only with PXEClient.\r
+ //\r
+ OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;\r
+ } else {\r
+ //\r
+ // It's a dhcp only offer, which is a pure dhcp6 offer packet.\r
+ //\r
+ OfferType = PxeOfferTypeDhcpOnly;\r
+ }\r
+\r
+ Cache6->OfferType = OfferType;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Cache the DHCPv6 ack packet, and parse it on demand.\r
+\r
+ @param[in] Private The pointer to PxeBc private data.\r
+ @param[in] Ack The pointer to the DHCPv6 ack packet.\r
+ @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyDhcp6Ack (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP6_PACKET *Ack,\r
+ IN BOOLEAN Verified\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);\r
+\r
+ if (Verified) {\r
+ //\r
+ // Parse the ack packet and store it into mode data if needed.\r
+ //\r
+ PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);\r
+ CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);\r
+ Mode->DhcpAckReceived = TRUE;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Cache the DHCPv6 proxy offer packet according to the received order.\r
+\r
+ @param[in] Private The pointer to PxeBc private data.\r
+ @param[in] OfferIndex The received order of offer packets.\r
+\r
+**/\r
+VOID\r
+PxeBcCopyDhcp6Proxy (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT32 OfferIndex\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_DHCP6_PACKET *Offer;\r
+\r
+ ASSERT (OfferIndex < Private->OfferNum);\r
+ ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+ Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;\r
+\r
+ //\r
+ // Cache the proxy offer packet and parse it.\r
+ //\r
+ PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);\r
+ PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);\r
+\r
+ //\r
+ // Store this packet into mode data.\r
+ //\r
+ CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);\r
+ Mode->ProxyOfferReceived = TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Retry to request bootfile name by the BINL offer.\r
+\r
+ @param[in] Private The pointer to PxeBc private data.\r
+ @param[in] Index The received order of offer packets.\r
+\r
+ @retval EFI_SUCCESS Successfully retried a request for the bootfile name.\r
+ @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRetryDhcp6Binl (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT32 Index\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ PXEBC_DHCP6_PACKET_CACHE *Offer;\r
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
+ EFI_IP_ADDRESS ServerIp;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
+ ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||\r
+ Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+ Private->IsDoDiscover = FALSE;\r
+ Offer = &Private->OfferBuffer[Index].Dhcp6;\r
+\r
+ ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+ //\r
+ // Parse out the next server address from the last offer, and store it\r
+ //\r
+ Status = PxeBcExtractBootFileUrl (\r
+ NULL,\r
+ &ServerIp.v6,\r
+ (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
+ NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.\r
+ //\r
+ Status = PxeBcDhcp6Discover (\r
+ Private,\r
+ 0,\r
+ NULL,\r
+ FALSE,\r
+ &ServerIp\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Cache6 = &Private->ProxyOffer.Dhcp6;\r
+ Status = PxeBcParseDhcp6Packet (Cache6);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&\r
+ Cache6->OfferType != PxeOfferTypeProxyWfm11a &&\r
+ Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
+ //\r
+ // This BINL ack doesn't have discovery option set or multicast option set\r
+ // or bootfile name specified.\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Mode->ProxyOfferReceived = TRUE;\r
+ CopyMem (\r
+ &Mode->ProxyOffer.Dhcpv6,\r
+ &Cache6->Packet.Offer.Dhcp6,\r
+ Cache6->Packet.Offer.Length\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.\r
+\r
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
+ @param[in] RcvdOffer The pointer to the received offer packet.\r
+\r
+**/\r
+VOID\r
+PxeBcCacheDhcp6Offer (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP6_PACKET *RcvdOffer\r
+ )\r
+{\r
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
+ EFI_DHCP6_PACKET *Offer;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+\r
+ Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;\r
+ Offer = &Cache6->Packet.Offer;\r
+\r
+ //\r
+ // Cache the content of DHCPv6 packet firstly.\r
+ //\r
+ PxeBcCacheDhcp6Packet (Offer, RcvdOffer);\r
+\r
+ //\r
+ // Validate the DHCPv6 packet, and parse the options and offer type.\r
+ //\r
+ if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
+ //\r
+ OfferType = Cache6->OfferType;\r
+ ASSERT (OfferType < PxeOfferTypeMax);\r
+ ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);\r
+\r
+ if (IS_PROXY_OFFER (OfferType)) {\r
+ //\r
+ // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.\r
+ //\r
+ Private->IsProxyRecved = TRUE;\r
+\r
+ if (OfferType == PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Cache all proxy BINL offers.\r
+ //\r
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+ Private->OfferCount[OfferType]++;\r
+ } else if (Private->OfferCount[OfferType] > 0) {\r
+ //\r
+ // Only cache the first PXE10/WFM11a offer, and discard the others.\r
+ //\r
+ Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
+ Private->OfferCount[OfferType] = 1;\r
+ } else {\r
+ return;\r
+ }\r
+ } else {\r
+ //\r
+ // It's a DHCPv6 offer with yiaddr, and cache them all.\r
+ //\r
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
+ Private->OfferCount[OfferType]++;\r
+ }\r
+\r
+ Private->OfferNum++;\r
+}\r
+\r
+\r
+/**\r
+ Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.\r
+\r
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcSelectDhcp6Offer (\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT32 OfferIndex;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+\r
+ Private->SelectIndex = 0;\r
+\r
+ if (Private->IsOfferSorted) {\r
+ //\r
+ // Select offer by default policy.\r
+ //\r
+ if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {\r
+ //\r
+ // 1. DhcpPxe10 offer\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {\r
+ //\r
+ // 2. DhcpWfm11a offer\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+ Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {\r
+ //\r
+ // 3. DhcpOnly offer and ProxyPxe10 offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+ Private->SelectProxyType = PxeOfferTypeProxyPxe10;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+ Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {\r
+ //\r
+ // 4. DhcpOnly offer and ProxyWfm11a offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+ Private->SelectProxyType = PxeOfferTypeProxyWfm11a;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {\r
+ //\r
+ // 5. DhcpBinl offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;\r
+\r
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
+ Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {\r
+ //\r
+ // 6. DhcpOnly offer and ProxyBinl offer.\r
+ //\r
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
+ Private->SelectProxyType = PxeOfferTypeProxyBinl;\r
+\r
+ } else {\r
+ //\r
+ // 7. DhcpOnly offer with bootfilename.\r
+ //\r
+ for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {\r
+ OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];\r
+ if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {\r
+ Private->SelectIndex = OfferIndex + 1;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // Select offer by received order.\r
+ //\r
+ for (Index = 0; Index < Private->OfferNum; Index++) {\r
+\r
+ OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;\r
+\r
+ if (IS_PROXY_OFFER (OfferType)) {\r
+ //\r
+ // Skip proxy offers\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (!Private->IsProxyRecved &&\r
+ OfferType == PxeOfferTypeDhcpOnly &&\r
+ Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
+ //\r
+ // Skip if DhcpOnly offer without any other proxy offers or bootfilename.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ Private->SelectIndex = Index + 1;\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Handle the DHCPv6 offer packet.\r
+\r
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
+\r
+ @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.\r
+ @retval EFI_NO_RESPONSE No response to the following request packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcHandleDhcp6Offer (\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
+ EFI_STATUS Status;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+ UINT32 ProxyIndex;\r
+ UINT32 SelectIndex;\r
+ UINT32 Index;\r
+\r
+ ASSERT (Private->SelectIndex > 0);\r
+ SelectIndex = (UINT32) (Private->SelectIndex - 1);\r
+ ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);\r
+ Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {\r
+ //\r
+ // DhcpBinl offer is selected, so need try to request bootfilename by this offer.\r
+ //\r
+ if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {\r
+ Status = EFI_NO_RESPONSE;\r
+ }\r
+ } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {\r
+\r
+ if (Private->IsProxyRecved) {\r
+ //\r
+ // DhcpOnly offer is selected, so need try to request bootfilename.\r
+ //\r
+ ProxyIndex = 0;\r
+ if (Private->IsOfferSorted) {\r
+ //\r
+ // The proxy offer should be determined if select by default policy.\r
+ // IsOfferSorted means all offers are labeled by OfferIndex.\r
+ //\r
+ ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);\r
+\r
+ if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Try all the cached ProxyBinl offer one by one to request bootfilename.\r
+ //\r
+ for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {\r
+\r
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];\r
+ if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {\r
+ break;\r
+ }\r
+ }\r
+ if (Index == Private->OfferCount[Private->SelectProxyType]) {\r
+ Status = EFI_NO_RESPONSE;\r
+ }\r
+ } else {\r
+ //\r
+ // For other proxy offers (pxe10 or wfm11a), only one is buffered.\r
+ //\r
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
+ }\r
+ } else {\r
+ //\r
+ // The proxy offer should not be determined if select by received order.\r
+ //\r
+ Status = EFI_NO_RESPONSE;\r
+\r
+ for (Index = 0; Index < Private->OfferNum; Index++) {\r
+\r
+ OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;\r
+\r
+ if (!IS_PROXY_OFFER (OfferType)) {\r
+ //\r
+ // Skip non proxy dhcp offers.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (OfferType == PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Try all the cached ProxyBinl offer one by one to request bootfilename.\r
+ //\r
+ if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {\r
+ continue;\r
+ }\r
+ }\r
+\r
+ Private->SelectProxyType = OfferType;\r
+ ProxyIndex = Index;\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {\r
+ //\r
+ // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.\r
+ //\r
+ PxeBcCopyDhcp6Proxy (Private, ProxyIndex);\r
+ }\r
+ } else {\r
+ //\r
+ // Othewise, the bootfilename must be included in DhcpOnly offer.\r
+ //\r
+ ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // All PXE boot information is ready by now.\r
+ //\r
+ PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);\r
+ Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Unregister the address by Ip6Config protocol.\r
+\r
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcUnregisterIp6Address (\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {\r
+ //\r
+ // PXE driver change the policy of IP6 driver, it's a chance to recover.\r
+ // Keep the point and there is no enough requirements to do recovery.\r
+ //\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Register the ready address by Ip6Config protocol.\r
+\r
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
+ @param[in] Address The pointer to the ready address.\r
+\r
+ @retval EFI_SUCCESS Registered the address succesfully.\r
+ @retval Others Failed to register the address.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRegisterIp6Address (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_IPv6_ADDRESS *Address\r
+ )\r
+{\r
+ EFI_IP6_PROTOCOL *Ip6;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
+ EFI_IP6_CONFIG_POLICY Policy;\r
+ EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;\r
+ UINTN DataSize;\r
+ EFI_EVENT TimeOutEvt;\r
+ EFI_EVENT MappedEvt;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+ TimeOutEvt = NULL;\r
+ MappedEvt = NULL;\r
+ DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
+ Ip6Cfg = Private->Ip6Cfg;\r
+ Ip6 = Private->Ip6;\r
+\r
+ ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
+ CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ //\r
+ // Get and store the current policy of IP6 driver.\r
+ //\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypePolicy,\r
+ &DataSize,\r
+ &Private->Ip6Policy\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // There is no channel between IP6 and PXE driver about address setting,\r
+ // so it has to set the new address by Ip6ConfigProtocol manually.\r
+ //\r
+ Policy = Ip6ConfigPolicyManual;\r
+ Status = Ip6Cfg->SetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypePolicy,\r
+ sizeof(EFI_IP6_CONFIG_POLICY),\r
+ &Policy\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // There is no need to recover later.\r
+ //\r
+ Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Create a timer as setting address timeout event since DAD in IP6 driver.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &TimeOutEvt\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Create a notify event to set address flag when DAD if IP6 driver succeeded.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcCommonNotify,\r
+ &Private->IsAddressOk,\r
+ &MappedEvt\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Ip6Cfg->RegisterDataNotify (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ MappedEvt\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Ip6Cfg->SetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),\r
+ &CfgAddr\r
+ );\r
+ if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Start the 5 secondes timer to wait for setting address.\r
+ //\r
+ Status = EFI_NO_MAPPING;\r
+ gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT);\r
+\r
+ while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
+ Ip6->Poll (Ip6);\r
+ if (Private->IsAddressOk) {\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+ if (MappedEvt != NULL) {\r
+ Ip6Cfg->UnregisterDataNotify (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeManualAddress,\r
+ MappedEvt\r
+ );\r
+ gBS->CloseEvent (MappedEvt);\r
+ }\r
+ if (TimeOutEvt != NULL) {\r
+ gBS->CloseEvent (TimeOutEvt);\r
+ }\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
+ to intercept events that occurred in the configuration process.\r
+\r
+ @param[in] This The pointer to the EFI DHCPv6 Protocol.\r
+ @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().\r
+ @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.\r
+ @param[in] Dhcp6Event The event that occurs in the current state, which usually means a\r
+ state transition.\r
+ @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.\r
+ @param[out] NewPacket The packet that is used to replace the Packet above.\r
+\r
+ @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.\r
+ @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol\r
+ driver will continue to wait for more packets.\r
+ @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDhcp6CallBack (\r
+ IN EFI_DHCP6_PROTOCOL *This,\r
+ IN VOID *Context,\r
+ IN EFI_DHCP6_STATE CurrentState,\r
+ IN EFI_DHCP6_EVENT Dhcp6Event,\r
+ IN EFI_DHCP6_PACKET *Packet,\r
+ OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
+ EFI_DHCP6_PACKET *SelectAd;\r
+ EFI_STATUS Status;\r
+ BOOLEAN Received;\r
+\r
+ if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&\r
+ (Dhcp6Event != Dhcp6SelectAdvertise) &&\r
+ (Dhcp6Event != Dhcp6SendSolicit) &&\r
+ (Dhcp6Event != Dhcp6SendRequest) &&\r
+ (Dhcp6Event != Dhcp6RcvdReply)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ ASSERT (Packet != NULL);\r
+\r
+ Private = (PXEBC_PRIVATE_DATA *) Context;\r
+ Mode = Private->PxeBc.Mode;\r
+ Callback = Private->PxeBcCallback;\r
+\r
+ //\r
+ // Callback to user when any traffic ocurred if has.\r
+ //\r
+ if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {\r
+ Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);\r
+ Status = Callback->Callback (\r
+ Callback,\r
+ Private->Function,\r
+ Received,\r
+ Packet->Length,\r
+ (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6\r
+ );\r
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+ return EFI_ABORTED;\r
+ }\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ switch (Dhcp6Event) {\r
+\r
+ case Dhcp6SendSolicit:\r
+ //\r
+ // Cache the dhcp discover packet to mode data directly.\r
+ //\r
+ CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);\r
+ break;\r
+\r
+ case Dhcp6RcvdAdvertise:\r
+ Status = EFI_NOT_READY;\r
+ if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {\r
+ //\r
+ // Cache the dhcp offers to OfferBuffer[] for select later, and record\r
+ // the OfferIndex and OfferCount.\r
+ //\r
+ PxeBcCacheDhcp6Offer (Private, Packet);\r
+ }\r
+ break;\r
+\r
+ case Dhcp6SendRequest:\r
+ //\r
+ // Store the request packet as seed packet for discover.\r
+ //\r
+ if (Private->Dhcp6Request != NULL) {\r
+ FreePool (Private->Dhcp6Request);\r
+ }\r
+ Private->Dhcp6Request = AllocateZeroPool (Packet->Size);\r
+ if (Private->Dhcp6Request != NULL) {\r
+ CopyMem (Private->Dhcp6Request, Packet, Packet->Size);\r
+ }\r
+ break;\r
+\r
+ case Dhcp6SelectAdvertise:\r
+ //\r
+ // Select offer by the default policy or by order, and record the SelectIndex\r
+ // and SelectProxyType.\r
+ //\r
+ PxeBcSelectDhcp6Offer (Private);\r
+\r
+ if (Private->SelectIndex == 0) {\r
+ Status = EFI_ABORTED;\r
+ } else {\r
+ ASSERT (NewPacket != NULL);\r
+ SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;\r
+ *NewPacket = AllocateZeroPool (SelectAd->Size);\r
+ ASSERT (*NewPacket != NULL);\r
+ CopyMem (*NewPacket, SelectAd, SelectAd->Size);\r
+ }\r
+ break;\r
+\r
+ case Dhcp6RcvdReply:\r
+ //\r
+ // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data\r
+ // without verification.\r
+ //\r
+ ASSERT (Private->SelectIndex != 0);\r
+ PxeBcCopyDhcp6Ack (Private, Packet, FALSE);\r
+ break;\r
+\r
+ default:\r
+ ASSERT (0);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+ @param[in] Private The pointer to PxeBc private data.\r
+ @param[in] Type PxeBc option boot item type.\r
+ @param[in] Layer The pointer to option boot item layer.\r
+ @param[in] UseBis Use BIS or not.\r
+ @param[in] DestIp The pointer to the server address.\r
+\r
+ @retval EFI_SUCCESS Successfully discovered the boot file.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
+ @retval Others Failed to discover the boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Discover (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN UINT16 *Layer,\r
+ IN BOOLEAN UseBis,\r
+ IN EFI_IP_ADDRESS *DestIp\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_UDP_PORT SrcPort;\r
+ EFI_PXE_BASE_CODE_UDP_PORT DestPort;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover;\r
+ UINTN DiscoverLen;\r
+ EFI_DHCP6_PACKET *Request;\r
+ UINTN RequestLen;\r
+ EFI_DHCP6_PACKET *Reply;\r
+ UINT8 *RequestOpt;\r
+ UINT8 *DiscoverOpt;\r
+ UINTN ReadSize;\r
+ UINT16 OpFlags;\r
+ UINT16 OpCode;\r
+ UINT16 OpLen;\r
+ UINT32 Xid;\r
+ EFI_STATUS Status;\r
+\r
+ PxeBc = &Private->PxeBc;\r
+ Mode = PxeBc->Mode;\r
+ Request = Private->Dhcp6Request;\r
+ SrcPort = PXEBC_BS_DISCOVER_PORT;\r
+ DestPort = PXEBC_BS_DISCOVER_PORT;\r
+ OpFlags = 0;\r
+\r
+ if (!UseBis && Layer != NULL) {\r
+ *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;\r
+ }\r
+\r
+ if (Request == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));\r
+ if (Discover == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Build the discover packet by the cached request packet before.\r
+ //\r
+ Xid = NET_RANDOM (NetRandomInitSeed ());\r
+ Discover->TransactionId = HTONL (Xid);\r
+ Discover->MessageType = Request->Dhcp6.Header.MessageType;\r
+ RequestOpt = Request->Dhcp6.Option;\r
+ DiscoverOpt = Discover->DhcpOptions;\r
+ DiscoverLen = sizeof (EFI_DHCP6_HEADER);\r
+ RequestLen = DiscoverLen;\r
+\r
+ while (RequestLen < Request->Length) {\r
+ OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);\r
+ OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);\r
+ if (OpCode != EFI_DHCP6_IA_TYPE_NA &&\r
+ OpCode != EFI_DHCP6_IA_TYPE_TA) {\r
+ //\r
+ // Copy all the options except IA option.\r
+ //\r
+ CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);\r
+ DiscoverOpt += (OpLen + 4);\r
+ DiscoverLen += (OpLen + 4);\r
+ }\r
+ RequestOpt += (OpLen + 4);\r
+ RequestLen += (OpLen + 4);\r
+ }\r
+\r
+ Status = PxeBc->UdpWrite (\r
+ PxeBc,\r
+ OpFlags,\r
+ &Private->ServerIp,\r
+ &DestPort,\r
+ NULL,\r
+ &Private->StationIp,\r
+ &SrcPort,\r
+ NULL,\r
+ NULL,\r
+ &DiscoverLen,\r
+ (VOID *) Discover\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Cache the right PXE reply packet here, set valid flag later.\r
+ // Especially for PXE discover packet, store it into mode data here.\r
+ //\r
+ if (Private->IsDoDiscover) {\r
+ CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);\r
+ Reply = &Private->PxeReply.Dhcp6.Packet.Ack;\r
+ } else {\r
+ Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;\r
+ }\r
+ ReadSize = (UINTN) Reply->Size;\r
+\r
+ Status = PxeBc->UdpRead (\r
+ PxeBc,\r
+ OpFlags,\r
+ &Private->StationIp,\r
+ &SrcPort,\r
+ &Private->ServerIp,\r
+ &DestPort,\r
+ NULL,\r
+ NULL,\r
+ &ReadSize,\r
+ (VOID *) &Reply->Dhcp6\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.\r
+\r
+ @param[in] Private The pointer to PxeBc private data.\r
+ @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL\r
+\r
+ @retval EFI_SUCCESS The S.A.R.R. process successfully finished.\r
+ @retval Others Failed to finish the S.A.R.R. process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Sarr (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP6_PROTOCOL *Dhcp6\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *PxeMode;\r
+ EFI_DHCP6_CONFIG_DATA Config;\r
+ EFI_DHCP6_MODE_DATA Mode;\r
+ EFI_DHCP6_RETRANSMISSION *Retransmit;\r
+ EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];\r
+ UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];\r
+ UINT32 OptCount;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+ PxeMode = Private->PxeBc.Mode;\r
+\r
+ //\r
+ // Build option list for the request packet.\r
+ //\r
+ OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer);\r
+ ASSERT (OptCount> 0);\r
+\r
+ Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
+ if (Retransmit == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
+ ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
+\r
+ Config.OptionCount = OptCount;\r
+ Config.OptionList = OptList;\r
+ Config.Dhcp6Callback = PxeBcDhcp6CallBack;\r
+ Config.CallbackContext = Private;\r
+ Config.IaInfoEvent = NULL;\r
+ Config.RapidCommit = FALSE;\r
+ Config.ReconfigureAccept = FALSE;\r
+ Config.IaDescriptor.IaId = 1;\r
+ Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;\r
+ Config.SolicitRetransmission = Retransmit;\r
+ Retransmit->Irt = 4;\r
+ Retransmit->Mrc = 4;\r
+ Retransmit->Mrt = 32;\r
+ Retransmit->Mrd = 60;\r
+\r
+ //\r
+ // Configure the DHCPv6 instance for PXE boot.\r
+ //\r
+ Status = Dhcp6->Configure (Dhcp6, &Config);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Retransmit);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Initialize the record fields for DHCPv6 offer in private data.\r
+ //\r
+ Private->IsProxyRecved = FALSE;\r
+ Private->OfferNum = 0;\r
+ Private->SelectIndex = 0;\r
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
+\r
+\r
+ //\r
+ // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
+ //\r
+ Status = Dhcp6->Start (Dhcp6);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_ICMP_ERROR) {\r
+ PxeMode->IcmpErrorReceived = TRUE;\r
+ }\r
+ Dhcp6->Configure (Dhcp6, NULL);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Get the acquired IPv6 address and store them.\r
+ //\r
+ Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp6->Stop (Dhcp6);\r
+ return Status;\r
+ }\r
+\r
+ ASSERT (Mode.Ia->State == Dhcp6Bound);\r
+ CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp6->Stop (Dhcp6);\r
+ return Status;\r
+ }\r
+\r
+ Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ PxeBcUnregisterIp6Address (Private);\r
+ Dhcp6->Stop (Dhcp6);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the selected offer whether BINL retry is needed.\r
+ //\r
+ Status = PxeBcHandleDhcp6Offer (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ PxeBcUnregisterIp6Address (Private);\r
+ Dhcp6->Stop (Dhcp6);\r
+ return Status;\r
+ }\r
+\r
+ AsciiPrint ("\n Station IP address is ");\r
+\r
+ PxeBcShowIp6Addr (&Private->StationIp.v6);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Functions declaration related with DHCPv6 for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_DHCP6_H__\r
+#define __EFI_PXEBC_DHCP6_H__\r
+\r
+#define PXEBC_DHCP6_OPTION_MAX_NUM 16\r
+#define PXEBC_DHCP6_OPTION_MAX_SIZE 312\r
+#define PXEBC_DHCP6_PACKET_MAX_SIZE 1472\r
+#define PXEBC_DHCP6_MAPPING_TIMEOUT 50000000 // 5 seconds, unit is 10nanosecond.\r
+#define PXEBC_IP6_POLICY_MAX 0xff\r
+\r
+#define PXEBC_DHCP6_S_PORT 547\r
+#define PXEBC_DHCP6_C_PORT 546\r
+\r
+#define PXEBC_DHCP6_OPT_CLIENT_ID 1\r
+#define PXEBC_DHCP6_OPT_SERVER_ID 2\r
+#define PXEBC_DHCP6_OPT_IA_NA 3\r
+#define PXEBC_DHCP6_OPT_IA_TA 4\r
+#define PXEBC_DHCP6_OPT_IAADDR 5\r
+#define PXEBC_DHCP6_OPT_ORO 6\r
+#define PXEBC_DHCP6_OPT_PREFERENCE 7\r
+#define PXEBC_DHCP6_OPT_ELAPSED_TIME 8\r
+#define PXEBC_DHCP6_OPT_REPLAY_MSG 9\r
+#define PXEBC_DHCP6_OPT_AUTH 11\r
+#define PXEBC_DHCP6_OPT_UNICAST 12\r
+#define PXEBC_DHCP6_OPT_STATUS_CODE 13\r
+#define PXEBC_DHCP6_OPT_RAPID_COMMIT 14\r
+#define PXEBC_DHCP6_OPT_USER_CLASS 15\r
+#define PXEBC_DHCP6_OPT_VENDOR_CLASS 16\r
+#define PXEBC_DHCP6_OPT_VENDOR_OPTS 17\r
+#define PXEBC_DHCP6_OPT_INTERFACE_ID 18\r
+#define PXEBC_DHCP6_OPT_RECONFIG_MSG 19\r
+#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT 20\r
+#define PXEBC_DHCP6_OPT_BOOT_FILE_URL 59 // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM 60 // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_OPT_ARCH 61 // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_OPT_UNDI 62 // Assigned by IANA, RFC 5970\r
+#define PXEBC_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's\r
+#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes.\r
+\r
+\r
+#define PXEBC_DHCP6_IDX_IA_NA 0\r
+#define PXEBC_DHCP6_IDX_BOOT_FILE_URL 1\r
+#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM 2\r
+#define PXEBC_DHCP6_IDX_VENDOR_CLASS 3\r
+#define PXEBC_DHCP6_IDX_MAX 4\r
+\r
+#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX "tftp://"\r
+#define PXEBC_TFTP_URL_SEPARATOR '/'\r
+#define PXEBC_ADDR_START_DELIMITER '['\r
+#define PXEBC_ADDR_END_DELIMITER ']'\r
+\r
+#define GET_NEXT_DHCP6_OPTION(Opt) \\r
+ (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \\r
+ sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1)\r
+\r
+#define GET_DHCP6_OPTION_SIZE(Pkt) \\r
+ ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER))\r
+\r
+#define IS_PROXY_OFFER(Type) \\r
+ ((Type) == PxeOfferTypeProxyBinl || \\r
+ (Type) == PxeOfferTypeProxyPxe10 || \\r
+ (Type) == PxeOfferTypeProxyWfm11a)\r
+\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+ UINT16 OpCode[256];\r
+} PXEBC_DHCP6_OPTION_ORO;\r
+\r
+typedef struct {\r
+ UINT8 Type;\r
+ UINT8 MajorVer;\r
+ UINT8 MinorVer;\r
+ UINT8 Reserved;\r
+} PXEBC_DHCP6_OPTION_UNDI;\r
+\r
+typedef struct {\r
+ UINT16 Type;\r
+} PXEBC_DHCP6_OPTION_ARCH;\r
+\r
+typedef struct {\r
+ UINT8 ClassIdentifier[10];\r
+ UINT8 ArchitecturePrefix[5];\r
+ UINT8 ArchitectureType[5];\r
+ UINT8 Lit3[1];\r
+ UINT8 InterfaceName[4];\r
+ UINT8 Lit4[1];\r
+ UINT8 UndiMajor[3];\r
+ UINT8 UndiMinor[3];\r
+} PXEBC_CLASS_ID;\r
+\r
+typedef struct {\r
+ UINT32 Vendor;\r
+ UINT16 ClassLen;\r
+ PXEBC_CLASS_ID ClassId;\r
+} PXEBC_DHCP6_OPTION_VENDOR_CLASS;\r
+\r
+#pragma pack()\r
+\r
+typedef union {\r
+ PXEBC_DHCP6_OPTION_ORO *Oro;\r
+ PXEBC_DHCP6_OPTION_UNDI *Undi;\r
+ PXEBC_DHCP6_OPTION_ARCH *Arch;\r
+ PXEBC_DHCP6_OPTION_VENDOR_CLASS *VendorClass;\r
+} PXEBC_DHCP6_OPTION_ENTRY;\r
+\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ EFI_DHCP6_PACKET_OPTION *Option;\r
+ UINT8 Precedence;\r
+} PXEBC_DHCP6_OPTION_NODE;\r
+\r
+typedef union {\r
+ EFI_DHCP6_PACKET Offer;\r
+ EFI_DHCP6_PACKET Ack;\r
+ UINT8 Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE];\r
+} PXEBC_DHCP6_PACKET;\r
+\r
+typedef struct {\r
+ PXEBC_DHCP6_PACKET Packet;\r
+ PXEBC_OFFER_TYPE OfferType;\r
+ EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX];\r
+} PXEBC_DHCP6_PACKET_CACHE;\r
+\r
+\r
+/**\r
+ Free all the nodes in the boot file list.\r
+\r
+ @param[in] Head The pointer to the head of the list.\r
+\r
+**/\r
+VOID\r
+PxeBcFreeBootFileOption (\r
+ IN LIST_ENTRY *Head\r
+ );\r
+\r
+\r
+/**\r
+ Parse the Boot File URL option.\r
+\r
+ @param[out] FileName The pointer to the boot file name.\r
+ @param[in, out] SrvAddr The pointer to the boot server address.\r
+ @param[in] BootFile The pointer to the boot file URL option data.\r
+ @param[in] Length Length of the boot file URL option data.\r
+\r
+ @retval EFI_ABORTED User canceled the operation.\r
+ @retval EFI_SUCCESS Selected the boot menu successfully.\r
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileUrl (\r
+ OUT UINT8 **FileName,\r
+ IN OUT EFI_IPv6_ADDRESS *SrvAddr,\r
+ IN CHAR8 *BootFile,\r
+ IN UINT16 Length\r
+ );\r
+\r
+\r
+/**\r
+ Parse the Boot File Parameter option.\r
+\r
+ @param[in] BootFilePara The pointer to the boot file parameter option data.\r
+ @param[out] BootFileSize The pointer to the parsed boot file size.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.\r
+ @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcExtractBootFileParam (\r
+ IN CHAR8 *BootFilePara,\r
+ OUT UINT16 *BootFileSize\r
+ );\r
+\r
+\r
+/**\r
+ Parse the cached DHCPv6 packet, including all the options.\r
+\r
+ @param[in] Cache6 The pointer to a cached DHCPv6 packet.\r
+\r
+ @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.\r
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid packet.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcParseDhcp6Packet (\r
+ IN PXEBC_DHCP6_PACKET_CACHE *Cache6\r
+ );\r
+\r
+\r
+/**\r
+ Register the ready address by Ip6Config protocol.\r
+\r
+ @param[in] Private The pointer to the PxeBc private data.\r
+ @param[in] Address The pointer to the ready address.\r
+\r
+ @retval EFI_SUCCESS Registered the address succesfully.\r
+ @retval Others Failed to register the address.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcRegisterIp6Address (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_IPv6_ADDRESS *Address\r
+ );\r
+\r
+\r
+/**\r
+ Unregister the address by Ip6Config protocol.\r
+\r
+ @param[in] Private The pointer to the PxeBc private data.\r
+\r
+**/\r
+VOID\r
+PxeBcUnregisterIp6Address (\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ );\r
+\r
+\r
+/**\r
+ Build and send out the request packet for the bootfile, and parse the reply.\r
+\r
+ @param[in] Private The pointer to the PxeBc private data.\r
+ @param[in] Type PxeBc option boot item type.\r
+ @param[in] Layer The pointer to the option boot item layer.\r
+ @param[in] UseBis Use BIS or not.\r
+ @param[in] DestIp The pointer to the server address.\r
+\r
+ @retval EFI_SUCCESS Successfully discovered theboot file.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.\r
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
+ @retval Others Failed to discover boot file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Discover (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN UINT16 Type,\r
+ IN UINT16 *Layer,\r
+ IN BOOLEAN UseBis,\r
+ IN EFI_IP_ADDRESS *DestIp\r
+ );\r
+\r
+\r
+/**\r
+ Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.\r
+\r
+ @param[in] Private The pointer to the PxeBc private data.\r
+ @param[in] Dhcp6 The pointer to EFI_DHCP6_PROTOCOL.\r
+\r
+ @retval EFI_SUCCESS The S.A.R.R. process successfully finished.\r
+ @retval Others Failed to finish the S.A.R.R. process.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcDhcp6Sarr (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_DHCP6_PROTOCOL *Dhcp6\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ Driver Binding functions implementationfor for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gPxeBcDriverBinding = {\r
+ PxeBcDriverBindingSupported,\r
+ PxeBcDriverBindingStart,\r
+ PxeBcDriverBindingStop,\r
+ 0xa,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+\r
+/**\r
+ Get the Nic handle using any child handle in the IPv4 stack.\r
+\r
+ @param[in] ControllerHandle Pointer to child handle over IPv4.\r
+\r
+ @return NicHandle The pointer to the Nic handle.\r
+\r
+**/\r
+EFI_HANDLE\r
+PxeBcGetNicByIp4Children (\r
+ IN EFI_HANDLE ControllerHandle\r
+ )\r
+{\r
+ EFI_HANDLE NicHandle;\r
+\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ return NULL;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return NicHandle;\r
+}\r
+\r
+\r
+/**\r
+ Get the Nic handle using any child handle in the IPv6 stack.\r
+\r
+ @param[in] ControllerHandle Pointer to child handle over IPv6.\r
+\r
+ @return NicHandle The pointer to the Nic handle.\r
+\r
+**/\r
+EFI_HANDLE\r
+PxeBcGetNicByIp6Children (\r
+ IN EFI_HANDLE ControllerHandle\r
+ )\r
+{\r
+ EFI_HANDLE NicHandle;\r
+\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid);\r
+ if (NicHandle == NULL) {\r
+ return NULL;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return NicHandle;\r
+}\r
+\r
+\r
+/**\r
+ Destroy the opened instances based on IPv4.\r
+\r
+ @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.\r
+ @param[in] Private Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcDestroyIp4Children (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ ASSERT(Private != NULL);\r
+\r
+ if (Private->ArpChild != NULL) {\r
+ //\r
+ // Close Arp for PxeBc->Arp and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->ArpChild,\r
+ &gEfiArpProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiArpServiceBindingProtocolGuid,\r
+ Private->ArpChild\r
+ );\r
+ }\r
+\r
+ if (Private->Ip4Child != NULL) {\r
+ //\r
+ // Close Ip4 for background ICMP error message and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Ip4Child,\r
+ &gEfiIp4ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiIp4ServiceBindingProtocolGuid,\r
+ Private->Ip4Child\r
+ );\r
+ }\r
+\r
+ if (Private->Udp4WriteChild != NULL) {\r
+ //\r
+ // Close Udp4 for PxeBc->UdpWrite and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Udp4WriteChild,\r
+ &gEfiUdp4ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp4ServiceBindingProtocolGuid,\r
+ Private->Udp4WriteChild\r
+ );\r
+ }\r
+\r
+ if (Private->Udp4ReadChild != NULL) {\r
+ //\r
+ // Close Udp4 for PxeBc->UdpRead and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Udp4ReadChild,\r
+ &gEfiUdp4ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp4ServiceBindingProtocolGuid,\r
+ Private->Udp4ReadChild\r
+ );\r
+ }\r
+\r
+ if (Private->Mtftp4Child != NULL) {\r
+ //\r
+ // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Mtftp4Child,\r
+ &gEfiMtftp4ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ Private->Mtftp4Child\r
+ );\r
+ }\r
+\r
+ if (Private->Dhcp4Child != NULL) {\r
+ //\r
+ // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Dhcp4Child,\r
+ &gEfiDhcp4ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiDhcp4ServiceBindingProtocolGuid,\r
+ Private->Dhcp4Child\r
+ );\r
+ }\r
+\r
+ if (Private->Ip4Nic != NULL) {\r
+ //\r
+ // Close PxeBc from the parent Nic handle and destroy the virtual handle.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Controller,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Ip4Nic->Controller\r
+ );\r
+\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Private->Ip4Nic->Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Private->Ip4Nic->DevicePath,\r
+ &gEfiLoadFileProtocolGuid,\r
+ &Private->Ip4Nic->LoadFile,\r
+ NULL\r
+ );\r
+ FreePool (Private->Ip4Nic);\r
+ }\r
+\r
+ Private->ArpChild = NULL;\r
+ Private->Ip4Child = NULL;\r
+ Private->Udp4WriteChild = NULL;\r
+ Private->Udp4ReadChild = NULL;\r
+ Private->Mtftp4Child = NULL;\r
+ Private->Dhcp4Child = NULL;\r
+ Private->Ip4Nic = NULL;\r
+}\r
+\r
+\r
+/**\r
+ Destroy the opened instances based on IPv6.\r
+\r
+ @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.\r
+ @param[in] Private Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+**/\r
+VOID\r
+PxeBcDestroyIp6Children (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ ASSERT(Private != NULL);\r
+\r
+ if (Private->Ip6Child != NULL) {\r
+ //\r
+ // Close Ip6 for Ip6->Ip6Config and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Ip6Child,\r
+ &gEfiIp6ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ Private->Ip6Child\r
+ );\r
+ }\r
+\r
+ if (Private->Udp6WriteChild != NULL) {\r
+ //\r
+ // Close Udp6 for PxeBc->UdpWrite and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Udp6WriteChild,\r
+ &gEfiUdp6ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ Private->Udp6WriteChild\r
+ );\r
+ }\r
+\r
+ if (Private->Udp6ReadChild != NULL) {\r
+ //\r
+ // Close Udp6 for PxeBc->UdpRead and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Udp6ReadChild,\r
+ &gEfiUdp6ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ Private->Udp6ReadChild\r
+ );\r
+ }\r
+\r
+ if (Private->Mtftp6Child != NULL) {\r
+ //\r
+ // Close Mtftp6 for PxeBc->Mtftp and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Mtftp6Child,\r
+ &gEfiMtftp6ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiMtftp6ServiceBindingProtocolGuid,\r
+ Private->Mtftp6Child\r
+ );\r
+ }\r
+\r
+ if (Private->Dhcp6Child != NULL) {\r
+ //\r
+ // Close Dhcp6 for PxeBc->Dhcp and destroy the instance.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Dhcp6Child,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Controller\r
+ );\r
+\r
+ NetLibDestroyServiceChild (\r
+ Private->Controller,\r
+ This->DriverBindingHandle,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ Private->Dhcp6Child\r
+ );\r
+ }\r
+\r
+ if (Private->Ip6Nic != NULL) {\r
+ //\r
+ // Close PxeBc from the parent Nic handle and destroy the virtual handle.\r
+ //\r
+ gBS->CloseProtocol (\r
+ Private->Controller,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Private->Ip6Nic->Controller\r
+ );\r
+ gBS->UninstallMultipleProtocolInterfaces (\r
+ Private->Ip6Nic->Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Private->Ip6Nic->DevicePath,\r
+ &gEfiLoadFileProtocolGuid,\r
+ &Private->Ip6Nic->LoadFile,\r
+ NULL\r
+ );\r
+ FreePool (Private->Ip6Nic);\r
+ }\r
+\r
+ Private->Ip6Child = NULL;\r
+ Private->Udp6WriteChild = NULL;\r
+ Private->Udp6ReadChild = NULL;\r
+ Private->Mtftp6Child = NULL;\r
+ Private->Dhcp6Child = NULL;\r
+ Private->Ip6Nic = NULL;\r
+ Private->Mode.Ipv6Available = FALSE;\r
+}\r
+\r
+\r
+/**\r
+ Create the opened instances based on IPv4.\r
+\r
+ @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL.\r
+ @param[in] ControllerHandle Handle of the child to destroy.\r
+ @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+ @retval EFI_SUCCESS The instances based on IPv4 were all created successfully.\r
+ @retval Others An unexpected error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcCreateIp4Children (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IPv4_DEVICE_PATH Ip4Node;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_UDP4_CONFIG_DATA *Udp4CfgData;\r
+ EFI_IP4_CONFIG_DATA *Ip4CfgData;\r
+ EFI_IP4_MODE_DATA Ip4ModeData;\r
+\r
+ if (Private->Ip4Nic != NULL) {\r
+ //\r
+ // Already created before.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiDhcp4ServiceBindingProtocolGuid,\r
+ &Private->Dhcp4Child\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Dhcp4Child,\r
+ &gEfiDhcp4ProtocolGuid,\r
+ (VOID **) &Private->Dhcp4,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ &Private->Mtftp4Child\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Mtftp4Child,\r
+ &gEfiMtftp4ProtocolGuid,\r
+ (VOID **) &Private->Mtftp4,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp4ServiceBindingProtocolGuid,\r
+ &Private->Udp4ReadChild\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Udp4ReadChild,\r
+ &gEfiUdp4ProtocolGuid,\r
+ (VOID **) &Private->Udp4Read,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp4ServiceBindingProtocolGuid,\r
+ &Private->Udp4WriteChild\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Udp4WriteChild,\r
+ &gEfiUdp4ProtocolGuid,\r
+ (VOID **) &Private->Udp4Write,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Arp child and open Arp protocol for PxeBc->Arp.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiArpServiceBindingProtocolGuid,\r
+ &Private->ArpChild\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->ArpChild,\r
+ &gEfiArpProtocolGuid,\r
+ (VOID **) &Private->Arp,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Ip4 child and open Ip4 protocol for background ICMP packets.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiIp4ServiceBindingProtocolGuid,\r
+ &Private->Ip4Child\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Ip4Child,\r
+ &gEfiIp4ProtocolGuid,\r
+ (VOID **) &Private->Ip4,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Get max packet size from Ip4 to calculate block size for Tftp later.\r
+ //\r
+ Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize;\r
+\r
+ Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));\r
+ if (Private->Ip4Nic == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Private->Ip4Nic->Private = Private;\r
+ Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;\r
+\r
+ //\r
+ // Create a device path node for Ipv4 virtual nic, and append it.\r
+ //\r
+ ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH));\r
+ Ip4Node.Header.Type = MESSAGING_DEVICE_PATH;\r
+ Ip4Node.Header.SubType = MSG_IPv4_DP;\r
+ Ip4Node.StaticIpAddress = FALSE;\r
+\r
+ SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node));\r
+\r
+ Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header);\r
+\r
+ if (Private->Ip4Nic->DevicePath == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ CopyMem (\r
+ &Private->Ip4Nic->LoadFile,\r
+ &gLoadFileProtocolTemplate,\r
+ sizeof (EFI_LOAD_FILE_PROTOCOL)\r
+ );\r
+\r
+ //\r
+ // Create a new handle for IPv4 virtual nic,\r
+ // and install PxeBaseCode, LoadFile and DevicePath protocols.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Private->Ip4Nic->Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Private->Ip4Nic->DevicePath,\r
+ &gEfiLoadFileProtocolGuid,\r
+ &Private->Ip4Nic->LoadFile,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Open PxeBaseCode protocol by child to setup a parent-child relationship between\r
+ // real NIC handle and the virtual IPv4 NIC handle.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ (VOID **) &PxeBc,\r
+ This->DriverBindingHandle,\r
+ Private->Ip4Nic->Controller,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Set default configure data for Udp4Read and Ip4 instance.\r
+ //\r
+ Mode = PxeBc->Mode;\r
+ Udp4CfgData = &Private->Udp4CfgData;\r
+ Ip4CfgData = &Private->Ip4CfgData;\r
+\r
+ Udp4CfgData->AcceptBroadcast = TRUE;\r
+ Udp4CfgData->AcceptAnyPort = TRUE;\r
+ Udp4CfgData->AllowDuplicatePort = TRUE;\r
+ Udp4CfgData->TypeOfService = Mode->ToS;\r
+ Udp4CfgData->TimeToLive = Mode->TTL;\r
+ Udp4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ Udp4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;\r
+\r
+ Ip4CfgData->AcceptIcmpErrors = TRUE;\r
+ Ip4CfgData->DefaultProtocol = EFI_IP_PROTO_ICMP;\r
+ Ip4CfgData->TypeOfService = Mode->ToS;\r
+ Ip4CfgData->TimeToLive = Mode->TTL;\r
+ Ip4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ Ip4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ PxeBcDestroyIp4Children (This, Private);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Create the opened instances based on IPv6.\r
+\r
+ @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL.\r
+ @param[in] ControllerHandle Handle of the child to destroy.\r
+ @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA.\r
+\r
+ @retval EFI_SUCCESS The instances based on IPv6 were all created successfully.\r
+ @retval Others An unexpected error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcCreateIp6Children (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN PXEBC_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IPv6_DEVICE_PATH Ip6Node;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_UDP6_CONFIG_DATA *Udp6CfgData;\r
+ EFI_IP6_CONFIG_DATA *Ip6CfgData;\r
+ EFI_IP6_MODE_DATA Ip6ModeData;\r
+\r
+ if (Private->Ip6Nic != NULL) {\r
+ //\r
+ // Already created before.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));\r
+\r
+ if (Private->Ip6Nic == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Private->Ip6Nic->Private = Private;\r
+ Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;\r
+\r
+ //\r
+ // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ &Private->Dhcp6Child\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Dhcp6Child,\r
+ &gEfiDhcp6ProtocolGuid,\r
+ (VOID **) &Private->Dhcp6,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiMtftp6ServiceBindingProtocolGuid,\r
+ &Private->Mtftp6Child\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Mtftp6Child,\r
+ &gEfiMtftp6ProtocolGuid,\r
+ (VOID **) &Private->Mtftp6,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ &Private->Udp6ReadChild\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Udp6ReadChild,\r
+ &gEfiUdp6ProtocolGuid,\r
+ (VOID **) &Private->Udp6Read,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiUdp6ServiceBindingProtocolGuid,\r
+ &Private->Udp6WriteChild\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Udp6WriteChild,\r
+ &gEfiUdp6ProtocolGuid,\r
+ (VOID **) &Private->Udp6Write,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create Ip6 child and open Ip6 protocol for background ICMP6 packets.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ This->DriverBindingHandle,\r
+ &gEfiIp6ServiceBindingProtocolGuid,\r
+ &Private->Ip6Child\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (\r
+ Private->Ip6Child,\r
+ &gEfiIp6ProtocolGuid,\r
+ (VOID **) &Private->Ip6,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Get max packet size from Ip6 to calculate block size for Tftp later.\r
+ //\r
+ Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize;\r
+\r
+ //\r
+ // Locate Ip6->Ip6Config and store it for set IPv6 address.\r
+ //\r
+ Status = gBS->HandleProtocol (\r
+ ControllerHandle,\r
+ &gEfiIp6ConfigProtocolGuid,\r
+ (VOID **) &Private->Ip6Cfg\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create a device path node for Ipv6 virtual nic, and append it.\r
+ //\r
+ ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH));\r
+ Ip6Node.Header.Type = MESSAGING_DEVICE_PATH;\r
+ Ip6Node.Header.SubType = MSG_IPv6_DP;\r
+ Ip6Node.StaticIpAddress = FALSE;\r
+\r
+ SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node));\r
+\r
+ Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header);\r
+\r
+ if (Private->Ip6Nic->DevicePath == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ CopyMem (\r
+ &Private->Ip6Nic->LoadFile,\r
+ &gLoadFileProtocolTemplate,\r
+ sizeof (EFI_LOAD_FILE_PROTOCOL)\r
+ );\r
+\r
+ //\r
+ // Create a new handle for IPv6 virtual nic,\r
+ // and install PxeBaseCode, LoadFile and DevicePath protocols.\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Private->Ip6Nic->Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Private->Ip6Nic->DevicePath,\r
+ &gEfiLoadFileProtocolGuid,\r
+ &Private->Ip6Nic->LoadFile,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Open PxeBaseCode protocol by child to setup a parent-child relationship between\r
+ // real NIC handle and the virtual IPv6 NIC handle.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ (VOID **) &PxeBc,\r
+ This->DriverBindingHandle,\r
+ Private->Ip6Nic->Controller,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Set IPv6 avaiable flag and set default configure data for\r
+ // Udp6Read and Ip6 instance.\r
+ //\r
+ Private->Mode.Ipv6Available = TRUE;\r
+ Udp6CfgData = &Private->Udp6CfgData;\r
+ Ip6CfgData = &Private->Ip6CfgData;\r
+\r
+ Udp6CfgData->AcceptAnyPort = TRUE;\r
+ Udp6CfgData->AllowDuplicatePort = TRUE;\r
+ Udp6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT;\r
+ Udp6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ Udp6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;\r
+\r
+ Ip6CfgData->AcceptIcmpErrors = TRUE;\r
+ Ip6CfgData->DefaultProtocol = IP6_ICMP;\r
+ Ip6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT;\r
+ Ip6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ Ip6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ PxeBcDestroyIp6Children (This, Private);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ The entry point for UefiPxeBc driver that installs the driver\r
+ binding and component name protocol on its image.\r
+\r
+ @param[in] ImageHandle The Image handle of the driver.\r
+ @param[in] SystemTable The system table.\r
+\r
+ @return EFI_SUCCESS\r
+ @return Others\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ return EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gPxeBcDriverBinding,\r
+ ImageHandle,\r
+ &gPxeBcComponentName,\r
+ &gPxeBcComponentName2\r
+ );\r
+}\r
+\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle. This service\r
+ is called by the EFI boot service ConnectController(). In\r
+ order to make drivers as small as possible, there are a few calling\r
+ restrictions for this service. ConnectController() must\r
+ follow these calling restrictions. If any other agent wishes to call\r
+ Supported() it must also follow these calling restrictions.\r
+\r
+ @param[in] This The pointer to the driver binding protocol.\r
+ @param[in] ControllerHandle The handle of device to be tested.\r
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child\r
+ device to be started.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval EFI_UNSUPPORTED This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Ip4Status;\r
+ EFI_STATUS Ip6Status;\r
+\r
+ //\r
+ // Try to open the Mtftp4 and Dhcp4 protocol to test whether IPv4 stack is ready.\r
+ //\r
+ Ip4Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiDhcp4ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Ip4Status)) {\r
+ Ip4Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ }\r
+\r
+ //\r
+ // Try to open the Mtftp6 and Dhcp6 protocol to test whether IPv4 stack is ready.\r
+ //\r
+ Ip6Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiDhcp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Ip6Status)) {\r
+ Ip6Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiMtftp6ServiceBindingProtocolGuid,\r
+ NULL,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ }\r
+\r
+ //\r
+ // It's unsupported case if both stack are not ready.\r
+ //\r
+ if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Start this driver on ControllerHandle. This service is called by the\r
+ EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start() it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This The pointer to the driver binding protocol.\r
+ @param[in] ControllerHandle The handle of device to be started.\r
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child\r
+ device to be started.\r
+\r
+ @retval EFI_SUCCESS This driver is installed to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS Ip4Status;\r
+ EFI_STATUS Ip6Status;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ (VOID **) &PxeBc,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Skip the initialization if the driver has been started already.\r
+ //\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);\r
+ } else {\r
+ //\r
+ // If the driver has not been started yet, it should do initialization.\r
+ //\r
+ Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));\r
+ if (Private == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (\r
+ &Private->PxeBc,\r
+ &gPxeBcProtocolTemplate,\r
+ sizeof (EFI_PXE_BASE_CODE_PROTOCOL)\r
+ );\r
+\r
+ Private->Signature = PXEBC_PRIVATE_DATA_SIGNATURE;\r
+ Private->Controller = ControllerHandle;\r
+ Private->Image = This->ImageHandle;\r
+ Private->PxeBc.Mode = &Private->Mode;\r
+ Private->Mode.Ipv6Supported = TRUE;\r
+ Private->Mode.AutoArp = TRUE;\r
+ Private->Mode.TTL = DEFAULT_TTL;\r
+ Private->Mode.ToS = DEFAULT_ToS;\r
+\r
+ //\r
+ // Open device path to prepare for appending virtual NIC node.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **) &Private->DevicePath,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Get the NII interface if it exists, it's not required.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+ (VOID **) &Private->Nii,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Private->Nii = NULL;\r
+ }\r
+\r
+ //\r
+ // Install PxeBaseCode protocol onto the real NIC handler.\r
+ //\r
+ Status = gBS->InstallProtocolInterface (\r
+ &ControllerHandle,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &Private->PxeBc\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Try to create virtual NIC handle for IPv4.\r
+ //\r
+ Ip4Status = PxeBcCreateIp4Children (This, ControllerHandle, Private);\r
+\r
+ //\r
+ // Try to create virtual NIC handle for IPv6.\r
+ //\r
+ Ip6Status = PxeBcCreateIp6Children (This, ControllerHandle, Private);\r
+\r
+ if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {\r
+ //\r
+ // Failed to start PXE driver if IPv4 and IPv6 stack are both not available.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ gBS->UninstallProtocolInterface (\r
+ ControllerHandle,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ &Private->PxeBc\r
+ );\r
+ PxeBcDestroyIp4Children (This, Private);\r
+ PxeBcDestroyIp6Children (This, Private);\r
+ FreePool (Private);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Stop this driver on ControllerHandle. This service is called by the\r
+ EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop() it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on.\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
+ children is zero stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCESS This driver was removed ControllerHandle.\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval Others This driver was not removed from this device\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ PXEBC_VIRTUAL_NIC *VirtualNic;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE NicHandle;\r
+ BOOLEAN IsIpv6;\r
+\r
+ Private = NULL;\r
+ NicHandle = NULL;\r
+ VirtualNic = NULL;\r
+ LoadFile = NULL;\r
+ PxeBc = NULL;\r
+ IsIpv6 = FALSE;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiLoadFileProtocolGuid,\r
+ (VOID **) &LoadFile,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Get the Nic handle by any pass-over service child handle.\r
+ //\r
+ NicHandle = PxeBcGetNicByIp4Children (ControllerHandle);\r
+ if (NicHandle == NULL) {\r
+ NicHandle = PxeBcGetNicByIp6Children (ControllerHandle);\r
+ if (NicHandle == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ } else {\r
+ IsIpv6 = TRUE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Try to retrieve the private data by PxeBc protocol.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ NicHandle,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ (VOID **) &PxeBc,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);\r
+\r
+ } else {\r
+ //\r
+ // It's a virtual handle with LoadFileProtocol.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ ControllerHandle,\r
+ &gEfiLoadFileProtocolGuid,\r
+ (VOID **) &LoadFile,\r
+ This->DriverBindingHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile);\r
+ Private = VirtualNic->Private;\r
+ NicHandle = Private->Controller;\r
+\r
+ if (Private->Ip6Nic == VirtualNic) {\r
+ IsIpv6 = TRUE;\r
+ }\r
+ }\r
+\r
+ if (Private->Ip4Nic != NULL && !IsIpv6) {\r
+ PxeBcDestroyIp4Children (This, Private);\r
+ }\r
+\r
+ if (Private->Ip6Nic != NULL && IsIpv6) {\r
+ PxeBcDestroyIp6Children (This, Private);\r
+ }\r
+\r
+ if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) {\r
+ gBS->UninstallProtocolInterface (\r
+ NicHandle,\r
+ &gEfiPxeBaseCodeProtocolGuid,\r
+ &Private->PxeBc\r
+ );\r
+ FreePool (Private);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ Driver Binding functions declaration for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_DRIVER_H__\r
+#define __EFI_PXEBC_DRIVER_H__\r
+\r
+extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName;\r
+extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2;\r
+\r
+/**\r
+ Test to see if this driver supports ControllerHandle. This service\r
+ is called by the EFI boot service ConnectController(). In\r
+ order to make drivers as small as possible, there are a few calling\r
+ restrictions for this service. ConnectController() must\r
+ follow these calling restrictions. If any other agent wishes to call\r
+ Supported() it must also follow these calling restrictions.\r
+\r
+ @param[in] This The pointer to the driver binding protocol.\r
+ @param[in] ControllerHandle The handle of device to be tested.\r
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to be started.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device.\r
+ @retval EFI_UNSUPPORTED This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ Start this driver on ControllerHandle. This service is called by the\r
+ EFI boot service ConnectController(). In order to make\r
+ drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Start() it\r
+ must also follow these calling restrictions.\r
+\r
+ @param[in] This The pointer to the driver binding protocol.\r
+ @param[in] ControllerHandle The handle of device to be started.\r
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child\r
+ device to be started.\r
+\r
+ @retval EFI_SUCCESS This driver is installed to ControllerHandle.\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.\r
+ @retval other This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ Stop this driver on ControllerHandle. This service is called by the\r
+ EFI boot service DisconnectController(). In order to\r
+ make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController()\r
+ must follow these calling restrictions. If any other agent wishes\r
+ to call Stop() it must also follow these calling restrictions.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] ControllerHandle Handle of device to stop driver on\r
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
+ children is zero stop the entire bus driver.\r
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCESS This driver is removed ControllerHandle\r
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.\r
+ @retval Others This driver was not removed from this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ );\r
+\r
+#endif\r
+\r
--- /dev/null
+/** @file\r
+ This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+/**\r
+ Enables the use of the PXE Base Code Protocol functions.\r
+\r
+ This function enables the use of the PXE Base Code Protocol functions. If the\r
+ Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then\r
+ EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted\r
+ addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted\r
+ addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported\r
+ field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will\r
+ be returned. If there is not enough memory or other resources to start the PXE\r
+ Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the\r
+ PXE Base Code Protocol will be started.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] UseIpv6 Specifies the type of IP addresses that are to be\r
+ used during the session that is being started.\r
+ Set to TRUE for IPv6, and FALSE for IPv4.\r
+\r
+ @retval EFI_SUCCESS The PXE Base Code Protocol was started.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval EFI_UNSUPPORTED UseIpv6 is TRUE, but the Ipv6Supported field of the\r
+ EFI_PXE_BASE_CODE_MODE structure is FALSE.\r
+ @retval EFI_ALREADY_STARTED The PXE Base Code Protocol is already in the started state.\r
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid\r
+ EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory or other resources to start the\r
+ PXE Base Code Protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcStart (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN BOOLEAN UseIpv6\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ UINTN Index;\r
+ EFI_STATUS Status;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ if (Mode->Started) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ //\r
+ // Detect whether using IPv6 or not, and set it into mode data.\r
+ //\r
+ if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) {\r
+ Mode->UsingIpv6 = TRUE;\r
+ } else if (!UseIpv6 && Private->Ip4Nic != NULL) {\r
+ Mode->UsingIpv6 = FALSE;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (Mode->UsingIpv6) {\r
+ AsciiPrint ("\n>>Start PXE over IPv6");\r
+ //\r
+ // Configure block size for TFTP as a default value to handle all link layers.\r
+ //\r
+ Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize -\r
+ PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);\r
+\r
+ //\r
+ // PXE over IPv6 starts here, initialize the fields and list header.\r
+ //\r
+ Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;\r
+ Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+ Private->DhcpAck.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+ Private->PxeReply.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+\r
+ for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {\r
+ Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;\r
+ }\r
+\r
+ //\r
+ // Create event and set status for token to capture ICMP6 error message.\r
+ //\r
+ Private->Icmp6Token.Status = EFI_NOT_READY;\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcIcmp6ErrorUpdate,\r
+ Private,\r
+ &Private->Icmp6Token.Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ } else {\r
+ AsciiPrint ("\n>>Start PXE over IPv4");\r
+ //\r
+ // Configure block size for TFTP as a default value to handle all link layers.\r
+ //\r
+ Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize -\r
+ PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);\r
+\r
+ //\r
+ // PXE over IPv4 starts here, initialize the fields.\r
+ //\r
+ Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+ Private->DhcpAck.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+ Private->PxeReply.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+\r
+ for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {\r
+ Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;\r
+ }\r
+\r
+ PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read);\r
+\r
+ //\r
+ // Create the event for Arp cache update.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ PxeBcArpCacheUpdate,\r
+ Private,\r
+ &Private->ArpUpdateEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Start a periodic timer by second to update Arp cache.\r
+ //\r
+ Status = gBS->SetTimer (\r
+ Private->ArpUpdateEvent,\r
+ TimerPeriodic,\r
+ TICKS_PER_SECOND\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Create event and set status for token to capture ICMP error message.\r
+ //\r
+ Private->Icmp6Token.Status = EFI_NOT_READY;\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcIcmpErrorUpdate,\r
+ Private,\r
+ &Private->IcmpToken.Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If PcdTftpBlockSize is set to non-zero, override the default value.\r
+ //\r
+ if (PcdGet64 (PcdTftpBlockSize) != 0) {\r
+ Private->BlockSize = (UINTN) PcdGet64 (PcdTftpBlockSize);\r
+ }\r
+\r
+ //\r
+ // Create event for UdpRead/UdpWrite timeout since they are both blocking API.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &Private->UdpTimeOutEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ Private->IsAddressOk = FALSE;\r
+ Mode->Started = TRUE;\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+ if (Mode->UsingIpv6) {\r
+ if (Private->Icmp6Token.Event != NULL) {\r
+ gBS->CloseEvent (Private->Icmp6Token.Event);\r
+ Private->Icmp6Token.Event = NULL;\r
+ }\r
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+ Private->Ip6->Configure (Private->Ip6, NULL);\r
+ } else {\r
+ if (Private->ArpUpdateEvent != NULL) {\r
+ gBS->CloseEvent (Private->ArpUpdateEvent);\r
+ Private->ArpUpdateEvent = NULL;\r
+ }\r
+ if (Private->IcmpToken.Event != NULL) {\r
+ gBS->CloseEvent (Private->IcmpToken.Event);\r
+ Private->IcmpToken.Event = NULL;\r
+ }\r
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+ Private->Ip4->Configure (Private->Ip4, NULL);\r
+ }\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Disable the use of the PXE Base Code Protocol functions.\r
+\r
+ This function stops all activity on the network device. All the resources allocated\r
+ in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is\r
+ set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE\r
+ structure is already FALSE, then EFI_NOT_STARTED will be returned.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+\r
+ @retval EFI_SUCCESS The PXE Base Code Protocol was stopped.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is already in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid\r
+ EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+ @retval Others\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcStop (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ BOOLEAN Ipv6Supported;\r
+ BOOLEAN Ipv6Available;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+ Ipv6Supported = Mode->Ipv6Supported;\r
+ Ipv6Available = Mode->Ipv6Available;\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Mode->UsingIpv6) {\r
+ //\r
+ // Configure all the instances for IPv6 as NULL.\r
+ //\r
+ ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));\r
+ Private->Dhcp6->Stop (Private->Dhcp6);\r
+ Private->Dhcp6->Configure (Private->Dhcp6, NULL);\r
+ Private->Udp6Write->Configure (Private->Udp6Write, NULL);\r
+ Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL);\r
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+ Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);\r
+ Private->Ip6->Configure (Private->Ip6, NULL);\r
+ PxeBcUnregisterIp6Address (Private);\r
+ if (Private->Icmp6Token.Event != NULL) {\r
+ gBS->CloseEvent (Private->Icmp6Token.Event);\r
+ Private->Icmp6Token.Event = NULL;\r
+ }\r
+ if (Private->Dhcp6Request != NULL) {\r
+ FreePool (Private->Dhcp6Request);\r
+ Private->Dhcp6Request = NULL;\r
+ }\r
+ if (Private->BootFileName != NULL) {\r
+ FreePool (Private->BootFileName);\r
+ Private->BootFileName = NULL;\r
+ }\r
+ } else {\r
+ //\r
+ // Configure all the instances for IPv4 as NULL.\r
+ //\r
+ ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ Private->Dhcp4->Stop (Private->Dhcp4);\r
+ Private->Dhcp4->Configure (Private->Dhcp4, NULL);\r
+ Private->Udp4Write->Configure (Private->Udp4Write, NULL);\r
+ Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL);\r
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+ Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);\r
+ Private->Ip4->Configure (Private->Ip4, NULL);\r
+ if (Private->ArpUpdateEvent != NULL) {\r
+ gBS->CloseEvent (Private->ArpUpdateEvent);\r
+ Private->ArpUpdateEvent = NULL;\r
+ }\r
+ if (Private->IcmpToken.Event != NULL) {\r
+ gBS->CloseEvent (Private->IcmpToken.Event);\r
+ Private->IcmpToken.Event = NULL;\r
+ }\r
+ }\r
+\r
+ gBS->CloseEvent (Private->UdpTimeOutEvent);\r
+ Private->CurSrcPort = 0;\r
+ Private->BootFileSize = 0;\r
+\r
+ //\r
+ // Reset the mode data.\r
+ //\r
+ ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));\r
+ Mode->Ipv6Available = Ipv6Available;\r
+ Mode->Ipv6Supported = Ipv6Supported;\r
+ Mode->AutoArp = TRUE;\r
+ Mode->TTL = DEFAULT_TTL;\r
+ Mode->ToS = DEFAULT_ToS;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6\r
+ S.A.R.R (solicit / advertise / request / reply) sequence.\r
+\r
+ If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before\r
+ they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will\r
+ be tried in the order in which they are received. Please see the Preboot Execution\r
+ Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI)\r
+ Specification for additional details on the implementation of DHCP.\r
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+ then the DHCP sequence will be stopped and EFI_ABORTED will be returned.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] SortOffers TRUE if the offers received should be sorted. Set to FALSE to\r
+ try the offers in the order that they are received.\r
+\r
+ @retval EFI_SUCCESS Valid DHCP has completed.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid\r
+ EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete the DHCP Protocol.\r
+ @retval EFI_ABORTED The callback function aborted the DHCP Protocol.\r
+ @retval EFI_TIMEOUT The DHCP Protocol timed out.\r
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the DHCP session.\r
+ @retval EFI_NO_RESPONSE Valid PXE offer was not received.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcDhcp (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN BOOLEAN SortOffers\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_STATUS Status;\r
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+ Mode->IcmpErrorReceived = FALSE;\r
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP;\r
+ Private->IsOfferSorted = SortOffers;\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Mode->UsingIpv6) {\r
+\r
+ //\r
+ // Stop Udp6Read instance\r
+ //\r
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+\r
+ //\r
+ // Start S.A.R.R. process to get a IPv6 address and other boot information.\r
+ //\r
+ Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6);\r
+ } else {\r
+\r
+ //\r
+ // Stop Udp4Read instance\r
+ //\r
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+\r
+ //\r
+ // Start D.O.R.A. process to get a IPv4 address and other boot information.\r
+ //\r
+ Status = PxeBcDhcp4Dora (Private, Private->Dhcp4);\r
+ }\r
+\r
+ //\r
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP\r
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+ //\r
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+ This->SetIpFilter (This, &IpFilter);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Attempts to complete the PXE Boot Server and/or boot image discovery sequence.\r
+\r
+ This function attempts to complete the PXE Boot Server and/or boot image discovery\r
+ sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the\r
+ PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the\r
+ EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the\r
+ PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure\r
+ will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE.\r
+ In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[],\r
+ has two uses: It is the Boot Server IP address list used for unicast discovery\r
+ (if the UseUCast field is TRUE), and it is the list used for Boot Server verification\r
+ (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure\r
+ is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot\r
+ Server reply of that type will be accepted. If the AcceptAnyResponse field is\r
+ FALSE, only responses from Boot Servers with matching IP addresses will be accepted.\r
+ This function can take at least 10 seconds to timeout and return control to the\r
+ caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be\r
+ returned. Please see the Preboot Execution Environment (PXE) Specification for\r
+ additional details on the implementation of the Discovery sequence.\r
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+ then the Discovery sequence is stopped and EFI_ABORTED will be returned.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] Type The type of bootstrap to perform.\r
+ @param[in] Layer Pointer to the boot server layer number to discover, which must be\r
+ PXE_BOOT_LAYER_INITIAL when a new server type is being\r
+ discovered.\r
+ @param[in] UseBis TRUE if Boot Integrity Services are to be used. FALSE otherwise.\r
+ @param[in] Info Pointer to a data structure that contains additional information\r
+ on the type of discovery operation that is to be performed.\r
+ It is optional.\r
+\r
+ @retval EFI_SUCCESS The Discovery sequence has been completed.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete Discovery.\r
+ @retval EFI_ABORTED The callback function aborted the Discovery sequence.\r
+ @retval EFI_TIMEOUT The Discovery sequence timed out.\r
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the PXE discovery\r
+ session.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcDiscover (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN UINT16 Type,\r
+ IN UINT16 *Layer,\r
+ IN BOOLEAN UseBis,\r
+ IN EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;\r
+ EFI_PXE_BASE_CODE_SRVLIST *SrvList;\r
+ PXEBC_BOOT_SVR_ENTRY *BootSvrEntry;\r
+ UINT16 Index;\r
+ EFI_STATUS Status;\r
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+ Mode->IcmpErrorReceived = FALSE;\r
+ BootSvrEntry = NULL;\r
+ SrvList = NULL;\r
+ Status = EFI_DEVICE_ERROR;\r
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ //\r
+ // Station address should be ready before do discover.\r
+ //\r
+ if (!Private->IsAddressOk) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Mode->UsingIpv6) {\r
+\r
+ //\r
+ // Stop Udp6Read instance\r
+ //\r
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+ } else {\r
+\r
+ //\r
+ // Stop Udp4Read instance\r
+ //\r
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+ }\r
+\r
+ //\r
+ // There are 3 methods to get the information for discover.\r
+ //\r
+ if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) {\r
+ //\r
+ // 1. Take the previous setting as the discover info.\r
+ //\r
+ if (!Mode->PxeDiscoverValid ||\r
+ !Mode->PxeReplyReceived ||\r
+ (!Mode->PxeBisReplyReceived && UseBis)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Info = &DefaultInfo;\r
+ Info->IpCnt = 1;\r
+ Info->UseUCast = TRUE;\r
+ SrvList = Info->SrvList;\r
+ SrvList[0].Type = Type;\r
+ SrvList[0].AcceptAnyResponse = FALSE;\r
+\r
+ CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
+\r
+ } else if (Info == NULL) {\r
+ //\r
+ // 2. Extract the discover information from the cached packets if unspecified.\r
+ //\r
+ Info = &DefaultInfo;\r
+ Status = PxeBcExtractDiscoverInfo (Private, Type, Info, &BootSvrEntry, &SrvList);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ } else {\r
+ //\r
+ // 3. Take the pass-in information as the discover info, and validate the server list.\r
+ //\r
+ SrvList = Info->SrvList;\r
+\r
+ if (!SrvList[0].AcceptAnyResponse) {\r
+ for (Index = 1; Index < Info->IpCnt; Index++) {\r
+ if (SrvList[Index].AcceptAnyResponse) {\r
+ break;\r
+ }\r
+ }\r
+ if (Index != Info->IpCnt) {\r
+ //\r
+ // It's invalid if the first server doesn't accecpt any response\r
+ // and meanwhile any of the rest servers accept any reponse.\r
+ //\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast.\r
+ //\r
+ if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) ||\r
+ (Info->MustUseList && Info->IpCnt == 0)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Private->IsDoDiscover = TRUE;\r
+\r
+ if (Info->UseUCast) {\r
+ //\r
+ // Do discover by unicast.\r
+ //\r
+ for (Index = 0; Index < Info->IpCnt; Index++) {\r
+ if (BootSvrEntry == NULL) {\r
+ CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS));\r
+ } else {\r
+ ASSERT (!Mode->UsingIpv6);\r
+ ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));\r
+ }\r
+\r
+ Status = PxeBcDiscoverBootServer (\r
+ Private,\r
+ Type,\r
+ Layer,\r
+ UseBis,\r
+ &SrvList[Index].IpAddr,\r
+ 0,\r
+ NULL\r
+ );\r
+ }\r
+ } else if (Info->UseMCast) {\r
+ //\r
+ // Do discover by multicast.\r
+ //\r
+ Status = PxeBcDiscoverBootServer (\r
+ Private,\r
+ Type,\r
+ Layer,\r
+ UseBis,\r
+ &Info->ServerMCastIp,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ } else if (Info->UseBCast) {\r
+ //\r
+ // Do discover by broadcast, but only valid for IPv4.\r
+ //\r
+ ASSERT (!Mode->UsingIpv6);\r
+ Status = PxeBcDiscoverBootServer (\r
+ Private,\r
+ Type,\r
+ Layer,\r
+ UseBis,\r
+ NULL,\r
+ Info->IpCnt,\r
+ SrvList\r
+ );\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Parse the cached PXE reply packet, and store it into mode data if valid.\r
+ //\r
+ if (Mode->UsingIpv6) {\r
+ Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6);\r
+ if (!EFI_ERROR (Status)) {\r
+ CopyMem (\r
+ &Mode->PxeReply.Dhcpv6,\r
+ &Private->PxeReply.Dhcp6.Packet.Offer,\r
+ Private->PxeReply.Dhcp6.Packet.Offer.Length\r
+ );\r
+ Mode->PxeReplyReceived = TRUE;\r
+ Mode->PxeDiscoverValid = TRUE;\r
+ }\r
+ } else {\r
+ Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4);\r
+ if (!EFI_ERROR (Status)) {\r
+ CopyMem (\r
+ &Mode->PxeReply.Dhcpv4,\r
+ &Private->PxeReply.Dhcp4.Packet.Offer,\r
+ Private->PxeReply.Dhcp4.Packet.Offer.Length\r
+ );\r
+ Mode->PxeReplyReceived = TRUE;\r
+ Mode->PxeDiscoverValid = TRUE;\r
+ }\r
+ }\r
+ }\r
+\r
+ON_EXIT:\r
+\r
+ //\r
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP\r
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+ //\r
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+ This->SetIpFilter (This, &IpFilter);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Used to perform TFTP and MTFTP services.\r
+\r
+ This function is used to perform TFTP and MTFTP services. This includes the\r
+ TFTP operations to get the size of a file, read a directory, read a file, and\r
+ write a file. It also includes the MTFTP operations to get the size of a file,\r
+ read a directory, and read a file. The type of operation is specified by Operation.\r
+ If the callback function that is invoked during the TFTP/MTFTP operation does\r
+ not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will\r
+ be returned.\r
+ For read operations, the return data will be placed in the buffer specified by\r
+ BufferPtr. If BufferSize is too small to contain the entire downloaded file,\r
+ then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero,\r
+ or the size of the requested file. (NOTE: the size of the requested file is only returned\r
+ if the TFTP server supports TFTP options). If BufferSize is large enough for the\r
+ read operation, then BufferSize will be set to the size of the downloaded file,\r
+ and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services\r
+ should use the get-file-size operations to determine the size of the downloaded\r
+ file prior to using the read-file operations-especially when downloading large\r
+ (greater than 64 MB) files-instead of making two calls to the read-file operation.\r
+ Following this recommendation will save time if the file is larger than expected\r
+ and the TFTP server does not support TFTP option extensions. Without TFTP option\r
+ extension support, the client must download the entire file, counting and discarding\r
+ the received packets, to determine the file size.\r
+ For write operations, the data to be sent is in the buffer specified by BufferPtr.\r
+ BufferSize specifies the number of bytes to send. If the write operation completes\r
+ successfully, then EFI_SUCCESS will be returned.\r
+ For TFTP "get file size" operations, the size of the requested file or directory\r
+ is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server\r
+ does not support options, the file will be downloaded into a bit bucket and the\r
+ length of the downloaded file will be returned. For MTFTP "get file size" operations,\r
+ if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED\r
+ will be returned.\r
+ This function can take up to 10 seconds to timeout and return control to the caller.\r
+ If the TFTP sequence does not complete, EFI_TIMEOUT will be returned.\r
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+ then the TFTP sequence is stopped and EFI_ABORTED will be returned.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] Operation The type of operation to perform.\r
+ @param[in, out] BufferPtr A pointer to the data buffer.\r
+ @param[in] Overwrite Only used on write file operations. TRUE if a file on a remote\r
+ server can be overwritten.\r
+ @param[in, out] BufferSize For get-file-size operations, *BufferSize returns the size of the\r
+ requested file.\r
+ @param[in] BlockSize The requested block size to be used during a TFTP transfer.\r
+ @param[in] ServerIp The TFTP / MTFTP server IP address.\r
+ @param[in] Filename A Null-terminated ASCII string that specifies a directory name\r
+ or a file name.\r
+ @param[in] Info Pointer to the MTFTP information.\r
+ @param[in] DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation.\r
+\r
+ @retval EFI_SUCCESS The TFTP/MTFTP operation was completed.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer is not large enough to complete the read operation.\r
+ @retval EFI_ABORTED The callback function aborted the TFTP/MTFTP operation.\r
+ @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out.\r
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the MTFTP session.\r
+ @retval EFI_TFTP_ERROR A TFTP error packet was received during the MTFTP session.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcMtftp (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,\r
+ IN OUT VOID *BufferPtr OPTIONAL,\r
+ IN BOOLEAN Overwrite,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN UINTN *BlockSize OPTIONAL,\r
+ IN EFI_IP_ADDRESS *ServerIp,\r
+ IN UINT8 *Filename,\r
+ IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL,\r
+ IN BOOLEAN DontUseBuffer\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_MTFTP4_CONFIG_DATA Mtftp4Config;\r
+ EFI_MTFTP6_CONFIG_DATA Mtftp6Config;\r
+ VOID *Config;\r
+ EFI_STATUS Status;\r
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;\r
+\r
+\r
+ if ((This == NULL) ||\r
+ (Filename == NULL) ||\r
+ (BufferSize == NULL) ||\r
+ (ServerIp == NULL) ||\r
+ ((BufferPtr == NULL) && DontUseBuffer) ||\r
+ ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) ||\r
+ (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Config = NULL;\r
+ Status = EFI_DEVICE_ERROR;\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ if (Mode->UsingIpv6) {\r
+ //\r
+ // Set configuration data for Mtftp6 instance.\r
+ //\r
+ ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA));\r
+ Config = &Mtftp6Config;\r
+ Mtftp6Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT;\r
+ Mtftp6Config.TryCount = PXEBC_MTFTP_RETRIES;\r
+ CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+ CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS));\r
+ //\r
+ // Stop Udp6Read instance\r
+ //\r
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+ } else {\r
+ //\r
+ // Set configuration data for Mtftp4 instance.\r
+ //\r
+ ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA));\r
+ Config = &Mtftp4Config;\r
+ Mtftp4Config.UseDefaultSetting = FALSE;\r
+ Mtftp4Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT;\r
+ Mtftp4Config.TryCount = PXEBC_MTFTP_RETRIES;\r
+ CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS));\r
+ //\r
+ // Stop Udp4Read instance\r
+ //\r
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+ }\r
+\r
+ Mode->TftpErrorReceived = FALSE;\r
+ Mode->IcmpErrorReceived = FALSE;\r
+\r
+ switch (Operation) {\r
+\r
+ case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE:\r
+ //\r
+ // Send TFTP request to get file size.\r
+ //\r
+ Status = PxeBcTftpGetFileSize (\r
+ Private,\r
+ Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferSize\r
+ );\r
+\r
+ break;\r
+\r
+ case EFI_PXE_BASE_CODE_TFTP_READ_FILE:\r
+ //\r
+ // Send TFTP request to read file.\r
+ //\r
+ Status = PxeBcTftpReadFile (\r
+ Private,\r
+ Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize,\r
+ DontUseBuffer\r
+ );\r
+\r
+ break;\r
+\r
+ case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:\r
+ //\r
+ // Send TFTP request to write file.\r
+ //\r
+ Status = PxeBcTftpWriteFile (\r
+ Private,\r
+ Config,\r
+ Filename,\r
+ Overwrite,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize\r
+ );\r
+\r
+ break;\r
+\r
+ case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:\r
+ //\r
+ // Send TFTP request to read directory.\r
+ //\r
+ Status = PxeBcTftpReadDirectory (\r
+ Private,\r
+ Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize,\r
+ DontUseBuffer\r
+ );\r
+\r
+ break;\r
+\r
+ case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE:\r
+ case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:\r
+ case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:\r
+ Status = EFI_UNSUPPORTED;\r
+\r
+ break;\r
+\r
+ default:\r
+ Status = EFI_INVALID_PARAMETER;\r
+\r
+ break;\r
+ }\r
+\r
+ if (Status == EFI_ICMP_ERROR) {\r
+ Mode->IcmpErrorReceived = TRUE;\r
+ }\r
+\r
+ //\r
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP\r
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+ //\r
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));\r
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;\r
+ This->SetIpFilter (This, &IpFilter);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Writes a UDP packet to the network interface.\r
+\r
+ This function writes a UDP packet specified by the (optional HeaderPtr and)\r
+ BufferPtr parameters to the network interface. The UDP header is automatically\r
+ built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp,\r
+ SrcIp, and SrcPort to build this header. If the packet is successfully built and\r
+ transmitted through the network interface, then EFI_SUCCESS will be returned.\r
+ If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will\r
+ be returned. If an ICMP error occurs during the transmission of the packet, then\r
+ the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and\r
+ EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return\r
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] OpFlags The UDP operation flags.\r
+ @param[in] DestIp The destination IP address.\r
+ @param[in] DestPort The destination UDP port number.\r
+ @param[in] GatewayIp The gateway IP address.\r
+ @param[in] SrcIp The source IP address.\r
+ @param[in, out] SrcPort The source UDP port number.\r
+ @param[in] HeaderSize An optional field which may be set to the length of a header\r
+ at HeaderPtr to be prefixed to the data at BufferPtr.\r
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be\r
+ prefixed to the data at BufferPtr.\r
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.\r
+ @param[in] BufferPtr A pointer to the data to be written.\r
+\r
+ @retval EFI_SUCCESS The UDP Write operation completed.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_BAD_BUFFER_SIZE The buffer is too long to be transmitted.\r
+ @retval EFI_ABORTED The callback function aborted the UDP Write operation.\r
+ @retval EFI_TIMEOUT The UDP Write operation timed out.\r
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the UDP write session.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcUdpWrite (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN UINT16 OpFlags,\r
+ IN EFI_IP_ADDRESS *DestIp,\r
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort,\r
+ IN EFI_IP_ADDRESS *GatewayIp OPTIONAL,\r
+ IN EFI_IP_ADDRESS *SrcIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL,\r
+ IN UINTN *HeaderSize OPTIONAL,\r
+ IN VOID *HeaderPtr OPTIONAL,\r
+ IN UINTN *BufferSize,\r
+ IN VOID *BufferPtr\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_UDP4_SESSION_DATA Udp4Session;\r
+ EFI_UDP6_SESSION_DATA Udp6Session;\r
+ EFI_STATUS Status;\r
+ BOOLEAN DoNotFragment;\r
+\r
+ if (This == NULL || DestIp == NULL || DestPort == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) {\r
+ DoNotFragment = FALSE;\r
+ } else {\r
+ DoNotFragment = TRUE;\r
+ }\r
+\r
+ if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) {\r
+ //\r
+ // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (!Private->IsAddressOk && SrcIp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Private->CurSrcPort == 0 ||\r
+ (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) {\r
+ //\r
+ // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed.\r
+ //\r
+ if (SrcPort != NULL) {\r
+ Private->CurSrcPort = *SrcPort;\r
+ }\r
+ }\r
+\r
+ if (Mode->UsingIpv6) {\r
+ Status = PxeBcConfigUdp6Write (\r
+ Private->Udp6Write,\r
+ &Private->StationIp.v6,\r
+ &Private->CurSrcPort\r
+ );\r
+ } else {\r
+ //\r
+ // Configure the UDPv4 instance with gateway information from DHCP server as default.\r
+ //\r
+ Status = PxeBcConfigUdp4Write (\r
+ Private->Udp4Write,\r
+ &Private->StationIp.v4,\r
+ &Private->SubnetMask.v4,\r
+ &Private->GatewayIp.v4,\r
+ &Private->CurSrcPort,\r
+ DoNotFragment\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Private->CurSrcPort = 0;\r
+ return EFI_INVALID_PARAMETER;\r
+ } else if (SrcPort != NULL) {\r
+ *SrcPort = Private->CurSrcPort;\r
+ }\r
+\r
+ //\r
+ // Start a timer as timeout event for this blocking API.\r
+ //\r
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);\r
+\r
+ if (Mode->UsingIpv6) {\r
+ //\r
+ // Construct UDPv6 session data.\r
+ //\r
+ ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA));\r
+ CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS));\r
+ Udp6Session.DestinationPort = *DestPort;\r
+ if (SrcIp != NULL) {\r
+ CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS));\r
+ }\r
+ if (SrcPort != NULL) {\r
+ Udp6Session.SourcePort = *SrcPort;\r
+ }\r
+\r
+ Status = PxeBcUdp6Write (\r
+ Private->Udp6Write,\r
+ &Udp6Session,\r
+ Private->UdpTimeOutEvent,\r
+ HeaderSize,\r
+ HeaderPtr,\r
+ BufferSize,\r
+ BufferPtr\r
+ );\r
+ } else {\r
+ //\r
+ // Construct UDPv4 session data.\r
+ //\r
+ ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA));\r
+ CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));\r
+ Udp4Session.DestinationPort = *DestPort;\r
+ if (SrcIp != NULL) {\r
+ CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS));\r
+ }\r
+ if (SrcPort != NULL) {\r
+ Udp4Session.SourcePort = *SrcPort;\r
+ }\r
+ //\r
+ // Override the gateway information if user specified.\r
+ //\r
+ Status = PxeBcUdp4Write (\r
+ Private->Udp4Write,\r
+ &Udp4Session,\r
+ Private->UdpTimeOutEvent,\r
+ (EFI_IPv4_ADDRESS *) GatewayIp,\r
+ HeaderSize,\r
+ HeaderPtr,\r
+ BufferSize,\r
+ BufferPtr\r
+ );\r
+ }\r
+\r
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);\r
+\r
+\r
+ //\r
+ // Reset the UdpWrite instance.\r
+ //\r
+ if (Mode->UsingIpv6) {\r
+ Private->Udp6Write->Configure (Private->Udp6Write, NULL);\r
+ } else {\r
+ Private->Udp4Write->Configure (Private->Udp4Write, NULL);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Reads a UDP packet from the network interface.\r
++\r
+ This function reads a UDP packet from a network interface. The data contents\r
+ are returned in (the optional HeaderPtr and) BufferPtr, and the size of the\r
+ buffer received is returned in BufferSize . If the input BufferSize is smaller\r
+ than the UDP packet received (less optional HeaderSize), it will be set to the\r
+ required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the\r
+ contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is\r
+ successfully received, then EFI_SUCCESS will be returned, and the information\r
+ from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if\r
+ they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort,\r
+ SrcIp, and SrcPort input values, different types of UDP packet receive filtering\r
+ will be performed. The following tables summarize these receive filter operations.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] OpFlags The UDP operation flags.\r
+ @param[in, out] DestIp The destination IP address.\r
+ @param[in, out] DestPort The destination UDP port number.\r
+ @param[in, out] SrcIp The source IP address.\r
+ @param[in, out] SrcPort The source UDP port number.\r
+ @param[in] HeaderSize An optional field which may be set to the length of a\r
+ header at HeaderPtr to be prefixed to the data at BufferPtr.\r
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be\r
+ prefixed to the data at BufferPtr.\r
+ @param[in, out] BufferSize A pointer to the size of the data at BufferPtr.\r
+ @param[in] BufferPtr A pointer to the data to be read.\r
+\r
+ @retval EFI_SUCCESS The UDP Read operation was completed.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval EFI_BUFFER_TOO_SMALL The packet is larger than Buffer can hold.\r
+ @retval EFI_ABORTED The callback function aborted the UDP Read operation.\r
+ @retval EFI_TIMEOUT The UDP Read operation timed out.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcUdpRead (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN UINT16 OpFlags,\r
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,\r
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL,\r
+ IN UINTN *HeaderSize OPTIONAL,\r
+ IN VOID *HeaderPtr OPTIONAL,\r
+ IN OUT UINTN *BufferSize,\r
+ IN VOID *BufferPtr\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_UDP4_COMPLETION_TOKEN Udp4Token;\r
+ EFI_UDP6_COMPLETION_TOKEN Udp6Token;\r
+ EFI_UDP4_RECEIVE_DATA *Udp4Rx;\r
+ EFI_UDP6_RECEIVE_DATA *Udp6Rx;\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsDone;\r
+ BOOLEAN IsMatched;\r
+ UINTN CopiedLen;\r
+\r
+ if (This == NULL || DestIp == NULL || DestPort == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+ IsDone = FALSE;\r
+ IsMatched = FALSE;\r
+ Udp4Rx = NULL;\r
+ Udp6Rx = NULL;\r
+\r
+ if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0 && DestPort == NULL) ||\r
+ ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0 && SrcIp == NULL) ||\r
+ ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0 && SrcPort == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((BufferSize == NULL) || (BufferPtr == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN));\r
+ ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN));\r
+\r
+ if (Mode->UsingIpv6) {\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcCommonNotify,\r
+ &IsDone,\r
+ &Udp6Token.Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ } else {\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcCommonNotify,\r
+ &IsDone,\r
+ &Udp4Token.Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Start a timer as timeout event for this blocking API.\r
+ //\r
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);\r
+ Mode->IcmpErrorReceived = FALSE;\r
+\r
+ //\r
+ // Read packet by Udp4Read/Udp6Read until matched or timeout.\r
+ //\r
+ while (!IsMatched && !EFI_ERROR (Status)) {\r
+ if (Mode->UsingIpv6) {\r
+ Status = PxeBcUdp6Read (\r
+ Private->Udp6Read,\r
+ &Udp6Token,\r
+ Mode,\r
+ Private->UdpTimeOutEvent,\r
+ OpFlags,\r
+ &IsDone,\r
+ &IsMatched,\r
+ DestIp,\r
+ DestPort,\r
+ SrcIp,\r
+ SrcPort\r
+ );\r
+ } else {\r
+ Status = PxeBcUdp4Read (\r
+ Private->Udp4Read,\r
+ &Udp4Token,\r
+ Mode,\r
+ Private->UdpTimeOutEvent,\r
+ OpFlags,\r
+ &IsDone,\r
+ &IsMatched,\r
+ DestIp,\r
+ DestPort,\r
+ SrcIp,\r
+ SrcPort\r
+ );\r
+ }\r
+ }\r
+\r
+ if (Status == EFI_ICMP_ERROR ||\r
+ Status == EFI_NETWORK_UNREACHABLE ||\r
+ Status == EFI_HOST_UNREACHABLE ||\r
+ Status == EFI_PROTOCOL_UNREACHABLE ||\r
+ Status == EFI_PORT_UNREACHABLE) {\r
+ //\r
+ // Get different return status for icmp error from Udp, refers to UEFI spec.\r
+ //\r
+ Mode->IcmpErrorReceived = TRUE;\r
+ }\r
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);\r
+\r
+ if (IsMatched) {\r
+ //\r
+ // Copy the rececived packet to user if matched by filter.\r
+ //\r
+ CopiedLen = 0;\r
+ if (Mode->UsingIpv6) {\r
+ Udp6Rx = Udp6Token.Packet.RxData;\r
+ ASSERT (Udp6Rx != NULL);\r
+ //\r
+ // Copy the header part of received data.\r
+ //\r
+ if (HeaderSize != NULL) {\r
+ CopiedLen = MIN (*HeaderSize, Udp6Rx->DataLength);\r
+ *HeaderSize = CopiedLen;\r
+ CopyMem (HeaderPtr, Udp6Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);\r
+ }\r
+ //\r
+ // Copy the other part of received data.\r
+ //\r
+ if (Udp6Rx->DataLength - CopiedLen > *BufferSize) {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ } else {\r
+ *BufferSize = Udp6Rx->DataLength - CopiedLen;\r
+ CopyMem (BufferPtr, (UINT8 *) Udp6Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);\r
+ }\r
+ //\r
+ // Recycle the receiving buffer after copy to user.\r
+ //\r
+ gBS->SignalEvent (Udp6Rx->RecycleSignal);\r
+ } else {\r
+ Udp4Rx = Udp4Token.Packet.RxData;\r
+ ASSERT (Udp4Rx != NULL);\r
+ //\r
+ // Copy the header part of received data.\r
+ //\r
+ if (HeaderSize != NULL) {\r
+ CopiedLen = MIN (*HeaderSize, Udp4Rx->DataLength);\r
+ *HeaderSize = CopiedLen;\r
+ CopyMem (HeaderPtr, Udp4Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);\r
+ }\r
+ //\r
+ // Copy the other part of received data.\r
+ //\r
+ if (Udp4Rx->DataLength - CopiedLen > *BufferSize) {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ } else {\r
+ *BufferSize = Udp4Rx->DataLength - CopiedLen;\r
+ CopyMem (BufferPtr, (UINT8 *) Udp4Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);\r
+ }\r
+ //\r
+ // Recycle the receiving buffer after copy to user.\r
+ //\r
+ gBS->SignalEvent (Udp4Rx->RecycleSignal);\r
+ }\r
+ }\r
+\r
+ if (Mode->UsingIpv6) {\r
+ Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token);\r
+ gBS->CloseEvent (Udp6Token.Event);\r
+ } else {\r
+ Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token);\r
+ gBS->CloseEvent (Udp4Token.Event);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Updates the IP receive filters of a network device and enables software filtering.\r
+\r
+ The NewFilter field is used to modify the network device's current IP receive\r
+ filter settings and to enable a software filter. This function updates the IpFilter\r
+ field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter.\r
+ The software filter is used when the USE_FILTER in OpFlags is set to UdpRead().\r
+ The current hardware filter remains in effect no matter what the settings of OpFlags.\r
+ This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those\r
+ packets whose reception is enabled in hardware-physical NIC address (unicast),\r
+ broadcast address, logical address or addresses (multicast), or all (promiscuous).\r
+ UdpRead() does not modify the IP filter settings.\r
+ Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive\r
+ filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.\r
+ If an application or driver wishes to preserve the IP receive filter settings,\r
+ it will have to preserve the IP receive filter settings before these calls, and\r
+ use SetIpFilter() to restore them after the calls. If incompatible filtering is\r
+ requested (for example, PROMISCUOUS with anything else), or if the device does not\r
+ support a requested filter setting and it cannot be accommodated in software\r
+ (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned.\r
+ The IPlist field is used to enable IPs other than the StationIP. They may be\r
+ multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP,\r
+ then both the StationIP and the IPs from the IPlist will be used.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] NewFilter Pointer to the new set of IP receive filters.\r
+\r
+ @retval EFI_SUCCESS The IP receive filter settings were updated.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetIpFilter (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_UDP4_CONFIG_DATA Udp4Cfg;\r
+ EFI_UDP6_CONFIG_DATA Udp6Cfg;\r
+ UINTN Index;\r
+ BOOLEAN NeedPromiscuous;\r
+\r
+ if (This == NULL || NewFilter == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+ Status = EFI_SUCCESS;\r
+ NeedPromiscuous = FALSE;\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ for (Index = 0; Index < NewFilter->IpCnt; Index++) {\r
+ ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);\r
+ if (!Mode->UsingIpv6 &&\r
+ IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) {\r
+ //\r
+ // IPv4 broadcast address should not be in IP filter.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&\r
+ (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) ||\r
+ NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) {\r
+ //\r
+ // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address\r
+ // is in IpList, promiscuous mode is needed.\r
+ //\r
+ NeedPromiscuous = TRUE;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Clear configuration for UdpRead and leave the original group joined before.\r
+ //\r
+ if (Mode->UsingIpv6) {\r
+ CopyMem(&Udp6Cfg, &Private->Udp6CfgData, sizeof (EFI_UDP6_CONFIG_DATA));\r
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
+ Udp6Cfg.AcceptPromiscuous = FALSE;\r
+ } else {\r
+ CopyMem(&Udp4Cfg, &Private->Udp4CfgData, sizeof (EFI_UDP4_CONFIG_DATA));\r
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);\r
+ Udp4Cfg.AcceptPromiscuous = FALSE;\r
+ Udp4Cfg.AcceptBroadcast = FALSE;\r
+ }\r
+\r
+ if (NeedPromiscuous ||\r
+ (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 ||\r
+ (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) {\r
+ //\r
+ // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets.\r
+ //\r
+ Udp4Cfg.AcceptPromiscuous = TRUE;\r
+ Udp6Cfg.AcceptPromiscuous = TRUE;\r
+\r
+ } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) {\r
+ //\r
+ // Configure UDPv4 to receive all broadcast packets.\r
+ //\r
+ Udp4Cfg.AcceptBroadcast = TRUE;\r
+ }\r
+\r
+ //\r
+ // Configure UDPv4/UDPv6 instance with the new configuration.\r
+ //\r
+ if (Mode->UsingIpv6) {\r
+ Status = Private->Udp6Read->Configure (Private->Udp6Read, &Udp6Cfg);\r
+ } else {\r
+ Status = Private->Udp4Read->Configure (Private->Udp4Read, &Udp4Cfg);\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) {\r
+\r
+ for (Index = 0; Index < NewFilter->IpCnt; Index++) {\r
+ //\r
+ // Join the multicast group if needed.\r
+ //\r
+ if (Mode->UsingIpv6) {\r
+ if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) {\r
+ Status = Private->Udp6Read->Groups (\r
+ Private->Udp6Read,\r
+ TRUE,\r
+ &NewFilter->IpList[Index].v6\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ } else {\r
+ if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) {\r
+ Status = Private->Udp4Read->Groups (\r
+ Private->Udp4Read,\r
+ TRUE,\r
+ &NewFilter->IpList[Index].v4\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Save the new IP filter into mode data.\r
+ //\r
+ CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter));\r
+\r
+ON_EXIT:\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6.\r
+\r
+ This function uses the ARP protocol to resolve a MAC address. The IP address specified\r
+ by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving\r
+ the specified address, then the ArpCacheEntries and ArpCache fields of the mode data\r
+ are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved\r
+ MAC address is placed there as well. If the PXE Base Code protocol is in the\r
+ stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters\r
+ a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is\r
+ returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,\r
+ then EFI_ABORTED is returned.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] IpAddr Pointer to the IP address that is used to resolve a MAC address.\r
+ @param[in] MacAddr If not NULL, a pointer to the MAC address that was resolved with the\r
+ ARP protocol.\r
+\r
+ @retval EFI_SUCCESS The IP or MAC address was resolved.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval EFI_ICMP_ERROR An error occur with the ICMP packet message.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcArp (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN EFI_IP_ADDRESS *IpAddr,\r
+ IN EFI_MAC_ADDRESS *MacAddr OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_EVENT ResolvedEvent;\r
+ EFI_STATUS Status;\r
+ EFI_MAC_ADDRESS TempMac;\r
+ EFI_MAC_ADDRESS ZeroMac;\r
+ BOOLEAN IsResolved;\r
+\r
+ if (This == NULL || IpAddr == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+ ResolvedEvent = NULL;\r
+ Status = EFI_SUCCESS;\r
+ IsResolved = FALSE;\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Mode->UsingIpv6) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Station address should be ready before do arp.\r
+ //\r
+ if (!Private->IsAddressOk) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Mode->IcmpErrorReceived = FALSE;\r
+ ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS));\r
+ ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS));\r
+\r
+ if (!Mode->AutoArp) {\r
+ //\r
+ // If AutoArp is FALSE, only search in the current Arp cache.\r
+ //\r
+ PxeBcArpCacheUpdate (NULL, Private);\r
+ if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto ON_EXIT;\r
+ }\r
+ } else {\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcCommonNotify,\r
+ &IsResolved,\r
+ &ResolvedEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // If AutoArp is TRUE, try to send Arp request on initiative.\r
+ //\r
+ Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac);\r
+ if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ while (!IsResolved) {\r
+ if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {\r
+ break;\r
+ }\r
+ }\r
+ if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_TIMEOUT;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Copy the Mac address to user if needed.\r
+ //\r
+ if (MacAddr != NULL && !EFI_ERROR (Status)) {\r
+ CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS));\r
+ }\r
+\r
+ON_EXIT:\r
+ if (ResolvedEvent != NULL) {\r
+ gBS->CloseEvent (ResolvedEvent);\r
+ }\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Updates the parameters that affect the operation of the PXE Base Code Protocol.\r
+\r
+ This function sets parameters that affect the operation of the PXE Base Code Protocol.\r
+ The parameter specified by NewAutoArp is used to control the generation of ARP\r
+ protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated\r
+ as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP\r
+ Protocol packets will be generated. In this case, the only mappings that are\r
+ available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure.\r
+ If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol\r
+ service, then the service will fail. This function updates the AutoArp field of\r
+ the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp.\r
+ The SetParameters() call must be invoked after a Callback Protocol is installed\r
+ to enable the use of callbacks.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] NewAutoArp If not NULL, a pointer to a value that specifies whether to replace the\r
+ current value of AutoARP.\r
+ @param[in] NewSendGUID If not NULL, a pointer to a value that specifies whether to replace the\r
+ current value of SendGUID.\r
+ @param[in] NewTTL If not NULL, a pointer to be used in place of the current value of TTL,\r
+ the "time to live" field of the IP header.\r
+ @param[in] NewToS If not NULL, a pointer to be used in place of the current value of ToS,\r
+ the "type of service" field of the IP header.\r
+ @param[in] NewMakeCallback If not NULL, a pointer to a value that specifies whether to replace the\r
+ current value of the MakeCallback field of the Mode structure.\r
+\r
+ @retval EFI_SUCCESS The new parameters values were updated.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetParameters (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN BOOLEAN *NewAutoArp OPTIONAL,\r
+ IN BOOLEAN *NewSendGUID OPTIONAL,\r
+ IN UINT8 *NewTTL OPTIONAL,\r
+ IN UINT8 *NewToS OPTIONAL,\r
+ IN BOOLEAN *NewMakeCallback OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_GUID SystemGuid;\r
+ EFI_STATUS Status;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (NewMakeCallback != NULL) {\r
+ if (*NewMakeCallback) {\r
+ //\r
+ // Update the previous PxeBcCallback protocol.\r
+ //\r
+ Status = gBS->HandleProtocol (\r
+ Private->Controller,\r
+ &gEfiPxeBaseCodeCallbackProtocolGuid,\r
+ (VOID **) &Private->PxeBcCallback\r
+ );\r
+\r
+ if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } else {\r
+ Private->PxeBcCallback = NULL;\r
+ }\r
+ Mode->MakeCallbacks = *NewMakeCallback;\r
+ }\r
+\r
+ if (NewSendGUID != NULL) {\r
+ if (*NewSendGUID && EFI_ERROR (PxeBcGetSystemGuid (&SystemGuid))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ Mode->SendGUID = *NewSendGUID;\r
+ }\r
+\r
+ if (NewAutoArp != NULL) {\r
+ Mode->AutoArp = *NewAutoArp;\r
+ }\r
+\r
+ if (NewTTL != NULL) {\r
+ Mode->TTL = *NewTTL;\r
+ }\r
+\r
+ if (NewToS != NULL) {\r
+ Mode->ToS = *NewToS;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Updates the station IP address and/or subnet mask values of a network device.\r
+\r
+ This function updates the station IP address and/or subnet mask values of a network\r
+ device. The NewStationIp field is used to modify the network device's current IP address.\r
+ If NewStationIP is NULL, then the current IP address will not be modified. Otherwise,\r
+ this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure\r
+ with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet\r
+ mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified.\r
+ Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE\r
+ structure with NewSubnetMask.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] NewStationIp Pointer to the new IP address to be used by the network device.\r
+ @param[in] NewSubnetMask Pointer to the new subnet mask to be used by the network device.\r
+\r
+ @retval EFI_SUCCESS The new station IP address and/or subnet mask were updated.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetStationIP (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN EFI_IP_ADDRESS *NewStationIp OPTIONAL,\r
+ IN EFI_IP_ADDRESS *NewSubnetMask OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_ARP_CONFIG_DATA ArpConfigData;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (NewStationIp != NULL &&\r
+ (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) &&\r
+ !NetIp6IsValidUnicast (&NewStationIp->v6))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (!Mode->UsingIpv6 &&\r
+ NewSubnetMask != NULL &&\r
+ !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Mode->UsingIpv6 && NewStationIp != NULL) {\r
+ //\r
+ // Set the IPv6 address by Ip6Config protocol.\r
+ //\r
+ Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+ } else if (!Mode->UsingIpv6 && NewStationIp != NULL) {\r
+ //\r
+ // Configure the corresponding ARP with the IPv4 address.\r
+ //\r
+ ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));\r
+\r
+ ArpConfigData.SwAddressType = 0x0800;\r
+ ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);\r
+ ArpConfigData.StationAddress = &NewStationIp->v4;\r
+\r
+ Private->Arp->Configure (Private->Arp, NULL);\r
+ Private->Arp->Configure (Private->Arp, &ArpConfigData);\r
+\r
+ if (NewSubnetMask != NULL) {\r
+ Mode->RouteTableEntries = 1;\r
+ Mode->RouteTable[0].IpAddr.Addr[0] = NewStationIp->Addr[0] & NewSubnetMask->Addr[0];\r
+ Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0];\r
+ Mode->RouteTable[0].GwAddr.Addr[0] = 0;\r
+ }\r
+\r
+ Private->IsAddressOk = TRUE;\r
+ }\r
+\r
+ if (NewStationIp != NULL) {\r
+ CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));\r
+ }\r
+\r
+ if (!Mode->UsingIpv6 && NewSubnetMask != NULL) {\r
+ CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS));\r
+ }\r
+\r
+ Status = PxeBcFlushStaionIp (Private, NewStationIp, NewSubnetMask);\r
+ON_EXIT:\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Updates the contents of the cached DHCP and Discover packets.\r
+\r
+ The pointers to the new packets are used to update the contents of the cached\r
+ packets in the EFI_PXE_BASE_CODE_MODE structure.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.\r
+ @param[in] NewDhcpDiscoverValid Pointer to a value that will replace the current\r
+ DhcpDiscoverValid field.\r
+ @param[in] NewDhcpAckReceived Pointer to a value that will replace the current\r
+ DhcpAckReceived field.\r
+ @param[in] NewProxyOfferReceived Pointer to a value that will replace the current\r
+ ProxyOfferReceived field.\r
+ @param[in] NewPxeDiscoverValid Pointer to a value that will replace the current\r
+ ProxyOfferReceived field.\r
+ @param[in] NewPxeReplyReceived Pointer to a value that will replace the current\r
+ PxeReplyReceived field.\r
+ @param[in] NewPxeBisReplyReceived Pointer to a value that will replace the current\r
+ PxeBisReplyReceived field.\r
+ @param[in] NewDhcpDiscover Pointer to the new cached DHCP Discover packet contents.\r
+ @param[in] NewDhcpAck Pointer to the new cached DHCP Ack packet contents.\r
+ @param[in] NewProxyOffer Pointer to the new cached Proxy Offer packet contents.\r
+ @param[in] NewPxeDiscover Pointer to the new cached PXE Discover packet contents.\r
+ @param[in] NewPxeReply Pointer to the new cached PXE Reply packet contents.\r
+ @param[in] NewPxeBisReply Pointer to the new cached PXE BIS Reply packet contents.\r
+\r
+ @retval EFI_SUCCESS The cached packet contents were updated.\r
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.\r
+ @retval EFI_INVALID_PARAMETER This is NULL or does not point to a valid\r
+ EFI_PXE_BASE_CODE_PROTOCOL structure.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeBcSetPackets (\r
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,\r
+ IN BOOLEAN *NewDhcpDiscoverValid OPTIONAL,\r
+ IN BOOLEAN *NewDhcpAckReceived OPTIONAL,\r
+ IN BOOLEAN *NewProxyOfferReceived OPTIONAL,\r
+ IN BOOLEAN *NewPxeDiscoverValid OPTIONAL,\r
+ IN BOOLEAN *NewPxeReplyReceived OPTIONAL,\r
+ IN BOOLEAN *NewPxeBisReplyReceived OPTIONAL,\r
+ IN EFI_PXE_BASE_CODE_PACKET *NewDhcpDiscover OPTIONAL,\r
+ IN EFI_PXE_BASE_CODE_PACKET *NewDhcpAck OPTIONAL,\r
+ IN EFI_PXE_BASE_CODE_PACKET *NewProxyOffer OPTIONAL,\r
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeDiscover OPTIONAL,\r
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeReply OPTIONAL,\r
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeBisReply OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ if (!Mode->Started) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (NewDhcpDiscoverValid != NULL) {\r
+ Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;\r
+ }\r
+\r
+ if (NewDhcpAckReceived != NULL) {\r
+ Mode->DhcpAckReceived = *NewDhcpAckReceived;\r
+ }\r
+\r
+ if (NewProxyOfferReceived != NULL) {\r
+ Mode->ProxyOfferReceived = *NewProxyOfferReceived;\r
+ }\r
+\r
+ if (NewPxeDiscoverValid != NULL) {\r
+ Mode->PxeDiscoverValid = *NewPxeDiscoverValid;\r
+ }\r
+\r
+ if (NewPxeReplyReceived != NULL) {\r
+ Mode->PxeReplyReceived = *NewPxeReplyReceived;\r
+ }\r
+\r
+ if (NewPxeBisReplyReceived != NULL) {\r
+ Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;\r
+ }\r
+\r
+ if (NewDhcpDiscover != NULL) {\r
+ CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+ }\r
+\r
+ if (NewDhcpAck != NULL) {\r
+ CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+ }\r
+\r
+ if (NewProxyOffer != NULL) {\r
+ CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+ }\r
+\r
+ if (NewPxeDiscover != NULL) {\r
+ CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+ }\r
+\r
+ if (NewPxeReply != NULL) {\r
+ CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+ }\r
+\r
+ if (NewPxeBisReply != NULL) {\r
+ CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate = {\r
+ EFI_PXE_BASE_CODE_PROTOCOL_REVISION,\r
+ EfiPxeBcStart,\r
+ EfiPxeBcStop,\r
+ EfiPxeBcDhcp,\r
+ EfiPxeBcDiscover,\r
+ EfiPxeBcMtftp,\r
+ EfiPxeBcUdpWrite,\r
+ EfiPxeBcUdpRead,\r
+ EfiPxeBcSetIpFilter,\r
+ EfiPxeBcArp,\r
+ EfiPxeBcSetParameters,\r
+ EfiPxeBcSetStationIP,\r
+ EfiPxeBcSetPackets,\r
+ NULL\r
+};\r
+\r
+\r
+/**\r
+ Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has\r
+ received, or is waiting to receive a packet.\r
+\r
+ This function is invoked when the PXE Base Code Protocol is about to transmit, has received,\r
+ or is waiting to receive a packet. Parameters Function and Received specify the type of event.\r
+ Parameters PacketLen and Packet specify the packet that generated the event. If these fields\r
+ are zero and NULL respectively, then this is a status update callback. If the operation specified\r
+ by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation\r
+ specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to\r
+ the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms.\r
+ The SetParameters() function must be called after a Callback Protocol is installed to enable the\r
+ use of callbacks.\r
+\r
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance.\r
+ @param[in] Function The PXE Base Code Protocol function that is waiting for an event.\r
+ @param[in] Received TRUE if the callback is being invoked due to a receive event. FALSE if\r
+ the callback is being invoked due to a transmit event.\r
+ @param[in] PacketLength The length, in bytes, of Packet. This field will have a value of zero if\r
+ this is a wait for receive event.\r
+ @param[in] PacketPtr If Received is TRUE, a pointer to the packet that was just received;\r
+ otherwise a pointer to the packet that is about to be transmitted.\r
+\r
+ @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation.\r
+ @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT If Function specifies an abort operation.\r
+\r
+**/\r
+EFI_PXE_BASE_CODE_CALLBACK_STATUS\r
+EFIAPI\r
+EfiPxeLoadFileCallback (\r
+ IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *This,\r
+ IN EFI_PXE_BASE_CODE_FUNCTION Function,\r
+ IN BOOLEAN Received,\r
+ IN UINT32 PacketLength,\r
+ IN EFI_PXE_BASE_CODE_PACKET *PacketPtr OPTIONAL\r
+ )\r
+{\r
+ EFI_INPUT_KEY Key;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Catch Ctrl-C or ESC to abort.\r
+ //\r
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) {\r
+\r
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;\r
+ }\r
+ }\r
+ //\r
+ // No print if receive packet\r
+ //\r
+ if (Received) {\r
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+ }\r
+ //\r
+ // Print only for three functions\r
+ //\r
+ switch (Function) {\r
+\r
+ case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:\r
+ //\r
+ // Print only for open MTFTP packets, not every MTFTP packets\r
+ //\r
+ if (PacketLength != 0 && PacketPtr != NULL) {\r
+ if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {\r
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+ }\r
+ }\r
+ break;\r
+\r
+ case EFI_PXE_BASE_CODE_FUNCTION_DHCP:\r
+ case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:\r
+ break;\r
+\r
+ default:\r
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+ }\r
+\r
+ if (PacketLength != 0 && PacketPtr != NULL) {\r
+ //\r
+ // Print '.' when transmit a packet\r
+ //\r
+ AsciiPrint (".");\r
+ }\r
+\r
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;\r
+}\r
+\r
+EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = {\r
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,\r
+ EfiPxeLoadFileCallback\r
+};\r
+\r
+\r
+/**\r
+ Causes the driver to load a specified file.\r
+\r
+ @param[in] This Protocol instance pointer.\r
+ @param[in] FilePath The device specific path of the file to load.\r
+ @param[in] BootPolicy If TRUE, indicates that the request originates from the\r
+ boot manager is attempting to load FilePath as a boot\r
+ selection. If FALSE, then FilePath must match an exact file\r
+ to be loaded.\r
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
+ code of EFI_SUCCESS, the amount of data transferred to\r
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
+ the size of Buffer required to retrieve the requested file.\r
+ @param[in] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
+ then no the size of the requested file is returned in\r
+ BufferSize.\r
+\r
+ @retval EFI_SUCCESS The file was loaded.\r
+ @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy.\r
+ @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or\r
+ BufferSize is NULL.\r
+ @retval EFI_NO_MEDIA No medium was present to load the file.\r
+ @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.\r
+ @retval EFI_NO_RESPONSE The remote system did not respond.\r
+ @retval EFI_NOT_FOUND The file was not found.\r
+ @retval EFI_ABORTED The file load process was manually cancelled.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiPxeLoadFile (\r
+ IN EFI_LOAD_FILE_PROTOCOL *This,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ IN BOOLEAN BootPolicy,\r
+ IN OUT UINTN *BufferSize,\r
+ IN VOID *Buffer OPTIONAL\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ PXEBC_VIRTUAL_NIC *VirtualNic;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
+ BOOLEAN UsingIpv6;\r
+ EFI_STATUS Status;\r
+ BOOLEAN MediaPresent;\r
+\r
+ VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This);\r
+ Private = VirtualNic->Private;\r
+ PxeBc = &Private->PxeBc;\r
+ UsingIpv6 = FALSE;\r
+ Status = EFI_DEVICE_ERROR;\r
+\r
+ if (This == NULL || BufferSize == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Only support BootPolicy\r
+ //\r
+ if (!BootPolicy) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Check media status before PXE start\r
+ //\r
+ MediaPresent = TRUE;\r
+ NetLibDetectMedia (Private->Controller, &MediaPresent);\r
+ if (!MediaPresent) {\r
+ return EFI_NO_MEDIA;\r
+ }\r
+\r
+ //\r
+ // Check whether the virtual nic is using IPv6 or not.\r
+ //\r
+ if (VirtualNic == Private->Ip6Nic) {\r
+ UsingIpv6 = TRUE;\r
+ }\r
+\r
+ //\r
+ // Start Pxe Base Code to initialize PXE boot.\r
+ //\r
+ Status = PxeBc->Start (PxeBc, UsingIpv6);\r
+ if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {\r
+ Status = PxeBcLoadBootFile (Private, BufferSize, Buffer);\r
+ }\r
+\r
+ if (Status != EFI_SUCCESS &&\r
+ Status != EFI_UNSUPPORTED &&\r
+ Status != EFI_BUFFER_TOO_SMALL) {\r
+ //\r
+ // There are three cases, which needn't stop pxebc here.\r
+ // 1. success to download file.\r
+ // 2. success to get file size.\r
+ // 3. unsupported.\r
+ //\r
+ PxeBc->Stop (PxeBc);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate = { EfiPxeLoadFile };\r
+\r
--- /dev/null
+/** @file\r
+ This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.\r
+ interfaces declaration.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_IMPL_H__\r
+#define __EFI_PXEBC_IMPL_H__\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Guid/SmBios.h>\r
+#include <IndustryStandard/SmBios.h>\r
+#include <Protocol/NetworkInterfaceIdentifier.h>\r
+#include <Protocol/Arp.h>\r
+#include <Protocol/Ip4.h>\r
+#include <Protocol/Ip6.h>\r
+#include <Protocol/Ip6Config.h>\r
+#include <Protocol/Udp4.h>\r
+#include <Protocol/Udp6.h>\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Dhcp6.h>\r
+#include <Protocol/Mtftp4.h>\r
+#include <Protocol/Mtftp6.h>\r
+#include <Protocol/PxeBaseCode.h>\r
+#include <Protocol/LoadFile.h>\r
+#include <Protocol/PxeBaseCodeCallBack.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/DriverBinding.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/DpcLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PcdLib.h>\r
+\r
+typedef struct _PXEBC_PRIVATE_DATA PXEBC_PRIVATE_DATA;\r
+typedef struct _PXEBC_VIRTUAL_NIC PXEBC_VIRTUAL_NIC;\r
+\r
+#include "PxeBcDriver.h"\r
+#include "PxeBcDhcp4.h"\r
+#include "PxeBcDhcp6.h"\r
+#include "PxeBcMtftp.h"\r
+#include "PxeBcBoot.h"\r
+#include "PxeBcSupport.h"\r
+\r
+#define PXEBC_DEFAULT_HOPLIMIT 64\r
+#define PXEBC_DEFAULT_LIFETIME 50000 // 50 ms, unit is microsecond\r
+#define PXEBC_UDP_TIMEOUT 30000000 // 3 seconds, unit is 100nanosecond\r
+#define PXEBC_MTFTP_TIMEOUT 4\r
+#define PXEBC_MTFTP_RETRIES 6\r
+#define PXEBC_DHCP_RETRIES 4 // refers to mPxeDhcpTimeout, also by PXE2.1 spec.\r
+#define PXEBC_MENU_MAX_NUM 24\r
+#define PXEBC_OFFER_MAX_NUM 16\r
+\r
+#define PXEBC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'P')\r
+#define PXEBC_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'V')\r
+#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a) CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE)\r
+#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE)\r
+\r
+typedef union {\r
+ PXEBC_DHCP4_PACKET_CACHE Dhcp4;\r
+ PXEBC_DHCP6_PACKET_CACHE Dhcp6;\r
+} PXEBC_DHCP_PACKET_CACHE;\r
+\r
+struct _PXEBC_VIRTUAL_NIC {\r
+ UINT32 Signature;\r
+ EFI_HANDLE Controller;\r
+ EFI_LOAD_FILE_PROTOCOL LoadFile;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ PXEBC_PRIVATE_DATA *Private;\r
+};\r
+\r
+struct _PXEBC_PRIVATE_DATA {\r
+ UINT32 Signature;\r
+ EFI_HANDLE Controller;\r
+ EFI_HANDLE Image;\r
+\r
+ PXEBC_VIRTUAL_NIC *Ip4Nic;\r
+ PXEBC_VIRTUAL_NIC *Ip6Nic;\r
+\r
+ EFI_HANDLE ArpChild;\r
+ EFI_HANDLE Ip4Child;\r
+ EFI_HANDLE Dhcp4Child;\r
+ EFI_HANDLE Mtftp4Child;\r
+ EFI_HANDLE Udp4ReadChild;\r
+ EFI_HANDLE Udp4WriteChild;\r
+\r
+ EFI_ARP_PROTOCOL *Arp;\r
+ EFI_IP4_PROTOCOL *Ip4;\r
+ EFI_DHCP4_PROTOCOL *Dhcp4;\r
+ EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+ EFI_UDP4_PROTOCOL *Udp4Read;\r
+ EFI_UDP4_PROTOCOL *Udp4Write;\r
+\r
+ EFI_HANDLE Ip6Child;\r
+ EFI_HANDLE Dhcp6Child;\r
+ EFI_HANDLE Mtftp6Child;\r
+ EFI_HANDLE Udp6ReadChild;\r
+ EFI_HANDLE Udp6WriteChild;\r
+\r
+ EFI_IP6_PROTOCOL *Ip6;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
+ EFI_DHCP6_PROTOCOL *Dhcp6;\r
+ EFI_MTFTP6_PROTOCOL *Mtftp6;\r
+ EFI_UDP6_PROTOCOL *Udp6Read;\r
+ EFI_UDP6_PROTOCOL *Udp6Write;\r
+\r
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;\r
+ EFI_PXE_BASE_CODE_PROTOCOL PxeBc;\r
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL LoadFileCallback;\r
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *PxeBcCallback;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+\r
+ EFI_PXE_BASE_CODE_MODE Mode;\r
+ EFI_PXE_BASE_CODE_FUNCTION Function;\r
+ UINT32 Ip6Policy;\r
+\r
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;\r
+ EFI_UDP6_CONFIG_DATA Udp6CfgData;\r
+ EFI_IP4_CONFIG_DATA Ip4CfgData;\r
+ EFI_IP6_CONFIG_DATA Ip6CfgData;\r
+\r
+ EFI_EVENT UdpTimeOutEvent;\r
+ EFI_EVENT ArpUpdateEvent;\r
+ EFI_IP4_COMPLETION_TOKEN IcmpToken;\r
+ EFI_IP6_COMPLETION_TOKEN Icmp6Token;\r
+\r
+ BOOLEAN IsAddressOk;\r
+ BOOLEAN IsOfferSorted;\r
+ BOOLEAN IsProxyRecved;\r
+ BOOLEAN IsDoDiscover;\r
+\r
+ EFI_IP_ADDRESS StationIp;\r
+ EFI_IP_ADDRESS SubnetMask;\r
+ EFI_IP_ADDRESS GatewayIp;\r
+ EFI_IP_ADDRESS ServerIp;\r
+ UINT16 CurSrcPort;\r
+\r
+ UINT32 Ip4MaxPacketSize;\r
+ UINT32 Ip6MaxPacketSize;\r
+ UINT8 *BootFileName;\r
+ UINTN BootFileSize;\r
+ UINTN BlockSize;\r
+\r
+ PXEBC_DHCP_PACKET_CACHE ProxyOffer;\r
+ PXEBC_DHCP_PACKET_CACHE DhcpAck;\r
+ PXEBC_DHCP_PACKET_CACHE PxeReply;\r
+ EFI_DHCP6_PACKET *Dhcp6Request;\r
+ EFI_DHCP4_PACKET SeedPacket;\r
+\r
+ //\r
+ // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.\r
+ //\r
+ // It supposed that\r
+ //\r
+ // OfferNum: 8\r
+ // OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl]\r
+ // (OfferBuffer is 0-based.)\r
+ //\r
+ // And assume that (DhcpPxe10 is the first priority actually.)\r
+ //\r
+ // SelectIndex: 2\r
+ // SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL\r
+ // (SelectIndex is 1-based, and 0 means no one is selected.)\r
+ //\r
+ // So it should be\r
+ //\r
+ // DhcpOnly DhcpPxe10 DhcpWfm11a DhcpBinl ProxyPxe10 ProxyWfm11a ProxyBinl Bootp\r
+ // OfferCount: [ 2(n), 1(n), 0(n), 1(n), 1(1), 0(1), 3(n), 1(1)]\r
+ //\r
+ // OfferIndex: {[ 2, 5, 0, 6, 3, 0, *0, 0]\r
+ // [ 4, 0, 0, 0, 0, 0, 1, 0]\r
+ // [ 0, 0, 0, 0, 0, 0, 7, 0]\r
+ // ... ]}\r
+ // (OfferIndex is 0-based.)\r
+ //\r
+ //\r
+ UINT32 SelectIndex;\r
+ UINT32 SelectProxyType;\r
+ PXEBC_DHCP_PACKET_CACHE OfferBuffer[PXEBC_OFFER_MAX_NUM];\r
+ UINT32 OfferNum;\r
+ UINT32 OfferCount[PxeOfferTypeMax];\r
+ UINT32 OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM];\r
+};\r
+\r
+extern EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate;\r
+extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate;\r
+extern EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate;\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Functions implementation related with Mtftp for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = {\r
+ "blksize",\r
+ "timeout",\r
+ "tsize",\r
+ "multicast"\r
+};\r
+\r
+\r
+/**\r
+ This is a callback function when packets are received or transmitted in Mtftp driver.\r
+\r
+ A callback function that is provided by the caller to intercept\r
+ the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the\r
+ EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept\r
+ EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to\r
+ EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().\r
+\r
+ @param[in] This Pointer to EFI_MTFTP6_PROTOCOL.\r
+ @param[in] Token Pointer to EFI_MTFTP6_TOKEN.\r
+ @param[in] PacketLen Length of EFI_MTFTP6_PACKET.\r
+ @param[in] Packet Pointer to EFI_MTFTP6_PACKET to be checked.\r
+\r
+ @retval EFI_SUCCESS The current operation succeeded.\r
+ @retval EFI_ABORTED Abort the current transfer process.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcMtftp6CheckPacket (\r
+ IN EFI_MTFTP6_PROTOCOL *This,\r
+ IN EFI_MTFTP6_TOKEN *Token,\r
+ IN UINT16 PacketLen,\r
+ IN EFI_MTFTP6_PACKET *Packet\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
+ EFI_STATUS Status;\r
+\r
+ Private = (PXEBC_PRIVATE_DATA *) Token->Context;\r
+ Callback = Private->PxeBcCallback;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) {\r
+ //\r
+ // Store the tftp error message into mode data and set the received flag.\r
+ //\r
+ Private->Mode.TftpErrorReceived = TRUE;\r
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+ AsciiStrnCpy (\r
+ Private->Mode.TftpError.ErrorString,\r
+ (CHAR8 *) Packet->Error.ErrorMessage,\r
+ PXE_MTFTP_ERROR_STRING_LENGTH\r
+ );\r
+ }\r
+\r
+ if (Callback != NULL) {\r
+ //\r
+ // Callback to user if has when received any tftp packet.\r
+ //\r
+ Status = Callback->Callback (\r
+ Callback,\r
+ Private->Function,\r
+ TRUE,\r
+ PacketLen,\r
+ (EFI_PXE_BASE_CODE_PACKET *) Packet\r
+ );\r
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+ //\r
+ // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+ //\r
+ Status = EFI_ABORTED;\r
+ } else {\r
+ //\r
+ // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to get the size of a file using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Sucessfully obtained the size of file.\r
+ @retval EFI_NOT_FOUND Parse the tftp ptions failed.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Has not obtained the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6GetFileSize (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP6_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ EFI_MTFTP6_PROTOCOL *Mtftp6;\r
+ EFI_MTFTP6_OPTION ReqOpt[2];\r
+ EFI_MTFTP6_PACKET *Packet;\r
+ EFI_MTFTP6_OPTION *Option;\r
+ UINT32 PktLen;\r
+ UINT8 OptBuf[128];\r
+ UINT32 OptCnt;\r
+ EFI_STATUS Status;\r
+\r
+ *BufferSize = 0;\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp6 = Private->Mtftp6;\r
+ Packet = NULL;\r
+ Option = NULL;\r
+ PktLen = 0;\r
+ OptCnt = 1;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp6->Configure (Mtftp6, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Build the required options for get info.\r
+ //\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];\r
+ PxeBcUintnToAscDec (0, OptBuf);\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Status = Mtftp6->GetInfo (\r
+ Mtftp6,\r
+ FALSE,\r
+ Filename,\r
+ NULL,\r
+ (UINT8) OptCnt,\r
+ ReqOpt,\r
+ &PktLen,\r
+ &Packet\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_TFTP_ERROR) {\r
+ //\r
+ // Store the tftp error message into mode data and set the received flag.\r
+ //\r
+ Private->Mode.TftpErrorReceived = TRUE;\r
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+ AsciiStrnCpy (\r
+ Private->Mode.TftpError.ErrorString,\r
+ (CHAR8 *) Packet->Error.ErrorMessage,\r
+ PXE_MTFTP_ERROR_STRING_LENGTH\r
+ );\r
+ }\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Parse the options in the reply packet.\r
+ //\r
+ OptCnt = 0;\r
+ Status = Mtftp6->ParseOptions (\r
+ Mtftp6,\r
+ PktLen,\r
+ Packet,\r
+ (UINT32 *) &OptCnt,\r
+ &Option\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Parse out the value of "tsize" option.\r
+ //\r
+ Status = EFI_NOT_FOUND;\r
+ while (OptCnt != 0) {\r
+ if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {\r
+ *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ OptCnt--;\r
+ }\r
+ FreePool (Option);\r
+\r
+ON_ERROR:\r
+ if (Packet != NULL) {\r
+ FreePool (Packet);\r
+ }\r
+ Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to get data of a file using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicates whether with a receive buffer.\r
+\r
+ @retval EFI_SUCCESS Successfully read the data from the special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6ReadFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP6_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ )\r
+{\r
+ EFI_MTFTP6_PROTOCOL *Mtftp6;\r
+ EFI_MTFTP6_TOKEN Token;\r
+ EFI_MTFTP6_OPTION ReqOpt[1];\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp6 = Private->Mtftp6;\r
+ OptCnt = 0;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp6->Configure (Mtftp6, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Token.Event = NULL;\r
+ Token.OverrideData = NULL;\r
+ Token.Filename = Filename;\r
+ Token.ModeStr = NULL;\r
+ Token.OptionCount = OptCnt;\r
+ Token.OptionList = ReqOpt;\r
+ Token.Context = Private;\r
+\r
+ if (DontUseBuffer) {\r
+ Token.BufferSize = 0;\r
+ Token.Buffer = NULL;\r
+ } else {\r
+ Token.BufferSize = *BufferSize;\r
+ Token.Buffer = BufferPtr;\r
+ }\r
+\r
+ Token.CheckPacket = PxeBcMtftp6CheckPacket;\r
+ Token.TimeoutCallback = NULL;\r
+ Token.PacketNeeded = NULL;\r
+\r
+ Status = Mtftp6->ReadFile (Mtftp6, &Token);\r
+ //\r
+ // Get the real size of received buffer.\r
+ //\r
+ *BufferSize = Token.BufferSize;\r
+\r
+ Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is used to write the data of a file using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] Overwrite Indicate whether with overwrite attribute.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Successfully wrote the data into a special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval other Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6WriteFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP6_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN BOOLEAN Overwrite,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ EFI_MTFTP6_PROTOCOL *Mtftp6;\r
+ EFI_MTFTP6_TOKEN Token;\r
+ EFI_MTFTP6_OPTION ReqOpt[1];\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp6 = Private->Mtftp6;\r
+ OptCnt = 0;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp6->Configure (Mtftp6, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Token.Event = NULL;\r
+ Token.OverrideData = NULL;\r
+ Token.Filename = Filename;\r
+ Token.ModeStr = NULL;\r
+ Token.OptionCount = OptCnt;\r
+ Token.OptionList = ReqOpt;\r
+ Token.BufferSize = *BufferSize;\r
+ Token.Buffer = BufferPtr;\r
+ Token.CheckPacket = PxeBcMtftp6CheckPacket;\r
+ Token.TimeoutCallback = NULL;\r
+ Token.PacketNeeded = NULL;\r
+\r
+ Status = Mtftp6->WriteFile (Mtftp6, &Token);\r
+ //\r
+ // Get the real size of transmitted buffer.\r
+ //\r
+ *BufferSize = Token.BufferSize;\r
+\r
+ Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to read the data (file) from a directory using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the data from the file included in directory.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp6ReadDirectory (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP6_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ )\r
+{\r
+ EFI_MTFTP6_PROTOCOL *Mtftp6;\r
+ EFI_MTFTP6_TOKEN Token;\r
+ EFI_MTFTP6_OPTION ReqOpt[1];\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp6 = Private->Mtftp6;\r
+ OptCnt = 0;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp6->Configure (Mtftp6, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Token.Event = NULL;\r
+ Token.OverrideData = NULL;\r
+ Token.Filename = Filename;\r
+ Token.ModeStr = NULL;\r
+ Token.OptionCount = OptCnt;\r
+ Token.OptionList = ReqOpt;\r
+ Token.Context = Private;\r
+\r
+ if (DontUseBuffer) {\r
+ Token.BufferSize = 0;\r
+ Token.Buffer = NULL;\r
+ } else {\r
+ Token.BufferSize = *BufferSize;\r
+ Token.Buffer = BufferPtr;\r
+ }\r
+\r
+ Token.CheckPacket = PxeBcMtftp6CheckPacket;\r
+ Token.TimeoutCallback = NULL;\r
+ Token.PacketNeeded = NULL;\r
+\r
+ Status = Mtftp6->ReadDirectory (Mtftp6, &Token);\r
+ //\r
+ // Get the real size of received buffer.\r
+ //\r
+ *BufferSize = Token.BufferSize;\r
+\r
+ Mtftp6->Configure (Mtftp6, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This is a callback function when packets are received or transmitted in Mtftp driver.\r
+\r
+ A callback function that is provided by the caller to intercept\r
+ the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the\r
+ EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept\r
+ EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to\r
+ EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().\r
+\r
+ @param[in] This Pointer to EFI_MTFTP4_PROTOCOL.\r
+ @param[in] Token Pointer to EFI_MTFTP4_TOKEN.\r
+ @param[in] PacketLen Length of EFI_MTFTP4_PACKET.\r
+ @param[in] Packet Pointer to EFI_MTFTP4_PACKET to be checked.\r
+\r
+ @retval EFI_SUCCESS The current operation succeeeded.\r
+ @retval EFI_ABORTED Abort the current transfer process.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PxeBcMtftp4CheckPacket (\r
+ IN EFI_MTFTP4_PROTOCOL *This,\r
+ IN EFI_MTFTP4_TOKEN *Token,\r
+ IN UINT16 PacketLen,\r
+ IN EFI_MTFTP4_PACKET *Packet\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
+ EFI_STATUS Status;\r
+\r
+ Private = (PXEBC_PRIVATE_DATA *) Token->Context;\r
+ Callback = Private->PxeBcCallback;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) {\r
+ //\r
+ // Store the tftp error message into mode data and set the received flag.\r
+ //\r
+ Private->Mode.TftpErrorReceived = TRUE;\r
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+ AsciiStrnCpy (\r
+ Private->Mode.TftpError.ErrorString,\r
+ (CHAR8 *) Packet->Error.ErrorMessage,\r
+ PXE_MTFTP_ERROR_STRING_LENGTH\r
+ );\r
+ }\r
+\r
+ if (Callback != NULL) {\r
+ //\r
+ // Callback to user if has when received any tftp packet.\r
+ //\r
+ Status = Callback->Callback (\r
+ Callback,\r
+ Private->Function,\r
+ TRUE,\r
+ PacketLen,\r
+ (EFI_PXE_BASE_CODE_PACKET *) Packet\r
+ );\r
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
+ //\r
+ // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+ //\r
+ Status = EFI_ABORTED;\r
+ } else {\r
+ //\r
+ // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to get size of a file using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the size of file.\r
+ @retval EFI_NOT_FOUND Parse the tftp options failed.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Did not obtain the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4GetFileSize (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP4_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+ EFI_MTFTP4_OPTION ReqOpt[2];\r
+ EFI_MTFTP4_PACKET *Packet;\r
+ EFI_MTFTP4_OPTION *Option;\r
+ UINT32 PktLen;\r
+ UINT8 OptBuf[128];\r
+ UINT32 OptCnt;\r
+ EFI_STATUS Status;\r
+\r
+ *BufferSize = 0;\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp4 = Private->Mtftp4;\r
+ Packet = NULL;\r
+ Option = NULL;\r
+ PktLen = 0;\r
+ OptCnt = 1;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp4->Configure (Mtftp4, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Build the required options for get info.\r
+ //\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];\r
+ PxeBcUintnToAscDec (0, OptBuf);\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Status = Mtftp4->GetInfo (\r
+ Mtftp4,\r
+ FALSE,\r
+ Filename,\r
+ NULL,\r
+ (UINT8) OptCnt,\r
+ ReqOpt,\r
+ &PktLen,\r
+ &Packet\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_TFTP_ERROR) {\r
+ //\r
+ // Store the tftp error message into mode data and set the received flag.\r
+ //\r
+ Private->Mode.TftpErrorReceived = TRUE;\r
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;\r
+ AsciiStrnCpy (\r
+ Private->Mode.TftpError.ErrorString,\r
+ (CHAR8 *) Packet->Error.ErrorMessage,\r
+ PXE_MTFTP_ERROR_STRING_LENGTH\r
+ );\r
+ }\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Parse the options in the reply packet.\r
+ //\r
+ OptCnt = 0;\r
+ Status = Mtftp4->ParseOptions (\r
+ Mtftp4,\r
+ PktLen,\r
+ Packet,\r
+ (UINT32 *) &OptCnt,\r
+ &Option\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_ERROR;\r
+ }\r
+\r
+ //\r
+ // Parse out the value of "tsize" option.\r
+ //\r
+ Status = EFI_NOT_FOUND;\r
+ while (OptCnt != 0) {\r
+ if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {\r
+ *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ OptCnt--;\r
+ }\r
+ FreePool (Option);\r
+\r
+ON_ERROR:\r
+ if (Packet != NULL) {\r
+ FreePool (Packet);\r
+ }\r
+ Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to read the data of a file using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.\r
+\r
+ @retval EFI_SUCCESS Successfully read the data from the special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4ReadFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP4_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ )\r
+{\r
+ EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+ EFI_MTFTP4_TOKEN Token;\r
+ EFI_MTFTP4_OPTION ReqOpt[1];\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp4 = Private->Mtftp4;\r
+ OptCnt = 0;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp4->Configure (Mtftp4, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Token.Event = NULL;\r
+ Token.OverrideData = NULL;\r
+ Token.Filename = Filename;\r
+ Token.ModeStr = NULL;\r
+ Token.OptionCount = OptCnt;\r
+ Token.OptionList = ReqOpt;\r
+ Token.Context = Private;\r
+\r
+ if (DontUseBuffer) {\r
+ Token.BufferSize = 0;\r
+ Token.Buffer = NULL;\r
+ } else {\r
+ Token.BufferSize = *BufferSize;\r
+ Token.Buffer = BufferPtr;\r
+ }\r
+\r
+ Token.CheckPacket = PxeBcMtftp4CheckPacket;\r
+ Token.TimeoutCallback = NULL;\r
+ Token.PacketNeeded = NULL;\r
+\r
+ Status = Mtftp4->ReadFile (Mtftp4, &Token);\r
+ //\r
+ // Get the real size of received buffer.\r
+ //\r
+ *BufferSize = Token.BufferSize;\r
+\r
+ Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to write the data of a file using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] Overwrite Indicates whether to use the overwrite attribute.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Successfully write the data into the special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval other Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4WriteFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP4_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN BOOLEAN Overwrite,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+ EFI_MTFTP4_TOKEN Token;\r
+ EFI_MTFTP4_OPTION ReqOpt[1];\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp4 = Private->Mtftp4;\r
+ OptCnt = 0;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp4->Configure (Mtftp4, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Token.Event = NULL;\r
+ Token.OverrideData = NULL;\r
+ Token.Filename = Filename;\r
+ Token.ModeStr = NULL;\r
+ Token.OptionCount = OptCnt;\r
+ Token.OptionList = ReqOpt;\r
+ Token.BufferSize = *BufferSize;\r
+ Token.Buffer = BufferPtr;\r
+ Token.CheckPacket = PxeBcMtftp4CheckPacket;\r
+ Token.TimeoutCallback = NULL;\r
+ Token.PacketNeeded = NULL;\r
+\r
+ Status = Mtftp4->WriteFile (Mtftp4, &Token);\r
+ //\r
+ // Get the real size of transmitted buffer.\r
+ //\r
+ *BufferSize = Token.BufferSize;\r
+\r
+ Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to get data (file) from a directory using Tftp.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.\r
+\r
+ @retval EFI_SUCCES Successfully obtained the data from the file included in the directory.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcMtftp4ReadDirectory (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN EFI_MTFTP4_CONFIG_DATA *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ )\r
+{\r
+ EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+ EFI_MTFTP4_TOKEN Token;\r
+ EFI_MTFTP4_OPTION ReqOpt[1];\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_DEVICE_ERROR;\r
+ Mtftp4 = Private->Mtftp4;\r
+ OptCnt = 0;\r
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;\r
+\r
+ Status = Mtftp4->Configure (Mtftp4, Config);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (BlockSize != NULL) {\r
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);\r
+ OptCnt++;\r
+ }\r
+\r
+ Token.Event = NULL;\r
+ Token.OverrideData = NULL;\r
+ Token.Filename = Filename;\r
+ Token.ModeStr = NULL;\r
+ Token.OptionCount = OptCnt;\r
+ Token.OptionList = ReqOpt;\r
+ Token.Context = Private;\r
+\r
+ if (DontUseBuffer) {\r
+ Token.BufferSize = 0;\r
+ Token.Buffer = NULL;\r
+ } else {\r
+ Token.BufferSize = *BufferSize;\r
+ Token.Buffer = BufferPtr;\r
+ }\r
+\r
+ Token.CheckPacket = PxeBcMtftp4CheckPacket;\r
+ Token.TimeoutCallback = NULL;\r
+ Token.PacketNeeded = NULL;\r
+\r
+ Status = Mtftp4->ReadDirectory (Mtftp4, &Token);\r
+ //\r
+ // Get the real size of received buffer.\r
+ //\r
+ *BufferSize = Token.BufferSize;\r
+\r
+ Mtftp4->Configure (Mtftp4, NULL);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is wrapper to get the file size using TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to configure data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the size of file.\r
+ @retval EFI_NOT_FOUND Parse the tftp options failed.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Did not obtain the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpGetFileSize (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ if (Private->PxeBc.Mode->UsingIpv6) {\r
+ return PxeBcMtftp6GetFileSize (\r
+ Private,\r
+ (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferSize\r
+ );\r
+ } else {\r
+ return PxeBcMtftp4GetFileSize (\r
+ Private,\r
+ (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferSize\r
+ );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is a wrapper to get file using TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to config data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.\r
+\r
+ @retval EFI_SUCCESS Sucessfully read the data from the special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ )\r
+{\r
+ if (Private->PxeBc.Mode->UsingIpv6) {\r
+ return PxeBcMtftp6ReadFile (\r
+ Private,\r
+ (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize,\r
+ DontUseBuffer\r
+ );\r
+ } else {\r
+ return PxeBcMtftp4ReadFile (\r
+ Private,\r
+ (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize,\r
+ DontUseBuffer\r
+ );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is a wrapper to write file using TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to config data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] Overwrite Indicate whether with overwrite attribute.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Successfully wrote the data into a special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval other Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpWriteFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN BOOLEAN Overwrite,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize\r
+ )\r
+{\r
+ if (Private->PxeBc.Mode->UsingIpv6) {\r
+ return PxeBcMtftp6WriteFile (\r
+ Private,\r
+ (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+ Filename,\r
+ Overwrite,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize\r
+ );\r
+ } else {\r
+ return PxeBcMtftp4WriteFile (\r
+ Private,\r
+ (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+ Filename,\r
+ Overwrite,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize\r
+ );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is a wrapper to get the data (file) from a directory using TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to config data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicatse whether to use a receive buffer.\r
+\r
+ @retval EFI_SUCCES Successfully obtained the data from the file included in the directory.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadDirectory (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ )\r
+{\r
+ if (Private->PxeBc.Mode->UsingIpv6) {\r
+ return PxeBcMtftp6ReadDirectory (\r
+ Private,\r
+ (EFI_MTFTP6_CONFIG_DATA *) Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize,\r
+ DontUseBuffer\r
+ );\r
+ } else {\r
+ return PxeBcMtftp4ReadDirectory (\r
+ Private,\r
+ (EFI_MTFTP4_CONFIG_DATA *) Config,\r
+ Filename,\r
+ BlockSize,\r
+ BufferPtr,\r
+ BufferSize,\r
+ DontUseBuffer\r
+ );\r
+ }\r
+}\r
+\r
--- /dev/null
+/** @file\r
+ Functions declaration related with Mtftp for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_MTFTP_H__\r
+#define __EFI_PXEBC_MTFTP_H__\r
+\r
+#define PXE_MTFTP_OPTION_BLKSIZE_INDEX 0\r
+#define PXE_MTFTP_OPTION_TIMEOUT_INDEX 1\r
+#define PXE_MTFTP_OPTION_TSIZE_INDEX 2\r
+#define PXE_MTFTP_OPTION_MULTICAST_INDEX 3\r
+#define PXE_MTFTP_OPTION_MAXIMUM_INDEX 4\r
+\r
+#define PXE_MTFTP_ERROR_STRING_LENGTH 127 // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR.\r
+#define PXE_MTFTP_DEFAULT_BLOCK_SIZE 512 // refer to rfc-1350.\r
+\r
+\r
+/**\r
+ This function is wrapper to get the file size using TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to configure data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the size of file.\r
+ @retval EFI_NOT_FOUND Parse the tftp ptions failed.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Did not obtain the size of the file.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpGetFileSize (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN OUT UINT64 *BufferSize\r
+ );\r
+\r
+\r
+/**\r
+ This function is a wrapper to get a file using TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to config data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.\r
+\r
+ @retval EFI_SUCCESS Successfully read the data from the special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Read data from file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ );\r
+\r
+\r
+/**\r
+ This function is a wrapper to put file with TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to config data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] Overwrite Indicates whether to use an overwrite attribute.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+\r
+ @retval EFI_SUCCESS Successfully wrote the data into the special file.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval other Write data into file failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpWriteFile (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN BOOLEAN Overwrite,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize\r
+ );\r
+\r
+\r
+/**\r
+ This function is a wrapper to get the data (file) from a directory using TFTP.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] Config Pointer to config data.\r
+ @param[in] Filename Pointer to boot file name.\r
+ @param[in] BlockSize Pointer to required block size.\r
+ @param[in] BufferPtr Pointer to buffer.\r
+ @param[in, out] BufferSize Pointer to buffer size.\r
+ @param[in] DontUseBuffer Indicates whether with a receive buffer.\r
+\r
+ @retval EFI_SUCCES Successfully obtained the data from the file included in directory.\r
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.\r
+ @retval Others Operation failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcTftpReadDirectory (\r
+ IN PXEBC_PRIVATE_DATA *Private,\r
+ IN VOID *Config,\r
+ IN UINT8 *Filename,\r
+ IN UINTN *BlockSize,\r
+ IN UINT8 *BufferPtr,\r
+ IN OUT UINT64 *BufferSize,\r
+ IN BOOLEAN DontUseBuffer\r
+ );\r
+#endif\r
--- /dev/null
+/** @file\r
+ Support functions implementation for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "PxeBcImpl.h"\r
+\r
+\r
+\r
+/**\r
+ This function returns SMBIOS string given the string number.\r
+\r
+ @param[in] Smbios The pointer to the SMBIOS structure\r
+ @param[in] StringNumber String number to return. 0 is used to skip all\r
+ strings and point to the next SMBIOS structure.\r
+\r
+ @return String The pointer to the next SMBIOS structure if\r
+ StringNumber == 0.\r
+\r
+**/\r
+CHAR8 *\r
+PxeBcGetSmbiosString (\r
+ IN SMBIOS_STRUCTURE_POINTER *Smbios,\r
+ IN UINT16 StringNumber\r
+ )\r
+{\r
+ UINT16 Index;\r
+ CHAR8 *String;\r
+\r
+ //\r
+ // Skip over formatted section.\r
+ //\r
+ String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);\r
+\r
+ //\r
+ // Look through unformated section.\r
+ //\r
+ for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {\r
+ if (StringNumber == Index) {\r
+ return String;\r
+ }\r
+\r
+ //\r
+ // Skip zero string.\r
+ //\r
+ while (*String != 0) {\r
+ String++;\r
+ }\r
+ String++;\r
+\r
+ if (*String == 0) {\r
+ //\r
+ // If double NULL then we are done.\r
+ // Return pointer to next structure in Smbios.\r
+ // if you pass in a 0 you will always get here\r
+ //\r
+ Smbios->Raw = (UINT8 *)++String;\r
+ return NULL;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+\r
+/**\r
+ This function obtains the system guid and the serial number from the smbios table.\r
+\r
+ @param[out] SystemGuid The pointer of the returned system guid.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the system guid.\r
+ @retval EFI_NOT_FOUND Did not find the SMBIOS table.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcGetSystemGuid (\r
+ OUT EFI_GUID *SystemGuid\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ SMBIOS_TABLE_ENTRY_POINT *SmbiosTable;\r
+ SMBIOS_STRUCTURE_POINTER Smbios;\r
+ SMBIOS_STRUCTURE_POINTER SmbiosEnd;\r
+ UINT16 Index;\r
+\r
+ SmbiosTable = NULL;\r
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);\r
+\r
+ if (EFI_ERROR (Status) || SmbiosTable == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;\r
+ SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);\r
+\r
+ for (Index = 0; Index < SmbiosTable->TableLength; Index++) {\r
+ if (Smbios.Hdr->Type == 1) {\r
+ if (Smbios.Hdr->Length < 0x19) {\r
+ //\r
+ // Older version did not support Guid and Serial number\r
+ //\r
+ continue;\r
+ }\r
+ //\r
+ // SMBIOS tables are byte packed so we need to do a byte copy to\r
+ // prevend alignment faults on Itanium-based platform.\r
+ //\r
+ CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));\r
+ PxeBcGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+ //\r
+ // Make Smbios point to the next record\r
+ //\r
+ PxeBcGetSmbiosString (&Smbios, 0);\r
+\r
+ if (Smbios.Raw >= SmbiosEnd.Raw) {\r
+ //\r
+ // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.\r
+ // given this we must double check against the length of the structure.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+ Flush the previous configration using the new station Ip address.\r
+\r
+ @param[in] Private The pointer to the PxeBc private data.\r
+ @param[in] StationIp The pointer to the station Ip address.\r
+ @param[in] SubnetMask The pointer to the subnet mask address for v4.\r
+\r
+ @retval EFI_SUCCESS Successfully flushed the previous configuration.\r
+ @retval Others Failed to flush using the new station Ip.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcFlushStaionIp (\r
+ PXEBC_PRIVATE_DATA *Private,\r
+ EFI_IP_ADDRESS *StationIp,\r
+ EFI_IP_ADDRESS *SubnetMask OPTIONAL\r
+ )\r
+{\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (StationIp != NULL);\r
+\r
+ Mode = Private->PxeBc.Mode;\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (Mode->UsingIpv6) {\r
+\r
+ CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
+ CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ //\r
+ // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address.\r
+ //\r
+ Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);\r
+ Private->Ip6->Configure (Private->Ip6, NULL);\r
+\r
+ Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ } else {\r
+ ASSERT (SubnetMask != NULL);\r
+ CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ //\r
+ // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.\r
+ //\r
+ Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);\r
+ Private->Ip4->Configure (Private->Ip4, NULL);\r
+\r
+ Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ }\r
+\r
+ON_EXIT:\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Notify the callback function when an event is triggered.\r
+\r
+ @param[in] Event The triggered event.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcCommonNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ *((BOOLEAN *) Context) = TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Do arp resolution from arp cache in PxeBcMode.\r
+\r
+ @param Mode The pointer to EFI_PXE_BASE_CODE_MODE.\r
+ @param Ip4Addr The Ip4 address for resolution.\r
+ @param MacAddress The resoluted MAC address if the resolution is successful.\r
+ The value is undefined if the resolution fails.\r
+\r
+ @retval TRUE Found an matched entry.\r
+ @retval FALSE Did not find a matched entry.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckArpCache (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN EFI_IPv4_ADDRESS *Ip4Addr,\r
+ OUT EFI_MAC_ADDRESS *MacAddress\r
+ )\r
+{\r
+ UINT32 Index;\r
+\r
+ ASSERT (!Mode->UsingIpv6);\r
+\r
+ //\r
+ // Check whether the current Arp cache in mode data contains this information or not.\r
+ //\r
+ for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {\r
+ if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) {\r
+ CopyMem (\r
+ MacAddress,\r
+ &Mode->ArpCache[Index].MacAddr,\r
+ sizeof (EFI_MAC_ADDRESS)\r
+ );\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ Update the arp cache periodically.\r
+\r
+ @param Event The pointer to EFI_PXE_BC_PROTOCOL.\r
+ @param Context Context of the timer event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcArpCacheUpdate (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_ARP_FIND_DATA *ArpEntry;\r
+ UINT32 EntryLength;\r
+ UINT32 EntryCount;\r
+ UINT32 Index;\r
+ EFI_STATUS Status;\r
+\r
+ Private = (PXEBC_PRIVATE_DATA *) Context;\r
+ Mode = Private->PxeBc.Mode;\r
+\r
+ ASSERT (!Mode->UsingIpv6);\r
+\r
+ //\r
+ // Get the current Arp cache from Arp driver.\r
+ //\r
+ Status = Private->Arp->Find (\r
+ Private->Arp,\r
+ TRUE,\r
+ NULL,\r
+ &EntryLength,\r
+ &EntryCount,\r
+ &ArpEntry,\r
+ TRUE\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Update the Arp cache in mode data.\r
+ //\r
+ Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES);\r
+\r
+ for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {\r
+ CopyMem (\r
+ &Mode->ArpCache[Index].IpAddr,\r
+ ArpEntry + 1,\r
+ ArpEntry->SwAddressLength\r
+ );\r
+ CopyMem (\r
+ &Mode->ArpCache[Index].MacAddr,\r
+ (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength,\r
+ ArpEntry->HwAddressLength\r
+ );\r
+ ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Notify function to handle the received ICMP message in DPC.\r
+\r
+ @param Context The PXEBC private data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmpErrorDpcHandle (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IP4_RECEIVE_DATA *RxData;\r
+ EFI_IP4_PROTOCOL *Ip4;\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ UINT8 Type;\r
+ UINTN Index;\r
+ UINT32 CopiedLen;\r
+ UINT8 *IcmpError;\r
+\r
+ Private = (PXEBC_PRIVATE_DATA *) Context;\r
+ Mode = &Private->Mode;\r
+ Status = Private->IcmpToken.Status;\r
+ RxData = Private->IcmpToken.Packet.RxData;\r
+ Ip4 = Private->Ip4;\r
+\r
+ ASSERT (!Mode->UsingIpv6);\r
+\r
+ if (Status == EFI_ABORTED) {\r
+ //\r
+ // It's triggered by user cancellation.\r
+ //\r
+ return;\r
+ }\r
+\r
+ if (RxData == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (Status != EFI_ICMP_ERROR) {\r
+ //\r
+ // The return status should be recognized as EFI_ICMP_ERROR.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&\r
+ !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {\r
+ //\r
+ // The source address of the received packet should be a valid unicast address.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {\r
+ //\r
+ // The destination address of the received packet should be equal to the host address.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) {\r
+ //\r
+ // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);\r
+\r
+ if (Type != ICMP_DEST_UNREACHABLE &&\r
+ Type != ICMP_SOURCE_QUENCH &&\r
+ Type != ICMP_REDIRECT &&\r
+ Type != ICMP_TIME_EXCEEDED &&\r
+ Type != ICMP_PARAMETER_PROBLEM) {\r
+ //\r
+ // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Copy the right ICMP error message into mode data.\r
+ //\r
+ CopiedLen = 0;\r
+ IcmpError = (UINT8 *) &Mode->IcmpError;\r
+\r
+ for (Index = 0; Index < RxData->FragmentCount; Index++) {\r
+ CopiedLen += RxData->FragmentTable[Index].FragmentLength;\r
+ if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {\r
+ CopyMem (\r
+ IcmpError,\r
+ RxData->FragmentTable[Index].FragmentBuffer,\r
+ RxData->FragmentTable[Index].FragmentLength\r
+ );\r
+ } else {\r
+ CopyMem (\r
+ IcmpError,\r
+ RxData->FragmentTable[Index].FragmentBuffer,\r
+ CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)\r
+ );\r
+ }\r
+ IcmpError += CopiedLen;\r
+ }\r
+\r
+ON_EXIT:\r
+ Private->IcmpToken.Status = EFI_NOT_READY;\r
+ Ip4->Receive (Ip4, &Private->IcmpToken);\r
+}\r
+\r
+\r
+/**\r
+ Callback function to update the latest ICMP6 error message.\r
+\r
+ @param Event The event signalled.\r
+ @param Context The context passed in using the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmpErrorUpdate (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context);\r
+}\r
+\r
+\r
+/**\r
+ Notify function to handle the received ICMP6 message in DPC.\r
+\r
+ @param Context The PXEBC private data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmp6ErrorDpcHandle (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ PXEBC_PRIVATE_DATA *Private;\r
+ EFI_IP6_RECEIVE_DATA *RxData;\r
+ EFI_IP6_PROTOCOL *Ip6;\r
+ EFI_PXE_BASE_CODE_MODE *Mode;\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ UINT8 Type;\r
+ UINT32 CopiedLen;\r
+ UINT8 *Icmp6Error;\r
+\r
+ Private = (PXEBC_PRIVATE_DATA *) Context;\r
+ Mode = &Private->Mode;\r
+ Status = Private->Icmp6Token.Status;\r
+ RxData = Private->Icmp6Token.Packet.RxData;\r
+ Ip6 = Private->Ip6;\r
+\r
+ ASSERT (Mode->UsingIpv6);\r
+\r
+ if (Status == EFI_ABORTED) {\r
+ //\r
+ // It's triggered by user cancellation.\r
+ //\r
+ return;\r
+ }\r
+\r
+ if (RxData == NULL) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (Status != EFI_ICMP_ERROR) {\r
+ //\r
+ // The return status should be recognized as EFI_ICMP_ERROR.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) {\r
+ //\r
+ // The source address of the received packet should be a valid unicast address.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) &&\r
+ !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) {\r
+ //\r
+ // The destination address of the received packet should be equal to the host address.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ if (RxData->Header->NextHeader != IP6_ICMP) {\r
+ //\r
+ // The nextheader in the header of the receveid packet should be IP6_ICMP.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);\r
+\r
+ if (Type != ICMP_V6_DEST_UNREACHABLE &&\r
+ Type != ICMP_V6_PACKET_TOO_BIG &&\r
+ Type != ICMP_V6_PACKET_TOO_BIG &&\r
+ Type != ICMP_V6_PARAMETER_PROBLEM) {\r
+ //\r
+ // The type of the receveid packet should be an ICMP6 error message.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Copy the right ICMP6 error message into mode data.\r
+ //\r
+ CopiedLen = 0;\r
+ Icmp6Error = (UINT8 *) &Mode->IcmpError;\r
+\r
+ for (Index = 0; Index < RxData->FragmentCount; Index++) {\r
+ CopiedLen += RxData->FragmentTable[Index].FragmentLength;\r
+ if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {\r
+ CopyMem (\r
+ Icmp6Error,\r
+ RxData->FragmentTable[Index].FragmentBuffer,\r
+ RxData->FragmentTable[Index].FragmentLength\r
+ );\r
+ } else {\r
+ CopyMem (\r
+ Icmp6Error,\r
+ RxData->FragmentTable[Index].FragmentBuffer,\r
+ CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)\r
+ );\r
+ }\r
+ Icmp6Error += CopiedLen;\r
+ }\r
+\r
+ON_EXIT:\r
+ Private->Icmp6Token.Status = EFI_NOT_READY;\r
+ Ip6->Receive (Ip6, &Private->Icmp6Token);\r
+}\r
+\r
+\r
+/**\r
+ Callback function to update the latest ICMP6 error message.\r
+\r
+ @param Event The event signalled.\r
+ @param Context The context passed in using the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmp6ErrorUpdate (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context);\r
+}\r
+\r
+\r
+/**\r
+ This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.\r
+ @param[in] StationIp The pointer to the station address.\r
+ @param[in] SubnetMask The pointer to the subnet mask.\r
+ @param[in] Gateway The pointer to the gateway address.\r
+ @param[in, out] SrcPort The pointer to the source port.\r
+ @param[in] DoNotFragment If TRUE, fragment is not enabled.\r
+ Otherwise, fragment is enabled.\r
+\r
+ @retval EFI_SUCCESS Successfully configured this instance.\r
+ @retval Others Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp4Write (\r
+ IN EFI_UDP4_PROTOCOL *Udp4,\r
+ IN EFI_IPv4_ADDRESS *StationIp,\r
+ IN EFI_IPv4_ADDRESS *SubnetMask,\r
+ IN EFI_IPv4_ADDRESS *Gateway,\r
+ IN OUT UINT16 *SrcPort,\r
+ IN BOOLEAN DoNotFragment\r
+ )\r
+{\r
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;\r
+ EFI_STATUS Status;\r
+\r
+ ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));\r
+\r
+ Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ Udp4CfgData.TypeOfService = DEFAULT_ToS;\r
+ Udp4CfgData.TimeToLive = DEFAULT_TTL;\r
+ Udp4CfgData.AllowDuplicatePort = TRUE;\r
+ Udp4CfgData.DoNotFragment = DoNotFragment;\r
+\r
+ CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp));\r
+ CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask));\r
+\r
+ Udp4CfgData.StationPort = *SrcPort;\r
+\r
+ //\r
+ // Reset the UDPv4 instance.\r
+ //\r
+ Udp4->Configure (Udp4, NULL);\r
+\r
+ Status = Udp4->Configure (Udp4, &Udp4CfgData);\r
+ if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) {\r
+ //\r
+ // The basic configuration is OK, need to add the default route entry\r
+ //\r
+ Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway);\r
+ if (EFI_ERROR (Status)) {\r
+ Udp4->Configure (Udp4, NULL);\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) && *SrcPort == 0) {\r
+ Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL);\r
+ *SrcPort = Udp4CfgData.StationPort;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to configure a UDPv6 instance for UdpWrite.\r
+\r
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.\r
+ @param[in] StationIp The pointer to the station address.\r
+ @param[in, out] SrcPort The pointer to the source port.\r
+\r
+ @retval EFI_SUCCESS Successfully configured this instance.\r
+ @retval Others Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp6Write (\r
+ IN EFI_UDP6_PROTOCOL *Udp6,\r
+ IN EFI_IPv6_ADDRESS *StationIp,\r
+ IN OUT UINT16 *SrcPort\r
+ )\r
+{\r
+ EFI_UDP6_CONFIG_DATA CfgData;\r
+ EFI_STATUS Status;\r
+\r
+ ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+ CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME;\r
+ CfgData.HopLimit = PXEBC_DEFAULT_HOPLIMIT;\r
+ CfgData.AllowDuplicatePort = TRUE;\r
+ CfgData.StationPort = *SrcPort;\r
+\r
+ CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+ //\r
+ // Reset the UDPv6 instance.\r
+ //\r
+ Udp6->Configure (Udp6, NULL);\r
+\r
+ Status = Udp6->Configure (Udp6, &CfgData);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) && *SrcPort == 0) {\r
+ Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL);\r
+ *SrcPort = CfgData.StationPort;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.\r
+ @param[in] Session The pointer to the UDP4 session data.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] Gateway The pointer to the gateway address.\r
+ @param[in] HeaderSize An optional field which may be set to the length of a header\r
+ at HeaderPtr to be prefixed to the data at BufferPtr.\r
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be\r
+ prefixed to the data at BufferPtr.\r
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.\r
+ @param[in] BufferPtr A pointer to the data to be written.\r
+\r
+ @retval EFI_SUCCESS Successfully send out data using Udp4Write.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Write (\r
+ IN EFI_UDP4_PROTOCOL *Udp4,\r
+ IN EFI_UDP4_SESSION_DATA *Session,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN EFI_IPv4_ADDRESS *Gateway OPTIONAL,\r
+ IN UINTN *HeaderSize OPTIONAL,\r
+ IN VOID *HeaderPtr OPTIONAL,\r
+ IN UINTN *BufferSize,\r
+ IN VOID *BufferPtr\r
+ )\r
+{\r
+ EFI_UDP4_COMPLETION_TOKEN Token;\r
+ EFI_UDP4_TRANSMIT_DATA *TxData;\r
+ UINT32 TxLength;\r
+ UINT32 FragCount;\r
+ UINT32 DataLength;\r
+ BOOLEAN IsDone;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Arrange one fragment buffer for data, and another fragment buffer for header if has.\r
+ //\r
+ FragCount = (HeaderSize != NULL) ? 2 : 1;\r
+ TxLength = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA);\r
+ TxData = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength);\r
+ if (TxData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TxData->FragmentCount = FragCount;\r
+ TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;\r
+ TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;\r
+ DataLength = (UINT32) *BufferSize;\r
+\r
+ if (HeaderSize != NULL) {\r
+ TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;\r
+ TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;\r
+ DataLength += (UINT32) *HeaderSize;\r
+ }\r
+\r
+ if (Gateway != NULL) {\r
+ TxData->GatewayAddress = Gateway;\r
+ }\r
+\r
+ TxData->UdpSessionData = Session;\r
+ TxData->DataLength = DataLength;\r
+ Token.Packet.TxData = TxData;\r
+ Token.Status = EFI_NOT_READY;\r
+ IsDone = FALSE;\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcCommonNotify,\r
+ &IsDone,\r
+ &Token.Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Udp4->Transmit (Udp4, &Token);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+ //\r
+ while (!IsDone &&\r
+ Token.Status == EFI_NOT_READY &&\r
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+ Udp4->Poll (Udp4);\r
+ }\r
+\r
+ Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;\r
+\r
+ON_EXIT:\r
+ if (Token.Event != NULL) {\r
+ gBS->CloseEvent (Token.Event);\r
+ }\r
+ FreePool (TxData);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.\r
+ @param[in] Session The pointer to the UDP6 session data.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] HeaderSize An optional field which may be set to the length of a header\r
+ at HeaderPtr to be prefixed to the data at BufferPtr.\r
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be\r
+ prefixed to the data at BufferPtr.\r
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.\r
+ @param[in] BufferPtr A pointer to the data to be written.\r
+\r
+ @retval EFI_SUCCESS Successfully sent out data using Udp6Write.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Write (\r
+ IN EFI_UDP6_PROTOCOL *Udp6,\r
+ IN EFI_UDP6_SESSION_DATA *Session,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN UINTN *HeaderSize OPTIONAL,\r
+ IN VOID *HeaderPtr OPTIONAL,\r
+ IN UINTN *BufferSize,\r
+ IN VOID *BufferPtr\r
+ )\r
+{\r
+ EFI_UDP6_COMPLETION_TOKEN Token;\r
+ EFI_UDP6_TRANSMIT_DATA *TxData;\r
+ UINT32 TxLength;\r
+ UINT32 FragCount;\r
+ UINT32 DataLength;\r
+ BOOLEAN IsDone;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Arrange one fragment buffer for data, and another fragment buffer for header if has.\r
+ //\r
+ FragCount = (HeaderSize != NULL) ? 2 : 1;\r
+ TxLength = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA);\r
+ TxData = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength);\r
+ if (TxData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TxData->FragmentCount = FragCount;\r
+ TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;\r
+ TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;\r
+ DataLength = (UINT32) *BufferSize;\r
+\r
+ if (HeaderSize != NULL) {\r
+ TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;\r
+ TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;\r
+ DataLength += (UINT32) *HeaderSize;\r
+ }\r
+\r
+ TxData->UdpSessionData = Session;\r
+ TxData->DataLength = DataLength;\r
+ Token.Packet.TxData = TxData;\r
+ Token.Status = EFI_NOT_READY;\r
+ IsDone = FALSE;\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PxeBcCommonNotify,\r
+ &IsDone,\r
+ &Token.Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ Status = Udp6->Transmit (Udp6, &Token);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ //\r
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+ //\r
+ while (!IsDone &&\r
+ Token.Status == EFI_NOT_READY &&\r
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+ Udp6->Poll (Udp6);\r
+ }\r
+\r
+ Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;\r
+\r
+ON_EXIT:\r
+ if (Token.Event != NULL) {\r
+ gBS->CloseEvent (Token.Event);\r
+ }\r
+ FreePool (TxData);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Check the received packet using the Ip filter.\r
+\r
+ @param[in] Mode The pointer to the mode data of PxeBc.\r
+ @param[in] Session The pointer to the current UDPv4 session.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Passed the Ip filter successfully.\r
+ @retval FALSE Failed to pass the Ip filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByIpFilter (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN UINT16 OpFlags\r
+ )\r
+{\r
+ EFI_IP_ADDRESS DestinationIp;\r
+ UINTN Index;\r
+\r
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) {\r
+ return TRUE;\r
+ }\r
+\r
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) {\r
+ return TRUE;\r
+ }\r
+\r
+ //\r
+ // Convert the destination address in session data to host order.\r
+ //\r
+ if (Mode->UsingIpv6) {\r
+ CopyMem (\r
+ &DestinationIp,\r
+ &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ NTOHLLL (&DestinationIp.v6);\r
+ } else {\r
+ ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (\r
+ &DestinationIp,\r
+ &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+ EFI_NTOHL (DestinationIp);\r
+ }\r
+\r
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 &&\r
+ (IP4_IS_MULTICAST (DestinationIp.Addr[0]) ||\r
+ IP6_IS_MULTICAST (&DestinationIp))) {\r
+ return TRUE;\r
+ }\r
+\r
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 &&\r
+ IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) {\r
+ ASSERT (!Mode->UsingIpv6);\r
+ return TRUE;\r
+ }\r
+\r
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&\r
+ (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) ||\r
+ EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) {\r
+ //\r
+ // Matched if the dest address is equal to the station address.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) {\r
+ ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);\r
+ if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) ||\r
+ EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) {\r
+ //\r
+ // Matched if the dest address is equal to any of address in the filter list.\r
+ //\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ Filter the received packet using the destination Ip.\r
+\r
+ @param[in] Mode The pointer to the mode data of PxeBc.\r
+ @param[in] Session The pointer to the current UDPv4 session.\r
+ @param[in, out] DestIp The pointer to the destination Ip address.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Passed the IPv4 filter successfully.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestIp (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT EFI_IP_ADDRESS *DestIp,\r
+ IN UINT16 OpFlags\r
+ )\r
+{\r
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) {\r
+ //\r
+ // Copy the destination address from the received packet if accept any.\r
+ //\r
+ if (DestIp != NULL) {\r
+ if (Mode->UsingIpv6) {\r
+ CopyMem (\r
+ DestIp,\r
+ &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ } else {\r
+ ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (\r
+ DestIp,\r
+ &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+ }\r
+\r
+ }\r
+ return TRUE;\r
+ } else if (DestIp != NULL &&\r
+ (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||\r
+ EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) {\r
+ //\r
+ // The destination address in the received packet is matched if present.\r
+ //\r
+ return TRUE;\r
+ } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||\r
+ EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) {\r
+ //\r
+ // The destination address in the received packet is equal to the host address.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ Check the received packet using the destination port.\r
+\r
+ @param[in] PxeBcMode The pointer to the mode data of PxeBc.\r
+ @param[in] Session The pointer to the current UDPv4 session.\r
+ @param[in, out] DestPort The pointer to the destination port.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Passed the IPv4 filter successfully.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestPort (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT UINT16 *DestPort,\r
+ IN UINT16 OpFlags\r
+ )\r
+{\r
+ UINT16 Port;\r
+\r
+ if (Mode->UsingIpv6) {\r
+ Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;\r
+ } else {\r
+ Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;\r
+ }\r
+\r
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) {\r
+ //\r
+ // Return the destination port in the received packet if accept any.\r
+ //\r
+ if (DestPort != NULL) {\r
+ *DestPort = Port;\r
+ }\r
+ return TRUE;\r
+ } else if (DestPort != NULL && *DestPort == Port) {\r
+ //\r
+ // The destination port in the received packet is matched if present.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ Filter the received packet using the source Ip.\r
+\r
+ @param[in] Mode The pointer to the mode data of PxeBc.\r
+ @param[in] Session The pointer to the current UDPv4 session.\r
+ @param[in, out] SrcIp The pointer to the source Ip address.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Passed the IPv4 filter successfully.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcIp (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT EFI_IP_ADDRESS *SrcIp,\r
+ IN UINT16 OpFlags\r
+ )\r
+{\r
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) {\r
+ //\r
+ // Copy the source address from the received packet if accept any.\r
+ //\r
+ if (SrcIp != NULL) {\r
+ if (Mode->UsingIpv6) {\r
+ CopyMem (\r
+ SrcIp,\r
+ &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ } else {\r
+ ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS));\r
+ CopyMem (\r
+ SrcIp,\r
+ &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress,\r
+ sizeof (EFI_IPv4_ADDRESS)\r
+ );\r
+ }\r
+\r
+ }\r
+ return TRUE;\r
+ } else if (SrcIp != NULL &&\r
+ (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) ||\r
+ EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) {\r
+ //\r
+ // The source address in the received packet is matched if present.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ Filter the received packet using the source port.\r
+\r
+ @param[in] Mode The pointer to the mode data of PxeBc.\r
+ @param[in] Session The pointer to the current UDPv4 session.\r
+ @param[in, out] SrcPort The pointer to the source port.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Passed the IPv4 filter successfully.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcPort (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT UINT16 *SrcPort,\r
+ IN UINT16 OpFlags\r
+ )\r
+{\r
+ UINT16 Port;\r
+\r
+ if (Mode->UsingIpv6) {\r
+ Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;\r
+ } else {\r
+ Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;\r
+ }\r
+\r
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) {\r
+ //\r
+ // Return the source port in the received packet if accept any.\r
+ //\r
+ if (SrcPort != NULL) {\r
+ *SrcPort = Port;\r
+ }\r
+ return TRUE;\r
+ } else if (SrcPort != NULL && *SrcPort == Port) {\r
+ //\r
+ // The source port in the received packet is matched if present.\r
+ //\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This function is to receive packet using Udp4Read.\r
+\r
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.\r
+ @param[in] Token The pointer to EFI_UDP4_COMPLETION_TOKEN.\r
+ @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] OpFlags The UDP operation flags.\r
+ @param[in] IsDone The pointer to the IsDone flag.\r
+ @param[out] IsMatched The pointer to the IsMatched flag.\r
+ @param[in, out] DestIp The pointer to the destination address.\r
+ @param[in, out] DestPort The pointer to the destination port.\r
+ @param[in, out] SrcIp The pointer to the source address.\r
+ @param[in, out] SrcPort The pointer to the source port.\r
+\r
+ @retval EFI_SUCCESS Successfully read the data using Udp4.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Read (\r
+ IN EFI_UDP4_PROTOCOL *Udp4,\r
+ IN EFI_UDP4_COMPLETION_TOKEN *Token,\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN UINT16 OpFlags,\r
+ IN BOOLEAN *IsDone,\r
+ OUT BOOLEAN *IsMatched,\r
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,\r
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL\r
+ )\r
+{\r
+ EFI_UDP4_RECEIVE_DATA *RxData;\r
+ EFI_UDP4_SESSION_DATA *Session;\r
+ EFI_STATUS Status;\r
+\r
+ Token->Status = EFI_NOT_READY;\r
+ *IsDone = FALSE;\r
+\r
+ Status = Udp4->Receive (Udp4, Token);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+ //\r
+ while (!(*IsDone) &&\r
+ Token->Status == EFI_NOT_READY &&\r
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+ //\r
+ // Poll the token utill reply/ICMPv6 error message received or timeout.\r
+ //\r
+ Udp4->Poll (Udp4);\r
+ if (Token->Status == EFI_ICMP_ERROR ||\r
+ Token->Status == EFI_NETWORK_UNREACHABLE ||\r
+ Token->Status == EFI_HOST_UNREACHABLE ||\r
+ Token->Status == EFI_PROTOCOL_UNREACHABLE ||\r
+ Token->Status == EFI_PORT_UNREACHABLE) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // check whether this packet matches the filters\r
+ //\r
+ RxData = Token->Packet.RxData;\r
+ Session = &RxData->UdpSession;\r
+\r
+ *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);\r
+ }\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);\r
+ }\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);\r
+ }\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);\r
+ }\r
+\r
+ if (!(*IsMatched)) {\r
+ //\r
+ // Recycle the receiving buffer if not matched.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to receive packets using Udp6Read.\r
+\r
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.\r
+ @param[in] Token The pointer to EFI_UDP6_COMPLETION_TOKEN.\r
+ @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] OpFlags The UDP operation flags.\r
+ @param[in] IsDone The pointer to the IsDone flag.\r
+ @param[out] IsMatched The pointer to the IsMatched flag.\r
+ @param[in, out] DestIp The pointer to the destination address.\r
+ @param[in, out] DestPort The pointer to the destination port.\r
+ @param[in, out] SrcIp The pointer to the source address.\r
+ @param[in, out] SrcPort The pointer to the source port.\r
+\r
+ @retval EFI_SUCCESS Successfully read data using Udp6.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Read (\r
+ IN EFI_UDP6_PROTOCOL *Udp6,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token,\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN UINT16 OpFlags,\r
+ IN BOOLEAN *IsDone,\r
+ OUT BOOLEAN *IsMatched,\r
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,\r
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL\r
+ )\r
+{\r
+ EFI_UDP6_RECEIVE_DATA *RxData;\r
+ EFI_UDP6_SESSION_DATA *Session;\r
+ EFI_STATUS Status;\r
+\r
+ Token->Status = EFI_NOT_READY;\r
+ *IsDone = FALSE;\r
+\r
+ Status = Udp6->Receive (Udp6, Token);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.\r
+ //\r
+ while (!(*IsDone) &&\r
+ Token->Status == EFI_NOT_READY &&\r
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+ //\r
+ // Poll the token utill reply/ICMPv6 error message received or timeout.\r
+ //\r
+ Udp6->Poll (Udp6);\r
+ if (Token->Status == EFI_ICMP_ERROR ||\r
+ Token->Status == EFI_NETWORK_UNREACHABLE ||\r
+ Token->Status == EFI_HOST_UNREACHABLE ||\r
+ Token->Status == EFI_PROTOCOL_UNREACHABLE ||\r
+ Token->Status == EFI_PORT_UNREACHABLE) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // check whether this packet matches the filters\r
+ //\r
+ RxData = Token->Packet.RxData;\r
+ Session = &RxData->UdpSession;\r
+\r
+ *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);\r
+ }\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);\r
+ }\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);\r
+ }\r
+\r
+ if (*IsMatched) {\r
+ *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);\r
+ }\r
+\r
+ if (!(*IsMatched)) {\r
+ //\r
+ // Recycle the receiving buffer if not matched.\r
+ //\r
+ gBS->SignalEvent (RxData->RecycleSignal);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This function is to display the IPv4 address.\r
+\r
+ @param[in] Ip The pointer to the IPv4 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp4Addr (\r
+ IN EFI_IPv4_ADDRESS *Ip\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < 4; Index++) {\r
+ AsciiPrint ("%d", Ip->Addr[Index]);\r
+ if (Index < 3) {\r
+ AsciiPrint (".");\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is to display the IPv6 address.\r
+\r
+ @param[in] Ip The pointer to the IPv6 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp6Addr (\r
+ IN EFI_IPv6_ADDRESS *Ip\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < 16; Index++) {\r
+\r
+ if (Ip->Addr[Index] != 0) {\r
+ AsciiPrint ("%x", Ip->Addr[Index]);\r
+ }\r
+ Index++;\r
+ if (Index > 15) {\r
+ return;\r
+ }\r
+ if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {\r
+ AsciiPrint ("0");\r
+ }\r
+ AsciiPrint ("%x", Ip->Addr[Index]);\r
+ if (Index < 15) {\r
+ AsciiPrint (":");\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is to convert UINTN to ASCII string with the required formatting.\r
+\r
+ @param[in] Number Numeric value to be converted.\r
+ @param[in] Buffer The pointer to the buffer for ASCII string.\r
+ @param[in] Length The length of the required format.\r
+\r
+**/\r
+VOID\r
+PxeBcUintnToAscDecWithFormat (\r
+ IN UINTN Number,\r
+ IN UINT8 *Buffer,\r
+ IN INTN Length\r
+ )\r
+{\r
+ UINTN Remainder;\r
+\r
+ while (Length > 0) {\r
+ Length--;\r
+ Remainder = Number % 10;\r
+ Number /= 10;\r
+ Buffer[Length] = (UINT8) ('0' + Remainder);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ This function is to convert a UINTN to a ASCII string, and return the\r
+ actual length of the buffer.\r
+\r
+ @param[in] Number Numeric value to be converted.\r
+ @param[in] Buffer The pointer to the buffer for ASCII string.\r
+\r
+ @return Length The actual length of the ASCII string.\r
+\r
+**/\r
+UINTN\r
+PxeBcUintnToAscDec (\r
+ IN UINTN Number,\r
+ IN UINT8 *Buffer\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN Length;\r
+ CHAR8 TempStr[64];\r
+\r
+ Index = 63;\r
+ TempStr[Index] = 0;\r
+\r
+ do {\r
+ Index--;\r
+ TempStr[Index] = (CHAR8) ('0' + (Number % 10));\r
+ Number = (UINTN) (Number / 10);\r
+ } while (Number != 0);\r
+\r
+ AsciiStrCpy ((CHAR8 *) Buffer, &TempStr[Index]);\r
+\r
+ Length = AsciiStrLen ((CHAR8 *) Buffer);\r
+\r
+ return Length;\r
+}\r
+\r
+\r
+/**\r
+ This function is to convert unicode hex number to a UINT8.\r
+\r
+ @param[out] Digit The converted UINT8 for output.\r
+ @param[in] Char The unicode hex number to be converted.\r
+\r
+ @retval EFI_SUCCESS Successfully converted the unicode hex.\r
+ @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUniHexToUint8 (\r
+ OUT UINT8 *Digit,\r
+ IN CHAR16 Char\r
+ )\r
+{\r
+ if ((Char >= L'0') && (Char <= L'9')) {\r
+ *Digit = (UINT8) (Char - L'0');\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if ((Char >= L'A') && (Char <= L'F')) {\r
+ *Digit = (UINT8) (Char - L'A' + 0x0A);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if ((Char >= L'a') && (Char <= L'f')) {\r
+ *Digit = (UINT8) (Char - L'a' + 0x0A);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return EFI_INVALID_PARAMETER;\r
+}\r
--- /dev/null
+/** @file\r
+ Support functions declaration for UefiPxeBc Driver.\r
+\r
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef __EFI_PXEBC_SUPPORT_H__\r
+#define __EFI_PXEBC_SUPPORT_H__\r
+\r
+\r
+#define ICMP_DEST_UNREACHABLE 3\r
+#define ICMP_SOURCE_QUENCH 4\r
+#define ICMP_REDIRECT 5\r
+#define ICMP_ECHO_REQUEST 8\r
+#define ICMP_TIME_EXCEEDED 11\r
+#define ICMP_PARAMETER_PROBLEM 12\r
+\r
+\r
+/**\r
+ This function obtain the system guid and serial number from the smbios table.\r
+\r
+ @param[out] SystemGuid The pointer of returned system guid.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained the system guid.\r
+ @retval EFI_NOT_FOUND Did not find the SMBIOS table.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcGetSystemGuid (\r
+ OUT EFI_GUID *SystemGuid\r
+ );\r
+\r
+\r
+/**\r
+ Flush the previous configration using the new station Ip address.\r
+\r
+ @param[in] Private Pointer to PxeBc private data.\r
+ @param[in] StationIp Pointer to the station Ip address.\r
+ @param[in] SubnetMask Pointer to the subnet mask address for v4.\r
+\r
+ @retval EFI_SUCCESS Successfully flushed the previous config.\r
+ @retval Others Failed to flush using the new station Ip.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcFlushStaionIp (\r
+ PXEBC_PRIVATE_DATA *Private,\r
+ EFI_IP_ADDRESS *StationIp,\r
+ EFI_IP_ADDRESS *SubnetMask OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ Notify callback function when an event is triggered.\r
+\r
+ @param[in] Event The triggered event.\r
+ @param[in] Context The opaque parameter to the function.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcCommonNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ Perform arp resolution from the arp cache in PxeBcMode.\r
+\r
+ @param Mode Pointer to EFI_PXE_BASE_CODE_MODE.\r
+ @param Ip4Addr The Ip4 address for resolution.\r
+ @param MacAddress The resoluted MAC address if the resolution is successful.\r
+ The value is undefined if resolution fails.\r
+\r
+ @retval TRUE Found a matched entry.\r
+ @retval FALSE Did not find a matched entry.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckArpCache (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN EFI_IPv4_ADDRESS *Ip4Addr,\r
+ OUT EFI_MAC_ADDRESS *MacAddress\r
+ );\r
+\r
+\r
+/**\r
+ Update arp cache periodically.\r
+\r
+ @param Event Pointer to EFI_PXE_BC_PROTOCOL.\r
+ @param Context Context of the timer event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcArpCacheUpdate (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ xxx\r
+\r
+ @param Event The event signaled.\r
+ @param Context The context passed in by the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmpErrorUpdate (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ xxx\r
+\r
+ @param Event The event signaled.\r
+ @param Context The context passed in by the event notifier.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeBcIcmp6ErrorUpdate (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ );\r
+\r
+\r
+/**\r
+ This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.\r
+ @param[in] StationIp Pointer to the station address.\r
+ @param[in] SubnetMask Pointer to the subnet mask.\r
+ @param[in] Gateway Pointer to the gateway address.\r
+ @param[in, out] SrcPort Pointer to the source port.\r
+ @param[in] DoNotFragment The flag of DoNotFragment bit in the IPv4\r
+ packet.\r
+\r
+ @retval EFI_SUCCESS Successfully configured this instance.\r
+ @retval Others Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp4Write (\r
+ IN EFI_UDP4_PROTOCOL *Udp4,\r
+ IN EFI_IPv4_ADDRESS *StationIp,\r
+ IN EFI_IPv4_ADDRESS *SubnetMask,\r
+ IN EFI_IPv4_ADDRESS *Gateway,\r
+ IN OUT UINT16 *SrcPort,\r
+ IN BOOLEAN DoNotFragment\r
+ );\r
+\r
+\r
+/**\r
+ This function is to configure a UDPv6 instance for UdpWrite.\r
+\r
+ @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL.\r
+ @param[in] StationIp Pointer to the station address.\r
+ @param[in, out] SrcPort Pointer to the source port.\r
+\r
+ @retval EFI_SUCCESS Successfuly configured this instance.\r
+ @retval Others Failed to configure this instance.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcConfigUdp6Write (\r
+ IN EFI_UDP6_PROTOCOL *Udp6,\r
+ IN EFI_IPv6_ADDRESS *StationIp,\r
+ IN OUT UINT16 *SrcPort\r
+ );\r
+\r
+/**\r
+ This function is to configure a UDPv4 instance for UdpWrite.\r
+\r
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.\r
+ @param[in] Session Pointer to the UDP4 session data.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] Gateway Pointer to the gateway address.\r
+ @param[in] HeaderSize An optional field which may be set to the length of a header\r
+ at HeaderPtr to be prefixed to the data at BufferPtr.\r
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be\r
+ prefixed to the data at BufferPtr.\r
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.\r
+ @param[in] BufferPtr A pointer to the data to be written.\r
+\r
+ @retval EFI_SUCCESS Successfully sent out data with Udp4Write.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Write (\r
+ IN EFI_UDP4_PROTOCOL *Udp4,\r
+ IN EFI_UDP4_SESSION_DATA *Session,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN EFI_IPv4_ADDRESS *Gateway OPTIONAL,\r
+ IN UINTN *HeaderSize OPTIONAL,\r
+ IN VOID *HeaderPtr OPTIONAL,\r
+ IN UINTN *BufferSize,\r
+ IN VOID *BufferPtr\r
+ );\r
+\r
+\r
+/**\r
+ This function is to configure a UDPv6 instance for UdpWrite.\r
+\r
+ @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL.\r
+ @param[in] Session Pointer to the UDP6 session data.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] HeaderSize An optional field which may be set to the length of a header\r
+ at HeaderPtr to be prefixed to the data at BufferPtr.\r
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be\r
+ prefixed to the data at BufferPtr.\r
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.\r
+ @param[in] BufferPtr A pointer to the data to be written.\r
+\r
+ @retval EFI_SUCCESS Successfully to send out data with Udp6Write.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Write (\r
+ IN EFI_UDP6_PROTOCOL *Udp6,\r
+ IN EFI_UDP6_SESSION_DATA *Session,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN UINTN *HeaderSize OPTIONAL,\r
+ IN VOID *HeaderPtr OPTIONAL,\r
+ IN UINTN *BufferSize,\r
+ IN VOID *BufferPtr\r
+ );\r
+\r
+\r
+/**\r
+ Check the received packet with the Ip filter.\r
+\r
+ @param[in] Mode Pointer to mode data of PxeBc.\r
+ @param[in] Session Pointer to the current UDPv4 session.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Succesfully passed the Ip filter.\r
+ @retval FALSE Failed to pass the Ip filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByIpFilter (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN UINT16 OpFlags\r
+ );\r
+\r
+\r
+/**\r
+ Filter the received packet with the destination Ip.\r
+\r
+ @param[in] Mode Pointer to mode data of PxeBc.\r
+ @param[in] Session Pointer to the current UDPv4 session.\r
+ @param[in, out] DestIp Pointer to the dest Ip address.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Succesfully passed the IPv4 filter.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestIp (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT EFI_IP_ADDRESS *DestIp,\r
+ IN UINT16 OpFlags\r
+ );\r
+\r
+\r
+/**\r
+ Check the received packet with the destination port.\r
+\r
+ @param[in] PxeBcMode Pointer to mode data of PxeBc.\r
+ @param[in] Session Pointer to the current UDPv4 session.\r
+ @param[in, out] DestPort Pointer to the destination port.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Succesfully passed the IPv4 filter.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcCheckByDestPort (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT UINT16 *DestPort,\r
+ IN UINT16 OpFlags\r
+ );\r
+\r
+\r
+/**\r
+ Filter the received packet with the source Ip.\r
+\r
+ @param[in] Mode Pointer to mode data of PxeBc.\r
+ @param[in] Session Pointer to the current UDPv4 session.\r
+ @param[in, out] SrcIp Pointer to the source Ip address.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Succesfully passed the IPv4 filter.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcIp (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT EFI_IP_ADDRESS *SrcIp,\r
+ IN UINT16 OpFlags\r
+ );\r
+\r
+\r
+/**\r
+ Filter the received packet with the source port.\r
+\r
+ @param[in] Mode Pointer to mode data of PxeBc.\r
+ @param[in] Session Pointer to the current UDPv4 session.\r
+ @param[in, out] SrcPort Pointer to the source port.\r
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.\r
+\r
+ @retval TRUE Succesfully passed the IPv4 filter.\r
+ @retval FALSE Failed to pass the IPv4 filter.\r
+\r
+**/\r
+BOOLEAN\r
+PxeBcFilterBySrcPort (\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN VOID *Session,\r
+ IN OUT UINT16 *SrcPort,\r
+ IN UINT16 OpFlags\r
+ );\r
+\r
+\r
+/**\r
+ This function is to receive packet with Udp4Read.\r
+\r
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.\r
+ @param[in] Token Pointer to EFI_UDP4_COMPLETION_TOKEN.\r
+ @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] OpFlags The UDP operation flags.\r
+ @param[in] IsDone Pointer to IsDone flag.\r
+ @param[out] IsMatched Pointer to IsMatched flag.\r
+ @param[in, out] DestIp Pointer to destination address.\r
+ @param[in, out] DestPort Pointer to destination port.\r
+ @param[in, out] SrcIp Pointer to source address.\r
+ @param[in, out] SrcPort Pointer to source port.\r
+\r
+ @retval EFI_SUCCESS Successfully read data with Udp4.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp4Read (\r
+ IN EFI_UDP4_PROTOCOL *Udp4,\r
+ IN EFI_UDP4_COMPLETION_TOKEN *Token,\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN UINT16 OpFlags,\r
+ IN BOOLEAN *IsDone,\r
+ OUT BOOLEAN *IsMatched,\r
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,\r
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ This function is to receive packet with Udp6Read.\r
+\r
+ @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL.\r
+ @param[in] Token Pointer to EFI_UDP6_COMPLETION_TOKEN.\r
+ @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE.\r
+ @param[in] TimeoutEvent The event for timeout.\r
+ @param[in] OpFlags The UDP operation flags.\r
+ @param[in] IsDone Pointer to IsDone flag.\r
+ @param[out] IsMatched Pointer to IsMatched flag.\r
+ @param[in, out] DestIp Pointer to destination address.\r
+ @param[in, out] DestPort Pointer to destination port.\r
+ @param[in, out] SrcIp Pointer to source address.\r
+ @param[in, out] SrcPort Pointer to source port.\r
+\r
+ @retval EFI_SUCCESS Successfully read data with Udp6.\r
+ @retval Others Failed to send out data.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUdp6Read (\r
+ IN EFI_UDP6_PROTOCOL *Udp6,\r
+ IN EFI_UDP6_COMPLETION_TOKEN *Token,\r
+ IN EFI_PXE_BASE_CODE_MODE *Mode,\r
+ IN EFI_EVENT TimeoutEvent,\r
+ IN UINT16 OpFlags,\r
+ IN BOOLEAN *IsDone,\r
+ OUT BOOLEAN *IsMatched,\r
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,\r
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,\r
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL\r
+ );\r
+\r
+\r
+/**\r
+ This function is to display the IPv4 address.\r
+\r
+ @param[in] Ip Pointer to the IPv4 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp4Addr (\r
+ IN EFI_IPv4_ADDRESS *Ip\r
+ );\r
+\r
+\r
+/**\r
+ This function is to display the IPv6 address.\r
+\r
+ @param[in] Ip Pointer to the IPv6 address.\r
+\r
+**/\r
+VOID\r
+PxeBcShowIp6Addr (\r
+ IN EFI_IPv6_ADDRESS *Ip\r
+ );\r
+\r
+\r
+/**\r
+ This function is to convert UINTN to ASCII string with required format.\r
+\r
+ @param[in] Number Numeric value to be converted.\r
+ @param[in] Buffer Pointer to the buffer for ASCII string.\r
+ @param[in] Length Length of the required format.\r
+\r
+**/\r
+VOID\r
+PxeBcUintnToAscDecWithFormat (\r
+ IN UINTN Number,\r
+ IN UINT8 *Buffer,\r
+ IN INTN Length\r
+ );\r
+\r
+\r
+/**\r
+ This function is to convert a UINTN to a ASCII string, and return the\r
+ actual length of the buffer.\r
+\r
+ @param[in] Number Numeric value to be converted.\r
+ @param[in] Buffer Pointer to the buffer for ASCII string.\r
+\r
+ @return Length The actual length of the ASCII string.\r
+\r
+**/\r
+UINTN\r
+PxeBcUintnToAscDec (\r
+ IN UINTN Number,\r
+ IN UINT8 *Buffer\r
+ );\r
+\r
+/**\r
+ This function is to convert unicode hex number to a UINT8.\r
+\r
+ @param[out] Digit The converted UINT8 for output.\r
+ @param[in] Char The unicode hex number to be converted.\r
+\r
+ @retval EFI_SUCCESS Successfully converted the unicode hex.\r
+ @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex.\r
+\r
+**/\r
+EFI_STATUS\r
+PxeBcUniHexToUint8 (\r
+ OUT UINT8 *Digit,\r
+ IN CHAR16 Char\r
+ );\r
+\r
+#endif\r
--- /dev/null
+## @file\r
+# Component name for module PxeBc\r
+#\r
+# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php.\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = UefiPxeBcDxe\r
+ FILE_GUID = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A\r
+ MODULE_TYPE = UEFI_DRIVER\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = PxeBcDriverEntryPoint\r
+ UNLOAD_IMAGE = NetLibDefaultUnload\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 IPF\r
+#\r
+\r
+[Sources]\r
+ ComponentName.c\r
+ PxeBcDriver.c\r
+ PxeBcDriver.h\r
+ PxeBcImpl.c\r
+ PxeBcImpl.h\r
+ PxeBcBoot.c\r
+ PxeBcBoot.h\r
+ PxeBcDhcp6.c\r
+ PxeBcDhcp6.h\r
+ PxeBcDhcp4.c\r
+ PxeBcDhcp4.h\r
+ PxeBcMtftp.c\r
+ PxeBcMtftp.h\r
+ PxeBcSupport.c\r
+ PxeBcSupport.h\r
+\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+\r
+[LibraryClasses]\r
+ BaseLib\r
+ UefiLib\r
+ UefiBootServicesTableLib\r
+ UefiDriverEntryPoint\r
+ BaseMemoryLib\r
+ MemoryAllocationLib\r
+ DebugLib\r
+ NetLib\r
+ DpcLib\r
+ DevicePathLib\r
+ PcdLib\r
+\r
+\r
+[Guids]\r
+ gEfiSmbiosTableGuid\r
+\r
+\r
+[Protocols]\r
+ gEfiDevicePathProtocolGuid\r
+ gEfiNetworkInterfaceIdentifierProtocolGuid_31\r
+ gEfiArpServiceBindingProtocolGuid\r
+ gEfiArpProtocolGuid\r
+ gEfiIp4ServiceBindingProtocolGuid\r
+ gEfiIp4ProtocolGuid\r
+ gEfiIp6ServiceBindingProtocolGuid\r
+ gEfiIp6ProtocolGuid\r
+ gEfiIp6ConfigProtocolGuid\r
+ gEfiUdp4ServiceBindingProtocolGuid\r
+ gEfiUdp4ProtocolGuid\r
+ gEfiMtftp4ServiceBindingProtocolGuid\r
+ gEfiMtftp4ProtocolGuid\r
+ gEfiDhcp4ServiceBindingProtocolGuid\r
+ gEfiDhcp4ProtocolGuid\r
+ gEfiUdp6ServiceBindingProtocolGuid\r
+ gEfiUdp6ProtocolGuid\r
+ gEfiMtftp6ServiceBindingProtocolGuid\r
+ gEfiMtftp6ProtocolGuid\r
+ gEfiDhcp6ServiceBindingProtocolGuid\r
+ gEfiDhcp6ProtocolGuid\r
+ gEfiPxeBaseCodeCallbackProtocolGuid\r
+ gEfiPxeBaseCodeProtocolGuid\r
+ gEfiLoadFileProtocolGuid\r
+\r
+[Pcd]\r
+ gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize ## CONSUMES\r