summaryrefslogtreecommitdiffstats
path: root/NetworkPkg
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg')
-rw-r--r--NetworkPkg/Application/IfConfig6/IfConfig6.c1781
-rw-r--r--NetworkPkg/Application/IfConfig6/IfConfig6.h84
-rw-r--r--NetworkPkg/Application/IfConfig6/IfConfig6.inf52
-rw-r--r--NetworkPkg/Application/IfConfig6/IfConfig6Strings.unibin0 -> 15424 bytes
-rw-r--r--NetworkPkg/Application/IpsecConfig/Delete.c110
-rw-r--r--NetworkPkg/Application/IpsecConfig/Delete.h42
-rw-r--r--NetworkPkg/Application/IpsecConfig/Dump.c530
-rw-r--r--NetworkPkg/Application/IpsecConfig/Dump.h34
-rw-r--r--NetworkPkg/Application/IpsecConfig/ForEach.c115
-rw-r--r--NetworkPkg/Application/IpsecConfig/ForEach.h54
-rw-r--r--NetworkPkg/Application/IpsecConfig/Helper.c419
-rw-r--r--NetworkPkg/Application/IpsecConfig/Helper.h143
-rw-r--r--NetworkPkg/Application/IpsecConfig/Indexer.c248
-rw-r--r--NetworkPkg/Application/IpsecConfig/Indexer.h58
-rw-r--r--NetworkPkg/Application/IpsecConfig/IpSecConfig.c809
-rw-r--r--NetworkPkg/Application/IpsecConfig/IpSecConfig.h123
-rw-r--r--NetworkPkg/Application/IpsecConfig/IpSecConfig.inf61
-rw-r--r--NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.unibin0 -> 22142 bytes
-rw-r--r--NetworkPkg/Application/IpsecConfig/Match.c163
-rw-r--r--NetworkPkg/Application/IpsecConfig/Match.h41
-rw-r--r--NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c2016
-rw-r--r--NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h158
-rw-r--r--NetworkPkg/Application/Ping6/Ia32/Tsc.c28
-rw-r--r--NetworkPkg/Application/Ping6/Ipf/Itc.c28
-rw-r--r--NetworkPkg/Application/Ping6/Ping6.c1179
-rw-r--r--NetworkPkg/Application/Ping6/Ping6.h92
-rw-r--r--NetworkPkg/Application/Ping6/Ping6.inf64
-rw-r--r--NetworkPkg/Application/Ping6/Ping6Strings.unibin0 -> 7222 bytes
-rw-r--r--NetworkPkg/Application/Ping6/X64/Tsc.c28
-rw-r--r--NetworkPkg/Application/VConfig/VConfig.c668
-rw-r--r--NetworkPkg/Application/VConfig/VConfig.inf47
-rw-r--r--NetworkPkg/Application/VConfig/VConfigStrings.unibin0 -> 8624 bytes
-rw-r--r--NetworkPkg/Dhcp6Dxe/ComponentName.c312
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c782
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h155
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf69
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c1220
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h597
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Io.c2965
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Io.h193
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c1146
-rw-r--r--NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h340
-rw-r--r--NetworkPkg/Ip6Dxe/ComponentName.c313
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Common.c796
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Common.h338
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Config.vfr170
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c2369
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h295
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigNv.c2116
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigNv.h73
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Driver.c930
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Driver.h185
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Dxe.inf100
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6DxeStrings.unibin0 -> 9574 bytes
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Icmp.c684
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Icmp.h108
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6If.c802
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6If.h267
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Impl.c1857
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Impl.h751
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Input.c1710
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Input.h235
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Mld.c908
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Mld.h198
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Nd.c3141
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Nd.h750
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6NvData.h70
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Option.c758
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Option.h191
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Output.c1077
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Output.h141
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Route.c635
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Route.h299
-rw-r--r--NetworkPkg/IpSecDxe/ComponentName.c310
-rw-r--r--NetworkPkg/IpSecDxe/IpSecConfigImpl.c2928
-rw-r--r--NetworkPkg/IpSecDxe/IpSecConfigImpl.h952
-rw-r--r--NetworkPkg/IpSecDxe/IpSecCryptIo.c133
-rw-r--r--NetworkPkg/IpSecDxe/IpSecCryptIo.h322
-rw-r--r--NetworkPkg/IpSecDxe/IpSecDebug.c172
-rw-r--r--NetworkPkg/IpSecDxe/IpSecDebug.h102
-rw-r--r--NetworkPkg/IpSecDxe/IpSecDriver.c282
-rw-r--r--NetworkPkg/IpSecDxe/IpSecDxe.inf63
-rw-r--r--NetworkPkg/IpSecDxe/IpSecImpl.c856
-rw-r--r--NetworkPkg/IpSecDxe/IpSecImpl.h313
-rw-r--r--NetworkPkg/IpSecDxe/IpSecSaEngine.c934
-rw-r--r--NetworkPkg/Mtftp6Dxe/ComponentName.c308
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c703
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h151
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf69
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c634
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h469
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Option.c416
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Option.h148
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c900
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Support.c1189
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Support.h359
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c602
-rw-r--r--NetworkPkg/NetworkPkg.dec21
-rw-r--r--NetworkPkg/NetworkPkg.dsc95
-rw-r--r--NetworkPkg/TcpDxe/ComponentName.c304
-rw-r--r--NetworkPkg/TcpDxe/SockImpl.c1230
-rw-r--r--NetworkPkg/TcpDxe/SockImpl.h103
-rw-r--r--NetworkPkg/TcpDxe/SockInterface.c999
-rw-r--r--NetworkPkg/TcpDxe/Socket.h924
-rw-r--r--NetworkPkg/TcpDxe/TcpDispatcher.c861
-rw-r--r--NetworkPkg/TcpDxe/TcpDriver.c891
-rw-r--r--NetworkPkg/TcpDxe/TcpDriver.h230
-rw-r--r--NetworkPkg/TcpDxe/TcpDxe.inf83
-rw-r--r--NetworkPkg/TcpDxe/TcpFunc.h724
-rw-r--r--NetworkPkg/TcpDxe/TcpInput.c1592
-rw-r--r--NetworkPkg/TcpDxe/TcpIo.c190
-rw-r--r--NetworkPkg/TcpDxe/TcpMain.c1074
-rw-r--r--NetworkPkg/TcpDxe/TcpMain.h758
-rw-r--r--NetworkPkg/TcpDxe/TcpMisc.c1281
-rw-r--r--NetworkPkg/TcpDxe/TcpOption.c374
-rw-r--r--NetworkPkg/TcpDxe/TcpOption.h145
-rw-r--r--NetworkPkg/TcpDxe/TcpOutput.c1219
-rw-r--r--NetworkPkg/TcpDxe/TcpProto.h342
-rw-r--r--NetworkPkg/TcpDxe/TcpTimer.c593
-rw-r--r--NetworkPkg/Udp6Dxe/ComponentName.c313
-rw-r--r--NetworkPkg/Udp6Dxe/Udp6Driver.c556
-rw-r--r--NetworkPkg/Udp6Dxe/Udp6Driver.h182
-rw-r--r--NetworkPkg/Udp6Dxe/Udp6Dxe.inf63
-rw-r--r--NetworkPkg/Udp6Dxe/Udp6Impl.c2131
-rw-r--r--NetworkPkg/Udp6Dxe/Udp6Impl.h673
-rw-r--r--NetworkPkg/Udp6Dxe/Udp6Main.c855
-rw-r--r--NetworkPkg/UefiPxeBcDxe/ComponentName.c312
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c1170
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h100
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c1599
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h389
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c1531
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h277
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c1339
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h104
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c2200
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h207
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c1105
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h136
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c1588
-rw-r--r--NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h491
-rw-r--r--NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf98
142 files changed, 83988 insertions, 0 deletions
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.c b/NetworkPkg/Application/IfConfig6/IfConfig6.c
new file mode 100644
index 0000000000..b2eada639a
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6.c
@@ -0,0 +1,1781 @@
+/** @file
+ The implementation for Shell application IfConfig6.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/ShellLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/HiiLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+
+#include "IfConfig6.h"
+
+EFI_HII_HANDLE mHiiHandle;
+
+EFI_GUID mEfiIfConfig6Guid = EFI_IFCONFIG6_GUID;
+SHELL_PARAM_ITEM mIfConfig6CheckList[] = {
+ {
+ L"-b",
+ TypeFlag
+ },
+ {
+ L"-s",
+ TypeMaxValue
+ },
+ {
+ L"-l",
+ TypeValue
+ },
+ {
+ L"-r",
+ TypeValue
+ },
+ {
+ L"-?",
+ TypeFlag
+ },
+ {
+ NULL,
+ TypeMax
+ },
+};
+
+VAR_CHECK_ITEM mSetCheckList[] = {
+ {
+ L"auto",
+ 0x00000001,
+ 0x00000001,
+ FlagTypeSingle
+ },
+ {
+ L"man",
+ 0x00000002,
+ 0x00000001,
+ FlagTypeSingle
+ },
+ {
+ L"host",
+ 0x00000004,
+ 0x00000002,
+ FlagTypeSingle
+ },
+ {
+ L"dad",
+ 0x00000008,
+ 0x00000004,
+ FlagTypeSingle
+ },
+ {
+ L"gw",
+ 0x00000010,
+ 0x00000008,
+ FlagTypeSingle
+ },
+ {
+ L"dns",
+ 0x00000020,
+ 0x00000010,
+ FlagTypeSingle
+ },
+ {
+ L"id",
+ 0x00000040,
+ 0x00000020,
+ FlagTypeSingle
+ },
+ {
+ NULL,
+ 0x0,
+ 0x0,
+ FlagTypeSkipUnknown
+ },
+};
+
+/**
+ Split a string with specified separator and save the substring to a list.
+
+ @param[in] String The pointer of the input string.
+ @param[in] Separator The specified separator.
+
+ @return The pointer of headnode of ARG_LIST.
+
+**/
+ARG_LIST *
+SplitStrToList (
+ IN CONST CHAR16 *String,
+ IN CHAR16 Separator
+ )
+{
+ CHAR16 *Str;
+ CHAR16 *ArgStr;
+ ARG_LIST *ArgList;
+ ARG_LIST *ArgNode;
+
+ if (*String == L'\0') {
+ return NULL;
+ }
+
+ //
+ // Copy the CONST string to a local copy.
+ //
+ Str = (CHAR16 *) AllocateZeroPool (StrSize (String));
+ ASSERT (Str != NULL);
+ Str = StrCpy (Str, String);
+ ArgStr = Str;
+
+ //
+ // init a node for the list head.
+ //
+ ArgNode = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));
+ ASSERT (ArgNode != NULL);
+ ArgList = ArgNode;
+
+ //
+ // Split the local copy and save in the list node.
+ //
+ while (*Str != L'\0') {
+ if (*Str == Separator) {
+ *Str = L'\0';
+ ArgNode->Arg = ArgStr;
+ ArgStr = Str + 1;
+ ArgNode->Next = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST));
+ ASSERT (ArgNode->Next != NULL);
+ ArgNode = ArgNode->Next;
+ }
+
+ Str++;
+ }
+
+ ArgNode->Arg = ArgStr;
+ ArgNode->Next = NULL;
+
+ return ArgList;
+}
+
+/**
+ Check the correctness of input Args with '-s' option.
+
+ @param[in] CheckList The pointer of VAR_CHECK_ITEM array.
+ @param[in] Name The pointer of input arg.
+ @param[in] Init The switch to execute the check.
+
+ @return The value of VAR_CHECK_CODE.
+
+**/
+VAR_CHECK_CODE
+IfConfig6RetriveCheckListByName(
+ IN VAR_CHECK_ITEM *CheckList,
+ IN CHAR16 *Name,
+ IN BOOLEAN Init
+)
+{
+ STATIC UINT32 CheckDuplicate;
+ STATIC UINT32 CheckConflict;
+ VAR_CHECK_CODE RtCode;
+ UINT32 Index;
+ VAR_CHECK_ITEM Arg;
+
+ if (Init) {
+ CheckDuplicate = 0;
+ CheckConflict = 0;
+ return VarCheckOk;
+ }
+
+ RtCode = VarCheckOk;
+ Index = 0;
+ Arg = CheckList[Index];
+
+ //
+ // Check the Duplicated/Conflicted/Unknown input Args.
+ //
+ while (Arg.FlagStr != NULL) {
+ if (StrCmp (Arg.FlagStr, Name) == 0) {
+
+ if (CheckDuplicate & Arg.FlagID) {
+ RtCode = VarCheckDuplicate;
+ break;
+ }
+
+ if (CheckConflict & Arg.ConflictMask) {
+ RtCode = VarCheckConflict;
+ break;
+ }
+
+ CheckDuplicate |= Arg.FlagID;
+ CheckConflict |= Arg.ConflictMask;
+ break;
+ }
+
+ Arg = CheckList[++Index];
+ }
+
+ if (Arg.FlagStr == NULL) {
+ RtCode = VarCheckUnknown;
+ }
+
+ return RtCode;
+}
+
+/**
+ The notify function of create event when performing a manual config.
+
+ @param[in] CheckList The pointer of Event.
+ @param[in] Context The pointer of Context.
+
+**/
+VOID
+EFIAPI
+IfConfig6ManualAddressNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Print MAC address.
+
+ @param[in] Node The pointer of MAC address buffer.
+ @param[in] Size The size of MAC address buffer.
+
+**/
+VOID
+IfConfig6PrintMacAddr (
+ IN UINT8 *Node,
+ IN UINT32 Size
+ )
+{
+ UINTN Index;
+
+ ASSERT (Size <= MACADDRMAXSIZE);
+
+ for (Index = 0; Index < Size; Index++) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_BODY), mHiiHandle, Node[Index]);
+ if (Index + 1 < Size) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);
+ }
+ }
+
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);
+}
+
+/**
+ Print IPv6 address.
+
+ @param[in] Ip The pointer of Ip bufffer in EFI_IPv6_ADDRESS format.
+ @param[in] PrefixLen The pointer of PrefixLen that describes the size Prefix.
+
+**/
+VOID
+IfConfig6PrintIpAddr (
+ IN EFI_IPv6_ADDRESS *Ip,
+ IN UINT8 *PrefixLen
+ )
+{
+ UINTN Index;
+ BOOLEAN Short;
+
+ Short = FALSE;
+
+ for (Index = 0; Index < PREFIXMAXLEN; Index = Index + 2) {
+
+ if (!Short && (Index + 1 < PREFIXMAXLEN) && (Index % 2 == 0) && (Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0)) {
+ //
+ // Deal with the case of ::.
+ //
+ if (Index == 0) {
+ //
+ // :: is at the beginning of the address.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);
+ }
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);
+
+ while ((Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0) && (Index < PREFIXMAXLEN)) {
+ Index = Index + 2;
+ if (Index > PREFIXMAXLEN - 2) {
+ break;
+ }
+ }
+
+ Short = TRUE;
+
+ if (Index == PREFIXMAXLEN) {
+ //
+ // :: is at the end of the address.
+ //
+ break;
+ }
+ }
+
+ if (Index < PREFIXMAXLEN - 1) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index]);
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index + 1]);
+ }
+
+ if (Index + 2 < PREFIXMAXLEN) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle);
+ }
+ }
+
+ if (PrefixLen != NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_PREFIX_LEN), mHiiHandle, *PrefixLen);
+ }
+}
+
+/**
+ Pick up host IPv6 address in string format from Args with "-s" option and convert it to EFI_IP6_CONFIG_MANUAL_ADDRESS format.
+
+ @param[in, out] Arg The pointer of the address of ARG_LIST which save Args with the "-s" option.
+ @param[out] Buf The pointer of the address of EFI_IP6_CONFIG_MANUAL_ADDRESS.
+ @param[out] Address The pointer of BufSize that describes the size of Buf in bytes.
+
+ @retval EFI_SUCCESS The convertion is successful.
+ @retval Others Does't find the host address, or it is an invalid IPv6 address in string format.
+
+**/
+EFI_STATUS
+IfConfig6ParseManualAddressList (
+ IN OUT ARG_LIST **Arg,
+ OUT EFI_IP6_CONFIG_MANUAL_ADDRESS **Buf,
+ OUT UINTN *BufSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *AddrBuf;
+ ARG_LIST *VarArg;
+ EFI_IPv6_ADDRESS Address;
+ UINT8 Prefix;
+ UINT8 AddrCnt;
+
+ AddrCnt = 0;
+ *BufSize = 0;
+ *Buf = NULL;
+ VarArg = *Arg;
+ Status = EFI_SUCCESS;
+
+ //
+ // Go through the list to check the correctness of input host ip6 address.
+ //
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {
+
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);
+
+ if (EFI_ERROR (Status)) {
+ //
+ // host ip ip ... gw
+ //
+ break;
+ }
+
+ VarArg = VarArg->Next;
+ AddrCnt++;
+ }
+
+ if (AddrCnt == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
+ ASSERT (AddrBuf != NULL);
+
+ AddrCnt = 0;
+ VarArg = *Arg;
+ Status = EFI_SUCCESS;
+
+ //
+ // Go through the list to fill in the EFI_IP6_CONFIG_MANUAL_ADDRESS structure.
+ //
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {
+
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // If prefix length is not set, set it as Zero here. In the IfConfigSetInterfaceInfo()
+ // Zero prefix, length will be transfered to default prefix length.
+ //
+ if (Prefix == 0xFF) {
+ Prefix = 0;
+ }
+ AddrBuf[AddrCnt].IsAnycast = FALSE;
+ AddrBuf[AddrCnt].PrefixLength = Prefix;
+ IP6_COPY_ADDRESS (&AddrBuf[AddrCnt].Address, &Address);
+ VarArg = VarArg->Next;
+ AddrCnt++;
+ }
+
+ *Arg = VarArg;
+
+ if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {
+ goto ON_ERROR;
+ }
+
+ *Buf = AddrBuf;
+ *BufSize = AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ FreePool (AddrBuf);
+ return Status;
+}
+
+/**
+ Pick up gw/dns IPv6 address in string format from Args with "-s" option and convert it to EFI_IPv6_ADDRESS format.
+
+ @param[in, out] Arg The pointer of the address of ARG_LIST that save Args with the "-s" option.
+ @param[out] Buf The pointer of the address of EFI_IPv6_ADDRESS.
+ @param[out] Address The pointer of BufSize that describes the size of Buf in bytes.
+
+ @retval EFI_SUCCESS The conversion is successful.
+ @retval Others Doesn't find the host address, or it is an invalid IPv6 address in string format.
+
+**/
+EFI_STATUS
+IfConfig6ParseGwDnsAddressList (
+ IN OUT ARG_LIST **Arg,
+ OUT EFI_IPv6_ADDRESS **Buf,
+ OUT UINTN *BufSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS *AddrBuf;
+ ARG_LIST *VarArg;
+ EFI_IPv6_ADDRESS Address;
+ UINT8 Prefix;
+ UINT8 AddrCnt;
+
+ AddrCnt = 0;
+ *BufSize = 0;
+ *Buf = NULL;
+ VarArg = *Arg;
+ Status = EFI_SUCCESS;
+
+ //
+ // Go through the list to check the correctness of input gw/dns address.
+ //
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {
+
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);
+
+ if (EFI_ERROR (Status)) {
+ //
+ // gw ip ip ... host
+ //
+ break;
+ }
+
+ VarArg = VarArg->Next;
+ AddrCnt++;
+ }
+
+ if (AddrCnt == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IPv6_ADDRESS));
+ ASSERT (AddrBuf != NULL);
+
+ AddrCnt = 0;
+ VarArg = *Arg;
+ Status = EFI_SUCCESS;
+
+ //
+ // Go through the list to fill in the EFI_IPv6_ADDRESS structure.
+ //
+ while ((!EFI_ERROR (Status)) && (VarArg != NULL)) {
+
+ Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ IP6_COPY_ADDRESS (&AddrBuf[AddrCnt], &Address);
+
+ VarArg = VarArg->Next;
+ AddrCnt++;
+ }
+
+ *Arg = VarArg;
+
+ if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) {
+ goto ON_ERROR;
+ }
+
+ *Buf = AddrBuf;
+ *BufSize = AddrCnt * sizeof (EFI_IPv6_ADDRESS);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ FreePool (AddrBuf);
+ return Status;
+}
+
+/**
+ Parse InterfaceId in string format from Args with the "-s" option and convert it to EFI_IP6_CONFIG_INTERFACE_ID format.
+
+ @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option.
+ @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID.
+
+ @retval EFI_SUCCESS The get status processed successfullly.
+ @retval EFI_INVALID_PARAMETER The get status process failed.
+
+**/
+EFI_STATUS
+IfConfig6ParseInterfaceId (
+ IN OUT ARG_LIST **Arg,
+ OUT EFI_IP6_CONFIG_INTERFACE_ID **IfId
+ )
+{
+ UINT8 Index;
+ UINT8 NodeVal;
+ CHAR16 *IdStr;
+
+ if (*Arg == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Index = 0;
+ IdStr = (*Arg)->Arg;
+ ASSERT (IfId != NULL);
+ *IfId = AllocateZeroPool (sizeof (EFI_IP6_CONFIG_INTERFACE_ID));
+ ASSERT (*IfId != NULL);
+
+ while ((*IdStr != L'\0') && (Index < 8)) {
+
+ NodeVal = 0;
+ while ((*IdStr != L':') && (*IdStr != L'\0')) {
+
+ if ((*IdStr <= L'F') && (*IdStr >= L'A')) {
+ NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'A' + 10);
+ } else if ((*IdStr <= L'f') && (*IdStr >= L'a')) {
+ NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'a' + 10);
+ } else if ((*IdStr <= L'9') && (*IdStr >= L'0')) {
+ NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'0');
+ } else {
+ FreePool (*IfId);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IdStr++;
+ }
+
+ (*IfId)->Id[Index++] = NodeVal;
+
+ if (*IdStr == L':') {
+ IdStr++;
+ }
+ }
+
+ *Arg = (*Arg)->Next;
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse dad in string format from Args with the "-s" option and convert it to UINT32 format.
+
+ @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option.
+ @param[out] Xmits The pointer of Xmits.
+
+ @retval EFI_SUCCESS The get status processed successfully.
+ @retval others The get status process failed.
+
+**/
+EFI_STATUS
+IfConfig6ParseDadXmits (
+ IN OUT ARG_LIST **Arg,
+ OUT UINT32 *Xmits
+ )
+{
+ CHAR16 *ValStr;
+
+ if (*Arg == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ValStr = (*Arg)->Arg;
+ *Xmits = 0;
+
+ while (*ValStr != L'\0') {
+
+ if ((*ValStr <= L'9') && (*ValStr >= L'0')) {
+
+ *Xmits = (*Xmits * 10) + (*ValStr - L'0');
+
+ } else {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ValStr++;
+ }
+
+ *Arg = (*Arg)->Next;
+ return EFI_SUCCESS;
+}
+
+/**
+ The get current status of all handles.
+
+ @param[in] ImageHandle The handle of ImageHandle.
+ @param[in] IfName The pointer of IfName(interface name).
+ @param[in] IfList The pointer of IfList(interface list).
+
+ @retval EFI_SUCCESS The get status processed successfully.
+ @retval others The get status process failed.
+
+**/
+EFI_STATUS
+IfConfig6GetInterfaceInfo (
+ IN EFI_HANDLE ImageHandle,
+ IN CHAR16 *IfName,
+ IN LIST_ENTRY *IfList
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleIndex;
+ UINTN HandleNum;
+ EFI_HANDLE *HandleBuffer;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+ IFCONFIG6_INTERFACE_CB *IfCb;
+ UINTN DataSize;
+
+ HandleBuffer = NULL;
+ HandleNum = 0;
+
+ IfInfo = NULL;
+ IfCb = NULL;
+
+ //
+ // Locate all the handles with ip6 service binding protocol.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ NULL,
+ &HandleNum,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status) || (HandleNum == 0)) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // Enumerate all handles that installed with ip6 service binding protocol.
+ //
+ for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
+ IfCb = NULL;
+ IfInfo = NULL;
+ DataSize = 0;
+
+ //
+ // Ip6config protocol and ip6 service binding protocol are installed
+ // on the same handle.
+ //
+ ASSERT (HandleBuffer != NULL);
+ Status = gBS->HandleProtocol (
+ HandleBuffer[HandleIndex],
+ &gEfiIp6ConfigProtocolGuid,
+ (VOID **) &Ip6Cfg
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ //
+ // Get the interface information size.
+ //
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &DataSize,
+ NULL
+ );
+
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+
+ IfInfo = AllocateZeroPool (DataSize);
+
+ if (IfInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ //
+ // Get the interface info.
+ //
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &DataSize,
+ IfInfo
+ );
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+ //
+ // Check the interface name if required.
+ //
+ if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) != 0)) {
+ FreePool (IfInfo);
+ continue;
+ }
+
+ DataSize = 0;
+ //
+ // Get the size of dns server list.
+ //
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeDnsServer,
+ &DataSize,
+ NULL
+ );
+
+ if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+
+ IfCb = AllocateZeroPool (sizeof (IFCONFIG6_INTERFACE_CB) + DataSize);
+
+ if (IfCb == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ IfCb->NicHandle = HandleBuffer[HandleIndex];
+ IfCb->IfInfo = IfInfo;
+ IfCb->IfCfg = Ip6Cfg;
+ IfCb->DnsCnt = (UINT32) (DataSize / sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Get the dns server list if has.
+ //
+ if (DataSize > 0) {
+
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeDnsServer,
+ &DataSize,
+ IfCb->DnsAddr
+ );
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+ }
+ //
+ // Get the interface id if has.
+ //
+ DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);
+ IfCb->IfId = AllocateZeroPool (DataSize);
+
+ if (IfCb->IfId == NULL) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeAltInterfaceId,
+ &DataSize,
+ IfCb->IfId
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ FreePool (IfCb->IfId);
+ IfCb->IfId = NULL;
+ }
+ //
+ // Get the config policy.
+ //
+ DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypePolicy,
+ &DataSize,
+ &IfCb->Policy
+ );
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+ //
+ // Get the dad transmits.
+ //
+ DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeDupAddrDetectTransmits,
+ &DataSize,
+ &IfCb->Xmits
+ );
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+
+ InsertTailList (IfList, &IfCb->Link);
+
+ if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) == 0)) {
+ //
+ // Only need the appointed interface, keep the allocated buffer.
+ //
+ IfCb = NULL;
+ IfInfo = NULL;
+ break;
+ }
+ }
+
+ if (HandleBuffer != NULL) {
+ FreePool (HandleBuffer);
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (IfInfo != NULL) {
+ FreePool (IfInfo);
+ }
+
+ if (IfCb != NULL) {
+ if (IfCb->IfId != NULL) {
+ FreePool (IfCb->IfId);
+ }
+
+ FreePool (IfCb);
+ }
+
+ return Status;
+}
+
+/**
+ The list process of the IfConfig6 application.
+
+ @param[in] IfList The pointer of IfList(interface list).
+
+ @retval EFI_SUCCESS The IfConfig6 list processed successfully.
+ @retval others The IfConfig6 list process failed.
+
+**/
+EFI_STATUS
+IfConfig6ShowInterfaceInfo (
+ IN LIST_ENTRY *IfList
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ IFCONFIG6_INTERFACE_CB *IfCb;
+ UINTN Index;
+
+ Entry = IfList->ForwardLink;
+ Status = EFI_SUCCESS;
+
+ if (IsListEmpty (IfList)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);
+ }
+
+ //
+ // Go through the interface list.
+ //
+ while (Entry != IfList) {
+
+ IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);
+
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);
+
+ //
+ // Print interface name.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IF_NAME), mHiiHandle, IfCb->IfInfo->Name);
+
+ //
+ // Print interface config policy.
+ //
+ if (IfCb->Policy == Ip6ConfigPolicyAutomatic) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_AUTO), mHiiHandle);
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_MAN), mHiiHandle);
+ }
+
+ //
+ // Print dad transmit.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DAD_TRANSMITS), mHiiHandle, IfCb->Xmits);
+
+ //
+ // Print interface id if has.
+ //
+ if (IfCb->IfId != NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_INTERFACE_ID_HEAD), mHiiHandle);
+
+ IfConfig6PrintMacAddr (
+ IfCb->IfId->Id,
+ 8
+ );
+ }
+ //
+ // Print mac address of the interface.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_HEAD), mHiiHandle);
+
+ IfConfig6PrintMacAddr (
+ IfCb->IfInfo->HwAddress.Addr,
+ IfCb->IfInfo->HwAddressSize
+ );
+
+ //
+ // Print ip addresses list of the interface.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_HEAD), mHiiHandle);
+
+ for (Index = 0; Index < IfCb->IfInfo->AddressInfoCount; Index++) {
+ IfConfig6PrintIpAddr (
+ &IfCb->IfInfo->AddressInfo[Index].Address,
+ &IfCb->IfInfo->AddressInfo[Index].PrefixLength
+ );
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);
+ }
+
+ //
+ // Print dns server addresses list of the interface if has.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DNS_ADDR_HEAD), mHiiHandle);
+
+ for (Index = 0; Index < IfCb->DnsCnt; Index++) {
+ IfConfig6PrintIpAddr (
+ &IfCb->DnsAddr[Index],
+ NULL
+ );
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);
+ }
+
+ //
+ // Print route table of the interface if has.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_ROUTE_HEAD), mHiiHandle);
+
+ for (Index = 0; Index < IfCb->IfInfo->RouteCount; Index++) {
+ IfConfig6PrintIpAddr (
+ &IfCb->IfInfo->RouteTable[Index].Destination,
+ &IfCb->IfInfo->RouteTable[Index].PrefixLength
+ );
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_JOINT), mHiiHandle);
+
+ IfConfig6PrintIpAddr (
+ &IfCb->IfInfo->RouteTable[Index].Gateway,
+ NULL
+ );
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle);
+ }
+
+ Entry = Entry->ForwardLink;
+ }
+
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle);
+
+ return Status;
+}
+
+/**
+ The clean process of the IfConfig6 application.
+
+ @param[in] IfList The pointer of IfList(interface list).
+
+ @retval EFI_SUCCESS The IfConfig6 clean processed successfully.
+ @retval others The IfConfig6 clean process failed.
+
+**/
+EFI_STATUS
+IfConfig6ClearInterfaceInfo (
+ IN LIST_ENTRY *IfList
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ IFCONFIG6_INTERFACE_CB *IfCb;
+ EFI_IP6_CONFIG_POLICY Policy;
+
+ Policy = Ip6ConfigPolicyAutomatic;
+ Entry = IfList->ForwardLink;
+ Status = EFI_SUCCESS;
+
+ if (IsListEmpty (IfList)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);
+ }
+
+ //
+ // Go through the interface list.
+ //
+ while (Entry != IfList) {
+
+ IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);
+
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypePolicy,
+ sizeof (EFI_IP6_CONFIG_POLICY),
+ &Policy
+ );
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ Entry = Entry->ForwardLink;
+ }
+
+ return Status;
+}
+
+/**
+ The set process of the IfConfig6 application.
+
+ @param[in] IfList The pointer of IfList(interface list).
+ @param[in] VarArg The pointer of ARG_LIST(Args with "-s" option).
+
+ @retval EFI_SUCCESS The IfConfig6 set processed successfully.
+ @retval others The IfConfig6 set process failed.
+
+**/
+EFI_STATUS
+IfConfig6SetInterfaceInfo (
+ IN LIST_ENTRY *IfList,
+ IN ARG_LIST *VarArg
+ )
+{
+ EFI_STATUS Status;
+ IFCONFIG6_INTERFACE_CB *IfCb;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *CfgManAddr;
+ EFI_IPv6_ADDRESS *CfgAddr;
+ UINTN AddrSize;
+ EFI_IP6_CONFIG_INTERFACE_ID *InterfaceId;
+ UINT32 DadXmits;
+ UINT32 CurDadXmits;
+ UINTN CurDadXmitsLen;
+ EFI_IP6_CONFIG_POLICY Policy;
+
+ VAR_CHECK_CODE CheckCode;
+ EFI_EVENT TimeOutEvt;
+ EFI_EVENT MappedEvt;
+ BOOLEAN IsAddressOk;
+
+ UINTN DataSize;
+ UINT32 Index;
+ UINT32 Index2;
+ BOOLEAN IsAddressSet;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+
+ CfgManAddr = NULL;
+ CfgAddr = NULL;
+ TimeOutEvt = NULL;
+ MappedEvt = NULL;
+ IfInfo = NULL;
+ InterfaceId = NULL;
+ CurDadXmits = 0;
+
+ if (IsListEmpty (IfList)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle);
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Make sure to set only one interface each time.
+ //
+ IfCb = BASE_CR (IfList->ForwardLink, IFCONFIG6_INTERFACE_CB, Link);
+ Status = EFI_SUCCESS;
+
+ //
+ // Initialize check list mechanism.
+ //
+ CheckCode = IfConfig6RetriveCheckListByName(
+ NULL,
+ NULL,
+ TRUE
+ );
+
+ //
+ // Create events & timers for asynchronous settings.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeOutEvt
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IfConfig6ManualAddressNotify,
+ &IsAddressOk,
+ &MappedEvt
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Parse the setting variables.
+ //
+ while (VarArg != NULL) {
+ //
+ // Check invalid parameters (duplication & unknown & conflict).
+ //
+ CheckCode = IfConfig6RetriveCheckListByName(
+ mSetCheckList,
+ VarArg->Arg,
+ FALSE
+ );
+
+ if (VarCheckOk != CheckCode) {
+ switch (CheckCode) {
+ case VarCheckDuplicate:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_DUPLICATE_COMMAND), mHiiHandle, VarArg->Arg);
+ break;
+
+ case VarCheckConflict:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_CONFLICT_COMMAND), mHiiHandle, VarArg->Arg);
+ break;
+
+ case VarCheckUnknown:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_UNKNOWN_COMMAND), mHiiHandle, VarArg->Arg);
+ break;
+
+ default:
+ break;
+ }
+
+ VarArg = VarArg->Next;
+ continue;
+ }
+ //
+ // Process valid variables.
+ //
+ if (StrCmp(VarArg->Arg, L"auto") == 0) {
+ //
+ // Set automaic config policy
+ //
+ Policy = Ip6ConfigPolicyAutomatic;
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypePolicy,
+ sizeof (EFI_IP6_CONFIG_POLICY),
+ &Policy
+ );
+
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+
+ VarArg= VarArg->Next;
+
+ } else if (StrCmp (VarArg->Arg, L"man") == 0) {
+ //
+ // Set manual config policy.
+ //
+ Policy = Ip6ConfigPolicyManual;
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypePolicy,
+ sizeof (EFI_IP6_CONFIG_POLICY),
+ &Policy
+ );
+
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+
+ VarArg= VarArg->Next;
+
+ } else if (StrCmp (VarArg->Arg, L"host") == 0) {
+ //
+ // Parse till the next tag or the end of command line.
+ //
+ VarArg = VarArg->Next;
+ Status = IfConfig6ParseManualAddressList (
+ &VarArg,
+ &CfgManAddr,
+ &AddrSize
+ );
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_INVALID_PARAMETER) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"host");
+ continue;
+ } else {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Set static host ip6 address list.
+ // This is a asynchronous process.
+ //
+ IsAddressOk = FALSE;
+
+ Status = IfCb->IfCfg->RegisterDataNotify (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeManualAddress,
+ MappedEvt
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeManualAddress,
+ AddrSize,
+ CfgManAddr
+ );
+
+ if (Status == EFI_NOT_READY) {
+ //
+ // Get current dad transmits count.
+ //
+ CurDadXmitsLen = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
+ IfCb->IfCfg->GetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeDupAddrDetectTransmits,
+ &CurDadXmitsLen,
+ &CurDadXmits
+ );
+
+ gBS->SetTimer (TimeOutEvt, TimerRelative, 50000000 + 10000000 * CurDadXmits);
+
+ while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
+ if (IsAddressOk) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ IfCb->IfCfg->UnregisterDataNotify (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeManualAddress,
+ MappedEvt
+ );
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_MAN_HOST), mHiiHandle, Status);
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether the address is set successfully.
+ //
+ DataSize = 0;
+
+ Status = IfCb->IfCfg->GetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &DataSize,
+ NULL
+ );
+
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_EXIT;
+ }
+
+ IfInfo = AllocateZeroPool (DataSize);
+
+ if (IfInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = IfCb->IfCfg->GetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &DataSize,
+ IfInfo
+ );
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_EXIT;
+ }
+
+ for ( Index = 0; Index < (UINTN) (AddrSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); Index++) {
+ IsAddressSet = FALSE;
+ //
+ // By default, the prefix length 0 is regarded as 64.
+ //
+ if (CfgManAddr[Index].PrefixLength == 0) {
+ CfgManAddr[Index].PrefixLength = 64;
+ }
+
+ for (Index2 = 0; Index2 < IfInfo->AddressInfoCount; Index2++) {
+ if (EFI_IP6_EQUAL (&IfInfo->AddressInfo[Index2].Address, &CfgManAddr[Index].Address) &&
+ (IfInfo->AddressInfo[Index2].PrefixLength == CfgManAddr[Index].PrefixLength)) {
+ IsAddressSet = TRUE;
+ break;
+ }
+ }
+
+ if (!IsAddressSet) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_ADDRESS_FAILED), mHiiHandle);
+ IfConfig6PrintIpAddr (
+ &CfgManAddr[Index].Address,
+ &CfgManAddr[Index].PrefixLength
+ );
+ }
+ }
+
+ } else if (StrCmp (VarArg->Arg, L"gw") == 0) {
+ //
+ // Parse till the next tag or the end of command line.
+ //
+ VarArg = VarArg->Next;
+ Status = IfConfig6ParseGwDnsAddressList (
+ &VarArg,
+ &CfgAddr,
+ &AddrSize
+ );
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_INVALID_PARAMETER) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"gw");
+ continue;
+ } else {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Set static gateway ip6 address list.
+ //
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeGateway,
+ AddrSize,
+ CfgAddr
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ } else if (StrCmp (VarArg->Arg, L"dns") == 0) {
+ //
+ // Parse till the next tag or the end of command line.
+ //
+ VarArg = VarArg->Next;
+ Status = IfConfig6ParseGwDnsAddressList (
+ &VarArg,
+ &CfgAddr,
+ &AddrSize
+ );
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_INVALID_PARAMETER) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"dns");
+ continue;
+ } else {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Set static dhs server ip6 address list.
+ //
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeDnsServer,
+ AddrSize,
+ CfgAddr
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ } else if (StrCmp (VarArg->Arg, L"id") == 0) {
+ //
+ // Parse till the next tag or the end of command line.
+ //
+ VarArg = VarArg->Next;
+ Status = IfConfig6ParseInterfaceId (&VarArg, &InterfaceId);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Set alternative interface id.
+ //
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeAltInterfaceId,
+ sizeof (EFI_IP6_CONFIG_INTERFACE_ID),
+ InterfaceId
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ } else if (StrCmp (VarArg->Arg, L"dad") == 0) {
+ //
+ // Parse till the next tag or the end of command line.
+ //
+ VarArg = VarArg->Next;
+ Status = IfConfig6ParseDadXmits (&VarArg, &DadXmits);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Set dad transmits count.
+ //
+ Status = IfCb->IfCfg->SetData (
+ IfCb->IfCfg,
+ Ip6ConfigDataTypeDupAddrDetectTransmits,
+ sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),
+ &DadXmits
+ );
+
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ON_EXIT:
+
+ if (CfgManAddr != NULL) {
+ FreePool (CfgManAddr);
+ }
+
+ if (CfgAddr != NULL) {
+ FreePool (CfgAddr);
+ }
+
+ if (MappedEvt != NULL) {
+ gBS->CloseEvent (MappedEvt);
+ }
+
+ if (TimeOutEvt != NULL) {
+ gBS->CloseEvent (TimeOutEvt);
+ }
+
+ if (IfInfo != NULL) {
+ FreePool (IfInfo);
+ }
+
+ return Status;
+
+}
+
+/**
+ The IfConfig6 main process.
+
+ @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS IfConfig6 processed successfully.
+ @retval others The IfConfig6 process failed.
+
+**/
+EFI_STATUS
+IfConfig6 (
+ IN IFCONFIG6_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Get configure information of all interfaces.
+ //
+ Status = IfConfig6GetInterfaceInfo (
+ Private->ImageHandle,
+ Private->IfName,
+ &Private->IfList
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ switch (Private->OpCode) {
+ case IfConfig6OpList:
+ Status = IfConfig6ShowInterfaceInfo (&Private->IfList);
+ break;
+
+ case IfConfig6OpClear:
+ Status = IfConfig6ClearInterfaceInfo (&Private->IfList);
+ break;
+
+ case IfConfig6OpSet:
+ Status = IfConfig6SetInterfaceInfo (&Private->IfList, Private->VarArg);
+ break;
+
+ default:
+ Status = EFI_ABORTED;
+ }
+
+ON_EXIT:
+
+ return Status;
+}
+
+/**
+ The IfConfig6 cleanup process, free the allocated memory.
+
+ @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA.
+
+**/
+VOID
+IfConfig6Cleanup (
+ IN IFCONFIG6_PRIVATE_DATA *Private
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ IFCONFIG6_INTERFACE_CB *IfCb;
+ ARG_LIST *ArgNode;
+ ARG_LIST *ArgHead;
+
+ ASSERT (Private != NULL);
+
+ //
+ // Clean the list which save the set config Args.
+ //
+ if (Private->VarArg != NULL) {
+ ArgHead = Private->VarArg;
+
+ while (ArgHead->Next != NULL) {
+ ArgNode = ArgHead->Next;
+ FreePool (ArgHead);
+ ArgHead = ArgNode;
+ }
+
+ FreePool (ArgHead);
+ }
+
+ if (Private->IfName != NULL)
+ FreePool (Private->IfName);
+
+
+ //
+ // Clean the IFCONFIG6_INTERFACE_CB list.
+ //
+ Entry = Private->IfList.ForwardLink;
+ NextEntry = Entry->ForwardLink;
+
+ while (Entry != &Private->IfList) {
+
+ IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link);
+
+ RemoveEntryList (&IfCb->Link);
+
+ if (IfCb->IfId != NULL) {
+
+ FreePool (IfCb->IfId);
+ }
+
+ if (IfCb->IfInfo != NULL) {
+
+ FreePool (IfCb->IfInfo);
+ }
+
+ FreePool (IfCb);
+
+ Entry = NextEntry;
+ NextEntry = Entry->ForwardLink;
+ }
+
+ FreePool (Private);
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including
+ both device drivers and bus drivers.
+
+ The entry point for the IfConfig6 application which parses the command line input and calls the IfConfig6 process.
+
+ @param[in] ImageHandle The image handle of this application.
+ @param[in] SystemTable The pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval Others Some errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+IfConfig6Initialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ IFCONFIG6_PRIVATE_DATA *Private;
+ LIST_ENTRY *ParamPackage;
+ CONST CHAR16 *ValueStr;
+ ARG_LIST *ArgList;
+ CHAR16 *ProblemParam;
+ CHAR16 *Str;
+
+ Private = NULL;
+
+ //
+ // Register our string package with HII and return the handle to it.
+ //
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IfConfig6Strings, NULL);
+ ASSERT (mHiiHandle != NULL);
+
+ Status = ShellCommandLineParseEx (mIfConfig6CheckList, &ParamPackage, &ProblemParam, TRUE, FALSE);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_COMMAND), mHiiHandle, ProblemParam);
+ goto ON_EXIT;
+ }
+
+ //
+ // To handle no option.
+ //
+ if (!ShellCommandLineGetFlag (ParamPackage, L"-r") && !ShellCommandLineGetFlag (ParamPackage, L"-s") &&
+ !ShellCommandLineGetFlag (ParamPackage, L"-?") && !ShellCommandLineGetFlag (ParamPackage, L"-l")) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_LACK_OPTION), mHiiHandle);
+ goto ON_EXIT;
+ }
+ //
+ // To handle conflict options.
+ //
+ if (((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-s"))) ||
+ ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||
+ ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||
+ ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) ||
+ ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) ||
+ ((ShellCommandLineGetFlag (ParamPackage, L"-l")) && (ShellCommandLineGetFlag (ParamPackage, L"-?")))) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_CONFLICT_OPTIONS), mHiiHandle);
+ goto ON_EXIT;
+ }
+ //
+ // To show the help information of ifconfig6 command.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_HELP), mHiiHandle);
+ goto ON_EXIT;
+ }
+
+ Status = EFI_INVALID_PARAMETER;
+
+ Private = AllocateZeroPool (sizeof (IFCONFIG6_PRIVATE_DATA));
+
+ if (Private == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ InitializeListHead (&Private->IfList);
+
+ //
+ // To get interface name for the list option.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {
+ Private->OpCode = IfConfig6OpList;
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
+ if (ValueStr != NULL) {
+ Str = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));
+ ASSERT (Str != NULL);
+
+ Str = StrCpy (Str, ValueStr);
+ Private->IfName = Str;
+ }
+ }
+ //
+ // To get interface name for the clear option.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-r")) {
+ Private->OpCode = IfConfig6OpClear;
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-r");
+ if (ValueStr != NULL) {
+ Str = (CHAR16 *) AllocateZeroPool (StrSize (ValueStr));
+ ASSERT (Str != NULL);
+
+ Str = StrCpy (Str, ValueStr);
+ Private->IfName = Str;
+ }
+ }
+ //
+ // To get interface name and corresponding Args for the set option.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-s")) {
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
+ if (ValueStr == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_INTERFACE), mHiiHandle);
+ goto ON_EXIT;
+ }
+ //
+ // To split the configuration into multi-section.
+ //
+ ArgList = SplitStrToList (ValueStr, L' ');
+ ASSERT (ArgList != NULL);
+
+ Private->OpCode = IfConfig6OpSet;
+ Private->IfName = ArgList->Arg;
+
+ Private->VarArg = ArgList->Next;
+
+ if (Private->IfName == NULL || Private->VarArg == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_COMMAND), mHiiHandle);
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Main process of ifconfig6.
+ //
+ Status = IfConfig6 (Private);
+
+ON_EXIT:
+
+ ShellCommandLineFreeVarList (ParamPackage);
+ HiiRemovePackages (mHiiHandle);
+ if (Private != NULL)
+ IfConfig6Cleanup (Private);
+
+ return Status;
+}
+
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.h b/NetworkPkg/Application/IfConfig6/IfConfig6.h
new file mode 100644
index 0000000000..eea7df55d1
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6.h
@@ -0,0 +1,84 @@
+/** @file
+ The interface function declaration of shell application IfConfig6.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IFCONFIG6_H_
+#define _IFCONFIG6_H_
+
+#define EFI_IFCONFIG6_GUID \
+ { \
+ 0xbab7296b, 0x222c, 0x4408, {0x9e, 0x6c, 0xc2, 0x5c, 0x18, 0x7e, 0xff, 0x33} \
+ }
+
+enum {
+ IfConfig6OpList = 1,
+ IfConfig6OpSet = 2,
+ IfConfig6OpClear = 3
+};
+
+typedef enum {
+ VarCheckReserved = -1,
+ VarCheckOk = 0,
+ VarCheckDuplicate,
+ VarCheckConflict,
+ VarCheckUnknown,
+ VarCheckLackValue,
+ VarCheckOutOfMem
+} VAR_CHECK_CODE;
+
+typedef enum {
+ FlagTypeSingle = 0,
+ FlagTypeNeedVar,
+ FlagTypeNeedSet,
+ FlagTypeSkipUnknown
+} VAR_CHECK_FLAG_TYPE;
+
+#define MACADDRMAXSIZE 32
+#define PREFIXMAXLEN 16
+
+typedef struct _IFCONFIG6_INTERFACE_CB {
+ EFI_HANDLE NicHandle;
+ LIST_ENTRY Link;
+ EFI_IP6_CONFIG_PROTOCOL *IfCfg;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+ EFI_IP6_CONFIG_INTERFACE_ID *IfId;
+ EFI_IP6_CONFIG_POLICY Policy;
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS Xmits;
+ UINT32 DnsCnt;
+ EFI_IPv6_ADDRESS DnsAddr[1];
+} IFCONFIG6_INTERFACE_CB;
+
+typedef struct _ARG_LIST ARG_LIST;
+
+struct _ARG_LIST {
+ ARG_LIST *Next;
+ CHAR16 *Arg;
+};
+
+typedef struct _IFCONFIG6_PRIVATE_DATA {
+ EFI_HANDLE ImageHandle;
+ LIST_ENTRY IfList;
+
+ UINT32 OpCode;
+ CHAR16 *IfName;
+ ARG_LIST *VarArg;
+} IFCONFIG6_PRIVATE_DATA;
+
+typedef struct _VAR_CHECK_ITEM{
+ CHAR16 *FlagStr;
+ UINT32 FlagID;
+ UINT32 ConflictMask;
+ VAR_CHECK_FLAG_TYPE FlagType;
+} VAR_CHECK_ITEM;
+#endif
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6.inf b/NetworkPkg/Application/IfConfig6/IfConfig6.inf
new file mode 100644
index 0000000000..dd3ab64493
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6.inf
@@ -0,0 +1,52 @@
+## @file
+# Component description file for Shell application IfConfig6.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = IfConfig6
+ FILE_GUID = 6F71926E-60CE-428d-AA58-A3D9FB879429
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = IfConfig6Initialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF
+#
+[Sources]
+ IfConfig6Strings.uni
+ IfConfig6.c
+ IfConfig6.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ UefiBootServicesTableLib
+ UefiApplicationEntryPoint
+ BaseMemoryLib
+ ShellLib
+ MemoryAllocationLib
+ DebugLib
+ HiiLib
+ NetLib
+
+[Protocols]
+ gEfiIp6ServiceBindingProtocolGuid ## CONSUMS
+ gEfiIp6ConfigProtocolGuid ## CONSUMS
diff --git a/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
new file mode 100644
index 0000000000..a5e7fd04cf
--- /dev/null
+++ b/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
Binary files differ
diff --git a/NetworkPkg/Application/IpsecConfig/Delete.c b/NetworkPkg/Application/IpsecConfig/Delete.c
new file mode 100644
index 0000000000..caeb1c8350
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Delete.c
@@ -0,0 +1,110 @@
+/** @file
+ The implementation of delete policy entry function in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfig.h"
+#include "Indexer.h"
+#include "Delete.h"
+#include "Match.h"
+#include "ForEach.h"
+
+/**
+ Private function to delete entry information in database.
+
+ @param[in] Selector The pointer to EFI_IPSEC_CONFIG_SELECTOR structure.
+ @param[in] Data The pointer to Data.
+ @param[in] Context The pointer to DELETE_POLICY_ENTRY_CONTEXT.
+
+ @retval EFI_ABORTED Abort the iteration.
+ @retval EFI_SUCCESS Continue the iteration.
+**/
+EFI_STATUS
+DeletePolicyEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN DELETE_POLICY_ENTRY_CONTEXT *Context
+ )
+{
+ if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {
+ Context->Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ Context->DataType,
+ Selector,
+ NULL,
+ NULL
+ );
+ //
+ // Abort the iteration after the insertion.
+ //
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Flush or delete entry information in the database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Delete entry information successfully.
+ @retval EFI_NOT_FOUND Can't find the specified entry.
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+FlushOrDeletePolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ )
+{
+ EFI_STATUS Status;
+ DELETE_POLICY_ENTRY_CONTEXT Context;
+ CONST CHAR16 *ValueStr;
+
+ //
+ // If user wants to remove all.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {
+ Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ DataType,
+ NULL,
+ NULL,
+ NULL
+ );
+ } else {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");
+ if (ValueStr == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);
+ return EFI_NOT_FOUND;
+ }
+
+ Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);
+ if (!EFI_ERROR (Status)) {
+ Context.DataType = DataType;
+ Context.Status = EFI_NOT_FOUND;
+ ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) DeletePolicyEntry, &Context);
+ Status = Context.Status;
+
+ if (Status == EFI_NOT_FOUND) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);
+ } else if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DELETE_FAILED), mHiiHandle, mAppName);
+ }
+ }
+ }
+
+ return Status;
+}
diff --git a/NetworkPkg/Application/IpsecConfig/Delete.h b/NetworkPkg/Application/IpsecConfig/Delete.h
new file mode 100644
index 0000000000..49f3b0d5fa
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Delete.h
@@ -0,0 +1,42 @@
+/** @file
+ The internal structure and function declaration of delete policy entry function
+ in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __DELETE_H_
+#define __DELETE_H_
+
+typedef struct {
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;
+ POLICY_ENTRY_INDEXER Indexer;
+ EFI_STATUS Status; //Indicate whether deletion succeeds.
+} DELETE_POLICY_ENTRY_CONTEXT;
+
+/**
+ Flush or delete entry information in the database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Delete entry information successfully.
+ @retval EFI_NOT_FOUND Can't find the specified entry.
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+FlushOrDeletePolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ );
+
+#endif
diff --git a/NetworkPkg/Application/IpsecConfig/Dump.c b/NetworkPkg/Application/IpsecConfig/Dump.c
new file mode 100644
index 0000000000..004ab1089c
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Dump.c
@@ -0,0 +1,530 @@
+/** @file
+ The implementation of dump policy entry function in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfig.h"
+#include "Dump.h"
+#include "ForEach.h"
+#include "Helper.h"
+
+/**
+ Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure.
+
+ @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure.
+
+ @return the value of version.
+**/
+UINTN
+GetVerFromAddrInfo (
+ IN EFI_IP_ADDRESS_INFO *AddressInfo
+)
+{
+ if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) &&
+ (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) {
+ return IP_VERSION_4;
+ } else {
+ return IP_VERSION_6;
+ }
+}
+
+/**
+ Private function called to get the version information from a EFI_IP_ADDRESS structure.
+
+ @param[in] Address The pointer to the EFI_IP_ADDRESS structure.
+
+ @return The value of the version.
+**/
+UINTN
+GetVerFromIpAddr (
+ IN EFI_IP_ADDRESS *Address
+)
+{
+ if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) {
+ return IP_VERSION_4;
+ } else {
+ return IP_VERSION_6;
+ }
+}
+
+/**
+ Private function called to print an ASCII string in unicode char format.
+
+ @param[in] Str The pointer to the ASCII string.
+ @param[in] Length The value of the ASCII string length.
+**/
+VOID
+DumpAsciiString (
+ IN CHAR8 *Str,
+ IN UINTN Length
+ )
+{
+ UINTN Index;
+ for (Index = 0; Index < Length; Index++) {
+ Print (L"%c", (CHAR16) Str[Index]);
+ }
+}
+
+/**
+ Private function called to print EFI_IP_ADDRESS_INFO content.
+
+ @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure.
+**/
+VOID
+DumpAddressInfo (
+ IN EFI_IP_ADDRESS_INFO *AddressInfo
+ )
+{
+ if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) {
+ Print (
+ L"%d.%d.%d.%d",
+ (UINTN) AddressInfo->Address.v4.Addr[0],
+ (UINTN) AddressInfo->Address.v4.Addr[1],
+ (UINTN) AddressInfo->Address.v4.Addr[2],
+ (UINTN) AddressInfo->Address.v4.Addr[3]
+ );
+ if (AddressInfo->PrefixLength != 32) {
+ Print (L"/%d", (UINTN) AddressInfo->PrefixLength);
+ }
+ }
+
+ if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) {
+ Print (
+ L"%x:%x:%x:%x:%x:%x:%x:%x",
+ (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]),
+ (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]),
+ (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]),
+ (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]),
+ (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]),
+ (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]),
+ (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]),
+ (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15])
+ );
+ if (AddressInfo->PrefixLength != 128) {
+ Print (L"/%d", AddressInfo->PrefixLength);
+ }
+ }
+}
+
+/**
+ Private function called to print EFI_IP_ADDRESS content.
+
+ @param[in] IpAddress The pointer to the EFI_IP_ADDRESS structure.
+**/
+VOID
+DumpIpAddress (
+ IN EFI_IP_ADDRESS *IpAddress
+ )
+{
+ if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) {
+ Print (
+ L"%d.%d.%d.%d",
+ (UINTN) IpAddress->v4.Addr[0],
+ (UINTN) IpAddress->v4.Addr[1],
+ (UINTN) IpAddress->v4.Addr[2],
+ (UINTN) IpAddress->v4.Addr[3]
+ );
+ }
+
+ if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) {
+ Print (
+ L"%x:%x:%x:%x:%x:%x:%x:%x",
+ (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]),
+ (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]),
+ (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]),
+ (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]),
+ (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]),
+ (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]),
+ (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]),
+ (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15])
+ );
+ }
+
+}
+
+/**
+ Private function called to print EFI_IPSEC_SPD_SELECTOR content.
+
+ @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
+**/
+VOID
+DumpSpdSelector (
+ IN EFI_IPSEC_SPD_SELECTOR *Selector
+ )
+{
+ UINT32 Index;
+ CHAR16 *Str;
+
+ for (Index = 0; Index < Selector->LocalAddressCount; Index++) {
+ if (Index > 0) {
+ Print (L",");
+ }
+
+ DumpAddressInfo (&Selector->LocalAddress[Index]);
+ }
+
+ if (Index == 0) {
+ Print (L"localhost");
+ }
+
+ Print (L" -> ");
+
+ for (Index = 0; Index < Selector->RemoteAddressCount; Index++) {
+ if (Index > 0) {
+ Print (L",");
+ }
+
+ DumpAddressInfo (&Selector->RemoteAddress[Index]);
+ }
+
+ Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol);
+ if (Str != NULL) {
+ Print (L" %s", Str);
+ } else {
+ Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol);
+ }
+
+ if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) {
+ Print (L" port:");
+ if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) {
+ Print (L"%d", Selector->LocalPort);
+ if (Selector->LocalPortRange != 0) {
+ Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange);
+ }
+ } else {
+ Print (L"any");
+ }
+
+ Print (L" -> ");
+ if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) {
+ Print (L"%d", Selector->RemotePort);
+ if (Selector->RemotePortRange != 0) {
+ Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange);
+ }
+ } else {
+ Print (L"any");
+ }
+ } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) {
+ Print (L" class/code:");
+ if (Selector->LocalPort != 0) {
+ Print (L"%d", (UINTN) (UINT8) Selector->LocalPort);
+ } else {
+ Print (L"any");
+ }
+
+ Print (L"/");
+ if (Selector->RemotePort != 0) {
+ Print (L"%d", (UINTN) (UINT8) Selector->RemotePort);
+ } else {
+ Print (L"any");
+ }
+ }
+}
+
+/**
+ Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content.
+
+ @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
+ @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure.
+ @param[in] EntryIndex The pointer to the Index in SPD Database.
+
+ @retval EFI_SUCCESS Dump SPD information successfully.
+**/
+EFI_STATUS
+DumpSpdEntry (
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,
+ IN EFI_IPSEC_SPD_DATA *Data,
+ IN UINTN *EntryIndex
+ )
+{
+ BOOLEAN HasPre;
+ CHAR16 DataName[128];
+ CHAR16 *String1;
+ CHAR16 *String2;
+ CHAR16 *String3;
+ UINT8 Index;
+
+ Print (L"%d.", (*EntryIndex)++);
+
+ //
+ // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400
+ // Protect PF:0x34323423 Name:First Entry
+ // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600]
+ // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set]
+ //
+
+ DumpSpdSelector (Selector);
+ Print (L"\n ");
+
+ Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction));
+ Print (L"PF:%08x ", Data->PackageFlag);
+
+ Index = 0;
+ while (Data->Name[Index] != 0) {
+ DataName[Index] = (CHAR16) Data->Name[Index];
+ Index++;
+ ASSERT (Index < 128);
+ }
+ DataName[Index] = L'\0';
+
+ Print (L"Name:%s", DataName);
+
+ if (Data->Action == EfiIPsecActionProtect) {
+ Print (L"\n ");
+ if (Data->ProcessingPolicy->ExtSeqNum) {
+ Print (L"ext-sequence ");
+ }
+
+ if (Data->ProcessingPolicy->SeqOverflow) {
+ Print (L"sequence-overflow ");
+ }
+
+ if (Data->ProcessingPolicy->FragCheck) {
+ Print (L"fragment-check ");
+ }
+
+ HasPre = FALSE;
+ if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) {
+ Print (HasPre ? L"," : L"life:[");
+ Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount);
+ HasPre = TRUE;
+ }
+
+ if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) {
+ Print (HasPre ? L"," : L"life:[");
+ Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime);
+ HasPre = TRUE;
+ }
+
+ if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) {
+ Print (HasPre ? L"," : L"life:[");
+ Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime);
+ HasPre = TRUE;
+ }
+
+ if (HasPre) {
+ Print (L"]");
+ }
+
+ if (HasPre || Data->ProcessingPolicy->ExtSeqNum ||
+ Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) {
+ Print (L"\n ");
+ }
+
+ String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol);
+ String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo);
+ String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo);
+ Print (
+ L"%s Auth:%s Encrypt:%s ",
+ String1,
+ String2,
+ String3
+ );
+
+ Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode));
+ if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
+ Print (L"[");
+ DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress);
+ Print (L" -> ");
+ DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);
+ Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption));
+ }
+ }
+
+ Print (L"\n");
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA content.
+
+ @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure.
+ @param[in] Data The pointer to the EFI_IPSEC_SA_DATA structure.
+ @param[in] EntryIndex The pointer to the Index in the SAD Database.
+
+ @retval EFI_SUCCESS Dump SAD information successfully.
+**/
+EFI_STATUS
+DumpSadEntry (
+ IN EFI_IPSEC_SA_ID *SaId,
+ IN EFI_IPSEC_SA_DATA *Data,
+ IN UINTN *EntryIndex
+ )
+{
+ BOOLEAN HasPre;
+ CHAR16 *String1;
+ CHAR16 *String2;
+
+ //
+ // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx
+ // Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34
+ // Auth:xxxx/password Encrypt:yyyy/password
+ // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400
+ //
+
+ Print (L"%d.", (*EntryIndex)++);
+ Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol));
+ Print (L"Destination:");
+ DumpIpAddress (&SaId->DestAddress);
+ Print (L"\n");
+
+ Print (
+ L" Mode:%s SeqNum:%lx AntiReplayWin:%d ",
+ MapIntegerToString (Data->Mode, mMapIpSecMode),
+ Data->SNCount,
+ (UINTN) Data->AntiReplayWindows
+ );
+
+ HasPre = FALSE;
+ if (Data->SaLifetime.ByteCount != 0) {
+ Print (HasPre ? L"," : L"life:[");
+ Print (L"%lxB", Data->SaLifetime.ByteCount);
+ HasPre = TRUE;
+ }
+
+ if (Data->SaLifetime.SoftLifetime != 0) {
+ Print (HasPre ? L"," : L"life:[");
+ Print (L"%lxs", Data->SaLifetime.SoftLifetime);
+ HasPre = TRUE;
+ }
+
+ if (Data->SaLifetime.HardLifetime != 0) {
+ Print (HasPre ? L"," : L"life:[");
+ Print (L"%lxS", Data->SaLifetime.HardLifetime);
+ HasPre = TRUE;
+ }
+
+ if (HasPre) {
+ Print (L"] ");
+ }
+
+ Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU);
+
+ if (SaId->Proto == EfiIPsecAH) {
+ Print (
+ L" Auth:%s/%s\n",
+ MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo),
+ Data->AlgoInfo.AhAlgoInfo.AuthKey
+ );
+ } else {
+ String1 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo);
+ String2 = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo);
+ Print (
+ L" Auth:%s/%s Encrypt:%s/%s\n",
+ String1,
+ Data->AlgoInfo.EspAlgoInfo.AuthKey,
+ String2,
+ Data->AlgoInfo.EspAlgoInfo.EncKey
+ );
+ }
+
+ if (Data->SpdSelector != NULL) {
+ Print (L" ");
+ DumpSpdSelector (Data->SpdSelector);
+ Print (L"\n");
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content.
+
+ @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure.
+ @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure.
+ @param[in] EntryIndex The pointer to the Index in the PAD Database.
+
+ @retval EFI_SUCCESS Dump PAD information successfully.
+**/
+EFI_STATUS
+DumpPadEntry (
+ IN EFI_IPSEC_PAD_ID *PadId,
+ IN EFI_IPSEC_PAD_DATA *Data,
+ IN UINTN *EntryIndex
+ )
+{
+ CHAR16 *String1;
+ CHAR16 *String2;
+
+ //
+ // ADDR:10.23.17.34/15
+ // IDEv1 PreSharedSecret IKE-ID
+ // password
+ //
+
+ Print (L"%d.", (*EntryIndex)++);
+
+ if (PadId->PeerIdValid) {
+ Print (L"ID:%s", PadId->Id.PeerId);
+ } else {
+ Print (L"ADDR:");
+ DumpAddressInfo (&PadId->Id.IpAddress);
+ }
+
+ Print (L"\n");
+
+ String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto);
+ String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod);
+ Print (
+ L" %s %s",
+ String1,
+ String2
+ );
+
+ if (Data->IkeIdFlag) {
+ Print (L"IKE-ID");
+ }
+
+ Print (L"\n");
+
+ if (Data->AuthData != NULL) {
+ DumpAsciiString (Data->AuthData, Data->AuthDataSize);
+ Print (L"\n");
+ }
+
+ if (Data->RevocationData != NULL) {
+ Print (L" %s\n", Data->RevocationData);
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+VISIT_POLICY_ENTRY mDumpPolicyEntry[] = {
+ (VISIT_POLICY_ENTRY) DumpSpdEntry,
+ (VISIT_POLICY_ENTRY) DumpSadEntry,
+ (VISIT_POLICY_ENTRY) DumpPadEntry
+};
+
+/**
+ Print all entry information in the database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Dump all information successfully.
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+ListPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ )
+{
+ UINTN EntryIndex;
+
+ EntryIndex = 0;
+ return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex);
+}
+
diff --git a/NetworkPkg/Application/IpsecConfig/Dump.h b/NetworkPkg/Application/IpsecConfig/Dump.h
new file mode 100644
index 0000000000..3c475dd304
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Dump.h
@@ -0,0 +1,34 @@
+/** @file
+ The function declaration of dump policy entry function in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _DUMP_H_
+#define _DUMP_H_
+
+/**
+ Print all entry information in the database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Dump all information successfully.
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+ListPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ );
+
+#endif
diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.c b/NetworkPkg/Application/IpsecConfig/ForEach.c
new file mode 100644
index 0000000000..7b3b9b17a8
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/ForEach.c
@@ -0,0 +1,115 @@
+/** @file
+ The implementation to go through each entry in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfig.h"
+#include "ForEach.h"
+
+
+/**
+ Enumerate all entries in the database to execute specified operations according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] Routine The pointer to the function of a specified operation.
+ @param[in] Context The pointer to the context of a function.
+
+ @retval EFI_SUCCESS Execute specified operation successfully.
+**/
+EFI_STATUS
+ForeachPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN VISIT_POLICY_ENTRY Routine,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS GetNextStatus;
+ EFI_STATUS GetDataStatus;
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;
+ VOID *Data;
+ UINTN SelectorSize;
+ UINTN DataSize;
+ BOOLEAN FirstGetNext;
+
+ FirstGetNext = TRUE;
+ SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR);
+ Selector = AllocateZeroPool (SelectorSize);
+
+ DataSize = 0;
+ Data = NULL;
+
+ while (TRUE) {
+ GetNextStatus = mIpSecConfig->GetNextSelector (
+ mIpSecConfig,
+ DataType,
+ &SelectorSize,
+ Selector
+ );
+ if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {
+ gBS->FreePool (Selector);
+ Selector = FirstGetNext ? AllocateZeroPool (SelectorSize) : AllocatePool (SelectorSize);
+
+ GetNextStatus = mIpSecConfig->GetNextSelector (
+ mIpSecConfig,
+ DataType,
+ &SelectorSize,
+ Selector
+ );
+ }
+
+ if (EFI_ERROR (GetNextStatus)) {
+ break;
+ }
+
+ FirstGetNext = FALSE;
+
+ GetDataStatus = mIpSecConfig->GetData (
+ mIpSecConfig,
+ DataType,
+ Selector,
+ &DataSize,
+ Data
+ );
+ if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {
+ if (Data != NULL) {
+ gBS->FreePool (Data);
+ }
+
+ Data = AllocateZeroPool (DataSize);
+ GetDataStatus = mIpSecConfig->GetData (
+ mIpSecConfig,
+ DataType,
+ Selector,
+ &DataSize,
+ Data
+ );
+ }
+
+ ASSERT_EFI_ERROR (GetDataStatus);
+
+ if (EFI_ERROR (Routine (Selector, Data, Context))) {
+ break;
+ }
+ }
+
+ if (Data != NULL) {
+ gBS->FreePool (Data);
+ }
+
+ if (Selector != NULL) {
+ gBS->FreePool (Selector);
+ }
+
+ return EFI_SUCCESS;
+}
+
diff --git a/NetworkPkg/Application/IpsecConfig/ForEach.h b/NetworkPkg/Application/IpsecConfig/ForEach.h
new file mode 100644
index 0000000000..fc309300c0
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/ForEach.h
@@ -0,0 +1,54 @@
+/** @file
+ The internal structure and function declaration of the implementation
+ to go through each entry in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _FOREACH_H_
+#define _FOREACH_H_
+
+/**
+ The prototype for the DumpSpdEntry()/DumpSadEntry()/DumpPadEntry().
+ Print EFI_IPSEC_CONFIG_SELECTOR and corresponding content.
+
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.
+ @param[in] Data The pointer to the corresponding data.
+ @param[in] Context The pointer to the Index in SPD/SAD/PAD Database.
+
+ @retval EFI_SUCCESS Dump SPD/SAD/PAD information successfully.
+**/
+typedef
+EFI_STATUS
+(*VISIT_POLICY_ENTRY) (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context
+ );
+
+/**
+ Enumerate all entry in the database to execute a specified operation according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] Routine The pointer to function of a specified operation.
+ @param[in] Context The pointer to the context of a function.
+
+ @retval EFI_SUCCESS Execute specified operation successfully.
+**/
+EFI_STATUS
+ForeachPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN VISIT_POLICY_ENTRY Routine,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/NetworkPkg/Application/IpsecConfig/Helper.c b/NetworkPkg/Application/IpsecConfig/Helper.c
new file mode 100644
index 0000000000..5013ad9b76
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Helper.c
@@ -0,0 +1,419 @@
+/** @file
+ The assistant function implementation for IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfig.h"
+#include "Helper.h"
+
+/**
+ Helper function called to change an input parameter in the string format to a number.
+
+ @param[in] FlagStr The pointer to the flag string.
+ @param[in] Maximum Greatest value number.
+ @param[in, out] ValuePtr The pointer to the input parameter in string format.
+ @param[in] ByteCount The valid byte count
+ @param[in] Map The pointer to the STR2INT table.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+ @param[in] FormatMask The bit mask.
+ BIT 0 set indicates the value of a flag might be a number.
+ BIT 1 set indicates the value of a flag might be a string that needs to be looked up.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_FOUND The input parameter can't be found.
+ @retval EFI_INVALID_PARAMETER The input parameter is an invalid input.
+**/
+EFI_STATUS
+GetNumber (
+ IN CHAR16 *FlagStr,
+ IN UINT64 Maximum,
+ IN OUT VOID *ValuePtr,
+ IN UINTN ByteCount,
+ IN STR2INT *Map,
+ IN LIST_ENTRY *ParamPackage,
+ IN UINT32 FormatMask
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Value64;
+ BOOLEAN Converted;
+ UINTN Index;
+ CONST CHAR16 *ValueStr;
+
+ ASSERT (FormatMask & (FORMAT_NUMBER | FORMAT_STRING));
+
+ Converted = FALSE;
+ Value64 = 0;
+ ValueStr = ShellCommandLineGetValue (ParamPackage, FlagStr);
+
+ if (ValueStr == NULL) {
+ return EFI_NOT_FOUND;
+ } else {
+ //
+ // Try to convert to integer directly if MaybeNumber is TRUE.
+ //
+ if ((FormatMask & FORMAT_NUMBER) != 0) {
+ Value64 = StrToUInteger (ValueStr, &Status);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Convert successfully.
+ //
+ if (Value64 > Maximum) {
+ //
+ // But the result is invalid
+ //
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ FlagStr,
+ ValueStr
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Converted = TRUE;
+ }
+ }
+
+ if (!Converted && ((FormatMask & FORMAT_STRING) != 0)) {
+ //
+ // Convert falied, so use String->Integer map.
+ //
+ Value64 = MapStringToInteger (ValueStr, Map);
+ if (Value64 == (UINT32) -1) {
+ //
+ // Cannot find the string in the map.
+ //
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ FlagStr,
+ ValueStr
+ );
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ACCEPT_PARAMETERS), mHiiHandle);
+ for (Index = 0; Map[Index].String != NULL; Index++) {
+ Print (L" %s", Map[Index].String);
+ }
+
+ Print (L"\n");
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ CopyMem (ValuePtr, &Value64, ByteCount);
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Helper function called to convert a string containing an Ipv4 or Ipv6 Internet Protocol address
+ into a proper address for the EFI_IP_ADDRESS structure.
+
+ @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.
+ @param[out] Ip The pointer to the EFI_IP_ADDRESS structure to contain the result.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EfiInetAddr2 (
+ IN CHAR16 *Ptr,
+ OUT EFI_IP_ADDRESS *Ip
+ )
+{
+ EFI_STATUS Status;
+
+ if ((Ptr == NULL) || (Ip == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Parse the input address as Ipv4 Address first.
+ //
+ Status = NetLibStrToIp4 (Ptr, &Ip->v4);
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = NetLibStrToIp6 (Ptr, &Ip->v6);
+ return Status;
+}
+
+/**
+ Helper function called to calculate the prefix length associated with the string
+ containing an Ipv4 or Ipv6 Internet Protocol address.
+
+ @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.
+ @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval Others Other mistake case.
+**/
+EFI_STATUS
+EfiInetAddrRange (
+ IN CHAR16 *Ptr,
+ OUT EFI_IP_ADDRESS_INFO *Addr
+ )
+{
+ EFI_STATUS Status;
+
+ if ((Ptr == NULL) || (Addr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = NetLibStrToIp4 (Ptr, &Addr->Address.v4);
+ if (!EFI_ERROR (Status)) {
+ if ((UINT32)(*Addr->Address.v4.Addr) == 0) {
+ Addr->PrefixLength = 0;
+ } else {
+ Addr->PrefixLength = 32;
+ }
+ return Status;
+ }
+
+ Status = NetLibStrToIp6andPrefix (Ptr, &Addr->Address.v6, &Addr->PrefixLength);
+ if (!EFI_ERROR (Status) && (Addr->PrefixLength == 0xFF)) {
+ Addr->PrefixLength = 128;
+ }
+
+ return Status;
+}
+
+/**
+ Helper function called to calculate the port range associated with the string.
+
+ @param[in] Ptr The pointer to the string containing a port and range.
+ @param[out] Port The pointer to the Port to contain the result.
+ @param[out] PortRange The pointer to the PortRange to contain the result.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval Others Other mistake case.
+**/
+EFI_STATUS
+EfiInetPortRange (
+ IN CHAR16 *Ptr,
+ OUT UINT16 *Port,
+ OUT UINT16 *PortRange
+ )
+{
+ CHAR16 *BreakPtr;
+ CHAR16 Ch;
+ EFI_STATUS Status;
+
+ for (BreakPtr = Ptr; (*BreakPtr != L'\0') && (*BreakPtr != L':'); BreakPtr++) {
+ ;
+ }
+
+ Ch = *BreakPtr;
+ *BreakPtr = L'\0';
+ *Port = (UINT16) StrToUInteger (Ptr, &Status);
+ *BreakPtr = Ch;
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *PortRange = 0;
+ if (*BreakPtr == L':') {
+ BreakPtr++;
+ *PortRange = (UINT16) StrToUInteger (BreakPtr, &Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (*PortRange < *Port) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *PortRange = (UINT16) (*PortRange - *Port);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Helper function called to transfer a string to an unsigned integer.
+
+ @param[in] Str The pointer to the string.
+ @param[out] Status The operation status.
+
+ @return The integer value of converted Str.
+**/
+UINT64
+StrToUInteger (
+ IN CONST CHAR16 *Str,
+ OUT EFI_STATUS *Status
+ )
+{
+ UINT64 Value;
+ UINT64 NewValue;
+ CHAR16 *StrTail;
+ CHAR16 Char;
+ UINTN Base;
+ UINTN Len;
+
+ Base = 10;
+ Value = 0;
+ *Status = EFI_ABORTED;
+
+ //
+ // Skip leading white space.
+ //
+ while ((*Str != 0) && (*Str == ' ')) {
+ Str++;
+ }
+ //
+ // For NULL Str, just return.
+ //
+ if (*Str == 0) {
+ return 0;
+ }
+ //
+ // Skip white space in tail.
+ //
+ Len = StrLen (Str);
+ StrTail = (CHAR16 *) (Str + Len - 1);
+ while (*StrTail == ' ') {
+ *StrTail = 0;
+ StrTail--;
+ }
+
+ Len = StrTail - Str + 1;
+
+ //
+ // Check hex prefix '0x'.
+ //
+ if ((Len >= 2) && (*Str == '0') && ((*(Str + 1) == 'x') || (*(Str + 1) == 'X'))) {
+ Str += 2;
+ Len -= 2;
+ Base = 16;
+ }
+
+ if (Len == 0) {
+ return 0;
+ }
+ //
+ // Convert the string to value.
+ //
+ for (; Str <= StrTail; Str++) {
+
+ Char = *Str;
+
+ if (Base == 16) {
+ if (RShiftU64 (Value, 60) != 0) {
+ //
+ // Overflow here x16.
+ //
+ return 0;
+ }
+
+ NewValue = LShiftU64 (Value, 4);
+ } else {
+ if (RShiftU64 (Value, 61) != 0) {
+ //
+ // Overflow here x8.
+ //
+ return 0;
+ }
+
+ NewValue = LShiftU64 (Value, 3);
+ Value = LShiftU64 (Value, 1);
+ NewValue += Value;
+ if (NewValue < Value) {
+ //
+ // Overflow here.
+ //
+ return 0;
+ }
+ }
+
+ Value = NewValue;
+
+ if ((Base == 16) && (Char >= 'a') && (Char <= 'f')) {
+ Char = (CHAR16) (Char - 'a' + 'A');
+ }
+
+ if ((Base == 16) && (Char >= 'A') && (Char <= 'F')) {
+ Value += (Char - 'A') + 10;
+ } else if ((Char >= '0') && (Char <= '9')) {
+ Value += (Char - '0');
+ } else {
+ //
+ // Unexpected Char encountered.
+ //
+ return 0;
+ }
+ }
+
+ *Status = EFI_SUCCESS;
+ return Value;
+}
+
+/**
+ Helper function called to transfer a string to an unsigned integer according to the map table.
+
+ @param[in] Str The pointer to the string.
+ @param[in] Map The pointer to the map table.
+
+ @return The integer value of converted Str. If not found, then return -1.
+**/
+UINT32
+MapStringToInteger (
+ IN CONST CHAR16 *Str,
+ IN STR2INT *Map
+ )
+{
+ STR2INT *Item;
+
+ for (Item = Map; Item->String != NULL; Item++) {
+ if (StrCmp (Item->String, Str) == 0) {
+ return Item->Integer;
+ }
+ }
+
+ return (UINT32) -1;
+}
+
+/**
+ Helper function called to transfer an unsigned integer to a string according to the map table.
+
+ @param[in] Integer The pointer to the string.
+ @param[in] Map The pointer to the map table.
+
+ @return The converted Str. If not found, then return NULL.
+**/
+CHAR16 *
+MapIntegerToString (
+ IN UINT32 Integer,
+ IN STR2INT *Map
+ )
+{
+ STR2INT *Item;
+
+ for (Item = Map; Item->String != NULL; Item++) {
+ if (Integer == Item->Integer) {
+ return Item->String;
+ }
+ }
+
+ return NULL;
+}
diff --git a/NetworkPkg/Application/IpsecConfig/Helper.h b/NetworkPkg/Application/IpsecConfig/Helper.h
new file mode 100644
index 0000000000..d893145a76
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Helper.h
@@ -0,0 +1,143 @@
+/** @file
+ The assistant function declaration for IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _HELPER_H_
+#define _HELPER_H_
+
+#define FORMAT_NUMBER 0x1
+#define FORMAT_STRING 0x2
+
+/**
+ Helper function called to change input parameter in string format to number.
+
+ @param[in] FlagStr The pointer to the flag string.
+ @param[in] Maximum most value number.
+ @param[in, out] ValuePtr The pointer to the input parameter in string format.
+ @param[in] ByteCount The valid byte count
+ @param[in] Map The pointer to the STR2INT table.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+ @param[in] FormatMask The bit mask.
+ BIT 0 set indicates the value of flag might be number.
+ BIT 1 set indicates the value of flag might be a string that needs to be looked up.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_FOUND The input parameter can't be found.
+ @retval EFI_INVALID_PARAMETER The input parameter is an invalid input.
+**/
+EFI_STATUS
+GetNumber (
+ IN CHAR16 *FlagStr,
+ IN UINT64 Maximum,
+ IN OUT VOID *ValuePtr,
+ IN UINTN ByteCount,
+ IN STR2INT *Map,
+ IN LIST_ENTRY *ParamPackage,
+ IN UINT32 FormatMask
+ );
+
+/**
+ Helper function called to convert a string containing an (Ipv4) Internet Protocol dotted address
+ into a proper address for the EFI_IP_ADDRESS structure.
+
+ @param[in] Ptr The pointer to the string containing an (Ipv4) Internet Protocol dotted address.
+ @param[out] Ip The pointer to the Ip address structure to contain the result.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EfiInetAddr2 (
+ IN CHAR16 *Ptr,
+ OUT EFI_IP_ADDRESS *Ip
+ );
+
+/**
+ Helper function called to calculate the prefix length associated with the string
+ containing an Ipv4 or Ipv6 Internet Protocol address.
+
+ @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address.
+ @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval Others Other mistake case.
+**/
+EFI_STATUS
+EfiInetAddrRange (
+ IN CHAR16 *Ptr,
+ OUT EFI_IP_ADDRESS_INFO *Addr
+ );
+
+/**
+ Helper function called to calculate the port range associated with the string.
+
+ @param[in] Ptr The pointer to the string containing a port and range.
+ @param[out] Port The pointer to the Port to contain the result.
+ @param[out] PortRange The pointer to the PortRange to contain the result.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval Others Other mistake case.
+**/
+EFI_STATUS
+EfiInetPortRange (
+ IN CHAR16 *Ptr,
+ OUT UINT16 *Port,
+ OUT UINT16 *PortRange
+ );
+
+/**
+ Helper function called to transfer a string to an unsigned integer.
+
+ @param[in] Str The pointer to the string.
+ @param[out] Status The operation status.
+
+ @return The integer value of a converted str.
+**/
+UINT64
+StrToUInteger (
+ IN CONST CHAR16 *Str,
+ OUT EFI_STATUS *Status
+ );
+
+/**
+ Helper function called to transfer a string to an unsigned integer according to the map table.
+
+ @param[in] Str The pointer to the string.
+ @param[in] Map The pointer to the map table.
+
+ @return The integer value of converted str. If not found, then return -1.
+**/
+UINT32
+MapStringToInteger (
+ IN CONST CHAR16 *Str,
+ IN STR2INT *Map
+ );
+
+/**
+ Helper function called to transfer an unsigned integer to a string according to the map table.
+
+ @param[in] Integer The pointer to the string.
+ @param[in] Map The pointer to the map table.
+
+ @return The converted str. If not found, then return NULL.
+**/
+CHAR16 *
+MapIntegerToString (
+ IN UINT32 Integer,
+ IN STR2INT *Map
+ );
+
+#endif
diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.c b/NetworkPkg/Application/IpsecConfig/Indexer.c
new file mode 100644
index 0000000000..1762bbeb58
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Indexer.c
@@ -0,0 +1,248 @@
+/** @file
+ The implementation of construct ENTRY_INDEXER in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfig.h"
+#include "Indexer.h"
+#include "Helper.h"
+
+/**
+ Fill in SPD_ENTRY_INDEXER through ParamPackage list.
+
+ @param[in, out] Indexer The pointer to the SPD_ENTRY_INDEXER structure.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully.
+**/
+EFI_STATUS
+ConstructSpdIndexer (
+ IN OUT SPD_ENTRY_INDEXER *Indexer,
+ IN LIST_ENTRY *ParamPackage
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Value64;
+ CONST CHAR16 *ValueStr;
+
+ ValueStr = NULL;
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");
+ } else {
+ ASSERT (FALSE);
+ }
+
+ ASSERT (ValueStr != NULL);
+
+ Value64 = StrToUInteger (ValueStr, &Status);
+ if (!EFI_ERROR (Status)) {
+ Indexer->Index = (UINTN) Value64;
+ Indexer->Name = NULL;
+ } else {
+ UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) Indexer->Name);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fill in SAD_ENTRY_INDEXER through ParamPackage list.
+
+ @param[in, out] Indexer The pointer to the SAD_ENTRY_INDEXER structure.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully.
+ @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list.
+**/
+EFI_STATUS
+ConstructSadIndexer (
+ IN OUT SAD_ENTRY_INDEXER *Indexer,
+ IN LIST_ENTRY *ParamPackage
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status1;
+ UINT64 Value64;
+ CONST CHAR16 *ValueStr;
+
+ ValueStr = NULL;
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");
+ } else {
+ ASSERT (FALSE);
+ }
+
+ ASSERT (ValueStr != NULL);
+
+ Value64 = StrToUInteger (ValueStr, &Status);
+ if (!EFI_ERROR (Status)) {
+ Indexer->Index = (UINTN) Value64;
+ ZeroMem (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID));
+ } else {
+ if ((!ShellCommandLineGetFlag (ParamPackage, L"--lookup-spi")) ||
+ (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-ipsec-proto")) ||
+ (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-dest"))) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--lookup-spi --lookup-ipsec-proto --lookup-dest"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--lookup-spi",
+ (UINT32) -1,
+ &Indexer->SaId.Spi,
+ sizeof (UINT32),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ Status1 = GetNumber (
+ L"--lookup-ipsec-proto",
+ 0,
+ &Indexer->SaId.Proto,
+ sizeof (EFI_IPSEC_PROTOCOL_TYPE),
+ mMapIpSecProtocol,
+ ParamPackage,
+ FORMAT_STRING
+ );
+
+ if (EFI_ERROR (Status) || EFI_ERROR (Status1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-dest");
+ ASSERT (ValueStr != NULL);
+
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &Indexer->SaId.DestAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--lookup-dest",
+ ValueStr
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fill in PAD_ENTRY_INDEXER through ParamPackage list.
+
+ @param[in, out] Indexer The pointer to the PAD_ENTRY_INDEXER structure.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Filled in PAD_ENTRY_INDEXER successfully.
+ @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list.
+**/
+EFI_STATUS
+ConstructPadIndexer (
+ IN OUT PAD_ENTRY_INDEXER *Indexer,
+ IN LIST_ENTRY *ParamPackage
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Value64;
+ CONST CHAR16 *ValueStr;
+
+ ValueStr = NULL;
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d");
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");
+ } else {
+ ASSERT (FALSE);
+ }
+
+ ASSERT (ValueStr != NULL);
+
+ Value64 = StrToUInteger (ValueStr, &Status);
+
+ if (!EFI_ERROR (Status)) {
+ Indexer->Index = (UINTN) Value64;
+ ZeroMem (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID));
+ } else {
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"--lookup-peer-address")) {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-address");
+ ASSERT (ValueStr != NULL);
+
+ Indexer->PadId.PeerIdValid = FALSE;
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &Indexer->PadId.Id.IpAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--lookup-peer-address",
+ ValueStr
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-id");
+ if (ValueStr == NULL) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--lookup-peer-address --lookup-peer-id"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Indexer->PadId.PeerIdValid = TRUE;
+ StrnCpy ((CHAR16 *) Indexer->PadId.Id.PeerId, ValueStr, ARRAY_SIZE (Indexer->PadId.Id.PeerId) - 1);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[] = {
+ (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSpdIndexer,
+ (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSadIndexer,
+ (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructPadIndexer
+};
diff --git a/NetworkPkg/Application/IpsecConfig/Indexer.h b/NetworkPkg/Application/IpsecConfig/Indexer.h
new file mode 100644
index 0000000000..078f38a312
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Indexer.h
@@ -0,0 +1,58 @@
+/** @file
+ The internal structure and function declaration to construct ENTRY_INDEXER in
+ IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _INDEXER_H_
+#define _INDEXER_H_
+
+typedef struct {
+ UINT8 *Name;
+ UINTN Index; // Used only if Name is NULL.
+} SPD_ENTRY_INDEXER;
+
+typedef struct {
+ EFI_IPSEC_SA_ID SaId;
+ UINTN Index;
+} SAD_ENTRY_INDEXER;
+
+typedef struct {
+ EFI_IPSEC_PAD_ID PadId;
+ UINTN Index;
+} PAD_ENTRY_INDEXER;
+
+typedef union {
+ SPD_ENTRY_INDEXER Spd;
+ SAD_ENTRY_INDEXER Sad;
+ PAD_ENTRY_INDEXER Pad;
+} POLICY_ENTRY_INDEXER;
+
+/**
+ The prototype for the ConstructSpdIndexer()/ConstructSadIndexer()/ConstructPadIndexer().
+ Fill in SPD_ENTRY_INDEXER/SAD_ENTRY_INDEXER/PAD_ENTRY_INDEXER through ParamPackage list.
+
+ @param[in, out] Indexer The pointer to the POLICY_ENTRY_INDEXER union.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Filled in POLICY_ENTRY_INDEXER successfully.
+**/
+typedef
+EFI_STATUS
+(* CONSTRUCT_POLICY_ENTRY_INDEXER) (
+ IN POLICY_ENTRY_INDEXER *Indexer,
+ IN LIST_ENTRY *ParamPackage
+);
+
+extern CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[];
+#endif
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.c b/NetworkPkg/Application/IpsecConfig/IpSecConfig.c
new file mode 100644
index 0000000000..8006d84860
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.c
@@ -0,0 +1,809 @@
+/** @file
+ The main process for IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/HiiLib.h>
+
+#include <Protocol/IpSec.h>
+
+#include "IpSecConfig.h"
+#include "Dump.h"
+#include "Indexer.h"
+#include "PolicyEntryOperation.h"
+#include "Delete.h"
+#include "Helper.h"
+
+//
+// Used for ShellCommandLineParseEx only
+// and to ensure user inputs are in valid format
+//
+SHELL_PARAM_ITEM mIpSecConfigParamList[] = {
+ { L"-p", TypeValue },
+ { L"-a", TypeValue },
+ { L"-i", TypeValue },
+ { L"-e", TypeValue },
+ { L"-d", TypeValue },
+ { L"-f", TypeFlag },
+ { L"-l", TypeFlag },
+ { L"-enable", TypeFlag },
+ { L"-disable", TypeFlag },
+ { L"-status", TypeFlag },
+ { L"-?", TypeFlag },
+
+ //
+ // SPD Selector
+ //
+ { L"--local", TypeValue },
+ { L"--remote", TypeValue },
+ { L"--proto", TypeValue },
+ { L"--local-port", TypeValue },
+ { L"--remote-port", TypeValue },
+ { L"--icmp-type", TypeValue },
+ { L"--icmp-code", TypeValue },
+
+ //
+ // SPD Data
+ //
+ { L"--name", TypeValue },
+ { L"--packet-flag", TypeValue },
+ { L"--action", TypeValue },
+ { L"--lifebyte", TypeValue },
+ { L"--lifetime-soft", TypeValue },
+ { L"--lifetime", TypeValue },
+ { L"--mode", TypeValue },
+ { L"--tunnel-local", TypeValue },
+ { L"--tunnel-remote", TypeValue },
+ { L"--dont-fragment", TypeValue },
+ { L"--ipsec-proto", TypeValue },
+ { L"--auth-algo", TypeValue },
+ { L"--encrypt-algo", TypeValue },
+
+ { L"--ext-sequence", TypeFlag },
+ { L"--sequence-overflow", TypeFlag },
+ { L"--fragment-check", TypeFlag },
+ { L"--ext-sequence-", TypeFlag },
+ { L"--sequence-overflow-", TypeFlag },
+ { L"--fragment-check-", TypeFlag },
+
+ //
+ // SA ID
+ // --ipsec-proto
+ //
+ { L"--spi", TypeValue },
+ { L"--dest", TypeValue },
+ { L"--lookup-spi", TypeValue },
+ { L"--lookup-ipsec-proto", TypeValue },
+ { L"--lookup-dest", TypeValue },
+
+ //
+ // SA DATA
+ // --mode
+ // --auth-algo
+ // --encrypt-algo
+ //
+ { L"--sequence-number", TypeValue },
+ { L"--antireplay-window", TypeValue },
+ { L"--auth-key", TypeValue },
+ { L"--encrypt-key", TypeValue },
+ { L"--path-mtu", TypeValue },
+
+ //
+ // PAD ID
+ //
+ { L"--peer-id", TypeValue },
+ { L"--peer-address", TypeValue },
+ { L"--auth-proto", TypeValue },
+ { L"--auth-method", TypeValue },
+ { L"--ike-id", TypeValue },
+ { L"--ike-id-", TypeValue },
+ { L"--auth-data", TypeValue },
+ { L"--revocation-data", TypeValue },
+ { L"--lookup-peer-id", TypeValue },
+ { L"--lookup-peer-address", TypeValue },
+
+ { NULL, TypeMax },
+};
+
+//
+// -P
+//
+STR2INT mMapPolicy[] = {
+ { L"SPD", IPsecConfigDataTypeSpd },
+ { L"SAD", IPsecConfigDataTypeSad },
+ { L"PAD", IPsecConfigDataTypePad },
+ { NULL, 0 },
+};
+
+//
+// --proto
+//
+STR2INT mMapIpProtocol[] = {
+ { L"TCP", EFI_IP4_PROTO_TCP },
+ { L"UDP", EFI_IP4_PROTO_UDP },
+ { L"ICMP", EFI_IP4_PROTO_ICMP },
+ { NULL, 0 },
+};
+
+//
+// --action
+//
+STR2INT mMapIpSecAction[] = {
+ { L"Bypass", EfiIPsecActionBypass },
+ { L"Discard", EfiIPsecActionDiscard },
+ { L"Protect", EfiIPsecActionProtect },
+ { NULL, 0 },
+};
+
+//
+// --mode
+//
+STR2INT mMapIpSecMode[] = {
+ { L"Transport", EfiIPsecTransport },
+ { L"Tunnel", EfiIPsecTunnel },
+ { NULL, 0 },
+};
+
+//
+// --dont-fragment
+//
+STR2INT mMapDfOption[] = {
+ { L"clear", EfiIPsecTunnelClearDf },
+ { L"set", EfiIPsecTunnelSetDf },
+ { L"copy", EfiIPsecTunnelCopyDf },
+ { NULL, 0 },
+};
+
+//
+// --ipsec-proto
+//
+STR2INT mMapIpSecProtocol[] = {
+ { L"AH", EfiIPsecAH },
+ { L"ESP", EfiIPsecESP },
+ { NULL, 0 },
+};
+
+//
+// --auth-algo
+//
+STR2INT mMapAuthAlgo[] = {
+ { L"NONE", EFI_IPSEC_AALG_NONE },
+ { L"MD5HMAC", EFI_IPSEC_AALG_MD5HMAC },
+ { L"SHA1HMAC", EFI_IPSEC_AALG_SHA1HMAC },
+ { L"SHA2-256HMAC", EFI_IPSEC_AALG_SHA2_256HMAC },
+ { L"SHA2-384HMAC", EFI_IPSEC_AALG_SHA2_384HMAC },
+ { L"SHA2-512HMAC", EFI_IPSEC_AALG_SHA2_512HMAC },
+ { L"AES-XCBC-MAC", EFI_IPSEC_AALG_AES_XCBC_MAC },
+ { L"NULL", EFI_IPSEC_AALG_NULL },
+ { NULL, 0 },
+};
+
+//
+// --encrypt-algo
+//
+STR2INT mMapEncAlgo[] = {
+ { L"NONE", EFI_IPSEC_EALG_NONE },
+ { L"DESCBC", EFI_IPSEC_EALG_DESCBC },
+ { L"3DESCBC", EFI_IPSEC_EALG_3DESCBC },
+ { L"CASTCBC", EFI_IPSEC_EALG_CASTCBC },
+ { L"BLOWFISHCBC", EFI_IPSEC_EALG_BLOWFISHCBC },
+ { L"NULL", EFI_IPSEC_EALG_NULL },
+ { L"AESCBC", EFI_IPSEC_EALG_AESCBC },
+ { L"AESCTR", EFI_IPSEC_EALG_AESCTR },
+ { L"AES-CCM-ICV8", EFI_IPSEC_EALG_AES_CCM_ICV8 },
+ { L"AES-CCM-ICV12",EFI_IPSEC_EALG_AES_CCM_ICV12 },
+ { L"AES-CCM-ICV16",EFI_IPSEC_EALG_AES_CCM_ICV16 },
+ { L"AES-GCM-ICV8", EFI_IPSEC_EALG_AES_GCM_ICV8 },
+ { L"AES-GCM-ICV12",EFI_IPSEC_EALG_AES_GCM_ICV12 },
+ { L"AES-GCM-ICV16",EFI_IPSEC_EALG_AES_GCM_ICV16 },
+ { NULL, 0 },
+};
+
+//
+// --auth-proto
+//
+STR2INT mMapAuthProto[] = {
+ { L"IKEv1", EfiIPsecAuthProtocolIKEv1 },
+ { L"IKEv2", EfiIPsecAuthProtocolIKEv2 },
+ { NULL, 0 },
+};
+
+//
+// --auth-method
+//
+STR2INT mMapAuthMethod[] = {
+ { L"PreSharedSecret", EfiIPsecAuthMethodPreSharedSecret },
+ { L"Certificates", EfiIPsecAuthMethodCertificates },
+ { NULL, 0 },
+};
+
+EFI_IPSEC_PROTOCOL *mIpSec;
+EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig;
+EFI_HII_HANDLE mHiiHandle;
+EFI_GUID mEfiIpSecConfigGuid = EFI_IPSEC_CONFIG_GUID;
+CHAR16 mAppName[] = L"IpSecConfig";
+
+//
+// Used for IpSecConfigRetriveCheckListByName only to check the validation of user input
+//
+VAR_CHECK_ITEM mIpSecConfigVarCheckList[] = {
+ { L"-enable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-disable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-status", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-p", BIT(1), 0, BIT(2)|BIT(1)|BIT(0), 0 },
+
+ { L"-a", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-i", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-d", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-e", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-l", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },
+ { L"-f", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 },
+
+ { L"-?", BIT(0), BIT(0), BIT(2)|BIT(1)|BIT(0), 0 },
+
+ //
+ // SPD Selector
+ //
+ { L"--local", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--remote", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--proto", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--local-port", 0, 0, BIT(2)|BIT(1), BIT(0) },
+ { L"--remote-port", 0, 0, BIT(2)|BIT(1), BIT(0) },
+ { L"--icmp-type", 0, 0, BIT(2)|BIT(1), BIT(1) },
+ { L"--icmp-code", 0, 0, BIT(2)|BIT(1), BIT(1) },
+
+ //
+ // SPD Data
+ //
+ { L"--name", 0, 0, BIT(2), 0 },
+ { L"--packet-flag", 0, 0, BIT(2), 0 },
+ { L"--action", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--lifebyte", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--lifetime-soft", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--lifetime", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--mode", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--tunnel-local", 0, 0, BIT(2), 0 },
+ { L"--tunnel-remote", 0, 0, BIT(2), 0 },
+ { L"--dont-fragment", 0, 0, BIT(2), 0 },
+ { L"--ipsec-proto", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--auth-algo", 0, 0, BIT(2)|BIT(1), 0 },
+ { L"--encrypt-algo", 0, 0, BIT(2)|BIT(1), 0 },
+
+ { L"--ext-sequence", 0, 0, BIT(2), BIT(2) },
+ { L"--sequence-overflow", 0, 0, BIT(2), BIT(2) },
+ { L"--fragment-check", 0, 0, BIT(2), BIT(2) },
+ { L"--ext-sequence-", 0, 0, BIT(2), BIT(3) },
+ { L"--sequence-overflow-", 0, 0, BIT(2), BIT(3) },
+ { L"--fragment-check-", 0, 0, BIT(2), BIT(3) },
+
+ //
+ // SA ID
+ // --ipsec-proto
+ //
+ { L"--spi", 0, 0, BIT(1), 0 },
+ { L"--dest", 0, 0, BIT(1), 0 },
+ { L"--lookup-spi", 0, 0, BIT(1), 0 },
+ { L"--lookup-ipsec-proto", 0, 0, BIT(1), 0 },
+ { L"--lookup-dest", 0, 0, BIT(1), 0 },
+
+ //
+ // SA DATA
+ // --mode
+ // --auth-algo
+ // --encrypt-algo
+ //
+ { L"--sequence-number", 0, 0, BIT(1), 0 },
+ { L"--antireplay-window", 0, 0, BIT(1), 0 },
+ { L"--auth-key", 0, 0, BIT(1), 0 },
+ { L"--encrypt-key", 0, 0, BIT(1), 0 },
+ { L"--path-mtu", 0, 0, BIT(1), 0 },
+
+ //
+ // The example to add a PAD:
+ // "-A --peer-id Mike [--peer-address 10.23.2.2] --auth-proto IKE1/IKE2
+ // --auth-method PreSharedSeceret/Certificate --ike-id
+ // --auth-data 343343 --revocation-data 2342432"
+ // The example to delete a PAD:
+ // "-D * --lookup-peer-id Mike [--lookup-peer-address 10.23.2.2]"
+ // "-D 1"
+ // The example to edit a PAD:
+ // "-E * --lookup-peer-id Mike --auth-method Certificate"
+
+ //
+ // PAD ID
+ //
+ { L"--peer-id", 0, 0, BIT(0), BIT(4) },
+ { L"--peer-address", 0, 0, BIT(0), BIT(5) },
+ { L"--auth-proto", 0, 0, BIT(0), 0 },
+ { L"--auth-method", 0, 0, BIT(0), 0 },
+ { L"--IKE-ID", 0, 0, BIT(0), BIT(6) },
+ { L"--IKE-ID-", 0, 0, BIT(0), BIT(7) },
+ { L"--auth-data", 0, 0, BIT(0), 0 },
+ { L"--revocation-data", 0, 0, BIT(0), 0 },
+ { L"--lookup-peer-id", 0, 0, BIT(0), BIT(4) },
+ { L"--lookup-peer-address",0, 0, BIT(0), BIT(5) },
+
+ { NULL, 0, 0, 0, 0 },
+};
+
+/**
+ The function to allocate the proper sized buffer for various
+ EFI interfaces.
+
+ @param[in, out] Status Current status.
+ @param[in, out] Buffer Current allocated buffer, or NULL.
+ @param[in] BufferSize Current buffer size needed
+
+ @retval TRUE If the buffer was reallocated and the caller should try the API again.
+ @retval FALSE If the buffer was not reallocated successfully.
+**/
+BOOLEAN
+GrowBuffer (
+ IN OUT EFI_STATUS *Status,
+ IN OUT VOID **Buffer,
+ IN UINTN BufferSize
+ )
+{
+ BOOLEAN TryAgain;
+
+ ASSERT (Status != NULL);
+ ASSERT (Buffer != NULL);
+
+ //
+ // If this is an initial request, buffer will be null with a new buffer size.
+ //
+ if ((NULL == *Buffer) && (BufferSize != 0)) {
+ *Status = EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // If the status code is "buffer too small", resize the buffer.
+ //
+ TryAgain = FALSE;
+ if (*Status == EFI_BUFFER_TOO_SMALL) {
+
+ if (*Buffer != NULL) {
+ FreePool (*Buffer);
+ }
+
+ *Buffer = AllocateZeroPool (BufferSize);
+
+ if (*Buffer != NULL) {
+ TryAgain = TRUE;
+ } else {
+ *Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ //
+ // If there's an error, free the buffer.
+ //
+ if (!TryAgain && EFI_ERROR (*Status) && (*Buffer != NULL)) {
+ FreePool (*Buffer);
+ *Buffer = NULL;
+ }
+
+ return TryAgain;
+}
+
+/**
+ Function returns an array of handles that support the requested protocol
+ in a buffer allocated from a pool.
+
+ @param[in] SearchType Specifies which handle(s) are to be returned.
+ @param[in] Protocol Provides the protocol to search by.
+ This parameter is only valid for SearchType ByProtocol.
+
+ @param[in] SearchKey Supplies the search key depending on the SearchType.
+ @param[in, out] NoHandles The number of handles returned in Buffer.
+ @param[out] Buffer A pointer to the buffer to return the requested array of
+ handles that support Protocol.
+
+ @retval EFI_SUCCESS The resulting array of handles was returned.
+ @retval Others Other mistake case.
+**/
+EFI_STATUS
+LocateHandle (
+ IN EFI_LOCATE_SEARCH_TYPE SearchType,
+ IN EFI_GUID *Protocol OPTIONAL,
+ IN VOID *SearchKey OPTIONAL,
+ IN OUT UINTN *NoHandles,
+ OUT EFI_HANDLE **Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+
+ ASSERT (NoHandles != NULL);
+ ASSERT (Buffer != NULL);
+
+ //
+ // Initialize for GrowBuffer loop.
+ //
+ Status = EFI_SUCCESS;
+ *Buffer = NULL;
+ BufferSize = 50 * sizeof (EFI_HANDLE);
+
+ //
+ // Call the real function.
+ //
+ while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {
+ Status = gBS->LocateHandle (
+ SearchType,
+ Protocol,
+ SearchKey,
+ &BufferSize,
+ *Buffer
+ );
+ }
+
+ *NoHandles = BufferSize / sizeof (EFI_HANDLE);
+ if (EFI_ERROR (Status)) {
+ *NoHandles = 0;
+ }
+
+ return Status;
+}
+
+/**
+ Find the first instance of this protocol in the system and return its interface.
+
+ @param[in] ProtocolGuid The guid of the protocol.
+ @param[out] Interface The pointer to the first instance of the protocol.
+
+ @retval EFI_SUCCESS A protocol instance matching ProtocolGuid was found.
+ @retval Others A protocol instance matching ProtocolGuid was not found.
+**/
+EFI_STATUS
+LocateProtocol (
+ IN EFI_GUID *ProtocolGuid,
+ OUT VOID **Interface
+ )
+
+{
+ EFI_STATUS Status;
+ UINTN NumberHandles;
+ UINTN Index;
+ EFI_HANDLE *Handles;
+
+ *Interface = NULL;
+ Handles = NULL;
+ NumberHandles = 0;
+
+ Status = LocateHandle (ByProtocol, ProtocolGuid, NULL, &NumberHandles, &Handles);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_INFO, "LibLocateProtocol: Handle not found\n"));
+ return Status;
+ }
+
+ for (Index = 0; Index < NumberHandles; Index++) {
+ ASSERT (Handles != NULL);
+ Status = gBS->HandleProtocol (
+ Handles[Index],
+ ProtocolGuid,
+ Interface
+ );
+
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ return Status;
+}
+
+/**
+ Helper function called to check the conflicted flags.
+
+ @param[in] CheckList The pointer to the VAR_CHECK_ITEM table.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS No conflicted flags.
+ @retval EFI_INVALID_PARAMETER The input parameter is erroroneous or there are some conflicted flags.
+**/
+EFI_STATUS
+IpSecConfigRetriveCheckListByName (
+ IN VAR_CHECK_ITEM *CheckList,
+ IN LIST_ENTRY *ParamPackage
+)
+{
+
+ LIST_ENTRY *Node;
+ VAR_CHECK_ITEM *Item;
+ UINT32 Attribute1;
+ UINT32 Attribute2;
+ UINT32 Attribute3;
+ UINT32 Attribute4;
+ UINT32 Index;
+
+ Attribute1 = 0;
+ Attribute2 = 0;
+ Attribute3 = 0;
+ Attribute4 = 0;
+ Index = 0;
+ Item = mIpSecConfigVarCheckList;
+
+ if ((ParamPackage == NULL) || (CheckList == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Enumerate through the list of parameters that are input by user.
+ //
+ for (Node = GetFirstNode (ParamPackage); !IsNull (ParamPackage, Node); Node = GetNextNode (ParamPackage, Node)) {
+ if (((SHELL_PARAM_PACKAGE *) Node)->Name != NULL) {
+ //
+ // Enumerate the check list that defines the conflicted attributes of each flag.
+ //
+ for (; Item->VarName != NULL; Item++) {
+ if (StrCmp (((SHELL_PARAM_PACKAGE *) Node)->Name, Item->VarName) == 0) {
+ Index++;
+ if (Index == 1) {
+ Attribute1 = Item->Attribute1;
+ Attribute2 = Item->Attribute2;
+ Attribute3 = Item->Attribute3;
+ Attribute4 = Item->Attribute4;
+ } else {
+ Attribute1 &= Item->Attribute1;
+ Attribute2 |= Item->Attribute2;
+ Attribute3 &= Item->Attribute3;
+ Attribute4 |= Item->Attribute4;
+ if (Attribute1 != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Attribute2 != 0) {
+ if ((Index == 2) && (StrCmp (Item->VarName, L"-p") == 0)) {
+ continue;
+ }
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Attribute3 == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (((Attribute4 & 0xFF) == 0x03) || ((Attribute4 & 0xFF) == 0x0C) ||
+ ((Attribute4 & 0xFF) == 0x30) || ((Attribute4 & 0xFF) == 0xC0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ break;
+ }
+ }
+
+ Item = mIpSecConfigVarCheckList;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including
+ both device drivers and bus drivers.
+
+ The entry point for IpSecConfig application that parse the command line input and call an IpSecConfig process.
+
+ @param[in] ImageHandle The image handle of this application.
+ @param[in] SystemTable The pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeIpSecConfig (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;
+ UINT8 Value;
+ LIST_ENTRY *ParamPackage;
+ CONST CHAR16 *ValueStr;
+ CHAR16 *ProblemParam;
+ UINTN NonOptionCount;
+
+ //
+ // Register our string package with HII and return the handle to it.
+ //
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IpSecConfigStrings, NULL);
+ ASSERT (mHiiHandle != NULL);
+
+ Status = ShellCommandLineParseEx (mIpSecConfigParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, ProblemParam);
+ goto Done;
+ }
+
+ Status = IpSecConfigRetriveCheckListByName (mIpSecConfigVarCheckList, ParamPackage);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_MISTAKEN_OPTIONS), mHiiHandle);
+ goto Done;
+ }
+
+ Status = LocateProtocol (&gEfiIpSecConfigProtocolGuid, (VOID **) &mIpSecConfig);
+ if (EFI_ERROR (Status) || mIpSecConfig == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);
+ goto Done;
+ }
+
+ Status = LocateProtocol (&gEfiIpSecProtocolGuid, (VOID **) &mIpSec);
+ if (EFI_ERROR (Status) || mIpSec == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName);
+ goto Done;
+ }
+
+ //
+ // Enable IPsec.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-enable")) {
+ if (!(mIpSec->DisabledFlag)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_ENABLE), mHiiHandle, mAppName);
+ } else {
+ //
+ // Set enable flag.
+ //
+ Value = IPSEC_STATUS_ENABLED;
+ Status = gRT->SetVariable (
+ IPSECCONFIG_STATUS_NAME,
+ &gEfiIpSecConfigProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ sizeof (Value),
+ &Value
+ );
+ if (!EFI_ERROR (Status)) {
+ mIpSec->DisabledFlag = FALSE;
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_SUCCESS), mHiiHandle, mAppName);
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_FAILED), mHiiHandle, mAppName);
+ }
+ }
+
+ goto Done;
+ }
+
+ //
+ // Disable IPsec.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-disable")) {
+ if (mIpSec->DisabledFlag) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_DISABLE), mHiiHandle, mAppName);
+ } else {
+ //
+ // Set disable flag; however, leave it to be disabled in the callback function of DisabledEvent.
+ //
+ gBS->SignalEvent (mIpSec->DisabledEvent);
+ if (mIpSec->DisabledFlag) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_SUCCESS), mHiiHandle, mAppName);
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_FAILED), mHiiHandle, mAppName);
+ }
+ }
+
+ goto Done;
+ }
+
+ //
+ //IPsec Status.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"-status")) {
+ if (mIpSec->DisabledFlag) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_DISABLE), mHiiHandle, mAppName);
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_ENABLE), mHiiHandle, mAppName);
+ }
+
+ goto Done;
+ }
+
+ //
+ // Try to get policy database type.
+ //
+ DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) -1;
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-p");
+ if (ValueStr != NULL) {
+ DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) MapStringToInteger (ValueStr, mMapPolicy);
+ if (DataType == -1) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle, mAppName, ValueStr);
+ goto Done;
+ }
+ }
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {
+ switch (DataType) {
+ case (EFI_IPSEC_CONFIG_DATA_TYPE) -1:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_HELP), mHiiHandle);
+ break;
+
+ case IPsecConfigDataTypeSpd:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SPD_HELP), mHiiHandle);
+ break;
+
+ case IPsecConfigDataTypeSad:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SAD_HELP), mHiiHandle);
+ break;
+
+ case IPsecConfigDataTypePad:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PAD_HELP), mHiiHandle);
+ break;
+
+ default:
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle);
+ break;
+ }
+
+ goto Done;
+ }
+
+ NonOptionCount = ShellCommandLineGetCount ();
+ if ((NonOptionCount - 1) > 0) {
+ ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32) (NonOptionCount - 1));
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_REDUNDANCY_MANY), mHiiHandle, mAppName, ValueStr);
+ goto Done;
+ }
+
+ if (DataType == -1) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_DB), mHiiHandle, mAppName);
+ goto Done;
+ }
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {
+ Status = AddOrInsertPolicyEntry (DataType, ParamPackage);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-i")) {
+ Status = AddOrInsertPolicyEntry (DataType, ParamPackage);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) {
+ Status = EditPolicyEntry (DataType, ParamPackage);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) {
+ Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-f")) {
+ Status = FlushOrDeletePolicyEntry (DataType, ParamPackage);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-l")) {
+ Status = ListPolicyEntry (DataType, ParamPackage);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, mAppName);
+ goto Done;
+ }
+
+Done:
+ ShellCommandLineFreeVarList (ParamPackage);
+ HiiRemovePackages (mHiiHandle);
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.h b/NetworkPkg/Application/IpsecConfig/IpSecConfig.h
new file mode 100644
index 0000000000..d1a7681012
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.h
@@ -0,0 +1,123 @@
+/** @file
+ The internal structure and function declaration in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IPSEC_CONFIG_H_
+#define _IPSEC_CONFIG_H_
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiLib.h>
+#include <Library/ShellLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/IpSecConfig.h>
+
+#define EFI_IPSEC_CONFIG_GUID \
+ { \
+ 0x9db0c3ac, 0xd9d2, 0x4f96, {0x9e, 0xd7, 0x6d, 0xa6, 0x12, 0xa4, 0xf3, 0x27} \
+ }
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+#define IPSECCONFIG_STATUS_NAME L"IpSecStatus"
+
+#define BIT(x) (UINT32) (1 << (x))
+
+#define IPSEC_STATUS_DISABLED 0x0
+#define IPSEC_STATUS_ENABLED 0x1
+
+#define EFI_IP4_PROTO_ICMP 0x1
+#define EFI_IP4_PROTO_TCP 0x6
+#define EFI_IP4_PROTO_UDP 0x11
+
+#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF
+#define EFI_IPSEC_ANY_PORT 0
+
+typedef struct _VAR_CHECK_ITEM {
+ CHAR16 *VarName;
+ UINT32 Attribute1;
+ UINT32 Attribute2;
+ UINT32 Attribute3;
+ UINT32 Attribute4;
+} VAR_CHECK_ITEM;
+
+typedef struct _SHELL_PARAM_PACKAGE{
+ LIST_ENTRY Link;
+ CHAR16 *Name;
+ ParamType Type;
+ CHAR16 *Value;
+ UINTN OriginalPosition;
+} SHELL_PARAM_PACKAGE;
+
+typedef struct _STR2INT {
+ CHAR16 *String;
+ UINT32 Integer;
+} STR2INT;
+
+extern EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig;
+extern EFI_HII_HANDLE mHiiHandle;
+extern CHAR16 mAppName[];
+
+//
+// -P
+//
+extern STR2INT mMapPolicy[];
+
+//
+// --proto
+//
+extern STR2INT mMapIpProtocol[];
+
+//
+// --action
+//
+extern STR2INT mMapIpSecAction[];
+
+//
+// --mode
+//
+extern STR2INT mMapIpSecMode[];
+
+//
+// --dont-fragment
+//
+extern STR2INT mMapDfOption[];
+
+//
+// --ipsec-proto
+//
+extern STR2INT mMapIpSecProtocol[];
+//
+// --auth-algo
+//
+extern STR2INT mMapAuthAlgo[];
+
+//
+// --encrypt-algo
+//
+extern STR2INT mMapEncAlgo[];
+//
+// --auth-proto
+//
+extern STR2INT mMapAuthProto[];
+
+//
+// --auth-method
+//
+extern STR2INT mMapAuthMethod[];
+
+#endif
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf b/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf
new file mode 100644
index 0000000000..1e0d4f44db
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf
@@ -0,0 +1,61 @@
+## @file
+# Component description file for IpSecConfig6 application.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = IpSecConfig
+ FILE_GUID = 0922E604-F5EC-42ef-980D-A35E9A2B1844
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = InitializeIpSecConfig
+
+[Sources]
+ IpSecConfigStrings.uni
+ IpSecConfig.c
+ IpSecConfig.h
+ Dump.c
+ Dump.h
+ Indexer.c
+ Indexer.h
+ Match.c
+ Match.h
+ Delete.h
+ Delete.c
+ Helper.c
+ Helper.h
+ ForEach.c
+ ForEach.h
+ PolicyEntryOperation.c
+ PolicyEntryOperation.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ UefiBootServicesTableLib
+ UefiApplicationEntryPoint
+ BaseMemoryLib
+ ShellLib
+ MemoryAllocationLib
+ DebugLib
+ HiiLib
+ NetLib
+ UefiLib
+
+[Protocols]
+ gEfiIpSecProtocolGuid ##CONSUMS
+ gEfiIpSecConfigProtocolGuid ##CONSUMS
diff --git a/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
new file mode 100644
index 0000000000..fb0e27d802
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
Binary files differ
diff --git a/NetworkPkg/Application/IpsecConfig/Match.c b/NetworkPkg/Application/IpsecConfig/Match.c
new file mode 100644
index 0000000000..d6595ee8b8
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Match.c
@@ -0,0 +1,163 @@
+/** @file
+ The implementation of match policy entry function in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfig.h"
+#include "Indexer.h"
+#include "Match.h"
+
+/**
+ Private function to validate a buffer that should be filled with zero.
+
+ @param[in] Memory The pointer to the buffer.
+ @param[in] Size The size of the buffer.
+
+ @retval TRUE The memory is filled with zero.
+ @retval FALSE The memory isn't filled with zero.
+**/
+BOOLEAN
+IsMemoryZero (
+ IN VOID *Memory,
+ IN UINTN Size
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Size; Index++) {
+ if (*((UINT8 *) Memory + Index) != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Find the matching SPD with Indexer.
+
+ @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
+ @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure.
+ @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure.
+
+ @retval TRUE The matched SPD is found.
+ @retval FALSE The matched SPD is not found.
+**/
+BOOLEAN
+MatchSpdEntry (
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,
+ IN EFI_IPSEC_SPD_DATA *Data,
+ IN SPD_ENTRY_INDEXER *Indexer
+ )
+{
+ BOOLEAN Match;
+
+ Match = FALSE;
+ if (Indexer->Name != NULL) {
+ if ((Data->Name != NULL) && (AsciiStrCmp ((CHAR8 *) Indexer->Name, (CHAR8 *) Data->Name) == 0)) {
+ Match = TRUE;
+ }
+ } else {
+ if (Indexer->Index == 0) {
+ Match = TRUE;
+ }
+
+ Indexer->Index--;
+ }
+
+ return Match;
+}
+
+/**
+ Find the matching SAD with Indexer.
+
+ @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure.
+ @param[in] Data The pointer to the EFI_IPSEC_SA_DATA structure.
+ @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure.
+
+ @retval TRUE The matched SAD is found.
+ @retval FALSE The matched SAD is not found.
+**/
+BOOLEAN
+MatchSadEntry (
+ IN EFI_IPSEC_SA_ID *SaId,
+ IN EFI_IPSEC_SA_DATA *Data,
+ IN SAD_ENTRY_INDEXER *Indexer
+ )
+{
+ BOOLEAN Match;
+
+ Match = FALSE;
+ if (!IsMemoryZero (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID))) {
+ Match = (BOOLEAN) (CompareMem (&Indexer->SaId, SaId, sizeof (EFI_IPSEC_SA_ID)) == 0);
+ } else {
+ if (Indexer->Index == 0) {
+ Match = TRUE;
+ }
+ Indexer->Index--;
+ }
+
+ return Match;
+}
+
+/**
+ Find the matching PAD with Indexer.
+
+ @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure.
+ @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure.
+ @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure.
+
+ @retval TRUE The matched PAD is found.
+ @retval FALSE The matched PAD is not found.
+**/
+BOOLEAN
+MatchPadEntry (
+ IN EFI_IPSEC_PAD_ID *PadId,
+ IN EFI_IPSEC_PAD_DATA *Data,
+ IN PAD_ENTRY_INDEXER *Indexer
+ )
+{
+ BOOLEAN Match;
+
+ Match = FALSE;
+ if (!IsMemoryZero (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID))) {
+ Match = (BOOLEAN) ((Indexer->PadId.PeerIdValid == PadId->PeerIdValid) &&
+ ((PadId->PeerIdValid &&
+ (StrCmp (
+ (CONST CHAR16 *) Indexer->PadId.Id.PeerId,
+ (CONST CHAR16 *) PadId->Id.PeerId
+ ) == 0)) ||
+ ((!PadId->PeerIdValid) &&
+ (Indexer->PadId.Id.IpAddress.PrefixLength == PadId->Id.IpAddress.PrefixLength) &&
+ (CompareMem (
+ &Indexer->PadId.Id.IpAddress.Address,
+ &PadId->Id.IpAddress.Address,
+ sizeof (EFI_IP_ADDRESS)
+ ) == 0))));
+ } else {
+ if (Indexer->Index == 0) {
+ Match = TRUE;
+ }
+
+ Indexer->Index--;
+ }
+
+ return Match;
+}
+
+MATCH_POLICY_ENTRY mMatchPolicyEntry[] = {
+ (MATCH_POLICY_ENTRY) MatchSpdEntry,
+ (MATCH_POLICY_ENTRY) MatchSadEntry,
+ (MATCH_POLICY_ENTRY) MatchPadEntry
+};
+
diff --git a/NetworkPkg/Application/IpsecConfig/Match.h b/NetworkPkg/Application/IpsecConfig/Match.h
new file mode 100644
index 0000000000..1d73c5cfbc
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/Match.h
@@ -0,0 +1,41 @@
+/** @file
+ The internal structure and function declaration of
+ match policy entry function in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _MATCH_H_
+#define _MATCH_H_
+
+/**
+ The prototype for the MatchSpdEntry()/MatchSadEntry()/MatchPadEntry().
+ The functionality is to find the matching SPD/SAD/PAD with Indexer.
+
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.
+ @param[in] Data The pointer to corresponding Data.
+ @param[in] Indexer The pointer to the POLICY_ENTRY_INDEXER union.
+
+ @retval TRUE The matched SPD/SAD/PAD is found.
+ @retval FALSE The matched SPD/SAD/PAD is not found.
+**/
+typedef
+BOOLEAN
+(* MATCH_POLICY_ENTRY) (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN POLICY_ENTRY_INDEXER *Indexer
+ );
+
+extern MATCH_POLICY_ENTRY mMatchPolicyEntry[];
+
+#endif
diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c
new file mode 100644
index 0000000000..ddfbb4c504
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c
@@ -0,0 +1,2016 @@
+/** @file
+ The implementation of policy entry operation function in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfig.h"
+#include "Indexer.h"
+#include "Match.h"
+#include "Helper.h"
+#include "ForEach.h"
+#include "PolicyEntryOperation.h"
+
+/**
+ Fill in EFI_IPSEC_SPD_SELECTOR through ParamPackage list.
+
+ @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+ @param[in, out] ParamPackage The pointer to the Mask.
+
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+EFI_STATUS
+CreateSpdSelector (
+ OUT EFI_IPSEC_SPD_SELECTOR *Selector,
+ IN LIST_ENTRY *ParamPackage,
+ IN OUT UINT32 *Mask
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS ReturnStatus;
+ CONST CHAR16 *ValueStr;
+
+ Status = EFI_SUCCESS;
+ ReturnStatus = EFI_SUCCESS;
+
+ //
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local");
+ if (ValueStr != NULL) {
+ Selector->LocalAddressCount = 1;
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->LocalAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--local",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= LOCAL;
+ }
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote");
+ if (ValueStr != NULL) {
+ Selector->RemoteAddressCount = 1;
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->RemoteAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--remote",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= REMOTE;
+ }
+ }
+
+ Selector->NextLayerProtocol = EFI_IPSEC_ANY_PROTOCOL;
+
+ //
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
+ //
+ Status = GetNumber (
+ L"--proto",
+ (UINT16) -1,
+ &Selector->NextLayerProtocol,
+ sizeof (UINT16),
+ mMapIpProtocol,
+ ParamPackage,
+ FORMAT_NUMBER | FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= PROTO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Selector->LocalPort = EFI_IPSEC_ANY_PORT;
+ Selector->RemotePort = EFI_IPSEC_ANY_PORT;
+
+ //
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local-port");
+ if (ValueStr != NULL) {
+ Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->LocalPort, &Selector->LocalPortRange);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--local-port",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= LOCAL_PORT;
+ }
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote-port");
+ if (ValueStr != NULL) {
+ Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->RemotePort, &Selector->RemotePortRange);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--remote-port",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= REMOTE_PORT;
+ }
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
+ //
+ Status = GetNumber (
+ L"--icmp-type",
+ (UINT8) -1,
+ &Selector->LocalPort,
+ sizeof (UINT16),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= ICMP_TYPE;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR.
+ //
+ Status = GetNumber (
+ L"--icmp-code",
+ (UINT8) -1,
+ &Selector->RemotePort,
+ sizeof (UINT16),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= ICMP_CODE;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ return ReturnStatus;
+}
+
+/**
+ Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA through ParamPackage list.
+
+ @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
+ @param[out] Data The pointer to the EFI_IPSEC_SPD_DATA structure.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+ @param[out] Mask The pointer to the Mask.
+ @param[in] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+EFI_STATUS
+CreateSpdEntry (
+ OUT EFI_IPSEC_SPD_SELECTOR **Selector,
+ OUT EFI_IPSEC_SPD_DATA **Data,
+ IN LIST_ENTRY *ParamPackage,
+ OUT UINT32 *Mask,
+ IN BOOLEAN CreateNew
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS ReturnStatus;
+ CONST CHAR16 *ValueStr;
+ UINTN DataSize;
+
+ Status = EFI_SUCCESS;
+ *Mask = 0;
+
+ *Selector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR) + 2 * sizeof (EFI_IP_ADDRESS_INFO));
+ ASSERT (*Selector != NULL);
+
+ (*Selector)->LocalAddress = (EFI_IP_ADDRESS_INFO *) (*Selector + 1);
+ (*Selector)->RemoteAddress = (*Selector)->LocalAddress + 1;
+
+ ReturnStatus = CreateSpdSelector (*Selector, ParamPackage, Mask);
+
+ //
+ // SPD DATA
+ // NOTE: Allocate enough memory and add padding for different arch.
+ //
+ DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SPD_DATA));
+ DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_PROCESS_POLICY));
+ DataSize += sizeof (EFI_IPSEC_TUNNEL_OPTION);
+
+ *Data = AllocateZeroPool (DataSize);
+ ASSERT (*Data != NULL);
+
+ (*Data)->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (
+ (*Data + 1),
+ sizeof (UINTN)
+ );
+ (*Data)->ProcessingPolicy->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER (
+ ((*Data)->ProcessingPolicy + 1),
+ sizeof (UINTN)
+ );
+
+
+ //
+ // Convert user imput from string to integer, and fill in the Name in EFI_IPSEC_SPD_DATA.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--name");
+ if (ValueStr != NULL) {
+ UnicodeStrToAsciiStr (ValueStr, (CHAR8 *) (*Data)->Name);
+ *Mask |= NAME;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the PackageFlag in EFI_IPSEC_SPD_DATA.
+ //
+ Status = GetNumber (
+ L"--packet-flag",
+ (UINT8) -1,
+ &(*Data)->PackageFlag,
+ sizeof (UINT32),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= PACKET_FLAG;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the Action in EFI_IPSEC_SPD_DATA.
+ //
+ Status = GetNumber (
+ L"--action",
+ (UINT8) -1,
+ &(*Data)->Action,
+ sizeof (UINT32),
+ mMapIpSecAction,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= ACTION;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the ExtSeqNum in EFI_IPSEC_SPD_DATA.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence")) {
+ (*Data)->ProcessingPolicy->ExtSeqNum = TRUE;
+ *Mask |= EXT_SEQUENCE;
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence-")) {
+ (*Data)->ProcessingPolicy->ExtSeqNum = FALSE;
+ *Mask |= EXT_SEQUENCE;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the SeqOverflow in EFI_IPSEC_SPD_DATA.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow")) {
+ (*Data)->ProcessingPolicy->SeqOverflow = TRUE;
+ *Mask |= SEQUENCE_OVERFLOW;
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow-")) {
+ (*Data)->ProcessingPolicy->SeqOverflow = FALSE;
+ *Mask |= SEQUENCE_OVERFLOW;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the FragCheck in EFI_IPSEC_SPD_DATA.
+ //
+ if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check")) {
+ (*Data)->ProcessingPolicy->FragCheck = TRUE;
+ *Mask |= FRAGMENT_CHECK;
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check-")) {
+ (*Data)->ProcessingPolicy->FragCheck = FALSE;
+ *Mask |= FRAGMENT_CHECK;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the ProcessingPolicy in EFI_IPSEC_SPD_DATA.
+ //
+ Status = GetNumber (
+ L"--lifebyte",
+ (UINT64) -1,
+ &(*Data)->ProcessingPolicy->SaLifetime.ByteCount,
+ sizeof (UINT64),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= LIFEBYTE;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--lifetime",
+ (UINT64) -1,
+ &(*Data)->ProcessingPolicy->SaLifetime.HardLifetime,
+ sizeof (UINT64),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= LIFETIME;
+ }
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--lifetime-soft",
+ (UINT64) -1,
+ &(*Data)->ProcessingPolicy->SaLifetime.SoftLifetime,
+ sizeof (UINT64),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= LIFETIME_SOFT;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ (*Data)->ProcessingPolicy->Mode = EfiIPsecTransport;
+ Status = GetNumber (
+ L"--mode",
+ 0,
+ &(*Data)->ProcessingPolicy->Mode,
+ sizeof (UINT32),
+ mMapIpSecMode,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= MODE;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-local");
+ if (ValueStr != NULL) {
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->LocalTunnelAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--tunnel-local",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= TUNNEL_LOCAL;
+ }
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-remote");
+ if (ValueStr != NULL) {
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--tunnel-remote",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= TUNNEL_REMOTE;
+ }
+ }
+
+ (*Data)->ProcessingPolicy->TunnelOption->DF = EfiIPsecTunnelCopyDf;
+ Status = GetNumber (
+ L"--dont-fragment",
+ 0,
+ &(*Data)->ProcessingPolicy->TunnelOption->DF,
+ sizeof (UINT32),
+ mMapDfOption,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= DONT_FRAGMENT;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ (*Data)->ProcessingPolicy->Proto = EfiIPsecESP;
+ Status = GetNumber (
+ L"--ipsec-proto",
+ 0,
+ &(*Data)->ProcessingPolicy->Proto,
+ sizeof (UINT32),
+ mMapIpSecProtocol,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= IPSEC_PROTO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--encrypt-algo",
+ 0,
+ &(*Data)->ProcessingPolicy->EncAlgoId,
+ sizeof (UINT8),
+ mMapEncAlgo,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= ENCRYPT_ALGO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--auth-algo",
+ 0,
+ &(*Data)->ProcessingPolicy->AuthAlgoId,
+ sizeof (UINT8),
+ mMapAuthAlgo,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= AUTH_ALGO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Cannot check Mode against EfiIPsecTunnel, because user may want to change tunnel_remote only so the Mode is not set.
+ //
+ if ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE | DONT_FRAGMENT)) == 0) {
+ (*Data)->ProcessingPolicy->TunnelOption = NULL;
+ }
+
+ if ((*Mask & (EXT_SEQUENCE | SEQUENCE_OVERFLOW | FRAGMENT_CHECK | LIFEBYTE |
+ LIFETIME_SOFT | LIFETIME | MODE | TUNNEL_LOCAL | TUNNEL_REMOTE |
+ DONT_FRAGMENT | IPSEC_PROTO | AUTH_ALGO | ENCRYPT_ALGO)) == 0) {
+ if ((*Data)->Action != EfiIPsecActionProtect) {
+ //
+ // User may not provide additional parameter for Protect action, so we cannot simply set ProcessingPolicy to NULL.
+ //
+ (*Data)->ProcessingPolicy = NULL;
+ }
+ }
+
+ if (CreateNew) {
+ if ((*Mask & (LOCAL | REMOTE | PROTO | ACTION)) != (LOCAL | REMOTE | PROTO | ACTION)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--local --remote --proto --action"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else if (((*Data)->Action == EfiIPsecActionProtect) &&
+ ((*Data)->ProcessingPolicy->Mode == EfiIPsecTunnel) &&
+ ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE))) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--tunnel-local --tunnel-remote"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+ }
+
+ return ReturnStatus;
+}
+
+/**
+ Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA through ParamPackage list.
+
+ @param[out] SaId The pointer to the EFI_IPSEC_SA_ID structure.
+ @param[out] Data The pointer to the EFI_IPSEC_SA_DATA structure.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+ @param[out] Mask The pointer to the Mask.
+ @param[in] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+EFI_STATUS
+CreateSadEntry (
+ OUT EFI_IPSEC_SA_ID **SaId,
+ OUT EFI_IPSEC_SA_DATA **Data,
+ IN LIST_ENTRY *ParamPackage,
+ OUT UINT32 *Mask,
+ IN BOOLEAN CreateNew
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS ReturnStatus;
+ UINTN AuthKeyLength;
+ UINTN EncKeyLength;
+ CONST CHAR16 *ValueStr;
+ UINTN DataSize;
+
+ Status = EFI_SUCCESS;
+ ReturnStatus = EFI_SUCCESS;
+ *Mask = 0;
+ AuthKeyLength = 0;
+ EncKeyLength = 0;
+
+ *SaId = AllocateZeroPool (sizeof (EFI_IPSEC_SA_ID));
+ ASSERT (*SaId != NULL);
+
+ //
+ // Convert user imput from string to integer, and fill in the Spi in EFI_IPSEC_SA_ID.
+ //
+ Status = GetNumber (L"--spi", (UINT32) -1, &(*SaId)->Spi, sizeof (UINT32), NULL, ParamPackage, FORMAT_NUMBER);
+ if (!EFI_ERROR (Status)) {
+ *Mask |= SPI;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the Proto in EFI_IPSEC_SA_ID.
+ //
+ Status = GetNumber (
+ L"--ipsec-proto",
+ 0,
+ &(*SaId)->Proto,
+ sizeof (EFI_IPSEC_PROTOCOL_TYPE),
+ mMapIpSecProtocol,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= IPSEC_PROTO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--dest");
+ if (ValueStr != NULL) {
+ Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*SaId)->DestAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--dest",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= DEST;
+ }
+ }
+
+ //
+ // Convert user imput from string to integer, and fill in EFI_IPSEC_SA_DATA.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");
+ if (ValueStr != NULL) {
+ AuthKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");
+ if (ValueStr != NULL) {
+ EncKeyLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);
+ }
+
+ //
+ // EFI_IPSEC_SA_DATA:
+ // +------------
+ // | EFI_IPSEC_SA_DATA
+ // +-----------------------
+ // | AuthKey
+ // +-------------------------
+ // | EncKey
+ // +-------------------------
+ // | SpdSelector
+ //
+ // Notes: To make sure the address alignment add padding after each data if needed.
+ //
+ DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));
+ DataSize = ALIGN_VARIABLE (DataSize + AuthKeyLength);
+ DataSize = ALIGN_VARIABLE (DataSize + EncKeyLength);
+ DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_SPD_SELECTOR));
+ DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IP_ADDRESS_INFO));
+ DataSize += sizeof (EFI_IP_ADDRESS_INFO);
+
+
+
+ *Data = AllocateZeroPool (DataSize);
+ ASSERT (*Data != NULL);
+
+ (*Data)->ManualSet = TRUE;
+ (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER (((*Data) + 1), sizeof (UINTN));
+ (*Data)->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (
+ ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.AuthKey + AuthKeyLength),
+ sizeof (UINTN)
+ );
+ (*Data)->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER (
+ ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.EncKey + EncKeyLength),
+ sizeof (UINTN)
+ );
+ (*Data)->SpdSelector->LocalAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (
+ ((UINT8 *) (*Data)->SpdSelector + sizeof (EFI_IPSEC_SPD_SELECTOR)),
+ sizeof (UINTN));
+ (*Data)->SpdSelector->RemoteAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER (
+ (*Data)->SpdSelector->LocalAddress + 1,
+ sizeof (UINTN)
+ );
+
+ (*Data)->Mode = EfiIPsecTransport;
+ Status = GetNumber (
+ L"--mode",
+ 0,
+ &(*Data)->Mode,
+ sizeof (EFI_IPSEC_MODE),
+ mMapIpSecMode,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= MODE;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // According to RFC 4303-3.3.3. The first packet sent using a given SA
+ // will contain a sequence number of 1.
+ //
+ (*Data)->SNCount = 1;
+ Status = GetNumber (
+ L"--sequence-number",
+ (UINT64) -1,
+ &(*Data)->SNCount,
+ sizeof (UINT64),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= SEQUENCE_NUMBER;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ (*Data)->AntiReplayWindows = 0;
+ Status = GetNumber (
+ L"--antireplay-window",
+ (UINT8) -1,
+ &(*Data)->AntiReplayWindows,
+ sizeof (UINT8),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= SEQUENCE_NUMBER;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--encrypt-algo",
+ 0,
+ &(*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId,
+ sizeof (UINT8),
+ mMapEncAlgo,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= ENCRYPT_ALGO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key");
+ if (ValueStr != NULL ) {
+ (*Data)->AlgoInfo.EspAlgoInfo.EncKeyLength = EncKeyLength;
+ CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.EncKey, ValueStr, EncKeyLength);
+ *Mask |= ENCRYPT_KEY;
+ } else {
+ (*Data)->AlgoInfo.EspAlgoInfo.EncKey = NULL;
+ }
+
+ Status = GetNumber (
+ L"--auth-algo",
+ 0,
+ &(*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId,
+ sizeof (UINT8),
+ mMapAuthAlgo,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= AUTH_ALGO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key");
+ if (ValueStr != NULL) {
+ (*Data)->AlgoInfo.EspAlgoInfo.AuthKeyLength = AuthKeyLength;
+ CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.AuthKey, ValueStr, AuthKeyLength);
+ *Mask |= AUTH_KEY;
+ } else {
+ (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = NULL;
+ }
+
+ Status = GetNumber (
+ L"--lifebyte",
+ (UINT64) -1,
+ &(*Data)->SaLifetime.ByteCount,
+ sizeof (UINT64),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= LIFEBYTE;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--lifetime",
+ (UINT64) -1,
+ &(*Data)->SaLifetime.HardLifetime,
+ sizeof (UINT64),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= LIFETIME;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--lifetime-soft",
+ (UINT64) -1,
+ &(*Data)->SaLifetime.SoftLifetime,
+ sizeof (UINT64),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= LIFETIME_SOFT;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--path-mtu",
+ (UINT32) -1,
+ &(*Data)->PathMTU,
+ sizeof (UINT32),
+ NULL,
+ ParamPackage,
+ FORMAT_NUMBER
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= PATH_MTU;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ ReturnStatus = CreateSpdSelector ((*Data)->SpdSelector, ParamPackage, Mask);
+
+ if (CreateNew) {
+ if ((*Mask & (SPI | IPSEC_PROTO | DEST)) != (SPI | IPSEC_PROTO | DEST)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--spi --ipsec-proto --dest"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ if ((*SaId)->Proto == EfiIPsecAH) {
+ if ((*Mask & AUTH_ALGO) == 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
+ mHiiHandle,
+ mAppName,
+ L"--auth-algo"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != EFI_IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
+ mHiiHandle,
+ mAppName,
+ L"--auth-key"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if ((*Mask & ENCRYPT_ALGO) == 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
+ mHiiHandle,
+ mAppName,
+ L"--encrypt-algo"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else if ((*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (*Mask & ENCRYPT_KEY) == 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
+ mHiiHandle,
+ mAppName,
+ L"--encrypt-key"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+ }
+
+ return ReturnStatus;
+}
+
+/**
+ Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA through ParamPackage list.
+
+ @param[out] PadId The pointer to the EFI_IPSEC_PAD_ID structure.
+ @param[out] Data The pointer to the EFI_IPSEC_PAD_DATA structure.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+ @param[out] Mask The pointer to the Mask.
+ @param[in] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+EFI_STATUS
+CreatePadEntry (
+ OUT EFI_IPSEC_PAD_ID **PadId,
+ OUT EFI_IPSEC_PAD_DATA **Data,
+ IN LIST_ENTRY *ParamPackage,
+ OUT UINT32 *Mask,
+ IN BOOLEAN CreateNew
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS ReturnStatus;
+ EFI_FILE_HANDLE FileHandle;
+ UINT64 FileSize;
+ UINTN AuthDataLength;
+ UINTN RevocationDataLength;
+ UINTN DataLength;
+ UINTN Index;
+ CONST CHAR16 *ValueStr;
+ UINTN DataSize;
+
+ Status = EFI_SUCCESS;
+ ReturnStatus = EFI_SUCCESS;
+ *Mask = 0;
+ AuthDataLength = 0;
+ RevocationDataLength = 0;
+
+ *PadId = AllocateZeroPool (sizeof (EFI_IPSEC_PAD_ID));
+ ASSERT (*PadId != NULL);
+
+ //
+ // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_ID.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-address");
+ if (ValueStr != NULL) {
+ (*PadId)->PeerIdValid = FALSE;
+ Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &(*PadId)->Id.IpAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE),
+ mHiiHandle,
+ mAppName,
+ L"--peer-address",
+ ValueStr
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ *Mask |= PEER_ADDRESS;
+ }
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-id");
+ if (ValueStr != NULL) {
+ (*PadId)->PeerIdValid = TRUE;
+ StrnCpy ((CHAR16 *) (*PadId)->Id.PeerId, ValueStr, ARRAY_SIZE ((*PadId)->Id.PeerId) - 1);
+ *Mask |= PEER_ID;
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");
+ if (ValueStr != NULL) {
+ if (ValueStr[0] == L'@') {
+ //
+ // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"
+ //
+ Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
+ mHiiHandle,
+ mAppName,
+ &ValueStr[1]
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ Status = ShellGetFileSize (FileHandle, &FileSize);
+ ShellCloseFile (&FileHandle);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
+ mHiiHandle,
+ mAppName,
+ &ValueStr[1]
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else {
+ AuthDataLength = (UINTN) FileSize;
+ }
+ }
+ } else {
+ AuthDataLength = StrLen (ValueStr);
+ }
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");
+ if (ValueStr != NULL) {
+ RevocationDataLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16);
+ }
+
+ //
+ // Allocate Buffer for Data. Add padding after each struct to make sure the alignment
+ // in different Arch.
+ //
+ DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));
+ DataSize = ALIGN_VARIABLE (DataSize + AuthDataLength);
+ DataSize += RevocationDataLength;
+
+ *Data = AllocateZeroPool (DataSize);
+ ASSERT (*Data != NULL);
+
+ (*Data)->AuthData = (VOID *) ALIGN_POINTER ((*Data + 1), sizeof (UINTN));
+ (*Data)->RevocationData = (VOID *) ALIGN_POINTER (((UINT8 *) (*Data + 1) + AuthDataLength), sizeof (UINTN));
+ (*Data)->AuthProtocol = EfiIPsecAuthProtocolIKEv1;
+
+ //
+ // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_DATA.
+ //
+ Status = GetNumber (
+ L"--auth-proto",
+ 0,
+ &(*Data)->AuthProtocol,
+ sizeof (EFI_IPSEC_AUTH_PROTOCOL_TYPE),
+ mMapAuthProto,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= AUTH_PROTO;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetNumber (
+ L"--auth-method",
+ 0,
+ &(*Data)->AuthMethod,
+ sizeof (EFI_IPSEC_AUTH_METHOD),
+ mMapAuthMethod,
+ ParamPackage,
+ FORMAT_STRING
+ );
+ if (!EFI_ERROR (Status)) {
+ *Mask |= AUTH_METHOD;
+ }
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id")) {
+ (*Data)->IkeIdFlag = TRUE;
+ *Mask |= IKE_ID;
+ }
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id-")) {
+ (*Data)->IkeIdFlag = FALSE;
+ *Mask |= IKE_ID;
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data");
+ if (ValueStr != NULL) {
+ if (ValueStr[0] == L'@') {
+ //
+ // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat"
+ //
+
+ Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
+ mHiiHandle,
+ mAppName,
+ &ValueStr[1]
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ (*Data)->AuthData = NULL;
+ } else {
+ DataLength = AuthDataLength;
+ Status = ShellReadFile (FileHandle, &DataLength, (*Data)->AuthData);
+ ShellCloseFile (&FileHandle);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED),
+ mHiiHandle,
+ mAppName,
+ &ValueStr[1]
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ (*Data)->AuthData = NULL;
+ } else {
+ ASSERT (DataLength == AuthDataLength);
+ *Mask |= AUTH_DATA;
+ }
+ }
+ } else {
+ for (Index = 0; Index < AuthDataLength; Index++) {
+ ((CHAR8 *) (*Data)->AuthData)[Index] = (CHAR8) ValueStr[Index];
+ }
+ (*Data)->AuthDataSize = AuthDataLength;
+ *Mask |= AUTH_DATA;
+ }
+ }
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data");
+ if (ValueStr != NULL) {
+ CopyMem ((*Data)->RevocationData, ValueStr, RevocationDataLength);
+ (*Data)->RevocationDataSize = RevocationDataLength;
+ *Mask |= REVOCATION_DATA;
+ } else {
+ (*Data)->RevocationData = NULL;
+ }
+
+ if (CreateNew) {
+ if ((*Mask & (PEER_ID | PEER_ADDRESS)) == 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--peer-id --peer-address"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ } else if ((*Mask & (AUTH_METHOD | AUTH_DATA)) != (AUTH_METHOD | AUTH_DATA)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--auth-method --auth-data"
+ );
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ }
+ }
+
+ return ReturnStatus;
+}
+
+CREATE_POLICY_ENTRY mCreatePolicyEntry[] = {
+ (CREATE_POLICY_ENTRY) CreateSpdEntry,
+ (CREATE_POLICY_ENTRY) CreateSadEntry,
+ (CREATE_POLICY_ENTRY) CreatePadEntry
+};
+
+/**
+ Combine old SPD entry with new SPD entry.
+
+ @param[in, out] OldSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
+ @param[in, out] OldData The pointer to the EFI_IPSEC_SPD_DATA structure.
+ @param[in] NewSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
+ @param[in] NewData The pointer to the EFI_IPSEC_SPD_DATA structure.
+ @param[in] Mask The pointer to the Mask.
+ @param[out] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Combined successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+EFI_STATUS
+CombineSpdEntry (
+ IN OUT EFI_IPSEC_SPD_SELECTOR *OldSelector,
+ IN OUT EFI_IPSEC_SPD_DATA *OldData,
+ IN EFI_IPSEC_SPD_SELECTOR *NewSelector,
+ IN EFI_IPSEC_SPD_DATA *NewData,
+ IN UINT32 Mask,
+ OUT BOOLEAN *CreateNew
+ )
+{
+
+ //
+ // Process Selector
+ //
+ *CreateNew = FALSE;
+ if ((Mask & LOCAL) == 0) {
+ NewSelector->LocalAddressCount = OldSelector->LocalAddressCount;
+ NewSelector->LocalAddress = OldSelector->LocalAddress;
+ } else if ((NewSelector->LocalAddressCount != OldSelector->LocalAddressCount) ||
+ (CompareMem (NewSelector->LocalAddress, OldSelector->LocalAddress, NewSelector->LocalAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {
+ *CreateNew = TRUE;
+ }
+
+ if ((Mask & REMOTE) == 0) {
+ NewSelector->RemoteAddressCount = OldSelector->RemoteAddressCount;
+ NewSelector->RemoteAddress = OldSelector->RemoteAddress;
+ } else if ((NewSelector->RemoteAddressCount != OldSelector->RemoteAddressCount) ||
+ (CompareMem (NewSelector->RemoteAddress, OldSelector->RemoteAddress, NewSelector->RemoteAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) {
+ *CreateNew = TRUE;
+ }
+
+ if ((Mask & PROTO) == 0) {
+ NewSelector->NextLayerProtocol = OldSelector->NextLayerProtocol;
+ } else if (NewSelector->NextLayerProtocol != OldSelector->NextLayerProtocol) {
+ *CreateNew = TRUE;
+ }
+
+ switch (NewSelector->NextLayerProtocol) {
+ case EFI_IP4_PROTO_TCP:
+ case EFI_IP4_PROTO_UDP:
+ if ((Mask & LOCAL_PORT) == 0) {
+ NewSelector->LocalPort = OldSelector->LocalPort;
+ NewSelector->LocalPortRange = OldSelector->LocalPortRange;
+ } else if ((NewSelector->LocalPort != OldSelector->LocalPort) ||
+ (NewSelector->LocalPortRange != OldSelector->LocalPortRange)) {
+ *CreateNew = TRUE;
+ }
+
+ if ((Mask & REMOTE_PORT) == 0) {
+ NewSelector->RemotePort = OldSelector->RemotePort;
+ NewSelector->RemotePortRange = OldSelector->RemotePortRange;
+ } else if ((NewSelector->RemotePort != OldSelector->RemotePort) ||
+ (NewSelector->RemotePortRange != OldSelector->RemotePortRange)) {
+ *CreateNew = TRUE;
+ }
+ break;
+
+ case EFI_IP4_PROTO_ICMP:
+ if ((Mask & ICMP_TYPE) == 0) {
+ NewSelector->LocalPort = OldSelector->LocalPort;
+ } else if (NewSelector->LocalPort != OldSelector->LocalPort) {
+ *CreateNew = TRUE;
+ }
+
+ if ((Mask & ICMP_CODE) == 0) {
+ NewSelector->RemotePort = OldSelector->RemotePort;
+ } else if (NewSelector->RemotePort != OldSelector->RemotePort) {
+ *CreateNew = TRUE;
+ }
+ break;
+ }
+ //
+ // Process Data
+ //
+ if ((Mask & NAME) != 0) {
+ AsciiStrCpy ((CHAR8 *) OldData->Name, (CHAR8 *) NewData->Name);
+ }
+
+ if ((Mask & PACKET_FLAG) != 0) {
+ OldData->PackageFlag = NewData->PackageFlag;
+ }
+
+ if ((Mask & ACTION) != 0) {
+ OldData->Action = NewData->Action;
+ }
+
+ if (OldData->Action != EfiIPsecActionProtect) {
+ OldData->ProcessingPolicy = NULL;
+ } else {
+ //
+ // Protect
+ //
+ if (OldData->ProcessingPolicy == NULL) {
+ //
+ // Just point to new data if originally NULL.
+ //
+ OldData->ProcessingPolicy = NewData->ProcessingPolicy;
+ if (OldData->ProcessingPolicy->Mode == EfiIPsecTunnel &&
+ (Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)
+ ) {
+ //
+ // Change to Protect action and Tunnel mode, but without providing local/remote tunnel address.
+ //
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--tunnel-local --tunnel-remote"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ //
+ // Modify some of the data.
+ //
+ if ((Mask & EXT_SEQUENCE) != 0) {
+ OldData->ProcessingPolicy->ExtSeqNum = NewData->ProcessingPolicy->ExtSeqNum;
+ }
+
+ if ((Mask & SEQUENCE_OVERFLOW) != 0) {
+ OldData->ProcessingPolicy->SeqOverflow = NewData->ProcessingPolicy->SeqOverflow;
+ }
+
+ if ((Mask & FRAGMENT_CHECK) != 0) {
+ OldData->ProcessingPolicy->FragCheck = NewData->ProcessingPolicy->FragCheck;
+ }
+
+ if ((Mask & LIFEBYTE) != 0) {
+ OldData->ProcessingPolicy->SaLifetime.ByteCount = NewData->ProcessingPolicy->SaLifetime.ByteCount;
+ }
+
+ if ((Mask & LIFETIME_SOFT) != 0) {
+ OldData->ProcessingPolicy->SaLifetime.SoftLifetime = NewData->ProcessingPolicy->SaLifetime.SoftLifetime;
+ }
+
+ if ((Mask & LIFETIME) != 0) {
+ OldData->ProcessingPolicy->SaLifetime.HardLifetime = NewData->ProcessingPolicy->SaLifetime.HardLifetime;
+ }
+
+ if ((Mask & MODE) != 0) {
+ OldData->ProcessingPolicy->Mode = NewData->ProcessingPolicy->Mode;
+ }
+
+ if ((Mask & IPSEC_PROTO) != 0) {
+ OldData->ProcessingPolicy->Proto = NewData->ProcessingPolicy->Proto;
+ }
+
+ if ((Mask & AUTH_ALGO) != 0) {
+ OldData->ProcessingPolicy->AuthAlgoId = NewData->ProcessingPolicy->AuthAlgoId;
+ }
+
+ if ((Mask & ENCRYPT_ALGO) != 0) {
+ OldData->ProcessingPolicy->EncAlgoId = NewData->ProcessingPolicy->EncAlgoId;
+ }
+
+ if (OldData->ProcessingPolicy->Mode != EfiIPsecTunnel) {
+ OldData->ProcessingPolicy->TunnelOption = NULL;
+ } else {
+ if (OldData->ProcessingPolicy->TunnelOption == NULL) {
+ //
+ // Set from Transport mode to Tunnel mode, should ensure TUNNEL_LOCAL & TUNNEL_REMOTE both exists.
+ //
+ if ((Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--tunnel-local --tunnel-remote"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldData->ProcessingPolicy->TunnelOption = NewData->ProcessingPolicy->TunnelOption;
+ } else {
+ if ((Mask & TUNNEL_LOCAL) != 0) {
+ CopyMem (
+ &OldData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,
+ &NewData->ProcessingPolicy->TunnelOption->LocalTunnelAddress,
+ sizeof (EFI_IP_ADDRESS)
+ );
+ }
+
+ if ((Mask & TUNNEL_REMOTE) != 0) {
+ CopyMem (
+ &OldData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,
+ &NewData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress,
+ sizeof (EFI_IP_ADDRESS)
+ );
+ }
+
+ if ((Mask & DONT_FRAGMENT) != 0) {
+ OldData->ProcessingPolicy->TunnelOption->DF = NewData->ProcessingPolicy->TunnelOption->DF;
+ }
+ }
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Combine old SAD entry with new SAD entry.
+
+ @param[in, out] OldSaId The pointer to the EFI_IPSEC_SA_ID structure.
+ @param[in, out] OldData The pointer to the EFI_IPSEC_SA_DATA structure.
+ @param[in] NewSaId The pointer to the EFI_IPSEC_SA_ID structure.
+ @param[in] NewData The pointer to the EFI_IPSEC_SA_DATA structure.
+ @param[in] Mask The pointer to the Mask.
+ @param[out] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Combined successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+EFI_STATUS
+CombineSadEntry (
+ IN OUT EFI_IPSEC_SA_ID *OldSaId,
+ IN OUT EFI_IPSEC_SA_DATA *OldData,
+ IN EFI_IPSEC_SA_ID *NewSaId,
+ IN EFI_IPSEC_SA_DATA *NewData,
+ IN UINT32 Mask,
+ OUT BOOLEAN *CreateNew
+ )
+{
+
+ *CreateNew = FALSE;
+
+ if ((Mask & SPI) == 0) {
+ NewSaId->Spi = OldSaId->Spi;
+ } else if (NewSaId->Spi != OldSaId->Spi) {
+ *CreateNew = TRUE;
+ }
+
+ if ((Mask & IPSEC_PROTO) == 0) {
+ NewSaId->Proto = OldSaId->Proto;
+ } else if (NewSaId->Proto != OldSaId->Proto) {
+ *CreateNew = TRUE;
+ }
+
+ if ((Mask & DEST) == 0) {
+ CopyMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS));
+ } else if (CompareMem (&NewSaId->DestAddress, &OldSaId->DestAddress, sizeof (EFI_IP_ADDRESS)) != 0) {
+ *CreateNew = TRUE;
+ }
+
+ //
+ // Process SA_DATA.
+ //
+ if ((Mask & MODE) != 0) {
+ OldData->Mode = NewData->Mode;
+ }
+
+ if ((Mask & SEQUENCE_NUMBER) != 0) {
+ OldData->SNCount = NewData->SNCount;
+ }
+
+ if ((Mask & ANTIREPLAY_WINDOW) != 0) {
+ OldData->AntiReplayWindows = NewData->AntiReplayWindows;
+ }
+
+ if ((Mask & AUTH_ALGO) != 0) {
+ OldData->AlgoInfo.EspAlgoInfo.AuthAlgoId = NewData->AlgoInfo.EspAlgoInfo.AuthAlgoId;
+ }
+
+ if ((Mask & AUTH_KEY) != 0) {
+ OldData->AlgoInfo.EspAlgoInfo.AuthKey = NewData->AlgoInfo.EspAlgoInfo.AuthKey;
+ OldData->AlgoInfo.EspAlgoInfo.AuthKeyLength = NewData->AlgoInfo.EspAlgoInfo.AuthKeyLength;
+ }
+
+ if ((Mask & ENCRYPT_ALGO) != 0) {
+ OldData->AlgoInfo.EspAlgoInfo.EncAlgoId = NewData->AlgoInfo.EspAlgoInfo.EncAlgoId;
+ }
+
+ if ((Mask & ENCRYPT_KEY) != 0) {
+ OldData->AlgoInfo.EspAlgoInfo.EncKey = NewData->AlgoInfo.EspAlgoInfo.EncKey;
+ OldData->AlgoInfo.EspAlgoInfo.EncKeyLength = NewData->AlgoInfo.EspAlgoInfo.EncKeyLength;
+ }
+
+ if (NewSaId->Proto == EfiIPsecAH) {
+ if ((Mask & (ENCRYPT_ALGO | ENCRYPT_KEY)) != 0) {
+ //
+ // Should not provide encrypt_* if AH.
+ //
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_UNWANTED_PARAMETER),
+ mHiiHandle,
+ mAppName,
+ L"--encrypt-algo --encrypt-key"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (NewSaId->Proto == EfiIPsecESP && OldSaId->Proto == EfiIPsecAH) {
+ //
+ // AH -> ESP
+ // Should provide encrypt_algo at least.
+ //
+ if ((Mask & ENCRYPT_ALGO) == 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
+ mHiiHandle,
+ mAppName,
+ L"--encrypt-algo"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Encrypt_key should be provided if algorithm is not NONE.
+ //
+ if (NewData->AlgoInfo.EspAlgoInfo.EncAlgoId != EFI_IPSEC_EALG_NONE && (Mask & ENCRYPT_KEY) == 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER),
+ mHiiHandle,
+ mAppName,
+ L"--encrypt-algo"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if ((Mask & LIFEBYTE) != 0) {
+ OldData->SaLifetime.ByteCount = NewData->SaLifetime.ByteCount;
+ }
+
+ if ((Mask & LIFETIME_SOFT) != 0) {
+ OldData->SaLifetime.SoftLifetime = NewData->SaLifetime.SoftLifetime;
+ }
+
+ if ((Mask & LIFETIME) != 0) {
+ OldData->SaLifetime.HardLifetime = NewData->SaLifetime.HardLifetime;
+ }
+
+ if ((Mask & PATH_MTU) != 0) {
+ OldData->PathMTU = NewData->PathMTU;
+ }
+ //
+ // Process SpdSelector.
+ //
+ if (OldData->SpdSelector == NULL) {
+ if ((Mask & (LOCAL | REMOTE | PROTO | LOCAL_PORT | REMOTE_PORT | ICMP_TYPE | ICMP_CODE)) != 0) {
+ if ((Mask & (LOCAL | REMOTE | PROTO)) != (LOCAL | REMOTE | PROTO)) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS),
+ mHiiHandle,
+ mAppName,
+ L"--local --remote --proto"
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldData->SpdSelector = NewData->SpdSelector;
+ }
+ } else {
+ if ((Mask & LOCAL) != 0) {
+ OldData->SpdSelector->LocalAddressCount = NewData->SpdSelector->LocalAddressCount;
+ OldData->SpdSelector->LocalAddress = NewData->SpdSelector->LocalAddress;
+ }
+
+ if ((Mask & REMOTE) != 0) {
+ OldData->SpdSelector->RemoteAddressCount = NewData->SpdSelector->RemoteAddressCount;
+ OldData->SpdSelector->RemoteAddress = NewData->SpdSelector->RemoteAddress;
+ }
+
+ if ((Mask & PROTO) != 0) {
+ OldData->SpdSelector->NextLayerProtocol = NewData->SpdSelector->NextLayerProtocol;
+ }
+
+ if (OldData->SpdSelector != NULL) {
+ switch (OldData->SpdSelector->NextLayerProtocol) {
+ case EFI_IP4_PROTO_TCP:
+ case EFI_IP4_PROTO_UDP:
+ if ((Mask & LOCAL_PORT) != 0) {
+ OldData->SpdSelector->LocalPort = NewData->SpdSelector->LocalPort;
+ }
+
+ if ((Mask & REMOTE_PORT) != 0) {
+ OldData->SpdSelector->RemotePort = NewData->SpdSelector->RemotePort;
+ }
+ break;
+
+ case EFI_IP4_PROTO_ICMP:
+ if ((Mask & ICMP_TYPE) != 0) {
+ OldData->SpdSelector->LocalPort = (UINT8) NewData->SpdSelector->LocalPort;
+ }
+
+ if ((Mask & ICMP_CODE) != 0) {
+ OldData->SpdSelector->RemotePort = (UINT8) NewData->SpdSelector->RemotePort;
+ }
+ break;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Combine old PAD entry with new PAD entry.
+
+ @param[in, out] OldPadId The pointer to the EFI_IPSEC_PAD_ID structure.
+ @param[in, out] OldData The pointer to the EFI_IPSEC_PAD_DATA structure.
+ @param[in] NewPadId The pointer to the EFI_IPSEC_PAD_ID structure.
+ @param[in] NewData The pointer to the EFI_IPSEC_PAD_DATA structure.
+ @param[in] Mask The pointer to the Mask.
+ @param[out] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Combined successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+EFI_STATUS
+CombinePadEntry (
+ IN OUT EFI_IPSEC_PAD_ID *OldPadId,
+ IN OUT EFI_IPSEC_PAD_DATA *OldData,
+ IN EFI_IPSEC_PAD_ID *NewPadId,
+ IN EFI_IPSEC_PAD_DATA *NewData,
+ IN UINT32 Mask,
+ OUT BOOLEAN *CreateNew
+ )
+{
+
+ *CreateNew = FALSE;
+
+ if ((Mask & (PEER_ID | PEER_ADDRESS)) == 0) {
+ CopyMem (NewPadId, OldPadId, sizeof (EFI_IPSEC_PAD_ID));
+ } else {
+ if ((Mask & PEER_ID) != 0) {
+ if (OldPadId->PeerIdValid) {
+ if (StrCmp ((CONST CHAR16 *) OldPadId->Id.PeerId, (CONST CHAR16 *) NewPadId->Id.PeerId) != 0) {
+ *CreateNew = TRUE;
+ }
+ } else {
+ *CreateNew = TRUE;
+ }
+ } else {
+ //
+ // MASK & PEER_ADDRESS
+ //
+ if (OldPadId->PeerIdValid) {
+ *CreateNew = TRUE;
+ } else {
+ if ((CompareMem (&OldPadId->Id.IpAddress.Address, &NewPadId->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) ||
+ (OldPadId->Id.IpAddress.PrefixLength != NewPadId->Id.IpAddress.PrefixLength)) {
+ *CreateNew = TRUE;
+ }
+ }
+ }
+ }
+
+ if ((Mask & AUTH_PROTO) != 0) {
+ OldData->AuthProtocol = NewData->AuthProtocol;
+ }
+
+ if ((Mask & AUTH_METHOD) != 0) {
+ OldData->AuthMethod = NewData->AuthMethod;
+ }
+
+ if ((Mask & IKE_ID) != 0) {
+ OldData->IkeIdFlag = NewData->IkeIdFlag;
+ }
+
+ if ((Mask & AUTH_DATA) != 0) {
+ OldData->AuthDataSize = NewData->AuthDataSize;
+ OldData->AuthData = NewData->AuthData;
+ }
+
+ if ((Mask & REVOCATION_DATA) != 0) {
+ OldData->RevocationDataSize = NewData->RevocationDataSize;
+ OldData->RevocationData = NewData->RevocationData;
+ }
+
+ return EFI_SUCCESS;
+}
+
+COMBINE_POLICY_ENTRY mCombinePolicyEntry[] = {
+ (COMBINE_POLICY_ENTRY) CombineSpdEntry,
+ (COMBINE_POLICY_ENTRY) CombineSadEntry,
+ (COMBINE_POLICY_ENTRY) CombinePadEntry
+};
+
+/**
+ Edit entry information in the database.
+
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.
+ @param[in] Data The pointer to the data.
+ @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.
+
+ @retval EFI_SUCCESS Continue the iteration.
+ @retval EFI_ABORTED Abort the iteration.
+**/
+EFI_STATUS
+EditOperatePolicyEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN EDIT_POLICY_ENTRY_CONTEXT *Context
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN CreateNew;
+
+ if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {
+ ASSERT (Context->DataType < 3);
+
+ Status = mCombinePolicyEntry[Context->DataType] (
+ Selector,
+ Data,
+ Context->Selector,
+ Context->Data,
+ Context->Mask,
+ &CreateNew
+ );
+ if (!EFI_ERROR (Status)) {
+ if (CreateNew) {
+ //
+ // Insert new entry before old entry
+ //
+ Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ Context->DataType,
+ Context->Selector,
+ Data,
+ Selector
+ );
+ ASSERT_EFI_ERROR (Status);
+ //
+ // Delete old entry
+ //
+ Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ Context->DataType,
+ Selector,
+ NULL,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ } else {
+ Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ Context->DataType,
+ Context->Selector,
+ Data,
+ NULL
+ );
+ }
+ }
+
+ Context->Status = Status;
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Edit entry information in database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Edit entry information successfully.
+ @retval EFI_NOT_FOUND Can't find the specified entry.
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+EditPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ )
+{
+ EFI_STATUS Status;
+ EDIT_POLICY_ENTRY_CONTEXT Context;
+ CONST CHAR16 *ValueStr;
+
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e");
+ if (ValueStr == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);
+ return EFI_NOT_FOUND;
+ }
+
+ Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);
+ if (!EFI_ERROR (Status)) {
+ Context.DataType = DataType;
+ Context.Status = EFI_NOT_FOUND;
+ Status = mCreatePolicyEntry[DataType] (&Context.Selector, &Context.Data, ParamPackage, &Context.Mask, FALSE);
+ if (!EFI_ERROR (Status)) {
+ ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) EditOperatePolicyEntry, &Context);
+ Status = Context.Status;
+ }
+
+ if (Context.Selector != NULL) {
+ gBS->FreePool (Context.Selector);
+ }
+
+ if (Context.Data != NULL) {
+ gBS->FreePool (Context.Data);
+ }
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);
+ } else if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_EDIT_FAILED), mHiiHandle, mAppName);
+ }
+
+ return Status;
+
+}
+
+/**
+ Insert entry information in database.
+
+ @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure.
+ @param[in] Data The pointer to the data.
+ @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure.
+
+ @retval EFI_SUCCESS Continue the iteration.
+ @retval EFI_ABORTED Abort the iteration.
+**/
+EFI_STATUS
+InsertPolicyEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN INSERT_POLICY_ENTRY_CONTEXT *Context
+ )
+{
+ //
+ // Found the entry which we want to insert before.
+ //
+ if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) {
+
+ Context->Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ Context->DataType,
+ Context->Selector,
+ Context->Data,
+ Selector
+ );
+ //
+ // Abort the iteration after the insertion.
+ //
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Insert or add entry information in database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Insert or add entry information successfully.
+ @retval EFI_NOT_FOUND Can't find the specified entry.
+ @retval EFI_BUFFER_TOO_SMALL The entry already existed.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+AddOrInsertPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ )
+{
+ EFI_STATUS Status;
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;
+ VOID *Data;
+ INSERT_POLICY_ENTRY_CONTEXT Context;
+ UINT32 Mask;
+ UINTN DataSize;
+ CONST CHAR16 *ValueStr;
+
+ Status = mCreatePolicyEntry[DataType] (&Selector, &Data, ParamPackage, &Mask, TRUE);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Find if the Selector to be inserted already exists.
+ //
+ DataSize = 0;
+ Status = mIpSecConfig->GetData (
+ mIpSecConfig,
+ DataType,
+ Selector,
+ &DataSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_EXISTS), mHiiHandle, mAppName);
+ } else if (ShellCommandLineGetFlag (ParamPackage, L"-a")) {
+ Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ DataType,
+ Selector,
+ Data,
+ NULL
+ );
+ } else {
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i");
+ if (ValueStr == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr);
+ return EFI_NOT_FOUND;
+ }
+
+ Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage);
+ if (!EFI_ERROR (Status)) {
+ Context.DataType = DataType;
+ Context.Status = EFI_NOT_FOUND;
+ Context.Selector = Selector;
+ Context.Data = Data;
+
+ ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) InsertPolicyEntry, &Context);
+ Status = Context.Status;
+ if (Status == EFI_NOT_FOUND) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr);
+ }
+ }
+ }
+
+ gBS->FreePool (Selector);
+ gBS->FreePool (Data);
+ }
+
+ if (Status == EFI_UNSUPPORTED) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_UNSUPPORT), mHiiHandle, mAppName);
+ } else if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_FAILED), mHiiHandle, mAppName);
+ }
+
+ return Status;
+}
diff --git a/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h
new file mode 100644
index 0000000000..5161bacccb
--- /dev/null
+++ b/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h
@@ -0,0 +1,158 @@
+/** @file
+ The function declaration of policy entry operation in IpSecConfig application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _POLICY_ENTRY_OPERATION_H_
+#define _POLICY_ENTRY_OPERATION_H_
+
+#define LOCAL BIT(0)
+#define REMOTE BIT(1)
+#define PROTO BIT(2)
+#define LOCAL_PORT BIT(3)
+#define REMOTE_PORT BIT(4)
+#define ICMP_TYPE BIT(5)
+#define ICMP_CODE BIT(6)
+#define NAME BIT(7)
+#define PACKET_FLAG BIT(8)
+#define ACTION BIT(9)
+#define EXT_SEQUENCE BIT(10)
+#define SEQUENCE_OVERFLOW BIT(11)
+#define FRAGMENT_CHECK BIT(12)
+#define LIFEBYTE BIT(13)
+#define LIFETIME_SOFT BIT(14)
+#define LIFETIME BIT(15)
+#define MODE BIT(16)
+#define TUNNEL_LOCAL BIT(17)
+#define TUNNEL_REMOTE BIT(18)
+#define DONT_FRAGMENT BIT(19)
+#define IPSEC_PROTO BIT(20)
+#define AUTH_ALGO BIT(21)
+#define ENCRYPT_ALGO BIT(22)
+#define SPI BIT(23)
+#define DEST BIT(24)
+#define SEQUENCE_NUMBER BIT(25)
+#define ANTIREPLAY_WINDOW BIT(26)
+#define AUTH_KEY BIT(27)
+#define ENCRYPT_KEY BIT(28)
+#define PATH_MTU BIT(29)
+
+#define PEER_ID BIT(0)
+#define PEER_ADDRESS BIT(1)
+#define AUTH_PROTO BIT(2)
+#define AUTH_METHOD BIT(3)
+#define IKE_ID BIT(4)
+#define AUTH_DATA BIT(5)
+#define REVOCATION_DATA BIT(6)
+
+typedef struct {
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;
+ EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted.
+ VOID *Data;
+ UINT32 Mask;
+ POLICY_ENTRY_INDEXER Indexer;
+ EFI_STATUS Status; // Indicate whether insertion succeeds.
+} EDIT_POLICY_ENTRY_CONTEXT;
+
+typedef struct {
+ EFI_IPSEC_CONFIG_DATA_TYPE DataType;
+ EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted.
+ VOID *Data;
+ POLICY_ENTRY_INDEXER Indexer;
+ EFI_STATUS Status; // Indicate whether insertion succeeds.
+} INSERT_POLICY_ENTRY_CONTEXT;
+
+/**
+ The prototype for the CreateSpdEntry()/CreateSadEntry()/CreatePadEntry().
+ Fill in EFI_IPSEC_CONFIG_SELECTOR and corresponding data thru ParamPackage list.
+
+ @param[out] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union.
+ @param[out] Data The pointer to corresponding data.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+ @param[out] Mask The pointer to the Mask.
+ @param[in] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Filled in EFI_IPSEC_CONFIG_SELECTOR and corresponding data successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+typedef
+EFI_STATUS
+(*CREATE_POLICY_ENTRY) (
+ OUT EFI_IPSEC_CONFIG_SELECTOR **Selector,
+ OUT VOID **Data,
+ IN LIST_ENTRY *ParamPackage,
+ OUT UINT32 *Mask,
+ IN BOOLEAN CreateNew
+ );
+
+/**
+ The prototype for the CombineSpdEntry()/CombineSadEntry()/CombinePadEntry().
+ Combine old SPD/SAD/PAD entry with new SPD/SAD/PAD entry.
+
+ @param[in, out] OldSelector The pointer to the old EFI_IPSEC_CONFIG_SELECTOR union.
+ @param[in, out] OldData The pointer to the corresponding old data.
+ @param[in] NewSelector The pointer to the new EFI_IPSEC_CONFIG_SELECTOR union.
+ @param[in] NewData The pointer to the corresponding new data.
+ @param[in] Mask The pointer to the Mask.
+ @param[out] CreateNew The switch to create new.
+
+ @retval EFI_SUCCESS Combined successfully.
+ @retval EFI_INVALID_PARAMETER Invalid user input parameter.
+
+**/
+typedef
+EFI_STATUS
+(* COMBINE_POLICY_ENTRY) (
+ EFI_IPSEC_CONFIG_SELECTOR *OldSelector,
+ VOID *OldData,
+ EFI_IPSEC_CONFIG_SELECTOR *NewSelector,
+ VOID *NewData,
+ UINT32 Mask,
+ BOOLEAN *CreateNew
+ );
+
+/**
+ Insert or add entry information in database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Insert or add entry information successfully.
+ @retval EFI_NOT_FOUND Can't find the specified entry.
+ @retval EFI_BUFFER_TOO_SMALL The entry already existed.
+ @retval EFI_UNSUPPORTED The operation is not supported./
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+AddOrInsertPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ );
+
+/**
+ Edit entry information in the database according to datatype.
+
+ @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE.
+ @param[in] ParamPackage The pointer to the ParamPackage list.
+
+ @retval EFI_SUCCESS Edit entry information successfully.
+ @retval EFI_NOT_FOUND Can't find the specified entry.
+ @retval Others Some mistaken case.
+**/
+EFI_STATUS
+EditPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN LIST_ENTRY *ParamPackage
+ );
+#endif
diff --git a/NetworkPkg/Application/Ping6/Ia32/Tsc.c b/NetworkPkg/Application/Ping6/Ia32/Tsc.c
new file mode 100644
index 0000000000..e2eae99077
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ia32/Tsc.c
@@ -0,0 +1,28 @@
+/** @file
+ The implement to read TSC in IA32 platform.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/BaseLib.h>
+
+/**
+ Reads and returns the current value of the Time Stamp Counter (TSC).
+
+ @return The current value of TSC.
+
+**/
+UINT64
+ReadTime ()
+{
+ return AsmReadTsc ();
+}
diff --git a/NetworkPkg/Application/Ping6/Ipf/Itc.c b/NetworkPkg/Application/Ping6/Ipf/Itc.c
new file mode 100644
index 0000000000..131e5c0e30
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ipf/Itc.c
@@ -0,0 +1,28 @@
+/** @file
+ The implement to read ITC in IA64 platform.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/BaseLib.h>
+
+/**
+ Reads and returns the current value of the Interval Timer Counter Register (ITC).
+
+ @return The current value of ITC.
+
+**/
+UINT64
+ReadTime ()
+{
+ return AsmReadItc ();
+}
diff --git a/NetworkPkg/Application/Ping6/Ping6.c b/NetworkPkg/Application/Ping6/Ping6.c
new file mode 100644
index 0000000000..b783c5a027
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6.c
@@ -0,0 +1,1179 @@
+/** @file
+ The implementation for Ping6 application.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/ShellLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/HiiLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/Cpu.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+
+#include "Ping6.h"
+
+SHELL_PARAM_ITEM Ping6ParamList[] = {
+ {
+ L"-l",
+ TypeValue
+ },
+ {
+ L"-n",
+ TypeValue
+ },
+ {
+ L"-s",
+ TypeValue
+ },
+ {
+ L"-?",
+ TypeFlag
+ },
+ {
+ NULL,
+ TypeMax
+ },
+};
+
+//
+// Global Variables in Ping6 application.
+//
+EFI_HII_HANDLE mHiiHandle;
+CONST CHAR16 *mIp6DstString;
+CONST CHAR16 *mIp6SrcString;
+EFI_GUID mEfiPing6Guid = EFI_PING6_GUID;
+UINT32 mFrequency = 0;
+/**
+ Get and caculate the frequency in tick/ms.
+ The result is saved in the globle variable mFrequency
+
+ @retval EFI_SUCCESS Caculated the frequency successfully.
+ @retval Others Failed to caculate the frequency.
+
+**/
+EFI_STATUS
+Ping6GetFrequency (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_CPU_ARCH_PROTOCOL *Cpu;
+ UINT64 CurrentTick;
+ UINT32 TimerPeriod;
+
+ Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, (UINT64 *) &TimerPeriod);
+
+ if (EFI_ERROR (Status)) {
+ //
+ // For NT32 Simulator only. 358049 is a similar value to keep timer granularity.
+ // Set the timer period by ourselves.
+ //
+ TimerPeriod = NTTIMERPERIOD;
+ }
+ //
+ // The timer period is in femtosecond (1 femtosecond is 1e-15 second).
+ // So 1e+12 is divided by timer period to produce the freq in tick/ms.
+ //
+ mFrequency = (UINT32) DivU64x32 (1000000000000ULL, TimerPeriod);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get and caculate the duration in ms.
+
+ @param[in] Begin The start point of time.
+ @param[in] End The end point of time.
+
+ @return The duration in ms.
+
+**/
+UINT32
+Ping6CalculateTick (
+ IN UINT64 Begin,
+ IN UINT64 End
+ )
+{
+ ASSERT (End > Begin);
+ return (UINT32) DivU64x32 (End - Begin, mFrequency);
+}
+
+/**
+ Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
+
+ @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
+
+**/
+VOID
+Ping6DestroyTxInfo (
+ IN PING6_ICMP6_TX_INFO *TxInfo
+ )
+{
+ EFI_IP6_TRANSMIT_DATA *TxData;
+ EFI_IP6_FRAGMENT_DATA *FragData;
+ UINTN Index;
+
+ ASSERT (TxInfo != NULL);
+
+ if (TxInfo->Token != NULL) {
+
+ if (TxInfo->Token->Event != NULL) {
+ gBS->CloseEvent (TxInfo->Token->Event);
+ }
+
+ TxData = TxInfo->Token->Packet.TxData;
+ if (TxData != NULL) {
+
+ if (TxData->OverrideData != NULL) {
+ FreePool (TxData->OverrideData);
+ }
+
+ if (TxData->ExtHdrs != NULL) {
+ FreePool (TxData->ExtHdrs);
+ }
+
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+ FragData = TxData->FragmentTable[Index].FragmentBuffer;
+ if (FragData != NULL) {
+ FreePool (FragData);
+ }
+ }
+ }
+
+ FreePool (TxInfo->Token);
+ }
+
+ FreePool (TxInfo);
+}
+
+/**
+ Match the request, and reply with SequenceNum/TimeStamp.
+
+ @param[in] Private The pointer to PING6_PRIVATE_DATA.
+ @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.
+
+ @retval EFI_SUCCESS The match is successful.
+ @retval EFI_NOT_FOUND The reply can't be matched with any request.
+
+**/
+EFI_STATUS
+Ping6MatchEchoReply (
+ IN PING6_PRIVATE_DATA *Private,
+ IN ICMP6_ECHO_REQUEST_REPLY *Packet
+ )
+{
+ PING6_ICMP6_TX_INFO *TxInfo;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
+ TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
+
+ if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
+ Private->RxCount++;
+ RemoveEntryList (&TxInfo->Link);
+ Ping6DestroyTxInfo (TxInfo);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ The original intention is to send a request.
+ Currently, the application retransmits an icmp6 echo request packet
+ per second in sendnumber times that is specified by the user.
+ Because nothing can be done here, all things move to the timer rountine.
+
+ @param[in] Event A EFI_EVENT type event.
+ @param[in] Context The pointer to Context.
+
+**/
+VOID
+EFIAPI
+Ping6OnEchoRequestSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+}
+
+/**
+ receive reply, match and print reply infomation.
+
+ @param[in] Event A EFI_EVENT type event.
+ @param[in] Context The pointer to context.
+
+**/
+VOID
+EFIAPI
+Ping6OnEchoReplyReceived (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ PING6_PRIVATE_DATA *Private;
+ EFI_IP6_COMPLETION_TOKEN *RxToken;
+ EFI_IP6_RECEIVE_DATA *RxData;
+ ICMP6_ECHO_REQUEST_REPLY *Reply;
+ UINT32 PayLoad;
+ UINT32 Rtt;
+ CHAR8 Near;
+
+ Private = (PING6_PRIVATE_DATA *) Context;
+
+ if (Private->Status == EFI_ABORTED) {
+ return;
+ }
+
+ RxToken = &Private->RxToken;
+ RxData = RxToken->Packet.RxData;
+ Reply = RxData->FragmentTable[0].FragmentBuffer;
+ PayLoad = RxData->DataLength;
+
+ if (RxData->Header->NextHeader != IP6_ICMP) {
+ goto ON_EXIT;
+ }
+
+ if (!IP6_IS_MULTICAST (&Private->DstAddress) &&
+ !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {
+ goto ON_EXIT;
+ }
+
+ if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
+ goto ON_EXIT;
+ }
+
+ if (PayLoad != Private->BufferSize) {
+ goto ON_EXIT;
+ }
+ //
+ // Check whether the reply matches the sent request before.
+ //
+ Status = Ping6MatchEchoReply (Private, Reply);
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Display statistics on this icmp6 echo reply packet.
+ //
+ Rtt = Ping6CalculateTick (Reply->TimeStamp, ReadTime ());
+ if (Rtt != 0) {
+ Near = (CHAR8) '=';
+ } else {
+ Near = (CHAR8) '<';
+ }
+
+ Private->RttSum += Rtt;
+ Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin;
+ Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax;
+
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_PING6_REPLY_INFO),
+ mHiiHandle,
+ PayLoad,
+ mIp6DstString,
+ Reply->SequenceNum,
+ RxData->Header->HopLimit,
+ Near,
+ Rtt
+ );
+
+ON_EXIT:
+
+ if (Private->RxCount < Private->SendNum) {
+ //
+ // Continue to receive icmp6 echo reply packets.
+ //
+ RxToken->Status = EFI_ABORTED;
+
+ Status = Private->Ip6->Receive (Private->Ip6, RxToken);
+
+ if (EFI_ERROR (Status)) {
+ Private->Status = EFI_ABORTED;
+ }
+ } else {
+ //
+ // All reply have already been received from the dest host.
+ //
+ Private->Status = EFI_SUCCESS;
+ }
+ //
+ // Singal to recycle the each rxdata here, not at the end of process.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+}
+
+/**
+ Initial EFI_IP6_COMPLETION_TOKEN.
+
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.
+ @param[in] TimeStamp The TimeStamp of request.
+ @param[in] SequenceNum The SequenceNum of request.
+
+ @return The pointer of EFI_IP6_COMPLETION_TOKEN.
+
+**/
+EFI_IP6_COMPLETION_TOKEN *
+Ping6GenerateToken (
+ IN PING6_PRIVATE_DATA *Private,
+ IN UINT64 TimeStamp,
+ IN UINT16 SequenceNum
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP6_COMPLETION_TOKEN *Token;
+ EFI_IP6_TRANSMIT_DATA *TxData;
+ ICMP6_ECHO_REQUEST_REPLY *Request;
+
+ Request = AllocateZeroPool (Private->BufferSize);
+
+ if (Request == NULL) {
+ return NULL;
+ }
+ //
+ // Assembly icmp6 echo request packet.
+ //
+ Request->Type = ICMP_V6_ECHO_REQUEST;
+ Request->Code = 0;
+ Request->SequenceNum = SequenceNum;
+ Request->TimeStamp = TimeStamp;
+ Request->Identifier = 0;
+ //
+ // Leave check sum to ip6 layer, since it has no idea of source address
+ // selection.
+ //
+ Request->Checksum = 0;
+
+ TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));
+
+ if (TxData == NULL) {
+ FreePool (Request);
+ return NULL;
+ }
+ //
+ // Assembly ipv6 token for transmit.
+ //
+ TxData->OverrideData = 0;
+ TxData->ExtHdrsLength = 0;
+ TxData->ExtHdrs = NULL;
+ TxData->DataLength = Private->BufferSize;
+ TxData->FragmentCount = 1;
+ TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;
+ TxData->FragmentTable[0].FragmentLength = Private->BufferSize;
+
+ Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));
+
+ if (Token == NULL) {
+ FreePool (Request);
+ FreePool (TxData);
+ return NULL;
+ }
+
+ Token->Status = EFI_ABORTED;
+ Token->Packet.TxData = TxData;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ping6OnEchoRequestSent,
+ Private,
+ &Token->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Request);
+ FreePool (TxData);
+ FreePool (Token);
+ return NULL;
+ }
+
+ return Token;
+}
+
+/**
+ Transmit the EFI_IP6_COMPLETION_TOKEN.
+
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS Transmitted successfully.
+ @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
+ @retval others Transmitted unsuccessfully.
+
+**/
+EFI_STATUS
+Ping6SendEchoRequest (
+ IN PING6_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ PING6_ICMP6_TX_INFO *TxInfo;
+
+ TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));
+
+ if (TxInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TxInfo->TimeStamp = ReadTime ();
+ TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
+
+ TxInfo->Token = Ping6GenerateToken (
+ Private,
+ TxInfo->TimeStamp,
+ TxInfo->SequenceNum
+ );
+
+ if (TxInfo->Token == NULL) {
+ Ping6DestroyTxInfo (TxInfo);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);
+
+ if (EFI_ERROR (Status)) {
+ Ping6DestroyTxInfo (TxInfo);
+ return Status;
+ }
+
+ InsertTailList (&Private->TxList, &TxInfo->Link);
+ Private->TxCount++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Place a completion token into the receive packet queue to receive the echo reply.
+
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
+ @retval others Put the token into the receive packet queue unsuccessfully.
+
+**/
+EFI_STATUS
+Ping6ReceiveEchoReply (
+ IN PING6_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ping6OnEchoReplyReceived,
+ Private,
+ &Private->RxToken.Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Private->RxToken.Status = EFI_NOT_READY;
+
+ return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);
+}
+
+/**
+ Remove the timeout request from the list.
+
+ @param[in] Event A EFI_EVENT type event.
+ @param[in] Context The pointer to Context.
+
+**/
+VOID
+EFIAPI
+Ping6OnTimerRoutine (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ PING6_PRIVATE_DATA *Private;
+ PING6_ICMP6_TX_INFO *TxInfo;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ UINT32 Time;
+
+ Private = (PING6_PRIVATE_DATA *) Context;
+
+ //
+ // Retransmit icmp6 echo request packets per second in sendnumber times.
+ //
+ if (Private->TxCount < Private->SendNum) {
+
+ Status = Ping6SendEchoRequest (Private);
+ if (Private->TxCount != 0){
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), mHiiHandle, Private->TxCount + 1);
+ }
+ }
+ }
+ //
+ // Check whether any icmp6 echo request in the list timeout.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
+ TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
+ Time = Ping6CalculateTick (TxInfo->TimeStamp, ReadTime ());
+
+ //
+ // Remove the timeout echo request from txlist.
+ //
+ if (Time > PING6_DEFAULT_TIMEOUT) {
+
+ if (EFI_ERROR (TxInfo->Token->Status)) {
+ Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
+ }
+ //
+ // Remove the timeout icmp6 echo request from list.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), mHiiHandle, TxInfo->SequenceNum);
+
+ RemoveEntryList (&TxInfo->Link);
+ Ping6DestroyTxInfo (TxInfo);
+
+ if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
+ //
+ // All the left icmp6 echo request in the list timeout.
+ //
+ Private->Status = EFI_TIMEOUT;
+ }
+ }
+ }
+}
+
+/**
+ Create a valid IP6 instance.
+
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS Create a valid IP6 instance successfully.
+ @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully.
+ @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address.
+ @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
+ @retval EFI_NOT_FOUND The source address is not found.
+**/
+EFI_STATUS
+Ping6CreateIp6Instance (
+ IN PING6_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleIndex;
+ UINTN HandleNum;
+ EFI_HANDLE *HandleBuffer;
+ EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
+ EFI_IP6_CONFIG_DATA Ip6Config;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+ UINTN IfInfoSize;
+ EFI_IPv6_ADDRESS *Addr;
+ UINTN AddrIndex;
+
+ HandleBuffer = NULL;
+ Ip6Sb = NULL;
+ IfInfo = NULL;
+ IfInfoSize = 0;
+
+ //
+ // Locate all the handles with ip6 service binding protocol.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ NULL,
+ &HandleNum,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status) || (HandleNum == 0)) {
+ return EFI_ABORTED;
+ }
+ //
+ // Source address is required when pinging a link-local address on multi-
+ // interfaces host.
+ //
+ if (NetIp6IsLinkLocalAddr (&Private->DstAddress) &&
+ NetIp6IsUnspecifiedAddr (&Private->SrcAddress) &&
+ (HandleNum > 1)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), mHiiHandle);
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_ERROR;
+ }
+ //
+ // For each ip6 protocol, check interface addresses list.
+ //
+ for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
+
+ Ip6Sb = NULL;
+ IfInfo = NULL;
+ IfInfoSize = 0;
+
+ Status = gBS->HandleProtocol (
+ HandleBuffer[HandleIndex],
+ &gEfiIp6ServiceBindingProtocolGuid,
+ (VOID **) &Ip6Sb
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {
+ //
+ // No need to match interface address.
+ //
+ break;
+ } else {
+ //
+ // Ip6config protocol and ip6 service binding protocol are installed
+ // on the same handle.
+ //
+ Status = gBS->HandleProtocol (
+ HandleBuffer[HandleIndex],
+ &gEfiIp6ConfigProtocolGuid,
+ (VOID **) &Ip6Cfg
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ //
+ // Get the interface information size.
+ //
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &IfInfoSize,
+ NULL
+ );
+
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+
+ IfInfo = AllocateZeroPool (IfInfoSize);
+
+ if (IfInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ //
+ // Get the interface info.
+ //
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &IfInfoSize,
+ IfInfo
+ );
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+ //
+ // Check whether the source address is one of the interface addresses.
+ //
+ for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {
+
+ Addr = &(IfInfo->AddressInfo[AddrIndex].Address);
+ if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
+ //
+ // Match a certain interface address.
+ //
+ break;
+ }
+ }
+
+ if (AddrIndex < IfInfo->AddressInfoCount) {
+ //
+ // Found a nic handle with right interface address.
+ //
+ break;
+ }
+ }
+
+ FreePool (IfInfo);
+ IfInfo = NULL;
+ }
+ //
+ // No exact interface address matched.
+ //
+
+ if (HandleIndex == HandleNum) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), mHiiHandle, mIp6SrcString);
+ Status = EFI_NOT_FOUND;
+ goto ON_ERROR;
+ }
+
+ Private->NicHandle = HandleBuffer[HandleIndex];
+
+ ASSERT (Ip6Sb != NULL);
+ Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Ip6ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ (VOID **) &Private->Ip6,
+ Private->ImageHandle,
+ Private->Ip6ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
+
+ //
+ // Configure the ip6 instance for icmp6 packet exchange.
+ //
+ Ip6Config.DefaultProtocol = 58;
+ Ip6Config.AcceptAnyProtocol = FALSE;
+ Ip6Config.AcceptIcmpErrors = TRUE;
+ Ip6Config.AcceptPromiscuous = FALSE;
+ Ip6Config.TrafficClass = 0;
+ Ip6Config.HopLimit = 128;
+ Ip6Config.FlowLabel = 0;
+ Ip6Config.ReceiveTimeout = 0;
+ Ip6Config.TransmitTimeout = 0;
+
+ IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
+
+ IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
+
+ Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);
+
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), mHiiHandle, Status);
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (HandleBuffer != NULL) {
+ FreePool (HandleBuffer);
+ }
+
+ if (IfInfo != NULL) {
+ FreePool (IfInfo);
+ }
+
+ if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {
+ Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
+ }
+
+ return Status;
+}
+
+/**
+ Destory the IP6 instance.
+
+ @param[in] Private The pointer of PING6_PRIVATE_DATA.
+
+**/
+VOID
+Ping6DestoryIp6Instance (
+ IN PING6_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
+
+ gBS->CloseProtocol (
+ Private->Ip6ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ Private->ImageHandle,
+ Private->Ip6ChildHandle
+ );
+
+ Status = gBS->HandleProtocol (
+ Private->NicHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ (VOID **) &Ip6Sb
+ );
+
+ if (!EFI_ERROR(Status)) {
+ Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
+ }
+}
+
+/**
+ The Ping6 Process.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SendNumber The send request count.
+ @param[in] BufferSize The send buffer size.
+ @param[in] SrcAddress The source IPv6 address.
+ @param[in] DstAddress The destination IPv6 address.
+
+ @retval EFI_SUCCESS The ping6 processed successfullly.
+ @retval others The ping6 processed unsuccessfully.
+
+**/
+EFI_STATUS
+Ping6 (
+ IN EFI_HANDLE ImageHandle,
+ IN UINT32 SendNumber,
+ IN UINT32 BufferSize,
+ IN EFI_IPv6_ADDRESS *SrcAddress,
+ IN EFI_IPv6_ADDRESS *DstAddress
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ PING6_PRIVATE_DATA *Private;
+ PING6_ICMP6_TX_INFO *TxInfo;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+
+ Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));
+
+ ASSERT (Private != NULL);
+
+ Private->ImageHandle = ImageHandle;
+ Private->SendNum = SendNumber;
+ Private->BufferSize = BufferSize;
+ Private->RttMin = 0xFFFF;
+ Private->Status = EFI_NOT_READY;
+
+ InitializeListHead (&Private->TxList);
+
+ IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);
+ IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);
+
+ //
+ // Open and configure a ip6 instance for ping6.
+ //
+ Status = Ping6CreateIp6Instance (Private);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Print the command line itself.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), mHiiHandle, mIp6DstString, Private->BufferSize);
+ //
+ // Create a ipv6 token to receive the first icmp6 echo reply packet.
+ //
+ Status = Ping6ReceiveEchoReply (Private);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Create and start timer to send icmp6 echo request packet per second.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ping6OnTimerRoutine,
+ Private,
+ &Private->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Create a ipv6 token to send the first icmp6 echo request packet.
+ //
+ Status = Ping6SendEchoRequest (Private);
+ //
+ // EFI_NOT_READY for IPsec is enable and IKE is not established.
+ //
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
+ if(Status == EFI_NOT_FOUND) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), mHiiHandle, mIp6DstString);
+ }
+
+ goto ON_EXIT;
+ }
+
+ Status = gBS->SetTimer (
+ Private->Timer,
+ TimerPeriodic,
+ PING6_ONE_SECOND
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Control the ping6 process by two factors:
+ // 1. Hot key
+ // 2. Private->Status
+ // 2.1. success means all icmp6 echo request packets get reply packets.
+ // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
+ // 2.3. noready means ping6 process is on-the-go.
+ //
+ while (Private->Status == EFI_NOT_READY) {
+ Private->Ip6->Poll (Private->Ip6);
+
+ //
+ // Terminate the ping6 process by 'esc' or 'ctl-c'.
+ //
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+
+ if (!EFI_ERROR(Status)) {
+ if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||
+ ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {
+ goto ON_STAT;
+ }
+ }
+ }
+
+ON_STAT:
+ //
+ // Display the statistics in all.
+ //
+ gBS->SetTimer (Private->Timer, TimerCancel, 0);
+
+ if (Private->TxCount != 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_PING6_STAT),
+ mHiiHandle,
+ Private->TxCount,
+ Private->RxCount,
+ (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
+ Private->RttSum
+ );
+ }
+
+ if (Private->RxCount != 0) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_PING6_RTT),
+ mHiiHandle,
+ Private->RttMin,
+ Private->RttMax,
+ Private->RttSum / Private->RxCount
+ );
+ }
+
+ON_EXIT:
+
+ if (Private != NULL) {
+ Private->Status = EFI_ABORTED;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
+ TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
+
+ Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
+
+ RemoveEntryList (&TxInfo->Link);
+ Ping6DestroyTxInfo (TxInfo);
+ }
+
+ if (Private->Timer != NULL) {
+ gBS->CloseEvent (Private->Timer);
+ }
+
+ if (Private->Ip6 != NULL) {
+ Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);
+ }
+
+ if (Private->RxToken.Event != NULL) {
+ gBS->CloseEvent (Private->RxToken.Event);
+ }
+
+ if (Private->Ip6ChildHandle != NULL) {
+ Ping6DestoryIp6Instance (Private);
+ }
+
+ FreePool (Private);
+ }
+
+ return Status;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including
+ both device drivers and bus drivers.
+
+ The entry point for the Ping6 application that parses the command line input and calls the Ping6 process.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETETR Input parameters combination is invalid.
+ @retval Others Some errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializePing6 (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS DstAddress;
+ EFI_IPv6_ADDRESS SrcAddress;
+ UINT64 BufferSize;
+ UINTN SendNumber;
+ LIST_ENTRY *ParamPackage;
+ CONST CHAR16 *ValueStr;
+ CONST CHAR16 *ValueStrPtr;
+ UINTN NonOptionCount;
+
+ //
+ // Register our string package with HII and return the handle to it.
+ //
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, Ping6Strings, NULL);
+ ASSERT (mHiiHandle != NULL);
+
+ Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, NULL, TRUE, FALSE);
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);
+ goto ON_EXIT;
+ }
+
+ if (ShellCommandLineGetFlag (ParamPackage, L"-?")) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_HELP), mHiiHandle);
+ goto ON_EXIT;
+ }
+
+ SendNumber = 10;
+ BufferSize = 16;
+
+ //
+ // Parse the paramter of count number.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
+ ValueStrPtr = ValueStr;
+ if (ValueStr != NULL) {
+ SendNumber = ShellStrToUintn (ValueStrPtr);
+
+ //
+ // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
+ //
+ if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), mHiiHandle, ValueStr);
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Parse the paramter of buffer size.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
+ ValueStrPtr = ValueStr;
+ if (ValueStr != NULL) {
+ BufferSize = ShellStrToUintn (ValueStrPtr);
+
+ //
+ // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
+ //
+ if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), mHiiHandle, ValueStr);
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+
+ ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
+ ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Parse the paramter of source ip address.
+ //
+ ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
+ ValueStrPtr = ValueStr;
+ if (ValueStr != NULL) {
+ mIp6SrcString = ValueStr;
+ Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Parse the paramter of destination ip address.
+ //
+ NonOptionCount = ShellCommandLineGetCount();
+ ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));
+ if (NonOptionCount != 2) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle);
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ ValueStrPtr = ValueStr;
+ if (ValueStr != NULL) {
+ mIp6DstString = ValueStr;
+ Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr);
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Get frequency to calculate the time from ticks.
+ //
+ Status = Ping6GetFrequency ();
+
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Enter into ping6 process.
+ //
+ Status = Ping6 (
+ ImageHandle,
+ (UINT32)SendNumber,
+ (UINT32)BufferSize,
+ &SrcAddress,
+ &DstAddress
+ );
+
+ON_EXIT:
+ ShellCommandLineFreeVarList (ParamPackage);
+ HiiRemovePackages (mHiiHandle);
+ return Status;
+}
diff --git a/NetworkPkg/Application/Ping6/Ping6.h b/NetworkPkg/Application/Ping6/Ping6.h
new file mode 100644
index 0000000000..02271e1b58
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6.h
@@ -0,0 +1,92 @@
+/** @file
+ The interface function declaration of shell application Ping6 (Ping for v6 series).
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _PING6_H_
+#define _PING6_H_
+
+#define EFI_PING6_GUID \
+ { \
+ 0x3f0b2478, 0x3619, 0x46c5, {0x81, 0x50, 0xa5, 0xab, 0xdd, 0xb6, 0x6b, 0xd9} \
+ }
+
+#define PING6_DEFAULT_TIMEOUT 5000
+#define PING6_MAX_SEND_NUMBER 10000
+#define PING6_MAX_BUFFER_SIZE 32768
+#define PING6_ONE_SECOND 10000000
+
+//
+// A similar amount of time that passes in femtoseconds
+// for each increment of TimerValue. It is for NT32 only.
+//
+#define NTTIMERPERIOD 358049
+
+#pragma pack(1)
+
+typedef struct _ICMP6_ECHO_REQUEST_REPLY {
+ UINT8 Type;
+ UINT8 Code;
+ UINT16 Checksum;
+ UINT16 Identifier;
+ UINT16 SequenceNum;
+ UINT64 TimeStamp;
+ UINT8 Data[1];
+} ICMP6_ECHO_REQUEST_REPLY;
+
+#pragma pack()
+
+typedef struct _PING6_ICMP6_TX_INFO {
+ LIST_ENTRY Link;
+ UINT16 SequenceNum;
+ UINT64 TimeStamp;
+ EFI_IP6_COMPLETION_TOKEN *Token;
+} PING6_ICMP6_TX_INFO;
+
+typedef struct _PING6_PRIVATE_DATA {
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE NicHandle;
+ EFI_HANDLE Ip6ChildHandle;
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_EVENT Timer;
+
+ EFI_STATUS Status;
+ LIST_ENTRY TxList;
+ EFI_IP6_COMPLETION_TOKEN RxToken;
+ UINT16 RxCount;
+ UINT16 TxCount;
+ UINT32 RttSum;
+ UINT32 RttMin;
+ UINT32 RttMax;
+ UINT32 SequenceNum;
+
+ EFI_IPv6_ADDRESS SrcAddress;
+ EFI_IPv6_ADDRESS DstAddress;
+ UINT32 SendNum;
+ UINT32 BufferSize;
+} PING6_PRIVATE_DATA;
+
+/**
+ Reads and returns the current value of register.
+ In IA64, the register is the Interval Timer Vector (ITV).
+ In X86(IA32/X64), the register is the Time Stamp Counter (TSC)
+
+ @return The current value of the register.
+
+**/
+UINT64
+ReadTime (
+ VOID
+ );
+
+#endif
diff --git a/NetworkPkg/Application/Ping6/Ping6.inf b/NetworkPkg/Application/Ping6/Ping6.inf
new file mode 100644
index 0000000000..56b2163ead
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6.inf
@@ -0,0 +1,64 @@
+## @file
+# Component description file for Ping6 application.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = Ping6
+ FILE_GUID = F35F733F-5235-4d7b-83FA-97780CEBCB20
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = InitializePing6
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF
+#
+
+[Sources]
+ Ping6.c
+ Ping6Strings.uni
+ Ping6.h
+
+[Sources.IA32]
+ Ia32/Tsc.c
+
+[Sources.X64]
+ X64/Tsc.c
+
+[Sources.IPF]
+ Ipf/Itc.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ UefiBootServicesTableLib
+ UefiApplicationEntryPoint
+ BaseMemoryLib
+ ShellLib
+ MemoryAllocationLib
+ DebugLib
+ HiiLib
+ NetLib
+
+[Protocols]
+ gEfiCpuArchProtocolGuid ## CONSUMS
+ gEfiIp6ProtocolGuid ## CONSUMS
+ gEfiIp6ServiceBindingProtocolGuid ## CONSUMS
+ gEfiIp6ConfigProtocolGuid ## CONSUMS
diff --git a/NetworkPkg/Application/Ping6/Ping6Strings.uni b/NetworkPkg/Application/Ping6/Ping6Strings.uni
new file mode 100644
index 0000000000..c8b919c4ce
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/Ping6Strings.uni
Binary files differ
diff --git a/NetworkPkg/Application/Ping6/X64/Tsc.c b/NetworkPkg/Application/Ping6/X64/Tsc.c
new file mode 100644
index 0000000000..b3e7bdbb96
--- /dev/null
+++ b/NetworkPkg/Application/Ping6/X64/Tsc.c
@@ -0,0 +1,28 @@
+/** @file
+ The implement to read TSC in X64 platform.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/BaseLib.h>
+
+/**
+ Reads and returns the current value of Time Stamp Counter (TSC).
+
+ @return The current value of TSC
+
+**/
+UINT64
+ReadTime ()
+{
+ return AsmReadTsc ();
+}
diff --git a/NetworkPkg/Application/VConfig/VConfig.c b/NetworkPkg/Application/VConfig/VConfig.c
new file mode 100644
index 0000000000..c948131ffc
--- /dev/null
+++ b/NetworkPkg/Application/VConfig/VConfig.c
@@ -0,0 +1,668 @@
+/** @file
+ Shell application for VLAN configuration.
+
+ Copyright (C) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/VlanConfig.h>
+
+#include <Library/UefiApplicationEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/ShellLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/NetLib.h>
+
+#define INVALID_NIC_INDEX 0xffff
+#define INVALID_VLAN_ID 0xffff
+
+//
+// This is the generated String package data for all .UNI files.
+// This data array is ready to be used as input of HiiAddPackages() to
+// create a packagelist (which contains Form packages, String packages, etc).
+//
+extern UINT8 VConfigStrings[];
+
+EFI_HANDLE mImageHandle = NULL;
+EFI_HII_HANDLE mHiiHandle = NULL;
+
+SHELL_PARAM_ITEM mParamList[] = {
+ {
+ L"-l",
+ TypeValue
+ },
+ {
+ L"-a",
+ TypeMaxValue
+ },
+ {
+ L"-d",
+ TypeValue
+ },
+ {
+ NULL,
+ TypeMax
+ }
+};
+
+/**
+ Locate the network interface handle buffer.
+
+ @param[out] NumberOfHandles Pointer to the number of handles.
+ @param[out] HandleBuffer Pointer to the buffer to store the returned handles.
+
+**/
+VOID
+LocateNicHandleBuffer (
+ OUT UINTN *NumberOfHandles,
+ OUT EFI_HANDLE **HandleBuffer
+ )
+{
+ EFI_STATUS Status;
+
+ *NumberOfHandles = 0;
+ *HandleBuffer = NULL;
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiVlanConfigProtocolGuid,
+ NULL,
+ NumberOfHandles,
+ HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_LOCATE_FAIL), mHiiHandle, Status);
+ }
+}
+
+/**
+ Extract the decimal index from the network interface name.
+
+ @param[in] Name Name of the network interface.
+
+ @retval INVALID_NIC_INDEX Failed to extract the network interface index.
+ @return others The network interface index.
+
+**/
+UINTN
+NicNameToIndex (
+ IN CHAR16 *Name
+ )
+{
+ CHAR16 *Str;
+
+ Str = Name + 3;
+ if ((StrnCmp (Name, L"eth", 3) != 0) || (*Str == 0)) {
+ return INVALID_NIC_INDEX;
+ }
+
+ while (*Str != 0) {
+ if ((*Str < L'0') || (*Str > L'9')) {
+ return INVALID_NIC_INDEX;
+ }
+
+ Str++;
+ }
+
+ return (UINT16) StrDecimalToUintn (Name + 3);
+}
+
+/**
+ Find network interface device handle by its name.
+
+ @param[in] Name Name of the network interface.
+
+ @retval NULL Cannot find the network interface.
+ @return others Handle of the network interface.
+
+**/
+EFI_HANDLE
+NicNameToHandle (
+ IN CHAR16 *Name
+ )
+{
+ UINTN NumberOfHandles;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+ EFI_HANDLE Handle;
+
+ //
+ // Find all NIC handles.
+ //
+ LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);
+ if (NumberOfHandles == 0) {
+ return NULL;
+ }
+
+ Index = NicNameToIndex (Name);
+ if (Index >= NumberOfHandles) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_IF), mHiiHandle, Name);
+ Handle = NULL;
+ } else {
+ Handle = HandleBuffer[Index];
+ }
+
+ FreePool (HandleBuffer);
+ return Handle;
+}
+
+/**
+ Open VlanConfig protocol from a handle.
+
+ @param[in] Handle The handle to open the VlanConfig protocol.
+
+ @return The VlanConfig protocol interface.
+
+**/
+EFI_VLAN_CONFIG_PROTOCOL *
+OpenVlanConfigProtocol (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+
+ VlanConfig = NULL;
+ gBS->OpenProtocol (
+ Handle,
+ &gEfiVlanConfigProtocolGuid,
+ (VOID **) &VlanConfig,
+ mImageHandle,
+ Handle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ return VlanConfig;
+}
+
+/**
+ Close VlanConfig protocol of a handle.
+
+ @param[in] Handle The handle to close the VlanConfig protocol.
+
+**/
+VOID
+CloseVlanConfigProtocol (
+ IN EFI_HANDLE Handle
+ )
+{
+ gBS->CloseProtocol (
+ Handle,
+ &gEfiVlanConfigProtocolGuid,
+ mImageHandle,
+ Handle
+ );
+}
+
+/**
+ Display VLAN configuration of a network interface.
+
+ @param[in] Handle Handle of the network interface.
+ @param[in] NicIndex Index of the network interface.
+
+**/
+VOID
+ShowNicVlanInfo (
+ IN EFI_HANDLE Handle,
+ IN UINTN NicIndex
+ )
+{
+ CHAR16 *MacStr;
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ UINT16 NumberOfVlan;
+ EFI_VLAN_FIND_DATA *VlanData;
+
+ VlanConfig = OpenVlanConfigProtocol (Handle);
+ if (VlanConfig == NULL) {
+ return ;
+ }
+
+ MacStr = NULL;
+ Status = NetLibGetMacString (Handle, mImageHandle, &MacStr);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_MAC_FAIL), mHiiHandle, Status);
+ goto Exit;
+ }
+
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_ETH_MAC), mHiiHandle, NicIndex, MacStr);
+
+ Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VLAN), mHiiHandle);
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_FIND_FAIL), mHiiHandle, Status);
+ }
+
+ goto Exit;
+ }
+
+ for (Index = 0; Index < NumberOfVlan; Index++) {
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_VCONFIG_VLAN_DISPLAY),
+ mHiiHandle,
+ VlanData[Index].VlanId,
+ VlanData[Index].Priority
+ );
+ }
+
+ FreePool (VlanData);
+
+Exit:
+ CloseVlanConfigProtocol (Handle);
+
+ if (MacStr != NULL) {
+ FreePool (MacStr);
+ }
+}
+
+/**
+ Display the VLAN configuration of all, or a specified network interface.
+
+ @param[in] Name Name of the network interface. If NULL, the VLAN
+ configuration of all network will be displayed.
+
+**/
+VOID
+DisplayVlan (
+ IN CHAR16 *Name OPTIONAL
+ )
+{
+ UINTN NumberOfHandles;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+ EFI_HANDLE NicHandle;
+
+ if (Name != NULL) {
+ //
+ // Display specified NIC
+ //
+ NicHandle = NicNameToHandle (Name);
+ if (NicHandle == NULL) {
+ return ;
+ }
+
+ ShowNicVlanInfo (NicHandle, 0);
+ return ;
+ }
+
+ //
+ // Find all NIC handles
+ //
+ LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer);
+ if (NumberOfHandles == 0) {
+ return ;
+ }
+
+ for (Index = 0; Index < NumberOfHandles; Index++) {
+ ShowNicVlanInfo (HandleBuffer[Index], Index);
+ }
+
+ FreePool (HandleBuffer);
+}
+
+/**
+ Convert a NULL-terminated unicode decimal VLAN ID string to VLAN ID.
+
+ @param[in] String Pointer to VLAN ID string from user input.
+
+ @retval Value translated from String, or INVALID_VLAN_ID is string is invalid.
+
+**/
+UINT16
+StrToVlanId (
+ IN CHAR16 *String
+ )
+{
+ CHAR16 *Str;
+
+ if (String == NULL) {
+ return INVALID_VLAN_ID;
+ }
+
+ Str = String;
+ while ((*Str >= '0') && (*Str <= '9')) {
+ Str++;
+ }
+
+ if (*Str != 0) {
+ return INVALID_VLAN_ID;
+ }
+
+ return (UINT16) StrDecimalToUintn (String);
+}
+
+/**
+ Add a VLAN device.
+
+ @param[in] ParamStr Parameter string from user input.
+
+**/
+VOID
+AddVlan (
+ IN CHAR16 *ParamStr
+ )
+{
+ CHAR16 *Name;
+ CHAR16 *VlanIdStr;
+ CHAR16 *PriorityStr;
+ CHAR16 *StrPtr;
+ BOOLEAN IsSpace;
+ UINTN VlanId;
+ UINTN Priority;
+ EFI_HANDLE Handle;
+ EFI_HANDLE VlanHandle;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ EFI_STATUS Status;
+
+ VlanConfig = NULL;
+ Priority = 0;
+
+ if (ParamStr == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);
+ return ;
+ }
+
+ StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);
+ if (StrPtr == NULL) {
+ return ;
+ }
+
+ Name = StrPtr;
+ VlanIdStr = NULL;
+ PriorityStr = NULL;
+ IsSpace = FALSE;
+ while (*StrPtr != 0) {
+ if (*StrPtr == L' ') {
+ *StrPtr = 0;
+ IsSpace = TRUE;
+ } else {
+ if (IsSpace) {
+ //
+ // Start of a parameter.
+ //
+ if (VlanIdStr == NULL) {
+ //
+ // 2nd parameter is VLAN ID.
+ //
+ VlanIdStr = StrPtr;
+ } else if (PriorityStr == NULL) {
+ //
+ // 3rd parameter is Priority.
+ //
+ PriorityStr = StrPtr;
+ } else {
+ //
+ // Ignore else parameters.
+ //
+ break;
+ }
+ }
+
+ IsSpace = FALSE;
+ }
+
+ StrPtr++;
+ }
+
+ Handle = NicNameToHandle (Name);
+ if (Handle == NULL) {
+ goto Exit;
+ }
+
+ VlanConfig = OpenVlanConfigProtocol (Handle);
+ if (VlanConfig == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Check VLAN ID.
+ //
+ if ((VlanIdStr == NULL) || (*VlanIdStr == 0)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);
+ goto Exit;
+ }
+
+ VlanId = StrToVlanId (VlanIdStr);
+ if (VlanId > 4094) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);
+ goto Exit;
+ }
+
+ //
+ // Check Priority.
+ //
+ if ((PriorityStr != NULL) && (*PriorityStr != 0)) {
+ Priority = StrDecimalToUintn (PriorityStr);
+ if (Priority > 7) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_PRIORITY), mHiiHandle, PriorityStr);
+ goto Exit;
+ }
+ }
+
+ //
+ // Set VLAN
+ //
+ Status = VlanConfig->Set (VlanConfig, (UINT16) VlanId, (UINT8) Priority);
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_FAIL), mHiiHandle, Status);
+ goto Exit;
+ }
+
+ //
+ // Connect the VLAN device.
+ //
+ VlanHandle = NetLibGetVlanHandle (Handle, (UINT16) VlanId);
+ if (VlanHandle != NULL) {
+ gBS->ConnectController (VlanHandle, NULL, NULL, TRUE);
+ }
+
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_SUCCESS), mHiiHandle);
+
+Exit:
+ if (VlanConfig != NULL) {
+ CloseVlanConfigProtocol (Handle);
+ }
+
+ FreePool (Name);
+}
+
+/**
+ Remove a VLAN device.
+
+ @param[in] ParamStr Parameter string from user input.
+
+**/
+VOID
+DeleteVlan (
+ CHAR16 *ParamStr
+ )
+{
+ CHAR16 *Name;
+ CHAR16 *VlanIdStr;
+ CHAR16 *StrPtr;
+ UINTN VlanId;
+ EFI_HANDLE Handle;
+ EFI_VLAN_CONFIG_PROTOCOL *VlanConfig;
+ EFI_STATUS Status;
+ UINT16 NumberOfVlan;
+ EFI_VLAN_FIND_DATA *VlanData;
+
+ VlanConfig = NULL;
+
+ if (ParamStr == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle);
+ return ;
+ }
+
+ StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr);
+ if (StrPtr == NULL) {
+ return ;
+ }
+
+ Name = StrPtr;
+ VlanIdStr = NULL;
+ while (*StrPtr != 0) {
+ if (*StrPtr == L'.') {
+ *StrPtr = 0;
+ VlanIdStr = StrPtr + 1;
+ break;
+ }
+
+ StrPtr++;
+ }
+
+ Handle = NicNameToHandle (Name);
+ if (Handle == NULL) {
+ goto Exit;
+ }
+
+ VlanConfig = OpenVlanConfigProtocol (Handle);
+ if (VlanConfig == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Check VLAN ID
+ //
+ if (VlanIdStr == NULL || *VlanIdStr == 0) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle);
+ goto Exit;
+ }
+
+ VlanId = StrToVlanId (VlanIdStr);
+ if (VlanId > 4094) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr);
+ goto Exit;
+ }
+
+ //
+ // Delete VLAN.
+ //
+ Status = VlanConfig->Remove (VlanConfig, (UINT16) VlanId);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NOT_FOUND), mHiiHandle);
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_FAIL), mHiiHandle, Status);
+ }
+
+ goto Exit;
+ }
+
+ //
+ // Check whether this is the last VLAN to remove.
+ //
+ Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData);
+ if (EFI_ERROR (Status)) {
+ //
+ // This is the last VLAN to remove, try to connect the controller handle.
+ //
+ gBS->ConnectController (Handle, NULL, NULL, TRUE);
+ } else {
+ FreePool (VlanData);
+ }
+
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_SUCCESS), mHiiHandle);
+
+Exit:
+ if (VlanConfig != NULL) {
+ CloseVlanConfigProtocol (Handle);
+ }
+
+ FreePool (Name);
+}
+
+/**
+ The actual entry point for the application.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point executed successfully.
+ @retval other Some error occur when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+VlanConfigMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ LIST_ENTRY *List;
+ CONST CHAR16 *Str;
+
+ mImageHandle = ImageHandle;
+
+ //
+ // Register our string package to HII database.
+ //
+ mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, VConfigStrings, NULL);
+ if (mHiiHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ List = NULL;
+ ShellCommandLineParseEx (mParamList, &List, NULL, FALSE, FALSE);
+ if (List == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);
+ goto Exit;
+ }
+
+ if (ShellCommandLineGetFlag (List, L"-?")) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_HELP), mHiiHandle);
+ goto Exit;
+ }
+
+ if (ShellCommandLineGetFlag (List, L"-l")) {
+ Str = ShellCommandLineGetValue (List, L"-l");
+ DisplayVlan ((CHAR16 *) Str);
+ goto Exit;
+ }
+
+ if (ShellCommandLineGetFlag (List, L"-a")) {
+ Str = ShellCommandLineGetValue (List, L"-a");
+ AddVlan ((CHAR16 *) Str);
+ goto Exit;
+ }
+
+ if (ShellCommandLineGetFlag (List, L"-d")) {
+ Str = ShellCommandLineGetValue (List, L"-d");
+ DeleteVlan ((CHAR16 *) Str);
+ goto Exit;
+ }
+
+ //
+ // No valid argument till now.
+ //
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle);
+
+Exit:
+ if (List != NULL) {
+ ShellCommandLineFreeVarList (List);
+ }
+
+ //
+ // Remove our string package from HII database.
+ //
+ HiiRemovePackages (mHiiHandle);
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/Application/VConfig/VConfig.inf b/NetworkPkg/Application/VConfig/VConfig.inf
new file mode 100644
index 0000000000..e69da02f7b
--- /dev/null
+++ b/NetworkPkg/Application/VConfig/VConfig.inf
@@ -0,0 +1,47 @@
+## @file
+# Component files for VLAN configuration shell application.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VConfig
+ FILE_GUID = 87E36301-0406-44db-AAF3-9E0E591F3725
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = VlanConfigMain
+
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF
+#
+
+[Sources]
+ VConfigStrings.uni
+ VConfig.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ UefiApplicationEntryPoint
+ UefiBootServicesTableLib
+ UefiLib
+ ShellLib
+ NetLib
+ MemoryAllocationLib
+ HiiLib
+
+[Protocols]
+ gEfiVlanConfigProtocolGuid
diff --git a/NetworkPkg/Application/VConfig/VConfigStrings.uni b/NetworkPkg/Application/VConfig/VConfigStrings.uni
new file mode 100644
index 0000000000..e8bdcefee8
--- /dev/null
+++ b/NetworkPkg/Application/VConfig/VConfigStrings.uni
Binary files differ
diff --git a/NetworkPkg/Dhcp6Dxe/ComponentName.c b/NetworkPkg/Dhcp6Dxe/ComponentName.c
new file mode 100644
index 0000000000..314ca37d16
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/ComponentName.c
@@ -0,0 +1,312 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Dhcp6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that attempt to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that attempts to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language, from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName = {
+ Dhcp6ComponentNameGetDriverName,
+ Dhcp6ComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Dhcp6ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Dhcp6ComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDhcp6DriverNameTable[] = {
+ {
+ "eng;en",
+ L"DHCP6 Protocol Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDhcp6DriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gDhcp6ComponentName)
+ );
+}
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in the
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language, from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c
new file mode 100644
index 0000000000..d14f169f07
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c
@@ -0,0 +1,782 @@
+/** @file
+ Driver Binding functions and Service Binding functions
+ implementationfor for Dhcp6 Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = {
+ Dhcp6DriverBindingSupported,
+ Dhcp6DriverBindingStart,
+ Dhcp6DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = {
+ Dhcp6ServiceBindingCreateChild,
+ Dhcp6ServiceBindingDestroyChild
+};
+
+
+/**
+ Configure the default Udp6Io to receive all the DHCP6 traffic
+ on this network interface.
+
+ @param[in] UdpIo The pointer to Udp6Io to be configured.
+ @param[in] Context The pointer to the context.
+
+ @retval EFI_SUCCESS The Udp6Io is successfully configured.
+ @retval Others Failed to configure the Udp6Io.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ConfigureUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA *Config;
+
+ Udp6 = UdpIo->Protocol.Udp6;
+ Config = &(UdpIo->Config.Udp6);
+
+ ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ //
+ // Set Udp6 configure data for the Dhcp6 instance.
+ //
+ Config->AcceptPromiscuous = FALSE;
+ Config->AcceptAnyPort = FALSE;
+ Config->AllowDuplicatePort = FALSE;
+ Config->TrafficClass = 0;
+ Config->HopLimit = 128;
+ Config->ReceiveTimeout = 0;
+ Config->TransmitTimeout = 0;
+
+ //
+ // Configure an endpoint of client(0, 546), server(0, 0), the addresses
+ // will be overridden later. Note that we MUST not limit RemotePort.
+ // More details, refer to RFC 3315 section 5.2.
+ //
+ Config->StationPort = DHCP6_PORT_CLIENT;
+ Config->RemotePort = 0;
+
+ return Udp6->Configure (Udp6, Config);;
+}
+
+
+/**
+ Destory the Dhcp6 service. The Dhcp6 service may be partly initialized,
+ or partly destroyed. If a resource is destroyed, it is marked as such in
+ case the destroy failed and being called again later.
+
+ @param[in, out] Service The pointer to Dhcp6 service to be destroyed.
+
+**/
+VOID
+Dhcp6DestroyService (
+ IN OUT DHCP6_SERVICE *Service
+ )
+{
+ //
+ // All children instances should have been already destoryed here.
+ //
+ ASSERT (Service->NumOfChild == 0);
+
+ if (Service->ClientId != NULL) {
+ FreePool (Service->ClientId);
+ }
+
+ if (Service->UdpIo != NULL) {
+ UdpIoFreeIo (Service->UdpIo);
+ }
+
+ FreePool (Service);
+}
+
+
+/**
+ Create a new Dhcp6 service for the Nic controller.
+
+ @param[in] Controller The controller to be installed DHCP6 service
+ binding protocol.
+ @param[in] ImageHandle The image handle of the Dhcp6 driver.
+ @param[out] Service The return pointer of the new Dhcp6 service.
+
+ @retval EFI_SUCCESS The Dhcp6 service is created successfully.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+
+**/
+EFI_STATUS
+Dhcp6CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT DHCP6_SERVICE **Service
+ )
+{
+ DHCP6_SERVICE *Dhcp6Srv;
+
+ *Service = NULL;
+ Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE));
+
+ if (Dhcp6Srv == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Open the SNP protocol to get mode data later.
+ //
+ Dhcp6Srv->Snp = NULL;
+ NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp);
+ if (Dhcp6Srv->Snp == NULL) {
+ FreePool (Dhcp6Srv);
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Initialize the fields of the new Dhcp6 service.
+ //
+ Dhcp6Srv->Signature = DHCP6_SERVICE_SIGNATURE;
+ Dhcp6Srv->InDestory = FALSE;
+ Dhcp6Srv->Controller = Controller;
+ Dhcp6Srv->Image = ImageHandle;
+ Dhcp6Srv->Xid = (0xffffff & NET_RANDOM (NetRandomInitSeed ()));
+
+ CopyMem (
+ &Dhcp6Srv->ServiceBinding,
+ &gDhcp6ServiceBindingTemplate,
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)
+ );
+
+ //
+ // Generate client Duid in the format of Duid-llt.
+ //
+ Dhcp6Srv->ClientId = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode);
+
+ if (Dhcp6Srv->ClientId == NULL) {
+ FreePool (Dhcp6Srv);
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance.
+ //
+ Dhcp6Srv->UdpIo = UdpIoCreateIo (
+ Controller,
+ ImageHandle,
+ Dhcp6ConfigureUdpIo,
+ UDP_IO_UDP6_VERSION,
+ NULL
+ );
+
+ if (Dhcp6Srv->UdpIo == NULL) {
+ FreePool (Dhcp6Srv->ClientId);
+ FreePool (Dhcp6Srv);
+ return EFI_DEVICE_ERROR;
+ }
+
+ InitializeListHead (&Dhcp6Srv->Child);
+
+ *Service = Dhcp6Srv;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Destroy the Dhcp6 instance and recycle the resources.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+
+**/
+VOID
+Dhcp6DestroyInstance (
+ IN OUT DHCP6_INSTANCE *Instance
+ )
+{
+ //
+ // Clean up the retry list first.
+ //
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);
+ gBS->CloseEvent (Instance->Timer);
+
+ //
+ // Clean up the current configure data.
+ //
+ if (Instance->Config != NULL) {
+ Dhcp6CleanupConfigData (Instance->Config);
+ FreePool (Instance->Config);
+ }
+
+ //
+ // Clean up the current Ia.
+ //
+ if (Instance->IaCb.Ia != NULL) {
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+ FreePool (Instance->IaCb.Ia);
+ }
+
+ if (Instance->Unicast != NULL) {
+ FreePool (Instance->Unicast);
+ }
+
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ FreePool (Instance);
+}
+
+
+/**
+ Create the Dhcp6 instance and initialize it.
+
+ @param[in] Service The pointer to the Dhcp6 service.
+ @param[out] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS The Dhcp6 instance is created.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+
+**/
+EFI_STATUS
+Dhcp6CreateInstance (
+ IN DHCP6_SERVICE *Service,
+ OUT DHCP6_INSTANCE **Instance
+ )
+{
+ EFI_STATUS Status;
+ DHCP6_INSTANCE *Dhcp6Ins;
+
+ *Instance = NULL;
+ Dhcp6Ins = AllocateZeroPool (sizeof (DHCP6_INSTANCE));
+
+ if (Dhcp6Ins == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the fields of the new Dhcp6 instance.
+ //
+ Dhcp6Ins->Signature = DHCP6_INSTANCE_SIGNATURE;
+ Dhcp6Ins->UdpSts = EFI_ALREADY_STARTED;
+ Dhcp6Ins->Service = Service;
+ Dhcp6Ins->InDestory = FALSE;
+ Dhcp6Ins->MediaPresent = TRUE;
+
+ CopyMem (
+ &Dhcp6Ins->Dhcp6,
+ &gDhcp6ProtocolTemplate,
+ sizeof (EFI_DHCP6_PROTOCOL)
+ );
+
+ InitializeListHead (&Dhcp6Ins->TxList);
+ InitializeListHead (&Dhcp6Ins->InfList);
+
+ //
+ // There is a timer for each Dhcp6 instance, which is used to track the
+ // lease time of Ia and the retransmisson time of all sent packets.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Dhcp6OnTimerTick,
+ Dhcp6Ins,
+ &Dhcp6Ins->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Dhcp6Ins);
+ return Status;
+ }
+
+ *Instance = Dhcp6Ins;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Entry point of the DHCP6 driver to install various protocols.
+
+ @param[in] ImageHandle The handle of the UEFI image file.
+ @param[in] SystemTable The pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval Others Unexpected error occurs.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDhcp6DriverBinding,
+ ImageHandle,
+ &gDhcp6ComponentName,
+ &gDhcp6ComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be tested.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ return gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be started.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver is installed to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ DHCP6_SERVICE *Service;
+
+ //
+ // Check the Dhcp6 serivce whether already started.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Create and initialize the Dhcp6 service.
+ //
+ Status = Dhcp6CreateService (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &Service
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (Service != NULL);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ &Service->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ Dhcp6DestroyService (Service);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval other This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+
+ //
+ // Find and check the Nic handle by the controller handle.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (Service->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (NumberOfChildren == 0) {
+ //
+ // Destory the service itself if no child instance left.
+ //
+ Service->InDestory = TRUE;
+
+ Status = gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ if (EFI_ERROR (Status)) {
+ Service->InDestory = FALSE;
+ goto ON_EXIT;
+ }
+
+ Dhcp6DestroyService (Service);
+
+ } else {
+ //
+ // Destory all the children instances before destory the service.
+ //
+ while (!IsListEmpty (&Service->Child)) {
+ Instance = NET_LIST_HEAD (&Service->Child, DHCP6_INSTANCE, Link);
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+ }
+ //
+ // Any of child failed to be destroyed.
+ //
+ if (Service->NumOfChild != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing
+ UEFI handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+ VOID *Udp6;
+
+ if (This == NULL || ChildHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Service = DHCP6_SERVICE_FROM_THIS (This);
+
+ Status = Dhcp6CreateInstance (Service, &Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (Instance != NULL);
+
+ //
+ // Start the timer when the instance is ready to use.
+ //
+ Status = gBS->SetTimer (
+ Instance->Timer,
+ TimerPeriodic,
+ TICKS_PER_SECOND
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the DHCP6 protocol onto ChildHandle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiDhcp6ProtocolGuid,
+ &Instance->Dhcp6,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the UDP6 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Service->UdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ (VOID **) &Udp6,
+ gDhcp6DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiDhcp6ProtocolGuid,
+ &Instance->Dhcp6,
+ NULL
+ );
+ goto ON_ERROR;
+ }
+
+ //
+ // Add into the children list of its parent service.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&Service->Child, &Instance->Link);
+ Service->NumOfChild++;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ Dhcp6DestroyInstance (Instance);
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+
+ if (This == NULL || ChildHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiDhcp6ProtocolGuid,
+ (VOID **) &Dhcp6,
+ gDhcp6DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6);
+ Service = DHCP6_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != Service) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Instance->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance->InDestory = TRUE;
+
+ Status = gBS->CloseProtocol (
+ Service->UdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ gDhcp6DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestory = FALSE;
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Uninstall the MTFTP6 protocol first to enable a top down destruction.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiDhcp6ProtocolGuid,
+ Dhcp6
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestory = FALSE;
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Remove it from the children list of its parent service.
+ //
+ RemoveEntryList (&Instance->Link);
+ Service->NumOfChild--;
+
+ Dhcp6DestroyInstance (Instance);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h
new file mode 100644
index 0000000000..5a88be4d3b
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h
@@ -0,0 +1,155 @@
+/** @file
+ Driver Binding functions and Service Binding functions
+ declaration for Dhcp6 Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_DRIVER_H__
+#define __EFI_DHCP6_DRIVER_H__
+
+#include <Protocol/ServiceBinding.h>
+
+extern EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2;
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported(), it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start(), it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of
+ children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
new file mode 100644
index 0000000000..f10b07ac3c
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
@@ -0,0 +1,69 @@
+## @file
+# Component description file for Dhcp6 module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Dhcp6Dxe
+ FILE_GUID = 95E3669D-34BE-4775-A651-7EA41B69D89E
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Dhcp6DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gDhcp6DriverBinding
+# COMPONENT_NAME = gDhcp6ComponentName
+# COMPONENT_NAME2 = gDhcp6ComponentName2
+#
+
+[Sources]
+ Dhcp6Driver.c
+ Dhcp6Driver.h
+ Dhcp6Impl.c
+ Dhcp6Impl.h
+ Dhcp6Io.c
+ Dhcp6Io.h
+ Dhcp6Utility.c
+ Dhcp6Utility.h
+ ComponentName.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ DebugLib
+ NetLib
+ UdpIoLib
+
+
+[Protocols]
+ gEfiUdp6ServiceBindingProtocolGuid
+ gEfiUdp6ProtocolGuid
+ gEfiDhcp6ServiceBindingProtocolGuid
+ gEfiDhcp6ProtocolGuid
+
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
new file mode 100644
index 0000000000..3e73976ddb
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
@@ -0,0 +1,1220 @@
+/** @file
+ This EFI_DHCP6_PROTOCOL interface implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+//
+// Well-known multi-cast address defined in section-24.1 of rfc-3315
+//
+// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
+// ALL_DHCP_Servers address: FF05::1:3
+//
+EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
+EFI_IPv6_ADDRESS mAllDhcpServersAddress = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}};
+
+EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {
+ EfiDhcp6GetModeData,
+ EfiDhcp6Configure,
+ EfiDhcp6Start,
+ EfiDhcp6InfoRequest,
+ EfiDhcp6RenewRebind,
+ EfiDhcp6Decline,
+ EfiDhcp6Release,
+ EfiDhcp6Stop,
+ EfiDhcp6Parse
+};
+
+/**
+ Starts the DHCPv6 standard S.A.R.R. process.
+
+ The Start() function starts the DHCPv6 standard process. This function can
+ be called only when the state of Dhcp6 instance is in the Dhcp6Init state.
+ If the DHCP process completes successfully, the state of the Dhcp6 instance
+ will be transferred through Dhcp6Selecting and Dhcp6Requesting to the
+ Dhcp6Bound state.
+ Refer to rfc-3315 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this
+ opportunity to control the process.
+
+ @param[in] This The pointer to Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The DHCPv6 standard process has started, or it has
+ completed when CompletionEvent is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no
+ response was received from the server within the
+ specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCPv6 process.
+ @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6
+ standard process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Start (
+ IN EFI_DHCP6_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // The instance has already been started.
+ //
+ if (Instance->IaCb.Ia->State != Dhcp6Init) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Need to clear initial time to make sure that elapsed-time
+ // is set to 0 for first Solicit.
+ //
+ Instance->StartTime = 0;
+
+ //
+ // Send the solicit message to start S.A.R.R process.
+ //
+ Status = Dhcp6SendSolicitMsg (Instance);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchronous call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Stops the DHCPv6 standard S.A.R.R. process.
+
+ The Stop() function is used to stop the DHCPv6 standard process. After this
+ function is called successfully, the state of Dhcp6 instance is transferred
+ into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called
+ before DHCPv6 standard process can be started again. This function can be
+ called when the Dhcp6 instance is in any state.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Stop (
+ IN EFI_DHCP6_PROTOCOL *This
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ EFI_UDP6_PROTOCOL *Udp6;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+ Udp6 = Service->UdpIo->Protocol.Udp6;
+ Status = EFI_SUCCESS;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return Status;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // The instance has already been stopped.
+ //
+ if (Instance->IaCb.Ia->State == Dhcp6Init ||
+ Instance->IaCb.Ia->State == Dhcp6Selecting ||
+ Instance->IaCb.Ia->State == Dhcp6Requesting
+ ) {
+ return Status;
+ }
+
+ //
+ // Release the current ready Ia.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+ Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+ ASSERT (Udp6 != NULL);
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Udp6->Poll (Udp6);
+ }
+ Status = Instance->UdpSts;
+ }
+
+ //
+ // Clean up the session data for the released Ia.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Dhcp6CleanupSession (Instance, EFI_SUCCESS);
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Returns the current operating mode data for the Dhcp6 instance.
+
+ The GetModeData() function returns the current operating mode and
+ cached data packet for the Dhcp6 instance.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data.
+ @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance was not
+ configured.
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6GetModeData (
+ IN EFI_DHCP6_PROTOCOL *This,
+ OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL,
+ OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_DHCP6_IA *Ia;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+ UINT32 IaSize;
+ UINT32 IdSize;
+
+ if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ if (Instance->Config == NULL && Dhcp6ConfigData != NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Service->ClientId != NULL);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // User needs a copy of instance config data.
+ //
+ if (Dhcp6ConfigData != NULL) {
+ ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA));
+ //
+ // Duplicate config data, including all reference buffers.
+ //
+ if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // User need a copy of instance mode data.
+ //
+ if (Dhcp6ModeData != NULL) {
+ ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA));
+ //
+ // Duplicate a copy of EFI_DHCP6_DUID for client Id.
+ //
+ IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length);
+
+ Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize);
+ if (Dhcp6ModeData->ClientId == NULL) {
+ goto ON_ERROR;
+ }
+
+ CopyMem (
+ Dhcp6ModeData->ClientId,
+ Service->ClientId,
+ IdSize
+ );
+
+ Ia = Instance->IaCb.Ia;
+ if (Ia != NULL) {
+ //
+ // Duplicate a copy of EFI_DHCP6_IA for configured Ia.
+ //
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+
+ Dhcp6ModeData->Ia = AllocateZeroPool (IaSize);
+ if (Dhcp6ModeData->Ia == NULL) {
+ goto ON_ERROR;
+ }
+
+ CopyMem (
+ Dhcp6ModeData->Ia,
+ Ia,
+ IaSize
+ );
+
+ //
+ // Duplicate a copy of reply packet if has.
+ //
+ if (Ia->ReplyPacket != NULL) {
+ Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size);
+ if (Dhcp6ModeData->Ia->ReplyPacket == NULL) {
+ goto ON_ERROR;
+ }
+ CopyMem (
+ Dhcp6ModeData->Ia->ReplyPacket,
+ Ia->ReplyPacket,
+ Ia->ReplyPacket->Size
+ );
+ }
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Dhcp6ConfigData != NULL) {
+ Dhcp6CleanupConfigData (Dhcp6ConfigData);
+ }
+ if (Dhcp6ModeData != NULL) {
+ Dhcp6CleanupModeData (Dhcp6ModeData);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_OUT_OF_RESOURCES;
+}
+
+
+/**
+ Initializes, changes, or resets the operational settings for the Dhcp6 instance.
+
+ The Configure() function is used to initialize or clean up the configuration
+ data of the Dhcp6 instance:
+ - When Dhcp6CfgData is not NULL and Configure() is called successfully, the
+ configuration data will be initialized in the Dhcp6 instance, and the state
+ of the configured IA will be transferred into Dhcp6Init.
+ - When Dhcp6CfgData is NULL and Configure() is called successfully, the
+ configuration data will be cleaned up and no IA will be associated with
+ the Dhcp6 instance.
+ To update the configuration data for an Dhcp6 instance, the original data
+ must be cleaned up before setting the new configuration data.
+
+ @param[in] This The pointer to the Dhcp6 protocol
+ @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The Dhcp6 is configured successfully with the
+ Dhcp6Init state, or cleaned up the original
+ configuration setting.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance was already configured.
+ The Dhcp6 instance has already started the
+ DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.
+ @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Configure (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ DHCP6_INSTANCE *Other;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+ UINTN Index;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // Check the parameter of configure data.
+ //
+ if (Dhcp6CfgData != NULL) {
+ if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (Dhcp6CfgData->OptionList != NULL) {
+ for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) {
+ if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana ||
+ Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA &&
+ Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Dhcp6CfgData->SolicitRetransmission != NULL &&
+ Dhcp6CfgData->SolicitRetransmission->Mrc == 0 &&
+ Dhcp6CfgData->SolicitRetransmission->Mrd == 0
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure the (IaId, IaType) is unique over all the instances.
+ //
+ NET_LIST_FOR_EACH (Entry, &Service->Child) {
+ Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);
+ if (Other->IaCb.Ia != NULL &&
+ Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type &&
+ Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Dhcp6CfgData != NULL) {
+ //
+ // It's not allowed to configure one instance twice without configure null.
+ //
+ if (Instance->Config != NULL) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Duplicate config data including all reference buffers.
+ //
+ Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA));
+ if (Instance->Config == NULL) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData);
+ if (EFI_ERROR(Status)) {
+ FreePool (Instance->Config);
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the Ia descriptor from the config data, and leave the other
+ // fields of the Ia as default value 0.
+ //
+ Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA));
+ if (Instance->IaCb.Ia == NULL) {
+ Dhcp6CleanupConfigData (Instance->Config);
+ FreePool (Instance->Config);
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (
+ &Instance->IaCb.Ia->Descriptor,
+ &Dhcp6CfgData->IaDescriptor,
+ sizeof(EFI_DHCP6_IA_DESCRIPTOR)
+ );
+
+ } else {
+
+ if (Instance->Config == NULL) {
+ ASSERT (Instance->IaCb.Ia == NULL);
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // It's not allowed to configure a started instance as null.
+ //
+ if (Instance->IaCb.Ia->State != Dhcp6Init) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_ACCESS_DENIED;
+ }
+
+ Dhcp6CleanupConfigData (Instance->Config);
+ FreePool (Instance->Config);
+ Instance->Config = NULL;
+
+ FreePool (Instance->IaCb.Ia);
+ Instance->IaCb.Ia = NULL;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Request configuration information without the assignment of any
+ Ia addresses of the client.
+
+ The InfoRequest() function is used to request configuration information
+ without the assignment of any IPv6 address of the client. The client sends
+ out an Information Request packet to obtain the required configuration
+ information, and DHCPv6 server responds with a Reply packet containing
+ the information for the client. The received Reply packet will be passed
+ to the user by ReplyCallback function. If the user returns EFI_NOT_READY from
+ ReplyCallback, the Dhcp6 instance will continue to receive other Reply
+ packets unless timeout according to the Retransmission parameter.
+ Otherwise, the Information Request exchange process will be finished
+ successfully if user returns EFI_SUCCESS from ReplyCallback.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client
+ Identifier option and include it into Information Request
+ packet. Otherwise, Client Identifier option will not be included.
+ @param[in] OptionRequest The pointer to the buffer of option request options.
+ @param[in] OptionCount The option number in the OptionList.
+ @param[in] OptionList The list of appended options.
+ @param[in] Retransmission The pointer to the retransmission of the message.
+ @param[in] TimeoutEvent The event of timeout.
+ @param[in] ReplyCallback The callback function when the reply was received.
+ @param[in] CallbackContext The pointer to the parameter passed to the callback.
+
+ @retval EFI_SUCCESS The DHCPv6 information request exchange process
+ completed when TimeoutEvent is NULL. Information
+ Request packet has been sent to DHCPv6 server when
+ TimeoutEvent is not NULL.
+ @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed
+ because of no response, or not all requested-options
+ are responded by DHCPv6 servers when Timeout happened.
+ @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted
+ by user.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6InfoRequest (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission,
+ IN EFI_EVENT TimeoutEvent OPTIONAL,
+ IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,
+ IN VOID *CallbackContext OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+ DHCP6_INF_CB *InfCb;
+ UINTN Index;
+
+ if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OptionCount > 0 && OptionList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OptionList != NULL) {
+ for (Index = 0; Index < OptionCount; Index++) {
+ if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Create and initialize the control block for the info-request.
+ //
+ InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB));
+
+ if (InfCb == NULL) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InfCb->ReplyCallback = ReplyCallback;
+ InfCb->CallbackContext = CallbackContext;
+ InfCb->TimeoutEvent = TimeoutEvent;
+
+ InsertTailList (&Instance->InfList, &InfCb->Link);
+
+ //
+ // Send the info-request message to start exchange process.
+ //
+ Status = Dhcp6SendInfoRequestMsg (
+ Instance,
+ InfCb,
+ SendClientId,
+ OptionRequest,
+ OptionCount,
+ OptionList,
+ Retransmission
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateless exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (TimeoutEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ RemoveEntryList (&InfCb->Link);
+ FreePool (InfCb);
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Manually extend the valid and preferred lifetimes for the IPv6 addresses
+ of the configured IA and update other configuration parameters by sending a
+ Renew or Rebind packet.
+
+ The RenewRebind() function is used to manually extend the valid and preferred
+ lifetimes for the IPv6 addresses of the configured IA, and update other
+ configuration parameters by sending Renew or Rebind packet.
+ - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,
+ it sends Renew packet to the previously DHCPv6 server and transfer the
+ state of the configured IA to Dhcp6Renewing. If valid Reply packet received,
+ the state transfers to Dhcp6Bound and the valid and preferred timer restarts.
+ If fails, the state transfers to Dhcp6Bound, but the timer continues.
+ - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,
+ it will send a Rebind packet. If valid Reply packet is received, the state transfers
+ to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state
+ transfers to Dhcp6Init, and the IA can't be used.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.
+ Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.
+
+ @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process has
+ completed and at least one IPv6 address of the
+ configured IA has been bound again when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL.
+ The EFI DHCPv6 Protocol instance has sent Renew
+ or Rebind packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ALREADY_STARTED The state of the configured IA has already entered
+ Dhcp6Renewing when RebindRequest is FALSE.
+ The state of the configured IA has already entered
+ Dhcp6Rebinding when RebindRequest is TRUE.
+ @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted
+ by the user.
+ @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed
+ because of no response.
+ @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured
+ IA after the DHCPv6 renew/rebind exchange process.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6RenewRebind (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN RebindRequest
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // The instance has already entered renewing or rebinding state.
+ //
+ if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) ||
+ (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest)
+ ) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Send renew/rebind message to start exchange process.
+ //
+ Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Inform that one or more addresses assigned by a server are already
+ in use by another node.
+
+ The Decline() function is used to manually decline the assignment of
+ IPv6 addresses, which have been already used by another node. If all
+ IPv6 addresses of the configured IA are declined through this function,
+ the state of the IA will switch through Dhcp6Declining to Dhcp6Init.
+ Otherwise, the state of the IA will restore to Dhcp6Bound after the
+ declining process. The Decline() can only be called when the IA is in
+ Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,
+ this function is a blocking operation. It will return after the
+ declining process finishes, or aborted by user.
+
+ @param[in] This The pointer to EFI_DHCP6_PROTOCOL.
+ @param[in] AddressCount The number of declining addresses.
+ @param[in] Addresses The pointer to the buffer stored the declining
+ addresses.
+
+ @retval EFI_SUCCESS The DHCPv6 decline exchange process completed
+ when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
+ The Dhcp6 instance sent Decline packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 decline exchange process aborted by user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Decline (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_DHCP6_IA *DecIa;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL || AddressCount == 0 || Addresses == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Check whether all the declined addresses belongs to the configured Ia.
+ //
+ Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Deprive of all the declined addresses from the configured Ia, and create a
+ // DeclineIa used to create decline message.
+ //
+ DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (DecIa == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Send the decline message to start exchange process.
+ //
+ Status = Dhcp6SendDeclineMsg (Instance, DecIa);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ FreePool (DecIa);
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (DecIa != NULL) {
+ FreePool (DecIa);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Release one or more addresses associated with the configured Ia
+ for current instance.
+
+ The Release() function is used to manually release one or more
+ IPv6 addresses. If AddressCount is zero, it will release all IPv6
+ addresses of the configured IA. If all IPv6 addresses of the IA are
+ released through this function, the state of the IA will switch
+ through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the
+ IA will restore to Dhcp6Bound after the releasing process.
+ The Release() can only be called when the IA is in Dhcp6Bound state.
+ If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is
+ a blocking operation. It will return after the releasing process
+ finishes, or is aborted by user.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] AddressCount The number of releasing addresses.
+ @param[in] Addresses The pointer to the buffer stored the releasing
+ addresses.
+
+ @retval EFI_SUCCESS The DHCPv6 release exchange process
+ completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ was NULL. The Dhcp6 instance was sent Release
+ packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ was not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 release exchange process aborted by user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Release (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_DHCP6_IA *RelIa;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_SERVICE *Service;
+
+ if (This == NULL || (AddressCount != 0 && Addresses == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = DHCP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+
+ //
+ // The instance hasn't been configured.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ if (Instance->IaCb.Ia->State != Dhcp6Bound) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Check whether all the released addresses belongs to the configured Ia.
+ //
+ Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance->UdpSts = EFI_ALREADY_STARTED;
+
+ //
+ // Deprive of all the released addresses from the configured Ia, and create a
+ // ReleaseIa used to create release message.
+ //
+ RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
+
+ if (RelIa == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Send the release message to start exchange process.
+ //
+ Status = Dhcp6SendReleaseMsg (Instance, RelIa);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Register receive callback for the stateful exchange process.
+ //
+ Status = UdpIoRecvDatagram(
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ON_ERROR;
+ }
+
+ FreePool (RelIa);
+ gBS->RestoreTPL (OldTpl);
+
+ //
+ // Poll udp out of the net tpl if synchoronus call.
+ //
+ if (Instance->Config->IaInfoEvent == NULL) {
+ while (Instance->UdpSts == EFI_ALREADY_STARTED) {
+ Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
+ }
+ return Instance->UdpSts;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (RelIa != NULL) {
+ FreePool (RelIa);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Parse the option data in the Dhcp6 packet.
+
+ The Parse() function is used to retrieve the option list in the DHCPv6 packet.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] Packet The pointer to the Dhcp6 packet.
+ @param[in, out] OptionCount The number of option in the packet.
+ @param[out] PacketOptionList The array of pointers to each option in the packet.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options
+ that were found in the Packet.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Parse (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ )
+{
+ UINT32 OptCnt;
+ UINT32 OptLen;
+ UINT16 DataLen;
+ UINT8 *Start;
+ UINT8 *End;
+
+ if (This == NULL || Packet == NULL || OptionCount == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*OptionCount != 0 && PacketOptionList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The format of Dhcp6 option:
+ //
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-code | option-len (option data) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-data |
+ // | (option-len octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ OptCnt = 0;
+ OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER);
+ Start = Packet->Dhcp6.Option;
+ End = Start + OptLen;
+
+ //
+ // Calculate the number of option in the packet.
+ //
+ while (Start < End) {
+ DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;
+ Start += (NTOHS (DataLen) + 4);
+ OptCnt++;
+ }
+
+ //
+ // It will return buffer too small if pass-in option count is smaller than the
+ // actual count of options in the packet.
+ //
+ if (OptCnt > *OptionCount) {
+ *OptionCount = OptCnt;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ ZeroMem (
+ PacketOptionList,
+ (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *))
+ );
+
+ OptCnt = 0;
+ Start = Packet->Dhcp6.Option;
+
+ while (Start < End) {
+
+ PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start;
+ DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen;
+ Start += (NTOHS (DataLen) + 4);
+ OptCnt++;
+ }
+
+ return EFI_SUCCESS;
+}
+
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
new file mode 100644
index 0000000000..8fb1dfa382
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
@@ -0,0 +1,597 @@
+/** @file
+ Dhcp6 internal data structure and definition declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_IMPL_H__
+#define __EFI_DHCP6_IMPL_H__
+
+
+#include <Uefi.h>
+
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+
+#include <Library/UdpIoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+
+
+typedef struct _DHCP6_IA_CB DHCP6_IA_CB;
+typedef struct _DHCP6_INF_CB DHCP6_INF_CB;
+typedef struct _DHCP6_TX_CB DHCP6_TX_CB;
+typedef struct _DHCP6_SERVICE DHCP6_SERVICE;
+typedef struct _DHCP6_INSTANCE DHCP6_INSTANCE;
+
+#include "Dhcp6Utility.h"
+#include "Dhcp6Io.h"
+#include "Dhcp6Driver.h"
+
+#define DHCP6_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'S')
+#define DHCP6_INSTANCE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'I')
+
+//
+// Transmit parameters of solicit message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_SOL_MAX_DELAY 1
+#define DHCP6_SOL_IRT 1
+#define DHCP6_SOL_MRC 0
+#define DHCP6_SOL_MRT 120
+#define DHCP6_SOL_MRD 0
+//
+// Transmit parameters of request message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REQ_IRT 1
+#define DHCP6_REQ_MRC 10
+#define DHCP6_REQ_MRT 30
+#define DHCP6_REQ_MRD 0
+//
+// Transmit parameters of confirm message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_CNF_MAX_DELAY 1
+#define DHCP6_CNF_IRT 1
+#define DHCP6_CNF_MRC 0
+#define DHCP6_CNF_MRT 4
+#define DHCP6_CNF_MRD 10
+//
+// Transmit parameters of renew message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REN_IRT 10
+#define DHCP6_REN_MRC 0
+#define DHCP6_REN_MRT 600
+#define DHCP6_REN_MRD 0
+//
+// Transmit parameters of rebind message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REB_IRT 10
+#define DHCP6_REB_MRC 0
+#define DHCP6_REB_MRT 600
+#define DHCP6_REB_MRD 0
+//
+// Transmit parameters of information request message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_INF_MAX_DELAY 1
+#define DHCP6_INF_IRT 1
+#define DHCP6_INF_MRC 0
+#define DHCP6_INF_MRT 120
+#define DHCP6_INF_MRD 0
+//
+// Transmit parameters of release message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_REL_IRT 1
+#define DHCP6_REL_MRC 5
+#define DHCP6_REL_MRT 0
+#define DHCP6_REL_MRD 0
+//
+// Transmit parameters of decline message, refers to section-5.5 of rfc-3315.
+//
+#define DHCP6_DEC_IRT 1
+#define DHCP6_DEC_MRC 5
+#define DHCP6_DEC_MRT 0
+#define DHCP6_DEC_MRD 0
+
+#define DHCP6_PACKET_ALL 0
+#define DHCP6_PACKET_STATEFUL 1
+#define DHCP6_PACKET_STATELESS 2
+
+#define DHCP6_BASE_PACKET_SIZE 1024
+
+#define DHCP6_PORT_CLIENT 546
+#define DHCP6_PORT_SERVER 547
+
+#define DHCP6_INSTANCE_FROM_THIS(Instance) CR ((Instance), DHCP6_INSTANCE, Dhcp6, DHCP6_INSTANCE_SIGNATURE)
+#define DHCP6_SERVICE_FROM_THIS(Service) CR ((Service), DHCP6_SERVICE, ServiceBinding, DHCP6_SERVICE_SIGNATURE)
+
+extern EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress;
+extern EFI_IPv6_ADDRESS mAllDhcpServersAddress;
+extern EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate;
+
+//
+// Enumeration of Dhcp6 message type, refers to section-5.3 of rfc-3315.
+//
+typedef enum {
+ Dhcp6MsgSolicit = 1,
+ Dhcp6MsgAdvertise = 2,
+ Dhcp6MsgRequest = 3,
+ Dhcp6MsgConfirm = 4,
+ Dhcp6MsgRenew = 5,
+ Dhcp6MsgRebind = 6,
+ Dhcp6MsgReply = 7,
+ Dhcp6MsgRelease = 8,
+ Dhcp6MsgDecline = 9,
+ Dhcp6MsgReconfigure = 10,
+ Dhcp6MsgInfoRequest = 11
+} DHCP6_MSG_TYPE;
+
+//
+// Enumeration of option code in Dhcp6 packet, refers to section-24.3 of rfc-3315.
+//
+typedef enum {
+ Dhcp6OptClientId = 1,
+ Dhcp6OptServerId = 2,
+ Dhcp6OptIana = 3,
+ Dhcp6OptIata = 4,
+ Dhcp6OptIaAddr = 5,
+ Dhcp6OptRequestOption = 6,
+ Dhcp6OptPreference = 7,
+ Dhcp6OptElapsedTime = 8,
+ Dhcp6OptReplayMessage = 9,
+ Dhcp6OptAuthentication = 11,
+ Dhcp6OptServerUnicast = 12,
+ Dhcp6OptStatusCode = 13,
+ Dhcp6OptRapidCommit = 14,
+ Dhcp6OptUserClass = 15,
+ Dhcp6OptVendorClass = 16,
+ Dhcp6OptVendorInfo = 17,
+ Dhcp6OptInterfaceId = 18,
+ Dhcp6OptReconfigMessage = 19,
+ Dhcp6OptReconfigureAccept = 20
+} DHCP6_OPT_CODE;
+
+//
+// Enumeration of status code recorded by IANA, refers to section-24.4 of rfc-3315.
+//
+typedef enum {
+ Dhcp6StsSuccess = 0,
+ Dhcp6StsUnspecFail = 1,
+ Dhcp6StsNoAddrsAvail = 2,
+ Dhcp6StsNoBinding = 3,
+ Dhcp6StsNotOnLink = 4,
+ Dhcp6StsUseMulticast = 5
+} DHCP6_STS_CODE;
+
+//
+// Enumeration of Duid type recorded by IANA, refers to section-24.5 of rfc-3315.
+//
+typedef enum {
+ Dhcp6DuidTypeLlt = 1,
+ Dhcp6DuidTypeEn = 2,
+ Dhcp6DuidTypeLl = 3
+} DHCP6_DUID_TYPE;
+
+//
+// Control block for each IA.
+//
+struct _DHCP6_IA_CB {
+ EFI_DHCP6_IA *Ia;
+ UINT32 T1;
+ UINT32 T2;
+ UINT32 AllExpireTime;
+ UINT32 LeaseTime;
+};
+
+//
+// Control block for each transmitted message.
+//
+struct _DHCP6_TX_CB {
+ LIST_ENTRY Link;
+ UINT32 Xid;
+ EFI_DHCP6_PACKET *TxPacket;
+ EFI_DHCP6_RETRANSMISSION RetryCtl;
+ UINT32 RetryCnt;
+ UINT32 RetryExp;
+ UINT32 RetryLos;
+ UINT32 TickTime;
+ UINT16 *Elapsed;
+};
+
+//
+// Control block for each info-request message.
+//
+struct _DHCP6_INF_CB {
+ LIST_ENTRY Link;
+ UINT32 Xid;
+ EFI_DHCP6_INFO_CALLBACK ReplyCallback;
+ VOID *CallbackContext;
+ EFI_EVENT TimeoutEvent;
+};
+
+//
+// Control block for Dhcp6 instance, it's per configuration data.
+//
+struct _DHCP6_INSTANCE {
+ UINT32 Signature;
+ EFI_HANDLE Handle;
+ DHCP6_SERVICE *Service;
+ LIST_ENTRY Link;
+ EFI_DHCP6_PROTOCOL Dhcp6;
+ EFI_EVENT Timer;
+ EFI_DHCP6_CONFIG_DATA *Config;
+ EFI_DHCP6_IA *CacheIa;
+ DHCP6_IA_CB IaCb;
+ LIST_ENTRY TxList;
+ LIST_ENTRY InfList;
+ EFI_DHCP6_PACKET *AdSelect;
+ UINT8 AdPref;
+ EFI_IPv6_ADDRESS *Unicast;
+ EFI_STATUS UdpSts;
+ BOOLEAN InDestory;
+ BOOLEAN MediaPresent;
+ UINT64 StartTime;
+};
+
+//
+// Control block for Dhcp6 service, it's per Nic handle.
+//
+struct _DHCP6_SERVICE {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_DHCP6_DUID *ClientId;
+ UDP_IO *UdpIo;
+ UINT32 Xid;
+ LIST_ENTRY Child;
+ UINTN NumOfChild;
+ BOOLEAN InDestory;
+};
+
+/**
+ Starts the DHCPv6 standard S.A.R.R. process.
+
+ The Start() function starts the DHCPv6 standard process. This function can
+ be called only when the state of Dhcp6 instance is in the Dhcp6Init state.
+ If the DHCP process completes successfully, the state of the Dhcp6 instance
+ will be transferred through Dhcp6Selecting and Dhcp6Requesting to the
+ Dhcp6Bound state.
+ Refer to rfc-3315 for precise state transitions during this process. At the
+ time when each event occurs in this process, the callback function that was set
+ by EFI_DHCP6_PROTOCOL.Configure() will be called and the user can take this
+ opportunity to control the process.
+
+ @param[in] This The pointer to Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The DHCPv6 standard process has started, or it
+ completed when CompletionEvent was NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no
+ response was received from the server within the
+ specified timeout value.
+ @retval EFI_ABORTED The user aborted the DHCPv6 process.
+ @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6
+ standard process.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Start (
+ IN EFI_DHCP6_PROTOCOL *This
+ );
+
+/**
+ Stops the DHCPv6 standard S.A.R.R. process.
+
+ The Stop() function is used to stop the DHCPv6 standard process. After this
+ function is called successfully, the state of Dhcp6 instance is transferred
+ into the Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called
+ before DHCPv6 standard process can be started again. This function can be
+ called when the Dhcp6 instance is in any state.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+
+ @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Stop (
+ IN EFI_DHCP6_PROTOCOL *This
+ );
+
+/**
+ Returns the current operating mode data for the Dhcp6 instance.
+
+ The GetModeData() function returns the current operating mode and
+ cached data packet for the Dhcp6 instance.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data.
+ @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data.
+
+ @retval EFI_SUCCESS The mode data was returned.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance has not
+ been configured when Dhcp6ConfigData is
+ not NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6GetModeData (
+ IN EFI_DHCP6_PROTOCOL *This,
+ OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL,
+ OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL
+ );
+
+/**
+ Initializes, changes, or resets the operational settings for the Dhcp6 instance.
+
+ The Configure() function is used to initialize or clean up the configuration
+ data of the Dhcp6 instance:
+ - When Dhcp6CfgData is not NULL and Configure() is called successfully, the
+ configuration data will be initialized in the Dhcp6 instance and the state
+ of the configured IA will be transferred into Dhcp6Init.
+ - When Dhcp6CfgData is NULL and Configure() is called successfully, the
+ configuration data will be cleaned up and no IA will be associated with
+ the Dhcp6 instance.
+ To update the configuration data for an Dhcp6 instance, the original data
+ must be cleaned up before setting the new configuration data.
+
+ @param[in] This The pointer to the Dhcp6 protocol
+ @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA.
+
+ @retval EFI_SUCCESS The Dhcp6 is configured successfully with the
+ Dhcp6Init state, or cleaned up the original
+ configuration setting.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance has been already configured
+ when Dhcp6CfgData is not NULL.
+ The Dhcp6 instance has already started the
+ DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.
+ @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Configure (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL
+ );
+
+/**
+ Request configuration information without the assignment of any
+ Ia addresses of the client.
+
+ The InfoRequest() function is used to request configuration information
+ without the assignment of any IPv6 address of the client. Client sends
+ out Information Request packet to obtain the required configuration
+ information, and DHCPv6 server responds with Reply packet containing
+ the information for the client. The received Reply packet will be passed
+ to the user by ReplyCallback function. If user returns EFI_NOT_READY from
+ ReplyCallback, the Dhcp6 instance will continue to receive other Reply
+ packets unless timeout according to the Retransmission parameter.
+ Otherwise, the Information Request exchange process will be finished
+ successfully if user returns EFI_SUCCESS from ReplyCallback.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client
+ Identifier option and include it into Information Request
+ packet. Otherwise, Client Identifier option will not be included.
+ @param[in] OptionRequest The pointer to the buffer of option request options.
+ @param[in] OptionCount The option number in the OptionList.
+ @param[in] OptionList The list of appended options.
+ @param[in] Retransmission The pointer to the retransmission of the message.
+ @param[in] TimeoutEvent The event of timeout.
+ @param[in] ReplyCallback The callback function when a reply was received.
+ @param[in] CallbackContext The pointer to the parameter passed to the callback.
+
+ @retval EFI_SUCCESS The DHCPv6 information request exchange process
+ completed when TimeoutEvent is NULL. Information
+ Request packet has been sent to DHCPv6 server when
+ TimeoutEvent is not NULL.
+ @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed
+ because of no response, or not all requested-options
+ are responded to by DHCPv6 servers when Timeout happened.
+ @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted
+ by the user.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6InfoRequest (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission,
+ IN EFI_EVENT TimeoutEvent OPTIONAL,
+ IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,
+ IN VOID *CallbackContext OPTIONAL
+ );
+
+/**
+ Manually extend the valid and preferred lifetimes for the IPv6 addresses
+ of the configured IA and update other configuration parameters by sending
+ Renew or Rebind packet.
+
+ The RenewRebind() function is used to manually extend the valid and preferred
+ lifetimes for the IPv6 addresses of the configured IA and update other
+ configuration parameters by sending a Renew or Rebind packet.
+ - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,
+ it will send Renew packet to the previously DHCPv6 server and transfer the
+ state of the configured IA to Dhcp6Renewing. If valid Reply packet received,
+ the state transfers to Dhcp6Bound and the valid and preferred timer restarts.
+ If fails, the state transfers to Dhcp6Bound but the timer continues.
+ - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,
+ it will send a Rebind packet. If a valid Reply packet is received, the state transfers
+ to Dhcp6Bound, and the valid and preferred timer restarts. If it fails, the state
+ transfers to Dhcp6Init, and the IA can't be used.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.
+ Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.
+
+ @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process
+ completed and at least one IPv6 address of the
+ configured IA was bound again when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
+ The EFI DHCPv6 Protocol instance has sent Renew
+ or Rebind packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ALREADY_STARTED The state of the configured IA has already entered
+ Dhcp6Renewing when RebindRequest is FALSE.
+ The state of the configured IA has already entered
+ Dhcp6Rebinding when RebindRequest is TRUE.
+ @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted
+ by user.
+ @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed
+ because of no response.
+ @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured
+ IA after the DHCPv6 renew/rebind exchange process.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6RenewRebind (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN BOOLEAN RebindRequest
+ );
+
+/**
+ Inform that one or more addresses assigned by a server are already
+ in use by another node.
+
+ The Decline() function is used to manually decline the assignment of
+ IPv6 addresses, which have been already used by another node. If all
+ IPv6 addresses of the configured IA are declined through this function,
+ the state of the IA will switch through Dhcp6Declining to Dhcp6Init.
+ Otherwise, the state of the IA will restore to Dhcp6Bound after the
+ declining process. The Decline() can only be called when the IA is in
+ Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,
+ this function is a blocking operation. It will return after the
+ declining process finishes, or aborted by user.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] AddressCount The number of declining addresses.
+ @param[in] Addresses The pointer to the buffer stored the declining
+ addresses.
+
+ @retval EFI_SUCCESS The DHCPv6 decline exchange process completed
+ when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
+ The Dhcp6 instance has sent Decline packet when
+ EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 decline exchange process was aborted by the user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Decline (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ Release one or more addresses associated with the configured Ia
+ for the current instance.
+
+ The Release() function is used to manually release the one or more
+ IPv6 address. If AddressCount is zero, it will release all IPv6
+ addresses of the configured IA. If all IPv6 addresses of the IA are
+ released through this function, the state of the IA will switch
+ through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the
+ IA will restore to Dhcp6Bound after the releasing process.
+ The Release() can only be called when the IA is in a Dhcp6Bound state.
+ If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is
+ a blocking operation. It will return after the releasing process
+ finishes, or aborted by user.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] AddressCount The number of releasing addresses.
+ @param[in] Addresses The pointer to the buffer stored the releasing
+ addresses.
+ @retval EFI_SUCCESS The DHCPv6 release exchange process has
+ completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ is NULL. The Dhcp6 instance has sent Release
+ packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
+ is not NULL.
+ @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
+ state of the configured IA is not in Dhcp6Bound.
+ @retval EFI_ABORTED The DHCPv6 release exchange process was aborted by the user.
+ @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
+ the configured IA for this instance.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Release (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ Parse the option data in the Dhcp6 packet.
+
+ The Parse() function is used to retrieve the option list in the DHCPv6 packet.
+
+ @param[in] This The pointer to the Dhcp6 protocol.
+ @param[in] Packet The pointer to the Dhcp6 packet.
+ @param[in, out] OptionCount The number of option in the packet.
+ @param[out] PacketOptionList The array of pointers to the each option in the packet.
+
+ @retval EFI_SUCCESS The packet was successfully parsed.
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.
+ @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options
+ that were found in the Packet.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiDhcp6Parse (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL
+ );
+
+#endif
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
new file mode 100644
index 0000000000..761f9c2d36
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
@@ -0,0 +1,2965 @@
+/** @file
+ Dhcp6 internal functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+/**
+ Enqueue the packet into the retry list in case of timeout.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the Dhcp6 packet to retry.
+ @param[in] Elapsed The pointer to the elapsed time value in the packet.
+ @param[in] RetryCtl The pointer to the transmission control of the packet.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS Successfully enqueued the packet into the retry list according
+ to its message type.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected message type.
+
+**/
+EFI_STATUS
+Dhcp6EnqueueRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN UINT16 *Elapsed,
+ IN EFI_DHCP6_RETRANSMISSION *RetryCtl OPTIONAL
+ )
+{
+ DHCP6_TX_CB *TxCb;
+ DHCP6_IA_CB *IaCb;
+
+ ASSERT (Packet != NULL);
+
+ IaCb = &Instance->IaCb;
+ TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB));
+
+ if (TxCb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Save tx packet pointer, and it will be destoryed when reply received.
+ //
+ TxCb->TxPacket = Packet;
+ TxCb->Xid = Packet->Dhcp6.Header.TransactionId;
+
+ //
+ // Save pointer to elapsed-time value so we can update it on retransmits.
+ //
+ TxCb->Elapsed = Elapsed;
+
+ //
+ // Calculate the retransmission according to the the message type.
+ //
+ switch (Packet->Dhcp6.Header.MessageType) {
+ case Dhcp6MsgSolicit:
+ //
+ // Calculate the retransmission threshold value for solicit packet.
+ // Use the default value by rfc-3315 if user doesn't configure.
+ //
+ if (RetryCtl == NULL) {
+ TxCb->RetryCtl.Irt = DHCP6_SOL_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD;
+ } else {
+ TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT;
+ TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC;
+ TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT;
+ TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD;
+ }
+
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ FALSE
+ );
+ break;
+
+ case Dhcp6MsgRequest:
+ //
+ // Calculate the retransmission threshold value for request packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REQ_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgConfirm:
+ //
+ // Calculate the retransmission threshold value for confirm packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_CNF_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgRenew:
+ //
+ // Calculate the retransmission threshold value for renew packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REB_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REB_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REB_MRT;
+ TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgRebind:
+ //
+ // Calculate the retransmission threshold value for rebind packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REN_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REN_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REN_MRT;
+ TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgDecline:
+ //
+ // Calculate the retransmission threshold value for decline packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_DEC_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgRelease:
+ //
+ // Calculate the retransmission threshold value for release packet.
+ //
+ TxCb->RetryCtl.Irt = DHCP6_REL_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_REL_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_REL_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_REL_MRD;
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ case Dhcp6MsgInfoRequest:
+ //
+ // Calculate the retransmission threshold value for info-request packet.
+ // Use the default value by rfc-3315 if user doesn't configure.
+ //
+ if (RetryCtl == NULL) {
+ TxCb->RetryCtl.Irt = DHCP6_INF_IRT;
+ TxCb->RetryCtl.Mrc = DHCP6_INF_MRC;
+ TxCb->RetryCtl.Mrt = DHCP6_INF_MRT;
+ TxCb->RetryCtl.Mrd = DHCP6_INF_MRD;
+ } else {
+ TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT;
+ TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC;
+ TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT;
+ TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD;
+ }
+
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Irt,
+ TRUE,
+ TRUE
+ );
+ break;
+
+ default:
+ //
+ // Unexpected message type.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Insert into the retransmit list of the instance.
+ //
+ InsertTailList (&Instance->TxList, &TxCb->Link);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Dequeue the packet from retry list if reply received or timeout at last.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] PacketXid The packet transaction id to match.
+ @param[in] NeedSignal If TRUE, then an timeout event need be signaled when it is existed.
+ Otherwise, this parameter is ignored.
+
+ @retval EFI_SUCCESS Successfully dequeued the packet into retry list .
+ @retval EFI_NOT_FOUND There is no xid matched in retry list.
+
+**/
+EFI_STATUS
+Dhcp6DequeueRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT32 PacketXid,
+ IN BOOLEAN NeedSignal
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ DHCP6_TX_CB *TxCb;
+ DHCP6_INF_CB *InfCb;
+
+ //
+ // Seek the retransmit node in the retransmit list by packet xid.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+ ASSERT(TxCb->TxPacket);
+
+ if (TxCb->Xid == PacketXid) {
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+
+ //
+ // Seek the info-request node in the info-request list by packet xid.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {
+
+ InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);
+
+ if (InfCb->Xid == PacketXid) {
+ //
+ // Remove the info-request node, and signal the event if timeout.
+ //
+ if (InfCb->TimeoutEvent != NULL && NeedSignal) {
+ gBS->SignalEvent (InfCb->TimeoutEvent);
+ }
+
+ RemoveEntryList (&InfCb->Link);
+ FreePool (InfCb);
+ }
+ }
+ }
+ //
+ // Remove the retransmit node.
+ //
+ RemoveEntryList (&TxCb->Link);
+ ASSERT(TxCb->TxPacket);
+ FreePool (TxCb->TxPacket);
+ FreePool (TxCb);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Clean up the specific nodes in the retry list.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Scope The scope of cleanup nodes.
+
+**/
+VOID
+Dhcp6CleanupRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT32 Scope
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ DHCP6_TX_CB *TxCb;
+ DHCP6_INF_CB *InfCb;
+
+ //
+ // Clean up all the stateful messages from the retransmit list.
+ //
+ if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) {
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+ ASSERT(TxCb->TxPacket);
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) {
+ RemoveEntryList (&TxCb->Link);
+ FreePool (TxCb->TxPacket);
+ FreePool (TxCb);
+ }
+ }
+ }
+
+ //
+ // Clean up all the stateless messages from the retransmit list.
+ //
+ if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) {
+
+ //
+ // Clean up all the retransmit list for stateless messages.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+ ASSERT(TxCb->TxPacket);
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+ RemoveEntryList (&TxCb->Link);
+ FreePool (TxCb->TxPacket);
+ FreePool (TxCb);
+ }
+ }
+
+ //
+ // Clean up all the info-request messages list.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {
+
+ InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);
+
+ if (InfCb->TimeoutEvent != NULL) {
+ gBS->SignalEvent (InfCb->TimeoutEvent);
+ }
+ RemoveEntryList (&InfCb->Link);
+ FreePool (InfCb);
+ }
+ }
+}
+
+
+/**
+ Clean up the session of the instance stateful exchange.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+ @param[in] Status The return status from udp.
+
+**/
+VOID
+Dhcp6CleanupSession (
+ IN OUT DHCP6_INSTANCE *Instance,
+ IN EFI_STATUS Status
+ )
+{
+ UINTN Index;
+ EFI_DHCP6_IA *Ia;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ //
+ // Clean up the retransmit list for stateful messages.
+ //
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL);
+
+ if (Instance->Unicast != NULL) {
+ FreePool (Instance->Unicast);
+ }
+
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+
+ //
+ // Reinitialize the Ia fields of the instance.
+ //
+ Instance->UdpSts = Status;
+ Instance->AdSelect = NULL;
+ Instance->AdPref = 0;
+ Instance->Unicast = NULL;
+ Instance->IaCb.T1 = 0;
+ Instance->IaCb.T2 = 0;
+ Instance->IaCb.AllExpireTime = 0;
+ Instance->IaCb.LeaseTime = 0;
+
+ //
+ // Clear start time
+ //
+ Instance->StartTime = 0;
+
+ Ia = Instance->IaCb.Ia;
+ Ia->State = Dhcp6Init;
+ Ia->ReplyPacket = NULL;
+
+ //
+ // Set the addresses as zero lifetime, and then the notify
+ // function in Ip6Config will remove these timeout address.
+ //
+ for (Index = 0; Index < Ia->IaAddressCount; Index++) {
+ Ia->IaAddress[Index].PreferredLifetime = 0;
+ Ia->IaAddress[Index].ValidLifetime = 0;
+ }
+
+ //
+ //
+ // Signal the Ia information updated event to informal user.
+ //
+ if (Instance->Config->IaInfoEvent != NULL) {
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);
+ }
+}
+
+
+/**
+ Callback to user when Dhcp6 transmit/receive occurs.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Event The current Dhcp6 event.
+ @param[in, out] Packet The pointer to the packet sending or received.
+
+ @retval EFI_SUCCESS The user function returns success.
+ @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
+ @retval EFI_ABORTED The user function ask it to abort.
+
+**/
+EFI_STATUS
+EFIAPI
+Dhcp6CallbackUser (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_EVENT Event,
+ IN OUT EFI_DHCP6_PACKET **Packet
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *NewPacket;
+ EFI_DHCP6_CALLBACK Callback;
+ VOID *Context;
+
+ ASSERT (Packet != NULL);
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ NewPacket = NULL;
+ Status = EFI_SUCCESS;
+ Callback = Instance->Config->Dhcp6Callback;
+ Context = Instance->Config->CallbackContext;
+
+ //
+ // Callback to user with the new message if has.
+ //
+ if (Callback != NULL) {
+
+ Status = Callback (
+ &Instance->Dhcp6,
+ Context,
+ Instance->IaCb.Ia->State,
+ Event,
+ *Packet,
+ &NewPacket
+ );
+ //
+ // Updated the new packet from user to replace the original one.
+ //
+ if (NewPacket != NULL) {
+ ASSERT (*Packet != NULL);
+ FreePool (*Packet);
+ *Packet = NewPacket;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Update Ia according to the new reply message.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to reply messages.
+
+ @retval EFI_SUCCESS Updated the Ia information successfully.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+
+**/
+EFI_STATUS
+Dhcp6UpdateIaInfo (
+ IN OUT DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_STATE State;
+ UINT8 *Option;
+ UINT8 *IaInnerOpt;
+ UINT16 IaInnerLen;
+ UINT16 StsCode;
+ UINT32 T1;
+ UINT32 T2;
+
+ ASSERT (Instance->Config != NULL);
+ //
+ // If the reply was received in reponse to a solicit with rapid commit option,
+ // request, renew or rebind message, the client updates the information it has
+ // recorded about IAs from the IA options contained in the reply message:
+ // 1. record the T1 and T2 times
+ // 2. add any new addresses in the IA
+ // 3. discard any addresses from the IA, that have a valid lifetime of 0
+ // 4. update lifetimes for any addresses that alread recorded
+ // 5. leave unchanged any information about addresses
+ //
+ // See details in the section-18.1.8 of rfc-3315.
+ //
+ State = Dhcp6Init;
+ Option = Dhcp6SeekIaOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - sizeof (EFI_DHCP6_HEADER),
+ &Instance->Config->IaDescriptor
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // The format of the IA_NA option is:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_NA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T1 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T2 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_NA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ // The format of the IA_TA option is:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_TA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_TA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len + IaId) = 8
+ // sizeof (option-code + option-len + IaId + T1) = 12
+ // sizeof (option-code + option-len + IaId + T1 + T2) = 16
+ //
+ // The inner options still start with 2 bytes option-code and 2 bytes option-len.
+ //
+ if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {
+ T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8)));
+ T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12)));
+ IaInnerOpt = Option + 16;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12);
+ } else {
+ T1 = 0;
+ T2 = 0;
+ IaInnerOpt = Option + 8;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4);
+ }
+
+ //
+ // The format of the Status Code option is:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_STATUS_CODE | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | status-code | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ // . .
+ // . status-message .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len) = 4
+ //
+ StsCode = Dhcp6StsSuccess;
+ Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);
+
+ if (Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Generate control block for the Ia.
+ //
+ Status = Dhcp6GenerateIaCb (
+ Instance,
+ IaInnerOpt,
+ IaInnerLen,
+ T1,
+ T2
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Seek StatusCode Option in package. A Status Code option may appear in the
+ options field of a DHCP message and/or in the options field of another option.
+ See details in section 22.13, RFC3315.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to reply messages.
+ @param[out] Option The pointer to status code option.
+
+ @retval EFI_SUCCESS Seek status code option successfully.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+
+**/
+EFI_STATUS
+Dhcp6SeekStsOption (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet,
+ OUT UINT8 **Option
+ )
+{
+ UINT8 *IaInnerOpt;
+ UINT16 IaInnerLen;
+ UINT16 StsCode;
+
+ //
+ // Seek StatusCode option directly in DHCP message body. That is, search in
+ // non-encapsulated option fields.
+ //
+ *Option = Dhcp6SeekOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptStatusCode
+ );
+
+ if (*Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Seek in encapsulated options, IA_NA and IA_TA.
+ //
+ *Option = Dhcp6SeekIaOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - sizeof (EFI_DHCP6_HEADER),
+ &Instance->Config->IaDescriptor
+ );
+ if (*Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // The format of the IA_NA option is:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_NA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T1 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T2 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_NA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ // The format of the IA_TA option is:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_TA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_TA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len + IaId) = 8
+ // sizeof (option-code + option-len + IaId + T1) = 12
+ // sizeof (option-code + option-len + IaId + T1 + T2) = 16
+ //
+ // The inner options still start with 2 bytes option-code and 2 bytes option-len.
+ //
+ if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {
+ IaInnerOpt = *Option + 16;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12);
+ } else {
+ IaInnerOpt = *Option + 8;
+ IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4);
+ }
+
+ //
+ // The format of the Status Code option is:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_STATUS_CODE | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | status-code | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ // . .
+ // . status-message .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-code + option-len) = 4
+ //
+ *Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);
+ if (*Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Transmit Dhcp6 message by udpio.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to transmit message.
+ @param[in] Elapsed The pointer to the elapsed time value to fill in.
+
+ @retval EFI_SUCCESS Successfully transmitted the packet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Dhcp6TransmitPacket (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet,
+ IN UINT16 *Elapsed
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF *Wrap;
+ NET_FRAGMENT Frag;
+ UDP_END_POINT EndPt;
+ DHCP6_SERVICE *Service;
+
+ Service = Instance->Service;
+
+ //
+ // Wrap it into a netbuf then send it.
+ //
+ Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header;
+ Frag.Len = Packet->Length;
+
+ //
+ // Do not register free packet here, which will be handled in retry list.
+ //
+ Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL);
+
+ if (Wrap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Multicast the Dhcp6 message, unless get the unicast server address by option.
+ //
+ ZeroMem (&EndPt, sizeof (UDP_END_POINT));
+
+ if (Instance->Unicast != NULL) {
+ CopyMem (
+ &EndPt.RemoteAddr,
+ Instance->Unicast,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ } else {
+ CopyMem (
+ &EndPt.RemoteAddr,
+ &mAllDhcpRelayAndServersAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ }
+
+ EndPt.RemotePort = DHCP6_PORT_SERVER;
+ EndPt.LocalPort = DHCP6_PORT_CLIENT;
+
+ //
+ // Update the elapsed time value.
+ //
+ if (Elapsed != NULL) {
+ SetElapsedTime (Elapsed, Instance);
+ }
+
+ //
+ // Send out the message by the configured Udp6Io.
+ //
+ Status = UdpIoSendDatagram (
+ Service->UdpIo,
+ Wrap,
+ &EndPt,
+ NULL,
+ Dhcp6OnTransmitted,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Wrap);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create the solicit message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the solicit message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the solicit message.
+
+**/
+EFI_STATUS
+Dhcp6SendSolicitMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ UserLen = 0;
+
+ ASSERT (Service->ClientId != NULL);
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgSolicit;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for solicit message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send solicit packet with the state transition from Dhcp6init to
+ // Dhcp6selecting.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Selecting;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (
+ Instance,
+ Packet,
+ Elapsed,
+ Instance->Config->SolicitRetransmission
+ );
+}
+
+/**
+ Configure some parameter to initiate SolicitMsg.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the solicit message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the solicit message.
+
+**/
+EFI_STATUS
+Dhcp6InitSolicitMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ Instance->IaCb.T1 = 0;
+ Instance->IaCb.T2 = 0;
+ Instance->IaCb.Ia->IaAddressCount = 0;
+
+ return Dhcp6SendSolicitMsg (Instance);
+}
+
+
+/**
+ Create the request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the request message.
+
+**/
+EFI_STATUS
+Dhcp6SendRequestMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ ASSERT(Instance->AdSelect != NULL);
+ ASSERT(Instance->Config != NULL);
+ ASSERT(Instance->IaCb.Ia != NULL);
+ ASSERT(Instance->Service != NULL);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+
+ ASSERT(ClientId != NULL);
+
+ //
+ // Get the server Id from the selected advertisement message.
+ //
+ Option = Dhcp6SeekOption (
+ Instance->AdSelect->Dhcp6.Option,
+ Instance->AdSelect->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ UserLen = 0;
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgRequest;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for request message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send request packet with the state transition from Dhcp6selecting to
+ // Dhcp6requesting.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Requesting;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the decline message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] DecIa The pointer to the decline Ia.
+
+ @retval EFI_SUCCESS Created and sent the decline message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the decline message.
+
+**/
+EFI_STATUS
+Dhcp6SendDeclineMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *DecIa
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET *LastReply;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT16 Length;
+
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+ ASSERT (Instance->Service != NULL);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ LastReply = Instance->IaCb.Ia->ReplyPacket;
+
+ ASSERT (ClientId != NULL);
+ ASSERT (LastReply != NULL);
+
+ //
+ // Get the server Id from the last reply message.
+ //
+ Option = Dhcp6SeekOption (
+ LastReply->Dhcp6.Option,
+ LastReply->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // EFI_DHCP6_DUID contains a length field of 2 bytes.
+ //
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgDecline;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for rebind/renew message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+
+ Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0);
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send decline packet with the state transition from Dhcp6bound to
+ // Dhcp6declining.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Declining;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the release message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RelIa The pointer to the release Ia.
+
+ @retval EFI_SUCCESS Created and sent the release message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the release message.
+
+**/
+EFI_STATUS
+Dhcp6SendReleaseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *RelIa
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET *LastReply;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT16 Length;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ LastReply = Instance->IaCb.Ia->ReplyPacket;
+
+ ASSERT(ClientId);
+ ASSERT(LastReply);
+
+ //
+ // Get the server Id from the last reply message.
+ //
+ Option = Dhcp6SeekOption (
+ LastReply->Dhcp6.Option,
+ LastReply->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgRelease;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for rebind/renew message
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ //
+ // ServerId is extracted from packet, it's network order.
+ //
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0);
+
+ //
+ // Determine the size/length of packet
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send release packet with the state transition from Dhcp6bound to
+ // Dhcp6releasing.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Releasing;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the renew/rebind message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RebindRequest If TRUE, it is a Rebind type message.
+ Otherwise, it is a Renew type message.
+
+ @retval EFI_SUCCESS Created and sent the renew/rebind message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the renew/rebind message.
+
+**/
+EFI_STATUS
+Dhcp6SendRenewRebindMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN BOOLEAN RebindRequest
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET *LastReply;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_DUID *ServerId;
+ EFI_DHCP6_STATE State;
+ EFI_DHCP6_EVENT Event;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+
+ ASSERT(ClientId);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ UserLen = 0;
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for rebind/renew message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ if (!RebindRequest) {
+ //
+ // Get the server Id from the last reply message and
+ // insert it for rebind request.
+ //
+ LastReply = Instance->IaCb.Ia->ReplyPacket;
+ ASSERT (LastReply);
+
+ Option = Dhcp6SeekOption (
+ LastReply->Dhcp6.Option,
+ LastReply->Length - 4,
+ Dhcp6OptServerId
+ );
+ if (Option == NULL) {
+ FreePool (Packet);
+ return EFI_DEVICE_ERROR;
+ }
+
+ ServerId = (EFI_DHCP6_DUID *) (Option + 2);
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptServerId),
+ ServerId->Length,
+ ServerId->Duid
+ );
+ }
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ State = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing;
+ Event = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing;
+
+ Status = Dhcp6CallbackUser (Instance, Event, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send renew/rebind packet with the state transition from Dhcp6bound to
+ // Dhcp6renew/rebind.
+ // And sync the lease time when send renew/rebind, in case that user send
+ // renew/rebind actively.
+ //
+ Instance->IaCb.Ia->State = State;
+ Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+/**
+ Create the information request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] InfCb The pointer to the information request control block.
+ @param[in] SendClientId If TRUE, the client identifier option will be included in
+ information request message. Otherwise, the client identifier
+ option will not be included.
+ @param[in] OptionRequest The pointer to the option request option.
+ @param[in] OptionCount The number options in the OptionList.
+ @param[in] OptionList The array pointers to the appended options.
+ @param[in] Retransmission The pointer to the retransmission control.
+
+ @retval EFI_SUCCESS Created and sent the info-request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the info-request message.
+
+**/
+EFI_STATUS
+Dhcp6SendInfoRequestMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN DHCP6_INF_CB *InfCb,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[],
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ EFI_DHCP6_DUID *ClientId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Cursor;
+ UINT16 *Elapsed;
+ UINT32 UserLen;
+ UINTN Index;
+ UINT16 Length;
+
+ ASSERT(OptionRequest);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ UserLen = NTOHS (OptionRequest->OpLen) + 4;
+
+ ASSERT(ClientId);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ for (Index = 0; Index < OptionCount; Index++) {
+ UserLen += (NTOHS (OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize commone fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgInfoRequest;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ InfCb->Xid = Packet->Dhcp6.Header.TransactionId;
+
+ //
+ // Assembly Dhcp6 options for info-request message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ if (SendClientId) {
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+ }
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ OptionRequest->OpCode,
+ OptionRequest->OpLen,
+ OptionRequest->Data
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < OptionCount; Index++) {
+
+ UserOpt = OptionList[Index];
+ Cursor = Dhcp6AppendOption(
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Send info-request packet with no state.
+ //
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission);
+}
+
+
+/**
+ Create the Confirm message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Created and sent the confirm message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the confirm message.
+
+**/
+EFI_STATUS
+Dhcp6SendConfirmMsg (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINT8 *Cursor;
+ UINTN Index;
+ UINT16 Length;
+ UINT32 UserLen;
+ EFI_STATUS Status;
+ DHCP6_SERVICE *Service;
+ EFI_DHCP6_DUID *ClientId;
+ EFI_DHCP6_PACKET *Packet;
+ EFI_DHCP6_PACKET_OPTION *UserOpt;
+ UINT16 *Elapsed;
+
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+ ASSERT (Instance->Service != NULL);
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ ASSERT (ClientId != NULL);
+
+ //
+ // Calculate the added length of customized option list.
+ //
+ UserLen = 0;
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
+ }
+
+ //
+ // Create the Dhcp6 packet and initialize common fields.
+ //
+ Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
+ Packet->Length = sizeof (EFI_DHCP6_HEADER);
+ Packet->Dhcp6.Header.MessageType = Dhcp6MsgConfirm;
+ Packet->Dhcp6.Header.TransactionId = Service->Xid++;
+
+ //
+ // Assembly Dhcp6 options for solicit message.
+ //
+ Cursor = Packet->Dhcp6.Option;
+
+ Length = HTONS (ClientId->Length);
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ HTONS (Dhcp6OptClientId),
+ Length,
+ ClientId->Duid
+ );
+
+ Cursor = Dhcp6AppendETOption (
+ Cursor,
+ Instance,
+ &Elapsed
+ );
+
+ Cursor = Dhcp6AppendIaOption (
+ Cursor,
+ Instance->IaCb.Ia,
+ Instance->IaCb.T1,
+ Instance->IaCb.T2
+ );
+
+ //
+ // Append user-defined when configurate Dhcp6 service.
+ //
+ for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
+ UserOpt = Instance->Config->OptionList[Index];
+ Cursor = Dhcp6AppendOption (
+ Cursor,
+ UserOpt->OpCode,
+ UserOpt->OpLen,
+ UserOpt->Data
+ );
+ }
+
+ //
+ // Determine the size/length of packet.
+ //
+ Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
+ ASSERT (Packet->Size > Packet->Length + 8);
+
+ //
+ // Callback to user with the packet to be sent and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Send confirm packet with the state transition from Dhcp6Bound to
+ // Dhcp6Confirming.
+ //
+ Instance->IaCb.Ia->State = Dhcp6Confirming;
+
+ Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Packet);
+ return Status;
+ }
+
+ //
+ // Enqueue the sent packet for the retransmission in case reply timeout.
+ //
+ return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
+}
+
+
+
+/**
+ Handle with the Dhcp6 reply message.
+
+ @param[in] Instance The pointer to Dhcp6 instance.
+ @param[in] Packet The pointer to the Dhcp6 reply message.
+
+ @retval EFI_SUCCESS Processed the reply message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to process the reply message.
+
+**/
+EFI_STATUS
+Dhcp6HandleReplyMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Option;
+ UINT16 StsCode;
+
+ ASSERT (Instance->Config != NULL);
+ ASSERT (Instance->IaCb.Ia != NULL);
+ ASSERT (Packet != NULL);
+
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // If the client subsequently receives a valid reply message that includes a
+ // rapid commit option since send a solicit with rapid commit option before,
+ // preocess the reply message and discard any reply messages received in
+ // response to the request message.
+ // See details in the section-17.1.4 of rfc-3315.
+ //
+ Option = Dhcp6SeekOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptRapidCommit
+ );
+
+ if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // As to a valid reply packet in response to a request/renew/rebind packet,
+ // ignore the packet if not contains the Ia option
+ //
+ if (Instance->IaCb.Ia->State == Dhcp6Requesting ||
+ Instance->IaCb.Ia->State == Dhcp6Renewing ||
+ Instance->IaCb.Ia->State == Dhcp6Rebinding
+ ) {
+
+ Option = Dhcp6SeekIaOption (
+ Packet->Dhcp6.Option,
+ Packet->Length,
+ &Instance->Config->IaDescriptor
+ );
+ if (Option == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Callback to user with the received packet and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Dequeue the sent packet from retransmit list since reply received.
+ //
+ Status = Dhcp6DequeueRetry (
+ Instance,
+ Packet->Dhcp6.Header.TransactionId,
+ FALSE
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // When receive a valid reply packet in response to a decline/release packet,
+ // the client considers the decline/release event completed regardless of the
+ // status code.
+ //
+ if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) {
+
+ if (Instance->IaCb.Ia->IaAddressCount != 0) {
+ Instance->IaCb.Ia->State = Dhcp6Bound;
+ } else {
+ ASSERT (Instance->IaCb.Ia->ReplyPacket);
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ Instance->IaCb.Ia->ReplyPacket = NULL;
+ Instance->IaCb.Ia->State = Dhcp6Init;
+ }
+
+ //
+ // For sync, set the success flag out of polling in decline/release.
+ //
+ Instance->UdpSts = EFI_SUCCESS;
+
+ //
+ // For async, signal the Ia event to inform Ia infomation update.
+ //
+ if (Instance->Config->IaInfoEvent != NULL) {
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);
+ }
+
+ //
+ // Reset start time for next exchange.
+ //
+ Instance->StartTime = 0;
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Upon the receipt of a valid reply packet in response to a solicit, request,
+ // confirm, renew and rebind, the behavior depends on the status code option.
+ // See the details in the section-18.1.8 of rfc-3315.
+ //
+ Option = NULL;
+ Status = Dhcp6SeekStsOption (
+ Instance,
+ Packet,
+ &Option
+ );
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Reset start time for next exchange.
+ //
+ Instance->StartTime = 0;
+
+ //
+ // No status code or no error status code means succeed to reply.
+ //
+ Status = Dhcp6UpdateIaInfo (Instance, Packet);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Set bound state and store the reply packet.
+ //
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+
+ Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);
+
+ if (Instance->IaCb.Ia->ReplyPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);
+
+ Instance->IaCb.Ia->State = Dhcp6Bound;
+
+ //
+ // For sync, set the success flag out of polling in start/renewrebind.
+ //
+ Instance->UdpSts = EFI_SUCCESS;
+
+ //
+ // Maybe this is a new round DHCP process due to some reason, such as NotOnLink
+ // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that
+ // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP
+ // consumers can be notified to flush old address.
+ //
+ Dhcp6AppendCacheIa (Instance);
+
+ //
+ // For async, signal the Ia event to inform Ia infomation update.
+ //
+ if (Instance->Config->IaInfoEvent != NULL) {
+ gBS->SignalEvent (Instance->Config->IaInfoEvent);
+ }
+ } else if (Option != NULL) {
+ //
+ // Any error status code option is found.
+ //
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
+ switch (StsCode) {
+ case Dhcp6StsUnspecFail:
+ //
+ // It indicates the server is unable to process the message due to an
+ // unspecified failure condition, so just retry if possible.
+ //
+ break;
+
+ case Dhcp6StsUseMulticast:
+ //
+ // It indicates the server receives a message via unicast from a client
+ // to which the server has not sent a unicast option, so retry it by
+ // multi-cast address.
+ //
+ if (Instance->Unicast != NULL) {
+ FreePool (Instance->Unicast);
+ Instance->Unicast = NULL;
+ }
+ break;
+
+ case Dhcp6StsNotOnLink:
+ if (Instance->IaCb.Ia->State == Dhcp6Confirming) {
+ //
+ // Before initiate new round DHCP, cache the current IA.
+ //
+ Status = Dhcp6CacheIa (Instance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Restart S.A.R.R process to acquire new address.
+ //
+ Status = Dhcp6InitSolicitMsg (Instance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ break;
+
+ default:
+ //
+ // The other status code, just restart solicitation.
+ //
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Select the appointed Dhcp6 advertisement message.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] AdSelect The pointer to the selected Dhcp6 advertisement message.
+
+ @retval EFI_SUCCESS Selected the right advertisement message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to select the advertise message.
+
+**/
+EFI_STATUS
+Dhcp6SelectAdvertiseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *AdSelect
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Option;
+
+ ASSERT (AdSelect != NULL);
+
+ //
+ // Callback to user with the selected advertisement packet, and the user
+ // might overwrite it.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Instance->AdSelect = AdSelect;
+
+ //
+ // Dequeue the sent packet for the retransmission since advertisement selected.
+ //
+ Status = Dhcp6DequeueRetry (
+ Instance,
+ AdSelect->Dhcp6.Header.TransactionId,
+ FALSE
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Check whether there is server unicast option in the selected advertise
+ // packet, and update it.
+ //
+ Option = Dhcp6SeekOption(
+ AdSelect->Dhcp6.Option,
+ AdSelect->Length - 4,
+ Dhcp6OptServerUnicast
+ );
+
+ if (Option != NULL) {
+
+ Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS));
+
+ if (Instance->Unicast == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS));
+ }
+
+ //
+ // Update the information of the Ia by the selected advertisement message.
+ //
+ Status = Dhcp6UpdateIaInfo (Instance, AdSelect);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Send the request message to continue the S.A.R.R. process.
+ //
+ return Dhcp6SendRequestMsg (Instance);
+}
+
+
+/**
+ Handle with the Dhcp6 advertisement message.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the Dhcp6 advertisement message.
+
+ @retval EFI_SUCCESS Processed the advertisement message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to process the advertise message.
+
+**/
+EFI_STATUS
+Dhcp6HandleAdvertiseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Option;
+ UINT16 StsCode;
+ BOOLEAN Timeout;
+
+ ASSERT(Instance->Config);
+ ASSERT(Instance->IaCb.Ia);
+
+ Timeout = FALSE;
+ StsCode = Dhcp6StsSuccess;
+
+ //
+ // If the client does receives a valid reply message that includes a rapid
+ // commit option since a solicit with rapid commit optioin sent before, select
+ // this reply message. Or else, process the advertise messages as normal.
+ // See details in the section-17.1.4 of rfc-3315.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptRapidCommit
+ );
+
+ if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) {
+
+ return Dhcp6HandleReplyMsg (Instance, Packet);
+ }
+
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Client must ignore any advertise message that includes a status code option
+ // containing the value noaddrsavail, with the exception that the client may
+ // display the associated status message to the user.
+ // See the details in the section-17.1.3 of rfc-3315.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptStatusCode
+ );
+
+ if (Option != NULL) {
+ StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
+ if (StsCode != Dhcp6StsSuccess) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Callback to user with the received packet and check the user's feedback.
+ //
+ Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Success means user choose the current advertisement packet.
+ //
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ //
+ // Store the selected advertisement packet and set a flag.
+ //
+ Instance->AdSelect = AllocateZeroPool (Packet->Size);
+
+ if (Instance->AdSelect == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->AdSelect, Packet, Packet->Size);
+
+ Instance->AdPref = 0xff;
+
+ } else if (Status == EFI_NOT_READY) {
+ //
+ // Not_ready means user wants to continue to receive more advertise packets.
+ //
+ if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) {
+ //
+ // It's a tricky point. The timer routine set adpref as 0xff if the first
+ // rt timeout and no advertisement received, which means any advertisement
+ // received will be selected after the first rt.
+ //
+ Timeout = TRUE;
+ }
+
+ //
+ // Check whether the current packet has a 255 preference option or not.
+ // Take non-preference option as 0 value.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptPreference
+ );
+
+ if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) {
+ //
+ // No advertisements received before or preference is more than other
+ // advertisements received before. Then store the new packet and the
+ // preference value.
+ //
+ if (Instance->AdSelect != NULL) {
+ FreePool (Instance->AdSelect);
+ }
+
+ Instance->AdSelect = AllocateZeroPool (Packet->Size);
+
+ if (Instance->AdSelect == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (Instance->AdSelect, Packet, Packet->Size);
+
+ if (Option != NULL) {
+ Instance->AdPref = *(Option + 4);
+ }
+ } else {
+ //
+ // Non-preference and other advertisements received before or current
+ // preference is less than other advertisements received before.
+ // Leave the packet alone.
+ }
+
+ } else {
+ //
+ // Other error status means termination.
+ //
+ return Status;
+ }
+
+ //
+ // Client must collect advertise messages as more as possible until the first
+ // RT has elapsed, or get a highest preference 255 advertise.
+ // See details in the section-17.1.2 of rfc-3315.
+ //
+ if (Instance->AdPref == 0xff || Timeout) {
+ Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
+ }
+
+ return Status;
+}
+
+
+/**
+ The Dhcp6 stateful exchange process routine.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the received Dhcp6 message.
+
+**/
+VOID
+Dhcp6HandleStateful (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_DUID *ClientId;
+ DHCP6_SERVICE *Service;
+ UINT8 *Option;
+
+ Service = Instance->Service;
+ ClientId = Service->ClientId;
+ Status = EFI_SUCCESS;
+
+ if (Instance->InDestory || Instance->Config == NULL) {
+ goto ON_CONTINUE;
+ }
+
+ ASSERT (ClientId);
+ ASSERT (Instance->Config);
+ ASSERT (Instance->IaCb.Ia);
+
+ //
+ // Discard the packet if not advertisement or reply packet.
+ //
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Check whether include client Id or not.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptClientId
+ );
+
+ if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Check whether include server Id or not.
+ //
+ Option = Dhcp6SeekOption(
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptServerId
+ );
+
+ if (Option == NULL) {
+ goto ON_CONTINUE;
+ }
+
+ switch (Instance->IaCb.Ia->State) {
+ case Dhcp6Selecting:
+ //
+ // Handle the advertisement message when in the Dhcp6Selecting state.
+ // Do not need check return status, if failed, just continue to the next.
+ //
+ Dhcp6HandleAdvertiseMsg (Instance, Packet);
+ break;
+
+ case Dhcp6Requesting:
+ case Dhcp6Confirming:
+ case Dhcp6Renewing:
+ case Dhcp6Rebinding:
+ case Dhcp6Releasing:
+ case Dhcp6Declining:
+ //
+ // Handle the reply message when in the Dhcp6Requesting, Dhcp6Renewing
+ // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state.
+ // If failed here, it should reset the current session.
+ //
+ Status = Dhcp6HandleReplyMsg (Instance, Packet);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ break;
+ default:
+ //
+ // Other state has not supported yet.
+ //
+ break;
+ }
+
+ON_CONTINUE:
+ //
+ // Continue to receive the following Dhcp6 message.
+ //
+ Status = UdpIoRecvDatagram (
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ Dhcp6CleanupSession (Instance, Status);
+ }
+}
+
+
+/**
+ The Dhcp6 stateless exchange process routine.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Packet The pointer to the received Dhcp6 message.
+
+**/
+VOID
+Dhcp6HandleStateless (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ DHCP6_SERVICE *Service;
+ DHCP6_INF_CB *InfCb;
+ UINT8 *Option;
+ BOOLEAN IsMatched;
+
+ Service = Instance->Service;
+ Status = EFI_SUCCESS;
+ IsMatched = FALSE;
+ InfCb = NULL;
+
+ if (Instance->InDestory) {
+ goto ON_EXIT;
+ }
+
+ if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether it's a desired Info-request message by Xid.
+ //
+ while (!IsListEmpty (&Instance->InfList)) {
+ InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link);
+ if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) {
+ IsMatched = TRUE;
+ break;
+ }
+ }
+
+ if (!IsMatched) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether include server Id or not.
+ //
+ Option = Dhcp6SeekOption (
+ Packet->Dhcp6.Option,
+ Packet->Length - 4,
+ Dhcp6OptServerId
+ );
+
+ if (Option == NULL) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Callback to user with the received packet and check the user's feedback.
+ //
+ Status = InfCb->ReplyCallback (
+ &Instance->Dhcp6,
+ InfCb->CallbackContext,
+ Packet
+ );
+
+ if (Status == EFI_NOT_READY) {
+ //
+ // Success or aborted will both stop this info-request exchange process,
+ // but not ready means user wants to continue to receive reply.
+ //
+ goto ON_EXIT;
+ }
+
+ //
+ // Dequeue the sent packet from the txlist if the xid matched, and ignore
+ // if no xid matched.
+ //
+ Dhcp6DequeueRetry (
+ Instance,
+ Packet->Dhcp6.Header.TransactionId,
+ FALSE
+ );
+
+ //
+ // For sync, set the status out of polling for info-request.
+ //
+ Instance->UdpSts = Status;
+
+ON_EXIT:
+
+ Status = UdpIoRecvDatagram (
+ Service->UdpIo,
+ Dhcp6ReceivePacket,
+ Service,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS);
+ }
+}
+
+
+/**
+ The receive callback function for Dhcp6 exchange process.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6ReceivePacket (
+ IN NET_BUF *Udp6Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ EFI_DHCP6_HEADER *Head;
+ EFI_DHCP6_PACKET *Packet;
+ DHCP6_SERVICE *Service;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_TX_CB *TxCb;
+ UINT32 Size;
+ BOOLEAN IsDispatched;
+ BOOLEAN IsStateless;
+ LIST_ENTRY *Entry1;
+ LIST_ENTRY *Next1;
+ LIST_ENTRY *Entry2;
+ LIST_ENTRY *Next2;
+
+ ASSERT (Udp6Wrap != NULL);
+ ASSERT (Context != NULL);
+
+ Service = (DHCP6_SERVICE *) Context;
+ Instance = NULL;
+ Packet = NULL;
+ IsDispatched = FALSE;
+ IsStateless = FALSE;
+
+ if (EFI_ERROR (IoStatus)) {
+ return ;
+ }
+
+ //
+ // Copy the net buffer received from upd6 to a Dhcp6 packet.
+ //
+ Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize;
+ Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size);
+
+ if (Packet == NULL) {
+ goto ON_CONTINUE;
+ }
+
+ Packet->Size = Size;
+ Head = &Packet->Dhcp6.Header;
+ Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head);
+
+ if (Packet->Length == 0) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Dispatch packet to right instance by transaction id.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);
+
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link);
+
+ if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) {
+ //
+ // Find the corresponding packet in tx list, and check it whether belongs
+ // to stateful exchange process.
+ //
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+ IsStateless = TRUE;
+ }
+ IsDispatched = TRUE;
+ break;
+ }
+ }
+
+ if (IsDispatched) {
+ break;
+ }
+ }
+
+ //
+ // Skip this packet if not dispatched to any instance.
+ //
+ if (!IsDispatched) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // Dispatch the received packet ot the right instance.
+ //
+ if (IsStateless) {
+ Dhcp6HandleStateless (Instance, Packet);
+ } else {
+ Dhcp6HandleStateful (Instance, Packet);
+ }
+
+ON_CONTINUE:
+
+ NetbufFree (Udp6Wrap);
+
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+}
+
+/**
+ Detect Link movement for specified network device.
+
+ This routine will try to invoke Snp->GetStatus() to get the media status.
+ If media present status switches from unpresent to present, a link movement
+ is detected. Note that the underlying UNDI driver may not support reporting
+ media status from GET_STATUS command. If that, fail to detect link movement.
+
+ @param[in] Instance The pointer to DHCP6_INSTANCE.
+
+ @retval TRUE A link movement is detected.
+ @retval FALSE A link movement is not detected.
+
+**/
+BOOLEAN
+Dhcp6LinkMovDetect (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINT32 InterruptStatus;
+ BOOLEAN MediaPresent;
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+
+ ASSERT (Instance != NULL);
+ Snp = Instance->Service->Snp;
+ MediaPresent = Instance->MediaPresent;
+
+ //
+ // Check whether SNP support media detection
+ //
+ if (!Snp->Mode->MediaPresentSupported) {
+ return FALSE;
+ }
+
+ //
+ // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data
+ //
+ Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ Instance->MediaPresent = Snp->Mode->MediaPresent;
+ //
+ // Media transimit Unpresent to Present means new link movement is detected.
+ //
+ if (!MediaPresent && Instance->MediaPresent) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/**
+ The timer routine of the Dhcp6 instance for each second.
+
+ @param[in] Event The timer event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ DHCP6_INSTANCE *Instance;
+ DHCP6_TX_CB *TxCb;
+ DHCP6_IA_CB *IaCb;
+ UINT32 LossTime;
+
+ ASSERT (Context != NULL);
+
+ Instance = (DHCP6_INSTANCE *) Context;
+
+ //
+ // 1. Loop the tx list, count live time of every tx packet to check whether
+ // need re-transmit or not.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
+
+ TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
+
+ TxCb->TickTime++;
+
+ if (TxCb->TickTime > TxCb->RetryExp) {
+ //
+ // Handle the first rt in the transmission of solicit specially.
+ //
+ if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {
+ if (Instance->AdSelect == NULL) {
+ //
+ // Set adpref as 0xff here to indicate select any advertisement
+ // afterwards.
+ //
+ Instance->AdPref = 0xff;
+ } else {
+ //
+ // Select the advertisement received before.
+ //
+ Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
+ return;
+ }
+ }
+ //
+ // Increase the retry count for the packet and add up the total loss time.
+ //
+ TxCb->RetryCnt++;
+ TxCb->RetryLos += TxCb->RetryExp;
+
+ //
+ // Check whether overflow the max retry count limit for this packet
+ //
+ if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {
+ goto ON_CLOSE;
+ }
+
+ //
+ // Check whether overflow the max retry duration for this packet
+ //
+ if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {
+ goto ON_CLOSE;
+ }
+
+ //
+ // Re-calculate retry expire timeout for the next time.
+ //
+ // Firstly, Check the new calculated time whether overflow the max retry
+ // expire time.
+ //
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryExp,
+ FALSE,
+ TRUE
+ );
+
+ if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) {
+ TxCb->RetryExp = Dhcp6CalculateExpireTime (
+ TxCb->RetryCtl.Mrt,
+ TRUE,
+ TRUE
+ );
+ }
+
+ //
+ // Secondly, Check the new calculated time whether overflow the max retry
+ // duration time.
+ //
+ LossTime = TxCb->RetryLos + TxCb->RetryExp;
+ if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) {
+ TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos;
+ }
+
+ //
+ // Reset the tick time for the next retransmission
+ //
+ TxCb->TickTime = 0;
+
+ //
+ // Retransmit the last sent packet again.
+ //
+ Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);
+ }
+ }
+
+ //
+ // 2. Check the configured Ia, count lease time of every valid Ia to check
+ // whether need to renew or rebind this Ia.
+ //
+ IaCb = &Instance->IaCb;
+
+ if (Instance->Config == NULL || IaCb->Ia == NULL) {
+ return;
+ }
+
+ if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) {
+
+ IaCb->LeaseTime++;
+
+ if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) {
+ //
+ // Exceed t2, send rebind packet to extend the Ia lease.
+ //
+ Dhcp6SendRenewRebindMsg (Instance, TRUE);
+
+ } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) {
+
+ //
+ // Exceed t1, send renew packet to extend the Ia lease.
+ //
+ Dhcp6SendRenewRebindMsg (Instance, FALSE);
+ }
+ }
+
+ //
+ // 3. In any situation when a client may have moved to a new link, the
+ // client MUST initiate a Confirm/Reply message exchange.
+ //
+ if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) {
+ Dhcp6SendConfirmMsg (Instance);
+ }
+
+ return;
+
+ ON_CLOSE:
+
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||
+ TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew ||
+ TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm
+ ) {
+ //
+ // The failure of renew/Confirm will still switch to the bound state.
+ //
+ if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) ||
+ (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) {
+ ASSERT (Instance->IaCb.Ia);
+ Instance->IaCb.Ia->State = Dhcp6Bound;
+ }
+ //
+ // The failure of info-request will return no response.
+ //
+ if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
+ Instance->UdpSts = EFI_NO_RESPONSE;
+ }
+ Dhcp6DequeueRetry (
+ Instance,
+ TxCb->Xid,
+ TRUE
+ );
+ } else {
+ //
+ // The failure of the others will terminate current state machine if timeout.
+ //
+ Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE);
+ }
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h
new file mode 100644
index 0000000000..31459c96d3
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h
@@ -0,0 +1,193 @@
+/** @file
+ Dhcp6 internal functions declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_IO_H__
+#define __EFI_DHCP6_IO_H__
+
+
+/**
+ Clean up the specific nodes in the retry list.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] Scope The scope of cleanup nodes.
+
+**/
+VOID
+Dhcp6CleanupRetry (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT32 Scope
+ );
+
+/**
+ Clean up the session of the instance stateful exchange.
+
+ @param[in, out] Instance The pointer to the Dhcp6 instance.
+ @param[in] Status The return status from udp.
+
+**/
+VOID
+Dhcp6CleanupSession (
+ IN OUT DHCP6_INSTANCE *Instance,
+ IN EFI_STATUS Status
+ );
+
+/**
+ Create the solicit message and send it.
+
+ @param[in] Instance The pointer to Dhcp6 instance.
+
+ @retval EFI_SUCCESS Create and send the solicit message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the solicit message.
+
+**/
+EFI_STATUS
+Dhcp6SendSolicitMsg (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+/**
+ Create the request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+ @retval EFI_SUCCESS Create and send the request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the request message.
+
+**/
+EFI_STATUS
+Dhcp6SendRequestMsg (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+/**
+ Create the renew/rebind message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RebindRequest If TRUE, it is a Rebind type message.
+ Otherwise, it is a Renew type message.
+
+ @retval EFI_SUCCESS Create and send the renew/rebind message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the renew/rebind message.
+
+**/
+EFI_STATUS
+Dhcp6SendRenewRebindMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN BOOLEAN RebindRequest
+ );
+
+/**
+ Create the decline message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] DecIa The pointer to the decline Ia.
+
+ @retval EFI_SUCCESS Create and send the decline message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the decline message.
+
+**/
+EFI_STATUS
+Dhcp6SendDeclineMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *DecIa
+ );
+
+/**
+ Create the release message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] RelIa The pointer to the release Ia.
+
+ @retval EFI_SUCCESS Create and send the release message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others Failed to send the release message.
+
+**/
+EFI_STATUS
+Dhcp6SendReleaseMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN EFI_DHCP6_IA *RelIa
+ );
+
+/**
+ Create the information request message and send it.
+
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[in] InfCb The pointer to the information request control block.
+ @param[in] SendClientId If TRUE, the client identifier option will be included in
+ information request message. Otherwise, the client identifier
+ option will not be included.
+ @param[in] OptionRequest The pointer to the option request option.
+ @param[in] OptionCount The number options in the OptionList.
+ @param[in] OptionList The array pointers to the appended options.
+ @param[in] Retransmission The pointer to the retransmission control.
+
+ @retval EFI_SUCCESS Create and send the info-request message successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval Others Failed to send the info-request message.
+
+**/
+EFI_STATUS
+Dhcp6SendInfoRequestMsg (
+ IN DHCP6_INSTANCE *Instance,
+ IN DHCP6_INF_CB *InfCb,
+ IN BOOLEAN SendClientId,
+ IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
+ IN UINT32 OptionCount,
+ IN EFI_DHCP6_PACKET_OPTION *OptionList[],
+ IN EFI_DHCP6_RETRANSMISSION *Retransmission
+ );
+
+/**
+ The receive callback function for the Dhcp6 exchange process.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6ReceivePacket (
+ IN NET_BUF *Udp6Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+/**
+ The timer routine of the Dhcp6 instance for each second.
+
+ @param[in] Event The timer event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
new file mode 100644
index 0000000000..be7a985551
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
@@ -0,0 +1,1146 @@
+/** @file
+ Dhcp6 support functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Dhcp6Impl.h"
+
+
+/**
+ Generate client Duid in the format of Duid-llt.
+
+ @param[in] Mode The pointer to the mode of SNP.
+
+ @retval NULL If it failed to generate a client Id.
+ @retval others The pointer to the new client id.
+
+**/
+EFI_DHCP6_DUID *
+Dhcp6GenerateClientId (
+ IN EFI_SIMPLE_NETWORK_MODE *Mode
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP6_DUID *Duid;
+ EFI_TIME Time;
+ UINT32 Stamp;
+
+ //
+ // Attempt to get client Id from variable to keep it constant.
+ // See details in section-9 of rfc-3315.
+ //
+ Duid = GetVariable (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid);
+ if (Duid != NULL) {
+ return Duid;
+ }
+
+ //
+ // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.
+ //
+ gRT->GetTime (&Time, NULL);
+ Stamp = (UINT32)
+ (
+ (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *
+ 60 +
+ Time.Second
+ );
+
+ //
+ // The format of client identifier option:
+ //
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_CLIENTID | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // . .
+ // . DUID .
+ // . (variable length) .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ //
+ // The format of DUID-LLT:
+ //
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Duid type (1) | hardware type (16 bits) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | time (32 bits) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // . .
+ // . link-layer address (variable length) .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes
+ //
+ Duid = AllocateZeroPool (10 + Mode->HwAddressSize);
+ if (Duid == NULL) {
+ return NULL;
+ }
+
+ //
+ // sizeof (Duid-type + hardware-type + time) = 8 bytes
+ //
+ Duid->Length = (UINT16) (Mode->HwAddressSize + 8);
+
+ //
+ // Set the Duid-type, hardware-type, time and copy the hardware address.
+ //
+ WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt));
+ WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET));
+ WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp));
+
+ CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);
+
+ Status = gRT->SetVariable (
+ L"ClientId",
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ Duid->Length + 2,
+ (VOID *) Duid
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Duid;
+}
+
+
+/**
+ Copy the Dhcp6 configure data.
+
+ @param[in] DstCfg The pointer to the destination configure data.
+ @param[in] SorCfg The pointer to the source configure data.
+
+ @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CopyConfigData (
+ IN EFI_DHCP6_CONFIG_DATA *DstCfg,
+ IN EFI_DHCP6_CONFIG_DATA *SorCfg
+ )
+{
+ UINTN Index;
+ UINTN OptionListSize;
+ UINTN OptionSize;
+
+ CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA));
+
+ //
+ // Allocate another buffer for solicitretransmission, and copy it.
+ //
+ if (SorCfg->SolicitRetransmission != NULL) {
+
+ DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
+
+ if (DstCfg->SolicitRetransmission == NULL) {
+ //
+ // Error will be handled out of this function.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (
+ DstCfg->SolicitRetransmission,
+ SorCfg->SolicitRetransmission,
+ sizeof (EFI_DHCP6_RETRANSMISSION)
+ );
+ }
+
+ if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) {
+
+ OptionListSize = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *);
+ DstCfg->OptionList = AllocateZeroPool (OptionListSize);
+
+ if (DstCfg->OptionList == NULL) {
+ //
+ // Error will be handled out of this function.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < SorCfg->OptionCount; Index++) {
+
+ OptionSize = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4;
+ DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize);
+
+ if (DstCfg->OptionList[Index] == NULL) {
+ //
+ // Error will be handled out of this function.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (
+ DstCfg->OptionList[Index],
+ SorCfg->OptionList[Index],
+ OptionSize
+ );
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Clean up the configure data.
+
+ @param[in, out] CfgData The pointer to the configure data.
+
+**/
+VOID
+Dhcp6CleanupConfigData (
+ IN OUT EFI_DHCP6_CONFIG_DATA *CfgData
+ )
+{
+ UINTN Index;
+
+ ASSERT (CfgData != NULL);
+ //
+ // Clean up all fields in config data including the reference buffers, but do
+ // not free the config data buffer itself.
+ //
+ if (CfgData->OptionList != NULL) {
+ for (Index = 0; Index < CfgData->OptionCount; Index++) {
+ if (CfgData->OptionList[Index] != NULL) {
+ FreePool (CfgData->OptionList[Index]);
+ }
+ }
+ FreePool (CfgData->OptionList);
+ }
+
+ if (CfgData->SolicitRetransmission != NULL) {
+ FreePool (CfgData->SolicitRetransmission);
+ }
+
+ ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA));
+}
+
+
+/**
+ Clean up the mode data.
+
+ @param[in, out] ModeData The pointer to the mode data.
+
+**/
+VOID
+Dhcp6CleanupModeData (
+ IN OUT EFI_DHCP6_MODE_DATA *ModeData
+ )
+{
+ ASSERT (ModeData != NULL);
+ //
+ // Clean up all fields in mode data including the reference buffers, but do
+ // not free the mode data buffer itself.
+ //
+ if (ModeData->ClientId != NULL) {
+ FreePool (ModeData->ClientId);
+ }
+
+ if (ModeData->Ia != NULL) {
+
+ if (ModeData->Ia->ReplyPacket != NULL) {
+ FreePool (ModeData->Ia->ReplyPacket);
+ }
+ FreePool (ModeData->Ia);
+ }
+
+ ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA));
+}
+
+
+/**
+ Calculate the expire time by the algorithm defined in rfc.
+
+ @param[in] Base The base value of the time.
+ @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.
+ @param[in] NeedSigned If TRUE, the the signed factor is needed.
+
+ @return Expire The calculated result for the new expire time.
+
+**/
+UINT32
+Dhcp6CalculateExpireTime (
+ IN UINT32 Base,
+ IN BOOLEAN IsFirstRt,
+ IN BOOLEAN NeedSigned
+ )
+{
+ EFI_TIME Time;
+ BOOLEAN Signed;
+ UINT32 Seed;
+ UINT32 Expire;
+
+ //
+ // Take the 10bits of microsecond in system time as a uniform distribution.
+ // Take the 10th bit as a flag to determine it's signed or not.
+ //
+ gRT->GetTime (&Time, NULL);
+ Seed = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK);
+ Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE);
+ Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE);
+
+ //
+ // Calculate expire by the following algo:
+ // 1. base + base * (-0.1 ~ 0) for the first solicit
+ // 2. base + base * (-0.1 ~ 0.1) for the first other messages
+ // 3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages
+ // 4. base + base * (-0.1 ~ 0) for the more than mrt timeout
+ //
+ // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1).
+ //
+ if (IsFirstRt && Signed) {
+
+ Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+
+ } else if (IsFirstRt && !Signed) {
+
+ Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+
+ } else if (!IsFirstRt && Signed) {
+
+ Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+
+ } else {
+
+ Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
+ }
+
+ Expire = (Expire != 0) ? Expire : 1;
+
+ return Expire;
+}
+
+
+/**
+ Calculate the lease time by the algorithm defined in rfc.
+
+ @param[in] IaCb The pointer to the Ia control block.
+
+**/
+VOID
+Dhcp6CalculateLeaseTime (
+ IN DHCP6_IA_CB *IaCb
+ )
+{
+ EFI_DHCP6_IA_ADDRESS *IaAddr;
+ UINT32 MinLt;
+ UINT32 MaxLt;
+ UINTN Index;
+
+ ASSERT (IaCb->Ia->IaAddressCount > 0);
+
+ MinLt = (UINT32) (-1);
+ MaxLt = 0;
+
+ //
+ // Calculate minlt as min of all valid life time, and maxlt as max of all
+ // valid life time.
+ //
+ for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {
+ IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);
+ MinLt = MIN (MinLt, IaAddr->ValidLifetime);
+ MaxLt = MAX (MinLt, IaAddr->ValidLifetime);
+ }
+
+ //
+ // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer
+ // such information.
+ //
+ IaCb->T1 = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10);
+ IaCb->T2 = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10);
+ IaCb->AllExpireTime = MaxLt;
+ IaCb->LeaseTime = 0;
+}
+
+
+/**
+ Check whether the addresses are all included by the configured Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval EFI_SUCCESS The addresses are all included by the configured IA.
+ @retval EFI_NOT_FOUND The addresses are not included by the configured IA.
+
+**/
+EFI_STATUS
+Dhcp6CheckAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ UINTN Index1;
+ UINTN Index2;
+ BOOLEAN Found;
+
+ //
+ // Check whether the addresses are all included by the configured IA. And it
+ // will return success if address count is zero, which means all addresses.
+ //
+ for (Index1 = 0; Index1 < AddressCount; Index1++) {
+
+ Found = FALSE;
+
+ for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
+
+ if (CompareMem (
+ &Addresses[Index1],
+ &Ia->IaAddress[Index2],
+ sizeof (EFI_IPv6_ADDRESS)
+ ) == 0) {
+
+ Found = TRUE;
+ break;
+ }
+ }
+
+ if (!Found) {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Deprive the addresses from current Ia, and generate another eliminated Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval NULL If it failed to generate the deprived Ia.
+ @retval others The pointer to the deprived Ia.
+
+**/
+EFI_DHCP6_IA *
+Dhcp6DepriveAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ )
+{
+ EFI_DHCP6_IA *IaCopy;
+ UINTN IaCopySize;
+ UINTN Index1;
+ UINTN Index2;
+ BOOLEAN Found;
+
+ if (AddressCount == 0) {
+ //
+ // It means release all Ia addresses if address count is zero.
+ //
+ AddressCount = Ia->IaAddressCount;
+ }
+
+ ASSERT (AddressCount != 0);
+
+ IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ IaCopy = AllocateZeroPool (IaCopySize);
+
+ if (IaCopy == NULL) {
+ return NULL;
+ }
+
+ if (AddressCount == Ia->IaAddressCount) {
+ //
+ // If release all Ia addresses, just copy the configured Ia and then set
+ // its address count as zero.
+ // We may decline/release part of addresses at the begining. So it's a
+ // forwarding step to update address infor for decline/release, while the
+ // other infor such as Ia state will be updated when receiving reply.
+ //
+ CopyMem (IaCopy, Ia, IaCopySize);
+ Ia->IaAddressCount = 0;
+ return IaCopy;
+ }
+
+ CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA));
+
+ //
+ // Move the addresses from the Ia of instance to the deprived Ia.
+ //
+ for (Index1 = 0; Index1 < AddressCount; Index1++) {
+
+ Found = FALSE;
+
+ for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
+
+ if (CompareMem (
+ &Addresses[Index1],
+ &Ia->IaAddress[Index2],
+ sizeof (EFI_IPv6_ADDRESS)
+ ) == 0) {
+ //
+ // Copy the deprived address to the copy of Ia
+ //
+ CopyMem (
+ &IaCopy->IaAddress[Index1],
+ &Ia->IaAddress[Index2],
+ sizeof (EFI_DHCP6_IA_ADDRESS)
+ );
+ //
+ // Delete the deprived address from the instance Ia
+ //
+ if (Index2 + 1 < Ia->IaAddressCount) {
+ CopyMem (
+ &Ia->IaAddress[Index2],
+ &Ia->IaAddress[Index2 + 1],
+ (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS)
+ );
+ }
+ Found = TRUE;
+ break;
+ }
+ }
+ ASSERT (Found == TRUE);
+ }
+
+ Ia->IaAddressCount -= AddressCount;
+ IaCopy->IaAddressCount = AddressCount;
+
+ return IaCopy;
+}
+
+
+/**
+ The dummy ext buffer free callback routine.
+
+ @param[in] Arg The pointer to the parameter.
+
+**/
+VOID
+EFIAPI
+Dhcp6DummyExtFree (
+ IN VOID *Arg
+ )
+{
+}
+
+
+/**
+ The callback routine once message transmitted.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTransmitted (
+ IN NET_BUF *Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ NetbufFree (Wrap);
+}
+
+
+/**
+ Append the option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the buffer.
+ @param[in] OptType The option type.
+ @param[in] OptLen The length of option contents.
+ @param[in] Data The pointer to the option content.
+
+ @return Buf The position to append the next option.
+
+**/
+UINT8 *
+Dhcp6AppendOption (
+ IN OUT UINT8 *Buf,
+ IN UINT16 OptType,
+ IN UINT16 OptLen,
+ IN UINT8 *Data
+ )
+{
+ //
+ // The format of Dhcp6 option:
+ //
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-code | option-len (option data) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | option-data |
+ // | (option-len octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ ASSERT (OptLen != 0);
+
+ WriteUnaligned16 ((UINT16 *) Buf, OptType);
+ Buf += 2;
+ WriteUnaligned16 ((UINT16 *) Buf, OptLen);
+ Buf += 2;
+ CopyMem (Buf, Data, NTOHS (OptLen));
+ Buf += NTOHS (OptLen);
+
+ return Buf;
+}
+
+
+/**
+ Append the appointed Ia option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Ia The pointer to the Ia.
+ @param[in] T1 The time of T1.
+ @param[in] T2 The time of T2.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendIaOption (
+ IN OUT UINT8 *Buf,
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 T1,
+ IN UINT32 T2
+ )
+{
+ UINT8 *AddrOpt;
+ UINT16 *Len;
+ UINTN Index;
+ UINT16 Length;
+
+ //
+ // The format of IA_NA and IA_TA option:
+ //
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IA_NA | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | IAID (4 octets) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T1 (only for IA_NA) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | T2 (only for IA_NA) |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . IA_NA-options/IA_TA-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // Fill the value of Ia option type
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type));
+ Buf += 2;
+
+ //
+ // Fill the len of Ia option later, keep the pointer first
+ //
+ Len = (UINT16 *) Buf;
+ Buf += 2;
+
+ //
+ // Fill the value of iaid
+ //
+ WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId));
+ Buf += 4;
+
+ //
+ // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.
+ //
+ if (Ia->Descriptor.Type == Dhcp6OptIana) {
+ WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff));
+ Buf += 4;
+ WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff));
+ Buf += 4;
+ }
+
+ //
+ // Fill all the addresses belong to the Ia
+ //
+ for (Index = 0; Index < Ia->IaAddressCount; Index++) {
+
+ AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);
+ Length = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS));
+ Buf = Dhcp6AppendOption (
+ Buf,
+ HTONS (Dhcp6OptIaAddr),
+ Length,
+ AddrOpt
+ );
+ }
+
+ //
+ // Fill the value of Ia option length
+ //
+ *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2));
+
+ return Buf;
+}
+
+/**
+ Append the appointed Elapsed time option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[out] Elapsed The pointer to the elapsed time value in
+ the generated packet.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendETOption (
+ IN OUT UINT8 *Buf,
+ IN DHCP6_INSTANCE *Instance,
+ OUT UINT16 **Elapsed
+ )
+{
+ //
+ // The format of elapsed time option:
+ //
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_ELAPSED_TIME | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | elapsed-time |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // Fill the value of elapsed-time option type.
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime));
+ Buf += 2;
+
+ //
+ // Fill the len of elapsed-time option, which is fixed.
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS(2));
+ Buf += 2;
+
+ //
+ // Fill in elapsed time value with 0 value for now. The actual value is
+ // filled in later just before the packet is transmitted.
+ //
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS(0));
+ *Elapsed = (UINT16 *) Buf;
+ Buf += 2;
+
+ return Buf;
+}
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ EFI_TIME Time;
+ UINT64 CurrentStamp;
+ UINT64 ElapsedTimeValue;
+
+ //
+ // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month.
+ //
+ gRT->GetTime (&Time, NULL);
+ CurrentStamp = (UINT64)
+ (
+ ((((((Time.Year - 2000) * 360 +
+ (Time.Month - 1)) * 30 +
+ (Time.Day - 1)) * 24 + Time.Hour) * 60 +
+ Time.Minute) * 60 + Time.Second) * 100
+ + DivU64x32(Time.Nanosecond, 10000000)
+ );
+
+ //
+ // Sentinel value of 0 means that this is the first DHCP packet that we are
+ // sending and that we need to initialize the value. First DHCP Solicit
+ // gets 0 elapsed-time. Otherwise, calculate based on StartTime.
+ //
+ if (Instance->StartTime == 0) {
+ ElapsedTimeValue = 0;
+ Instance->StartTime = CurrentStamp;
+ } else {
+ ElapsedTimeValue = CurrentStamp - Instance->StartTime;
+
+ //
+ // If elapsed time cannot fit in two bytes, set it to 0xffff.
+ //
+ if (ElapsedTimeValue > 0xffff) {
+ ElapsedTimeValue = 0xffff;
+ }
+ }
+ WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue));
+}
+
+
+/**
+ Seek the address of the first byte of the option header.
+
+ @param[in] Buf The pointer to the buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] OptType The option type.
+
+ @retval NULL If it failed to seek the option.
+ @retval others The position to the option.
+
+**/
+UINT8 *
+Dhcp6SeekOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN UINT16 OptType
+ )
+{
+ UINT8 *Cursor;
+ UINT8 *Option;
+ UINT16 DataLen;
+ UINT16 OpCode;
+
+ Option = NULL;
+ Cursor = Buf;
+
+ //
+ // The format of Dhcp6 option refers to Dhcp6AppendOption().
+ //
+ while (Cursor < Buf + SeekLen) {
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
+ if (OpCode == HTONS (OptType)) {
+ Option = Cursor;
+ break;
+ }
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
+ Cursor += (DataLen + 4);
+ }
+
+ return Option;
+}
+
+
+/**
+ Seek the address of the first byte of the Ia option header.
+
+ @param[in] Buf The pointer to the buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] IaDesc The pointer to the Ia descriptor.
+
+ @retval NULL If it failed to seek the Ia option.
+ @retval others The position to the Ia option.
+
+**/
+UINT8 *
+Dhcp6SeekIaOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc
+ )
+{
+ UINT8 *Cursor;
+ UINT8 *Option;
+ UINT16 DataLen;
+ UINT16 OpCode;
+ UINT32 IaId;
+
+ //
+ // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption().
+ //
+ Option = NULL;
+ Cursor = Buf;
+
+ while (Cursor < Buf + SeekLen) {
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
+ IaId = ReadUnaligned32 ((UINT32 *) (Cursor + 4));
+ if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) {
+ Option = Cursor;
+ break;
+ }
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
+ Cursor += (DataLen + 4);
+ }
+
+ return Option;
+}
+
+
+/**
+ Parse the address option and update the address infomation.
+
+ @param[in] IaInnerOpt The pointer to the buffer.
+ @param[in] IaInnerLen The length to parse.
+ @param[out] AddrNum The number of addresses.
+ @param[in, out] AddrBuf The pointer to the address buffer.
+
+**/
+VOID
+Dhcp6ParseAddrOption (
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ OUT UINT32 *AddrNum,
+ IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf
+ )
+{
+ UINT8 *Cursor;
+ UINT16 DataLen;
+ UINT16 OpCode;
+ UINT32 ValidLt;
+
+ //
+ // The format of the IA Address option:
+ //
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPTION_IAADDR | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // | IPv6 address |
+ // | |
+ // | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | preferred-lifetime |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | valid-lifetime |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // . .
+ // . IAaddr-options .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // Two usage model:
+ //
+ // 1. Pass addrbuf == null, to get the addrnum over the Ia inner options.
+ // 2. Pass addrbuf != null, to resolve the addresses over the Ia inner
+ // options to the addrbuf.
+ //
+
+ Cursor = IaInnerOpt;
+ *AddrNum = 0;
+
+ while (Cursor < IaInnerOpt + IaInnerLen) {
+ //
+ // Count the Ia address option with non-0 valid time.
+ //
+ OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
+ ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24));
+ if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) {
+
+ if (AddrBuf != NULL) {
+ CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS));
+ AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime);
+ AddrBuf->ValidLifetime = NTOHL (AddrBuf->ValidLifetime);
+ AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));
+ }
+
+ (*AddrNum)++;
+ }
+ DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
+ Cursor += (DataLen + 4);
+ }
+}
+
+
+/**
+ Create a control blcok for the Ia according to the corresponding options.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+ @param[in] IaInnerOpt The pointer to the inner options in the Ia option.
+ @param[in] IaInnerLen The length of all the inner options in the Ia option.
+ @param[in] T1 T1 time in the Ia option.
+ @param[in] T2 T2 time in the Ia option.
+
+ @retval EFI_NOT_FOUND No valid IA option is found.
+ @retval EFI_SUCCESS Create an IA control block successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6GenerateIaCb (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ IN UINT32 T1,
+ IN UINT32 T2
+ )
+{
+ UINT32 AddrNum;
+ UINT32 IaSize;
+ EFI_DHCP6_IA *Ia;
+
+ if (Instance->IaCb.Ia == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Calculate the number of addresses for this Ia, excluding the addresses with
+ // the value 0 of valid lifetime.
+ //
+ Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL);
+
+ if (AddrNum == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Allocate for new IA.
+ //
+ IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ Ia = AllocateZeroPool (IaSize);
+
+ if (Ia == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill up this new IA fields.
+ //
+ Ia->State = Instance->IaCb.Ia->State;
+ Ia->IaAddressCount = AddrNum;
+ CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));
+ Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);
+
+ //
+ // Free original IA resource.
+ //
+ if (Instance->IaCb.Ia->ReplyPacket != NULL) {
+ FreePool (Instance->IaCb.Ia->ReplyPacket);
+ }
+ FreePool (Instance->IaCb.Ia);
+
+
+ ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB));
+
+ //
+ // Update IaCb to use new IA.
+ //
+ Instance->IaCb.Ia = Ia;
+
+ //
+
+ // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime.
+ //
+ Instance->IaCb.T1 = T1;
+ Instance->IaCb.T2 = T2;
+ Dhcp6CalculateLeaseTime (&Instance->IaCb);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cache the current IA configuration information.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+
+ @retval EFI_SUCCESS Cache the current IA successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CacheIa (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINTN IaSize;
+ EFI_DHCP6_IA *Ia;
+
+ Ia = Instance->IaCb.Ia;
+
+ if ((Instance->CacheIa == NULL) && (Ia != NULL)) {
+ //
+ // Cache the current IA.
+ //
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+
+ Instance->CacheIa = AllocateZeroPool (IaSize);
+ if (Instance->CacheIa == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (Instance->CacheIa, Ia, IaSize);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.
+
+ @param[in] Instance The pointer to DHCP6 instance.
+
+**/
+VOID
+Dhcp6AppendCacheIa (
+ IN DHCP6_INSTANCE *Instance
+ )
+{
+ UINT8 *Ptr;
+ UINTN Index;
+ UINTN IaSize;
+ UINTN NewIaSize;
+ EFI_DHCP6_IA *Ia;
+ EFI_DHCP6_IA *NewIa;
+ EFI_DHCP6_IA *CacheIa;
+
+ Ia = Instance->IaCb.Ia;
+ CacheIa = Instance->CacheIa;
+
+ if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) {
+ //
+ // There are old addresses existing. Merge with current addresses.
+ //
+ NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ NewIa = AllocateZeroPool (NewIaSize);
+ if (NewIa == NULL) {
+ return;
+ }
+
+ IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
+ CopyMem (NewIa, Ia, IaSize);
+
+ //
+ // Clear old address.ValidLifetime
+ //
+ for (Index = 0; Index < CacheIa->IaAddressCount; Index++) {
+ CacheIa->IaAddress[Index].ValidLifetime = 0;
+ }
+
+ NewIa->IaAddressCount += CacheIa->IaAddressCount;
+ Ptr = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount];
+ CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS));
+
+ //
+ // Migrate to the NewIa and free previous.
+ //
+ FreePool (Instance->CacheIa);
+ FreePool (Instance->IaCb.Ia);
+ Instance->CacheIa = NULL;
+ Instance->IaCb.Ia = NewIa;
+ }
+}
diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h
new file mode 100644
index 0000000000..62985a3d72
--- /dev/null
+++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h
@@ -0,0 +1,340 @@
+/** @file
+ Dhcp6 support functions declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_DHCP6_UTILITY_H__
+#define __EFI_DHCP6_UTILITY_H__
+
+
+#define DHCP6_10_BIT_MASK 0x3ff
+
+/**
+ Generate client Duid in the format of Duid-llt.
+
+ @param[in] Mode The pointer to the mode of SNP.
+
+ @retval NULL if failed to generate client Id.
+ @retval Others The pointer to the new client id.
+
+**/
+EFI_DHCP6_DUID *
+Dhcp6GenerateClientId (
+ IN EFI_SIMPLE_NETWORK_MODE *Mode
+ );
+
+/**
+ Copy the Dhcp6 configure data.
+
+ @param[in] DstCfg The pointer to the destination configure data.
+ @param[in] SorCfg The pointer to the source configure data.
+
+ @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CopyConfigData (
+ IN EFI_DHCP6_CONFIG_DATA *DstCfg,
+ IN EFI_DHCP6_CONFIG_DATA *SorCfg
+ );
+
+/**
+ Clean up the configure data.
+
+ @param[in, out] CfgData The pointer to the configure data.
+
+**/
+VOID
+Dhcp6CleanupConfigData (
+ IN OUT EFI_DHCP6_CONFIG_DATA *CfgData
+ );
+
+/**
+ Clean up the mode data.
+
+ @param[in, out] ModeData The pointer to the mode data.
+
+**/
+VOID
+Dhcp6CleanupModeData (
+ IN OUT EFI_DHCP6_MODE_DATA *ModeData
+ );
+
+/**
+ Calculate the expire time by the algorithm defined in rfc.
+
+ @param[in] Base The base value of the time.
+ @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.
+ @param[in] NeedSigned If TRUE, the the signed factor is needed.
+
+ @return Expire The calculated result for the new expire time.
+
+**/
+UINT32
+Dhcp6CalculateExpireTime (
+ IN UINT32 Base,
+ IN BOOLEAN IsFirstRt,
+ IN BOOLEAN NeedSigned
+ );
+
+/**
+ Calculate the lease time by the algorithm defined in rfc.
+
+ @param[in] IaCb The pointer to the Ia control block.
+
+**/
+VOID
+Dhcp6CalculateLeaseTime (
+ IN DHCP6_IA_CB *IaCb
+ );
+
+/**
+ Check whether the addresses are all included by the configured Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval EFI_SUCCESS The addresses are all included by the configured IA.
+ @retval EFI_NOT_FOUND The addresses are not included by the configured IA.
+
+**/
+EFI_STATUS
+Dhcp6CheckAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ Deprive the addresses from current Ia, and generate another eliminated Ia.
+
+ @param[in] Ia The pointer to the Ia.
+ @param[in] AddressCount The number of addresses.
+ @param[in] Addresses The pointer to the addresses buffer.
+
+ @retval NULL If failed to generate the deprived Ia.
+ @retval others The pointer to the deprived Ia.
+
+**/
+EFI_DHCP6_IA *
+Dhcp6DepriveAddress (
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 AddressCount,
+ IN EFI_IPv6_ADDRESS *Addresses
+ );
+
+/**
+ The dummy ext buffer free callback routine.
+
+ @param[in] Arg The pointer to the parameter.
+
+**/
+VOID
+EFIAPI
+Dhcp6DummyExtFree (
+ IN VOID *Arg
+ );
+
+/**
+ The callback routine once message transmitted.
+
+ @param[in] Udp6Wrap The pointer to the received net buffer.
+ @param[in] EndPoint The pointer to the udp end point.
+ @param[in] IoStatus The return status from udp io.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+Dhcp6OnTransmitted (
+ IN NET_BUF *Wrap,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+/**
+ Append the appointed option to the buf, and move the buf to the end.
+
+ @param[in, out] Buf The pointer to buffer.
+ @param[in] OptType The option type.
+ @param[in] OptLen The lenght of option content.s
+ @param[in] Data The pointer to the option content.
+
+ @return Buf The position to append the next option.
+
+**/
+UINT8 *
+Dhcp6AppendOption (
+ IN OUT UINT8 *Buf,
+ IN UINT16 OptType,
+ IN UINT16 OptLen,
+ IN UINT8 *Data
+ );
+
+/**
+ Append the Ia option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Ia The pointer to the Ia.
+ @param[in] T1 The time of T1.
+ @param[in] T2 The time of T2.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendIaOption (
+ IN OUT UINT8 *Buf,
+ IN EFI_DHCP6_IA *Ia,
+ IN UINT32 T1,
+ IN UINT32 T2
+ );
+
+/**
+ Append the appointed Elapsed time option to Buf, and move Buf to the end.
+
+ @param[in, out] Buf The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+ @param[out] Elapsed The pointer to the elapsed time value in
+ the generated packet.
+
+ @return Buf The position to append the next Ia option.
+
+**/
+UINT8 *
+Dhcp6AppendETOption (
+ IN OUT UINT8 *Buf,
+ IN DHCP6_INSTANCE *Instance,
+ OUT UINT16 **Elapsed
+ );
+
+/**
+ Set the elapsed time based on the given instance and the pointer to the
+ elapsed time option.
+
+ @param[in] Elapsed The pointer to the position to append.
+ @param[in] Instance The pointer to the Dhcp6 instance.
+**/
+VOID
+SetElapsedTime (
+ IN UINT16 *Elapsed,
+ IN DHCP6_INSTANCE *Instance
+ );
+
+/**
+ Seek the address of the first byte of the option header.
+
+ @param[in] Buf The pointer to buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] OptType The option type.
+
+ @retval NULL If failed to seek the option.
+ @retval others The position to the option.
+
+**/
+UINT8 *
+Dhcp6SeekOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN UINT16 OptType
+ );
+
+/**
+ Seek the address of the first byte of the Ia option header.
+
+ @param[in] Buf The pointer to the buffer.
+ @param[in] SeekLen The length to seek.
+ @param[in] IaDesc The pointer to the Ia descriptor.
+
+ @retval NULL If failed to seek the Ia option.
+ @retval others The position to the Ia option.
+
+**/
+UINT8 *
+Dhcp6SeekIaOption (
+ IN UINT8 *Buf,
+ IN UINT32 SeekLen,
+ IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc
+ );
+
+/**
+ Parse the address option and update the address info.
+
+ @param[in] IaInnerOpt The pointer to the buffer.
+ @param[in] IaInnerLen The length to parse.
+ @param[out] AddrNum The number of addresses.
+ @param[in, out] AddrBuf The pointer to the address buffer.
+
+**/
+VOID
+Dhcp6ParseAddrOption (
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ OUT UINT32 *AddrNum,
+ IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf
+ );
+
+/**
+ Create a control blcok for the Ia according to the corresponding options.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+ @param[in] IaInnerOpt The pointer to the inner options in the Ia option.
+ @param[in] IaInnerLen The length of all the inner options in the Ia option.
+ @param[in] T1 T1 time in the Ia option.
+ @param[in] T2 T2 time in the Ia option.
+
+ @retval EFI_NOT_FOUND No valid IA option is found.
+ @retval EFI_SUCCESS Create an IA control block successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6GenerateIaCb (
+ IN DHCP6_INSTANCE *Instance,
+ IN UINT8 *IaInnerOpt,
+ IN UINT16 IaInnerLen,
+ IN UINT32 T1,
+ IN UINT32 T2
+ );
+
+
+/**
+ Cache the current IA configuration information.
+
+ @param[in] Instance The pointer to DHCP6 Instance.
+
+ @retval EFI_SUCCESS Cache the current IA successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+Dhcp6CacheIa (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+
+/**
+ Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.
+
+ @param[in] Instance The pointer to DHCP6 instance.
+
+**/
+VOID
+Dhcp6AppendCacheIa (
+ IN DHCP6_INSTANCE *Instance
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/ComponentName.c b/NetworkPkg/Ip6Dxe/ComponentName.c
new file mode 100644
index 0000000000..c8382f74a9
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/ComponentName.c
@@ -0,0 +1,313 @@
+/** @file
+ Implementation of EFI_COMPONENT_NAME_PROTOCOL and
+ EFI_COMPONENT_NAME2_PROTOCOL protocol.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+//
+// EFI Component Name Functions
+//
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName = {
+ Ip6ComponentNameGetDriverName,
+ Ip6ComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip6ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip6ComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIp6DriverNameTable[] = {
+ {
+ "eng;en",
+ L"IP6 Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mIp6DriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gIp6ComponentName)
+ );
+
+}
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.c b/NetworkPkg/Ip6Dxe/Ip6Common.c
new file mode 100644
index 0000000000..2ae14a952c
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Common.c
@@ -0,0 +1,796 @@
+/** @file
+ The implementation of common functions shared by IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+/**
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number
+ of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,
+ only the address count is returned.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[out] AddressCount The number of returned addresses.
+ @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO.
+ This is an optional parameter.
+
+
+ @retval EFI_SUCCESS The address array successfully built.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+Ip6BuildEfiAddressList (
+ IN IP6_SERVICE *IpSb,
+ OUT UINT32 *AddressCount,
+ OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL
+ )
+{
+ UINT32 Count;
+ LIST_ENTRY *Entry;
+ EFI_IP6_ADDRESS_INFO *EfiAddrInfo;
+ IP6_ADDRESS_INFO *AddrInfo;
+
+ if (AddressCount == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IpSb->LinkLocalOk) {
+ Count = 1 + IpSb->DefaultInterface->AddressCount;
+ } else {
+ Count = 0;
+ }
+
+ *AddressCount = Count;
+
+ if ((AddressList == NULL) || (Count == 0)) {
+ return EFI_SUCCESS;
+ }
+
+ if (*AddressList == NULL) {
+ *AddressList = AllocatePool (sizeof (EFI_IP6_ADDRESS_INFO) * Count);
+ if (*AddressList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ EfiAddrInfo = *AddressList;
+
+ IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &IpSb->LinkLocalAddr);
+ EfiAddrInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;
+
+ EfiAddrInfo++;
+ Count = 1;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) {
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
+
+ IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &AddrInfo->Address);
+ EfiAddrInfo->PrefixLength = AddrInfo->PrefixLength;
+
+ EfiAddrInfo++;
+ Count++;
+ }
+
+ ASSERT (Count == *AddressCount);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Generate the multicast addresses identify the group of all IPv6 nodes or IPv6
+ routers defined in RFC4291.
+
+ All Nodes Addresses: FF01::1, FF02::1.
+ All Router Addresses: FF01::2, FF02::2, FF05::2.
+
+ @param[in] Router If TRUE, generate all routers addresses,
+ else generate all node addresses.
+ @param[in] Scope interface-local(1), link-local(2), or site-local(5)
+ @param[out] Ip6Addr The generated multicast address.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_SUCCESS The address is generated.
+
+**/
+EFI_STATUS
+Ip6SetToAllNodeMulticast (
+ IN BOOLEAN Router,
+ IN UINT8 Scope,
+ OUT EFI_IPv6_ADDRESS *Ip6Addr
+ )
+{
+ if (Ip6Addr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Router && Scope == IP6_SITE_LOCAL_SCOPE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (Ip6Addr, sizeof (EFI_IPv6_ADDRESS));
+ Ip6Addr->Addr[0] = 0xFF;
+ Ip6Addr->Addr[1] = Scope;
+
+ if (!Router) {
+ Ip6Addr->Addr[15] = 0x1;
+ } else {
+ Ip6Addr->Addr[15] = 0x2;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function converts MAC address to 64 bits interface ID according to RFC4291
+ and returns the interface ID. Currently only 48-bit MAC address is supported by
+ this function.
+
+ @param[in, out] IpSb The IP6 service binding instance.
+
+ @retval NULL The operation fails.
+ @return Pointer to the generated interface ID.
+
+**/
+UINT8 *
+Ip6CreateInterfaceID (
+ IN OUT IP6_SERVICE *IpSb
+ )
+{
+ UINT8 InterfaceId[8];
+ UINT8 Byte;
+ EFI_MAC_ADDRESS *MacAddr;
+ UINT32 AddrLen;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ AddrLen = IpSb->SnpMode.HwAddressSize;
+
+ //
+ // Currently only IEEE 802 48-bit MACs are supported to create link local address.
+ //
+ if (AddrLen != IP6_MAC_LEN || IpSb->InterfaceIdLen != IP6_IF_ID_LEN) {
+ return NULL;
+ }
+
+ MacAddr = &IpSb->SnpMode.CurrentAddress;
+
+ //
+ // Convert MAC address to 64 bits interface ID according to Appendix A of RFC4291:
+ // 1. Insert 0xFFFE to the middle
+ // 2. Invert the universal/local bit - bit 6 in network order
+ //
+ CopyMem (InterfaceId, MacAddr, 3);
+ InterfaceId[3] = 0xFF;
+ InterfaceId[4] = 0xFE;
+ CopyMem (&InterfaceId[5], &MacAddr->Addr[3], 3);
+
+ Byte = (UINT8) (InterfaceId[0] & IP6_U_BIT);
+ if (Byte == IP6_U_BIT) {
+ InterfaceId[0] &= ~IP6_U_BIT;
+ } else {
+ InterfaceId[0] |= IP6_U_BIT;
+ }
+
+ //
+ // Return the interface ID.
+ //
+ return AllocateCopyPool (IpSb->InterfaceIdLen, InterfaceId);
+}
+
+/**
+ This function creates link-local address from interface identifier. The
+ interface identifier is normally created from MAC address. It might be manually
+ configured by administrator if the link-local address created from MAC address
+ is a duplicate address.
+
+ @param[in, out] IpSb The IP6 service binding instance.
+
+ @retval NULL If the operation fails.
+ @return The generated Link Local address, in network order.
+
+**/
+EFI_IPv6_ADDRESS *
+Ip6CreateLinkLocalAddr (
+ IN OUT IP6_SERVICE *IpSb
+ )
+{
+ EFI_IPv6_ADDRESS *Ip6Addr;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ UINTN DataSize;
+ EFI_IP6_CONFIG_INTERFACE_ID InterfaceId;
+ EFI_STATUS Status;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ if (IpSb->InterfaceId != NULL) {
+ FreePool (IpSb->InterfaceId);
+ }
+
+ //
+ // Get the interface id if it is manully configured.
+ //
+ Ip6Config = &IpSb->Ip6ConfigInstance.Ip6Config;
+ DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);
+ ZeroMem (&InterfaceId, DataSize);
+
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ Ip6ConfigDataTypeAltInterfaceId,
+ &DataSize,
+ &InterfaceId
+ );
+ if (Status == EFI_NOT_FOUND) {
+ //
+ // Since the interface id is not configured, generate the interface id from
+ // MAC address.
+ //
+ IpSb->InterfaceId = Ip6CreateInterfaceID (IpSb);
+ if (IpSb->InterfaceId == NULL) {
+ return NULL;
+ }
+
+ CopyMem (&InterfaceId, IpSb->InterfaceId, IpSb->InterfaceIdLen);
+ //
+ // Record the interface id.
+ //
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeAltInterfaceId,
+ DataSize,
+ &InterfaceId
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (IpSb->InterfaceId);
+ IpSb->InterfaceId = NULL;
+ return NULL;
+ }
+ } else if (!EFI_ERROR (Status)) {
+ IpSb->InterfaceId = AllocateCopyPool (DataSize, &InterfaceId);
+ if (IpSb->InterfaceId == NULL) {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+
+ //
+ // Append FE80::/64 to the left of IPv6 address then return.
+ //
+ Ip6Addr = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS));
+ if (Ip6Addr == NULL) {
+ FreePool (IpSb->InterfaceId);
+ IpSb->InterfaceId = NULL;
+ return NULL;
+ }
+
+ CopyMem (&Ip6Addr->Addr[8], IpSb->InterfaceId, IpSb->InterfaceIdLen);
+ Ip6Addr->Addr[1] = 0x80;
+ Ip6Addr->Addr[0] = 0xFE;
+
+ return Ip6Addr;
+}
+
+/**
+ Compute the solicited-node multicast address for an unicast or anycast address,
+ by taking the low-order 24 bits of this address, and appending those bits to
+ the prefix FF02:0:0:0:0:1:FF00::/104.
+
+ @param[in] Ip6Addr The unicast or anycast address, in network order.
+ @param[out] MulticastAddr The generated solicited-node multicast address,
+ in network order.
+
+**/
+VOID
+Ip6CreateSNMulticastAddr (
+ IN EFI_IPv6_ADDRESS *Ip6Addr,
+ OUT EFI_IPv6_ADDRESS *MulticastAddr
+ )
+{
+ ASSERT (Ip6Addr != NULL && MulticastAddr != NULL);
+
+ ZeroMem (MulticastAddr, sizeof (EFI_IPv6_ADDRESS));
+
+ MulticastAddr->Addr[0] = 0xFF;
+ MulticastAddr->Addr[1] = 0x02;
+ MulticastAddr->Addr[11] = 0x1;
+ MulticastAddr->Addr[12] = 0xFF;
+
+ CopyMem (&MulticastAddr->Addr[13], &Ip6Addr->Addr[13], 3);
+}
+
+/**
+ Insert a node IP6_ADDRESS_INFO to an IP6 interface.
+
+ @param[in, out] IpIf Points to an IP6 interface.
+ @param[in] AddrInfo Points to IP6_ADDRESS_INFO
+
+**/
+VOID
+Ip6AddAddr (
+ IN OUT IP6_INTERFACE *IpIf,
+ IN IP6_ADDRESS_INFO *AddrInfo
+ )
+{
+ InsertHeadList (&IpIf->AddressList, &AddrInfo->Link);
+ IpIf->AddressCount++;
+}
+
+/**
+ Destroy the IP instance if its StationAddress is removed. It is the help function
+ for Ip6RemoveAddr().
+
+ @param[in, out] IpSb Points to an IP6 service binding instance.
+ @param[in] Address The to be removed address
+
+**/
+VOID
+Ip6DestroyInstanceByAddress (
+ IN OUT IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Address
+ )
+{
+ BOOLEAN OneDestroyed;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ LIST_ENTRY *Entry;
+ IP6_PROTOCOL *Instance;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ ServiceBinding = &IpSb->ServiceBinding;
+
+ //
+ // Upper layer IP protocol consumers may have tight relationship between several
+ // IP protocol instances, in other words, calling ServiceBinding->DestroyChild to
+ // destroy one IP child may cause other related IP children destroyed too. This
+ // will probably leave hole in the children list when we iterate it. So everytime
+ // we just destroy one child then back to the start point to iterate the list.
+ //
+ do {
+ OneDestroyed = FALSE;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
+
+ if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) {
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+ OneDestroyed = TRUE;
+ break;
+ }
+ }
+ } while (OneDestroyed);
+}
+
+/**
+ Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.
+
+ This function removes the matching IPv6 addresses from the address list and
+ adjusts the address count of the address list. If IpSb is not NULL, this function
+ calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the
+ its solicited-node multicast MAC address from the filter list and sends out
+ a Multicast Listener Done. If Prefix is NULL, all address in the address list
+ will be removed. If Prefix is not NULL, the address that matching the Prefix
+ with PrefixLength in the address list will be removed.
+
+ @param[in] IpSb NULL or points to IP6 service binding instance.
+ @param[in, out] AddressList Address list array.
+ @param[in, out] AddressCount The count of addresses in address list array.
+ @param[in] Prefix NULL or an IPv6 address prefix.
+ @param[in] PrefixLength The length of Prefix.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength
+ cannot be found in the address list.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+Ip6RemoveAddr (
+ IN IP6_SERVICE *IpSb OPTIONAL,
+ IN OUT LIST_ENTRY *AddressList,
+ IN OUT UINT32 *AddressCount,
+ IN EFI_IPv6_ADDRESS *Prefix OPTIONAL,
+ IN UINT8 PrefixLength
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_ADDRESS_INFO *AddrInfo;
+ EFI_IPv6_ADDRESS SnMCastAddr;
+
+ if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_NUM) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, AddressList) {
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
+
+ if (Prefix == NULL ||
+ (PrefixLength == 128 && EFI_IP6_EQUAL (Prefix, &AddrInfo->Address)) ||
+ (PrefixLength == AddrInfo->PrefixLength && NetIp6IsNetEqual (Prefix, &AddrInfo->Address, PrefixLength))
+ ) {
+ if (IpSb != NULL) {
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ Ip6CreateSNMulticastAddr (&AddrInfo->Address, &SnMCastAddr);
+ Ip6LeaveGroup (IpSb, &SnMCastAddr);
+
+ //
+ // Destroy any instance who is using the dying address as the source address.
+ //
+ Ip6DestroyInstanceByAddress (IpSb, &AddrInfo->Address);
+ }
+
+ RemoveEntryList (Entry);
+ FreePool (AddrInfo);
+ (*AddressCount)--;
+
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Check whether the incoming Ipv6 address is a solicited-node multicast address.
+
+ @param[in] Ip6 Ip6 address, in network order.
+
+ @retval TRUE Yes, solicited-node multicast address
+ @retval FALSE No
+
+**/
+BOOLEAN
+Ip6IsSNMulticastAddr (
+ IN EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ EFI_IPv6_ADDRESS Sn;
+ BOOLEAN Flag;
+
+ Ip6CreateSNMulticastAddr (Ip6, &Sn);
+ Flag = FALSE;
+
+ if (CompareMem (Sn.Addr, Ip6->Addr, 13) == 0) {
+ Flag = TRUE;
+ }
+
+ return Flag;
+}
+
+/**
+ Check whether the incoming IPv6 address is one of the maintained addresses in
+ the IP6 service binding instance.
+
+ @param[in] IpSb Points to a IP6 service binding instance.
+ @param[in] Address The IP6 address to be checked.
+ @param[out] Interface If not NULL, output the IP6 interface which
+ maintains the Address.
+ @param[out] AddressInfo If not NULL, output the IP6 address information
+ of the Address.
+
+ @retval TRUE Yes, it is one of the maintained address.
+ @retval FALSE No, it is not one of the maintained address.
+
+**/
+BOOLEAN
+Ip6IsOneOfSetAddress (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Address,
+ OUT IP6_INTERFACE **Interface OPTIONAL,
+ OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Entry2;
+ IP6_INTERFACE *IpIf;
+ IP6_ADDRESS_INFO *TmpAddressInfo;
+
+ //
+ // Check link-local address first
+ //
+ if (IpSb->LinkLocalOk && EFI_IP6_EQUAL (&IpSb->LinkLocalAddr, Address)) {
+ if (Interface != NULL) {
+ *Interface = IpSb->DefaultInterface;
+ }
+
+ if (AddressInfo != NULL) {
+ *AddressInfo = NULL;
+ }
+
+ return TRUE;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {
+ TmpAddressInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
+
+ if (EFI_IP6_EQUAL (&TmpAddressInfo->Address, Address)) {
+ if (Interface != NULL) {
+ *Interface = IpIf;
+ }
+
+ if (AddressInfo != NULL) {
+ *AddressInfo = TmpAddressInfo;
+ }
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Check whether the incoming MAC address is valid.
+
+ @param[in] IpSb Points to a IP6 service binding instance.
+ @param[in] LinkAddress The MAC address.
+
+ @retval TRUE Yes, it is valid.
+ @retval FALSE No, it is not valid.
+
+**/
+BOOLEAN
+Ip6IsValidLinkAddress (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_MAC_ADDRESS *LinkAddress
+ )
+{
+ UINT32 Index;
+
+ //
+ // TODO: might be updated later to be more acceptable.
+ //
+ for (Index = IpSb->SnpMode.HwAddressSize; Index < sizeof (EFI_MAC_ADDRESS); Index++) {
+ if (LinkAddress->Addr[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Copy the PrefixLength bits from Src to Dest.
+
+ @param[out] Dest A pointer to the buffer to copy to.
+ @param[in] Src A pointer to the buffer to copy from.
+ @param[in] PrefixLength The number of bits to copy.
+
+**/
+VOID
+Ip6CopyAddressByPrefix (
+ OUT EFI_IPv6_ADDRESS *Dest,
+ IN EFI_IPv6_ADDRESS *Src,
+ IN UINT8 PrefixLength
+ )
+{
+ UINT8 Byte;
+ UINT8 Bit;
+ UINT8 Mask;
+
+ ASSERT (Dest != NULL && Src != NULL);
+ ASSERT (PrefixLength < IP6_PREFIX_NUM);
+
+ Byte = (UINT8) (PrefixLength / 8);
+ Bit = (UINT8) (PrefixLength % 8);
+
+ ZeroMem (Dest, sizeof (EFI_IPv6_ADDRESS));
+
+ CopyMem (Dest, Src, Byte);
+
+ if (Bit > 0) {
+ Mask = (UINT8) (0xFF << (8 - Bit));
+ ASSERT (Byte < 16);
+ Dest->Addr[Byte] = (UINT8) (Src->Addr[Byte] & Mask);
+ }
+}
+
+/**
+ Get the MAC address for a multicast IP address. Call
+ Mnp's McastIpToMac to find the MAC address instead of
+ hard-coding the NIC to be Ethernet.
+
+ @param[in] Mnp The Mnp instance to get the MAC address.
+ @param[in] Multicast The multicast IP address to translate.
+ @param[out] Mac The buffer to hold the translated address.
+
+ @retval EFI_SUCCESS The multicast IP successfully
+ translated to a multicast MAC address.
+ @retval Other The address is not converted because an error occurred.
+
+**/
+EFI_STATUS
+Ip6GetMulticastMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN EFI_IPv6_ADDRESS *Multicast,
+ OUT EFI_MAC_ADDRESS *Mac
+ )
+{
+ EFI_IP_ADDRESS EfiIp;
+
+ IP6_COPY_ADDRESS (&EfiIp.v6, Multicast);
+
+ return Mnp->McastIpToMac (Mnp, TRUE, &EfiIp, Mac);
+}
+
+/**
+ Set the Ip6 variable data.
+
+ @param[in] IpSb Points to an IP6 service binding instance.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Ip6SetVariableData (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ UINT32 NumConfiguredInstance;
+ LIST_ENTRY *Entry;
+ UINTN VariableDataSize;
+ EFI_IP6_VARIABLE_DATA *Ip6VariableData;
+ EFI_IP6_ADDRESS_PAIR *Ip6AddressPair;
+ IP6_PROTOCOL *IpInstance;
+ CHAR16 *NewMacString;
+ EFI_STATUS Status;
+
+ NumConfiguredInstance = 0;
+
+ //
+ // Go through the children list to count the configured children.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
+
+ if (IpInstance->State == IP6_STATE_CONFIGED) {
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Calculate the size of the Ip6VariableData. As there may be no IP child,
+ // we should add extra buffer for the address paris only if the number of configured
+ // children is more than 1.
+ //
+ VariableDataSize = sizeof (EFI_IP6_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_IP6_ADDRESS_PAIR) * (NumConfiguredInstance - 1);
+ }
+
+ Ip6VariableData = AllocatePool (VariableDataSize);
+ if (Ip6VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ip6VariableData->DriverHandle = IpSb->Image;
+ Ip6VariableData->AddressCount = NumConfiguredInstance;
+
+ Ip6AddressPair = &Ip6VariableData->AddressPairs[0];
+
+ //
+ // Go through the children list to fill the configured children's address pairs.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
+
+ if (IpInstance->State == IP6_STATE_CONFIGED) {
+ Ip6AddressPair->InstanceHandle = IpInstance->Handle;
+ Ip6AddressPair->PrefixLength = IpInstance->PrefixLength;
+ IP6_COPY_ADDRESS (&Ip6AddressPair->Ip6Address, &IpInstance->ConfigData.StationAddress);
+
+ Ip6AddressPair++;
+ }
+ }
+
+ //
+ // Get the mac string.
+ //
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (IpSb->MacString != NULL) {
+ //
+ // The variable is set already, we're going to update it.
+ //
+ if (StrCmp (IpSb->MacString, NewMacString) != 0) {
+ //
+ // The mac address is changed, delete the previous variable first.
+ //
+ gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+ }
+
+ FreePool (IpSb->MacString);
+ }
+
+ IpSb->MacString = NewMacString;
+
+ Status = gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableDataSize,
+ (VOID *) Ip6VariableData
+ );
+
+Exit:
+ FreePool (Ip6VariableData);
+ return Status;
+}
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in] IpSb Ip6 service binding instance.
+
+**/
+VOID
+Ip6ClearVariableData (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ ASSERT (IpSb->MacString != NULL);
+
+ gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+
+ FreePool (IpSb->MacString);
+ IpSb->MacString = NULL;
+}
+
+/**
+ Convert the multibyte field in IP header's byter order.
+ In spite of its name, it can also be used to convert from
+ host to network byte order.
+
+ @param[in, out] Head The IP head to convert.
+
+ @return Point to the converted IP head.
+
+**/
+EFI_IP6_HEADER *
+Ip6NtohHead (
+ IN OUT EFI_IP6_HEADER *Head
+ )
+{
+ Head->FlowLabelL = NTOHS (Head->FlowLabelL);
+ Head->PayloadLength = NTOHS (Head->PayloadLength);
+
+ return Head;
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.h b/NetworkPkg/Ip6Dxe/Ip6Common.h
new file mode 100644
index 0000000000..c3755f4859
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Common.h
@@ -0,0 +1,338 @@
+/** @file
+ Common definition and functions for IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_COMMON_H__
+#define __EFI_IP6_COMMON_H__
+
+#define IP6_LINK_EQUAL(Mac1, Mac2) (CompareMem ((Mac1), (Mac2), sizeof (EFI_MAC_ADDRESS)) == 0)
+
+//
+// Convert the Microsecond to second. IP transmit/receive time is
+// in the unit of microsecond. IP ticks once per second.
+//
+#define IP6_US_TO_SEC(Us) (((Us) + 999999) / 1000000)
+
+#define IP6_ETHER_PROTO 0x86DD
+
+#define IP6_MAC_LEN 6
+#define IP6_IF_ID_LEN 8
+
+#define IP6_INTERFACE_LOCAL_SCOPE 1
+#define IP6_LINK_LOCAL_SCOPE 2
+#define IP6_SITE_LOCAL_SCOPE 5
+
+#define IP6_INFINIT_LIFETIME 0xFFFFFFFF
+
+#define IP6_HOP_LIMIT 255
+//
+// Make it to 64 since all 54 bits are zero.
+//
+#define IP6_LINK_LOCAL_PREFIX_LENGTH 64
+
+#define IP6_TIMER_INTERVAL_IN_MS 100
+#define IP6_ONE_SECOND_IN_MS 1000
+
+//
+// The packet is received as link level broadcast/multicast/promiscuous.
+//
+#define IP6_LINK_BROADCAST 0x00000001
+#define IP6_LINK_MULTICAST 0x00000002
+#define IP6_LINK_PROMISC 0x00000004
+
+#define IP6_U_BIT 0x02
+
+typedef enum {
+ Ip6Promiscuous = 1,
+ Ip6Unicast,
+ Ip6Multicast,
+ Ip6AnyCast
+} IP6_ADDRESS_TYPE;
+
+typedef struct _IP6_INTERFACE IP6_INTERFACE;
+typedef struct _IP6_PROTOCOL IP6_PROTOCOL;
+typedef struct _IP6_SERVICE IP6_SERVICE;
+typedef struct _IP6_ADDRESS_INFO IP6_ADDRESS_INFO;
+
+/**
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number
+ of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL,
+ only the address count is returned.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[out] AddressCount The number of returned addresses.
+ @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO.
+ This is an optional parameter.
+
+
+ @retval EFI_SUCCESS The address array is successfully build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+Ip6BuildEfiAddressList (
+ IN IP6_SERVICE *IpSb,
+ OUT UINT32 *AddressCount,
+ OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL
+ );
+
+/**
+ Generate the multicast addresses identify the group of all IPv6 nodes or IPv6
+ routers defined in RFC4291.
+
+ All Nodes Addresses: FF01::1, FF02::1.
+ All Router Addresses: FF01::2, FF02::2, FF05::2.
+
+ @param[in] Router If TRUE, generate all routers addresses,
+ else generate all node addresses.
+ @param[in] Scope interface-local(1), link-local(2), or site-local(5)
+ @param[out] Ip6Addr The generated multicast address.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_SUCCESS The address is generated.
+
+**/
+EFI_STATUS
+Ip6SetToAllNodeMulticast (
+ IN BOOLEAN Router,
+ IN UINT8 Scope,
+ OUT EFI_IPv6_ADDRESS *Ip6Addr
+ );
+
+/**
+ This function converts MAC address to 64 bits interface ID according to RFC4291
+ and returns the interface ID. Currently only 48-bit MAC address is supported by
+ this function.
+
+ @param[in, out] IpSb The IP6 service binding instance.
+
+ @retval NULL The operation fails.
+ @return Pointer to the generated interface ID.
+
+**/
+UINT8 *
+Ip6CreateInterfaceID (
+ IN OUT IP6_SERVICE *IpSb
+ );
+
+/**
+ This function creates link-local address from interface identifier. The
+ interface identifier is normally created from MAC address. It might be manually
+ configured by administrator if the link-local address created from MAC address
+ is a duplicate address.
+
+ @param[in, out] IpSb The IP6 service binding instance.
+
+ @retval NULL If the operation fails.
+ @return The generated Link Local address, in network order.
+
+**/
+EFI_IPv6_ADDRESS *
+Ip6CreateLinkLocalAddr (
+ IN OUT IP6_SERVICE *IpSb
+ );
+
+/**
+ Compute the solicited-node multicast address for an unicast or anycast address,
+ by taking the low-order 24 bits of this address, and appending those bits to
+ the prefix FF02:0:0:0:0:1:FF00::/104.
+
+ @param Ip6Addr The unicast or anycast address, in network order.
+ @param MulticastAddr The generated solicited-node multicast address,
+ in network order.
+
+**/
+VOID
+Ip6CreateSNMulticastAddr (
+ IN EFI_IPv6_ADDRESS *Ip6Addr,
+ OUT EFI_IPv6_ADDRESS *MulticastAddr
+ );
+
+/**
+ Check whether the incoming Ipv6 address is a solicited-node multicast address.
+
+ @param[in] Ip6 Ip6 address, in network order.
+
+ @retval TRUE Yes, solicited-node multicast address
+ @retval FALSE No
+
+**/
+BOOLEAN
+Ip6IsSNMulticastAddr (
+ IN EFI_IPv6_ADDRESS *Ip6
+ );
+
+/**
+ Check whether the incoming IPv6 address is one of the maintained address in
+ the IP6 service binding instance.
+
+ @param[in] IpSb Points to a IP6 service binding instance
+ @param[in] Address The IP6 address to be checked.
+ @param[out] Interface If not NULL, output the IP6 interface which
+ maintains the Address.
+ @param[out] AddressInfo If not NULL, output the IP6 address information
+ of the Address.
+
+ @retval TRUE Yes, it is one of the maintained addresses.
+ @retval FALSE No, it is not one of the maintained addresses.
+
+**/
+BOOLEAN
+Ip6IsOneOfSetAddress (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Address,
+ OUT IP6_INTERFACE **Interface OPTIONAL,
+ OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL
+ );
+
+/**
+ Check whether the incoming MAC address is valid.
+
+ @param[in] IpSb Points to a IP6 service binding instance.
+ @param[in] LinkAddress The MAC address.
+
+ @retval TRUE Yes, it is valid.
+ @retval FALSE No, it is not valid.
+
+**/
+BOOLEAN
+Ip6IsValidLinkAddress (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_MAC_ADDRESS *LinkAddress
+ );
+
+
+/**
+ Copy the PrefixLength bits from Src to Dest.
+
+ @param[out] Dest A pointer to the buffer to copy to.
+ @param[in] Src A pointer to the buffer to copy from.
+ @param[in] PrefixLength The number of bits to copy.
+
+**/
+VOID
+Ip6CopyAddressByPrefix (
+ OUT EFI_IPv6_ADDRESS *Dest,
+ IN EFI_IPv6_ADDRESS *Src,
+ IN UINT8 PrefixLength
+ );
+
+/**
+ Insert a node IP6_ADDRESS_INFO to an IP6 interface.
+
+ @param[in, out] IpIf Points to an IP6 interface.
+ @param[in] AddrInfo Points to an IP6_ADDRESS_INFO.
+
+**/
+VOID
+Ip6AddAddr (
+ IN OUT IP6_INTERFACE *IpIf,
+ IN IP6_ADDRESS_INFO *AddrInfo
+ );
+
+/**
+ Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO.
+
+ This function removes the matching IPv6 addresses from the address list and
+ adjusts the address count of the address list. If IpSb is not NULL, this function
+ calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the
+ its solicited-node multicast MAC address from the filter list and sends out
+ a Multicast Listener Done. If Prefix is NULL, all address in the address list
+ will be removed. If Prefix is not NULL, the address that matching the Prefix
+ with PrefixLength in the address list will be removed.
+
+ @param[in] IpSb NULL or points to IP6 service binding instance.
+ @param[in, out] AddressList address list array
+ @param[in, out] AddressCount the count of addresses in address list array
+ @param[in] Prefix NULL or an IPv6 address prefix
+ @param[in] PrefixLength the length of Prefix
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength
+ cannot be found in address list.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+Ip6RemoveAddr (
+ IN IP6_SERVICE *IpSb OPTIONAL,
+ IN OUT LIST_ENTRY *AddressList,
+ IN OUT UINT32 *AddressCount,
+ IN EFI_IPv6_ADDRESS *Prefix OPTIONAL,
+ IN UINT8 PrefixLength
+ );
+
+/**
+ Set the Ip6 variable data.
+
+ @param[in] IpSb Points to an IP6 service binding instance
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Ip6SetVariableData (
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in] IpSb Ip6 service binding instance.
+
+**/
+VOID
+Ip6ClearVariableData (
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ Get the MAC address for a multicast IP address. Call
+ Mnp's McastIpToMac to find the MAC address instead of
+ hard-coding the NIC to be Ethernet.
+
+ @param[in] Mnp The Mnp instance to get the MAC address.
+ @param[in] Multicast The multicast IP address to translate.
+ @param[out] Mac The buffer to hold the translated address.
+
+ @retval EFI_SUCCESS The multicast IP is successfully
+ translated to a multicast MAC address.
+ @retval Other The address is not converted because an error occurred.
+
+**/
+EFI_STATUS
+Ip6GetMulticastMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp,
+ IN EFI_IPv6_ADDRESS *Multicast,
+ OUT EFI_MAC_ADDRESS *Mac
+ );
+
+/**
+ Convert the multibyte field in IP header's byter order.
+ In spite of its name, it can also be used to convert from
+ host to network byte order.
+
+ @param[in, out] Head The IP head to convert.
+
+ @return Point to the converted IP head.
+
+**/
+EFI_IP6_HEADER *
+Ip6NtohHead (
+ IN OUT EFI_IP6_HEADER *Head
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Config.vfr b/NetworkPkg/Ip6Dxe/Ip6Config.vfr
new file mode 100644
index 0000000000..902cef6209
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Config.vfr
@@ -0,0 +1,170 @@
+/** @file
+ VFR file used by the IP6 configuration component.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6NvData.h"
+
+#define EFI_NETWORK_DEVICE_CLASS 0x04
+
+formset
+ guid = IP6_CONFIG_NVDATA_GUID,
+ title = STRING_TOKEN(STR_IP6_CONFIG_FORM_TITLE),
+ help = STRING_TOKEN(STR_IP6_CONFIG_FORM_HELP),
+ class = EFI_NETWORK_DEVICE_CLASS,
+ subclass = 0x03,
+
+ varstore IP6_CONFIG_IFR_NVDATA,
+ name = IP6_CONFIG_IFR_NVDATA,
+ guid = IP6_CONFIG_NVDATA_GUID;
+
+ form formid = FORMID_MAIN_FORM,
+ title = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE);
+
+ text
+ help = STRING_TOKEN(STR_IP6_INTERFACE_NAME_HELP),
+ text = STRING_TOKEN(STR_IP6_INTERFACE_NAME),
+ text = STRING_TOKEN(STR_IP6_INTERFACE_NAME_CONTENT);
+
+ text
+ help = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_HELP),
+ text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE),
+ text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_CONTENT);
+
+ text
+ help = STRING_TOKEN(STR_IP6_MAC_ADDRESS_HELP),
+ text = STRING_TOKEN(STR_IP6_MAC_ADDRESS),
+ text = STRING_TOKEN(STR_IP6_MAC_ADDRESS_CONTENT);
+
+ text
+ help = STRING_TOKEN(STR_IP6_HOST_ADDRESS_HELP),
+ text = STRING_TOKEN(STR_IP6_HOST_ADDRESS),
+ text = STRING_TOKEN(STR_NULL);
+
+ label HOST_ADDRESS_LABEL;
+ label LABEL_END;
+
+ text
+ help = STRING_TOKEN(STR_IP6_ROUTE_TABLE_HELP),
+ text = STRING_TOKEN(STR_IP6_ROUTE_TABLE),
+ text = STRING_TOKEN(STR_NULL);
+
+ label ROUTE_TABLE_LABEL;
+ label LABEL_END;
+
+ text
+ help = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS_HELP),
+ text = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS),
+ text = STRING_TOKEN(STR_NULL);
+
+ label GATEWAY_ADDRESS_LABEL;
+ label LABEL_END;
+
+ text
+ help = STRING_TOKEN(STR_IP6_DNS_ADDRESS_HELP),
+ text = STRING_TOKEN(STR_IP6_DNS_ADDRESS),
+ text = STRING_TOKEN(STR_NULL);
+
+ label DNS_ADDRESS_LABEL;
+ label LABEL_END;
+
+ string varid = IP6_CONFIG_IFR_NVDATA.InterfaceId,
+ prompt = STRING_TOKEN(STR_IP6_INTERFACE_ID),
+ help = STRING_TOKEN(STR_IP6_INTERFACE_ID_HELP),
+ flags = INTERACTIVE,
+ key = KEY_INTERFACE_ID,
+ minsize = INTERFACE_ID_STR_MIN_SIZE,
+ maxsize = INTERFACE_ID_STR_MAX_SIZE,
+ endstring;
+
+ numeric varid = IP6_CONFIG_IFR_NVDATA.DadTransmitCount,
+ prompt = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT),
+ help = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT_HELP),
+ flags = 0,
+ minimum = 0,
+ maximum = DAD_MAX_TRANSMIT_COUNT,
+ step = 0,
+ endnumeric;
+
+ oneof varid = IP6_CONFIG_IFR_NVDATA.Policy,
+ prompt = STRING_TOKEN(STR_POLICY_TYPE_PROMPT),
+ help = STRING_TOKEN(STR_POLICY_TYPE_HELP),
+ option text = STRING_TOKEN(STR_POLICY_TYPE_AUTO), value = IP6_POLICY_AUTO, flags = DEFAULT;
+ option text = STRING_TOKEN(STR_POLICY_TYPE_MANUAL), value = IP6_POLICY_MANUAL, flags = 0;
+ endoneof;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ suppressif ideqval IP6_CONFIG_IFR_NVDATA.Policy == IP6_POLICY_AUTO;
+ goto FORMID_MANUAL_CONFIG_FORM,
+ prompt = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM),
+ help = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM_HELP),
+ flags = 0;
+ subtitle text = STRING_TOKEN(STR_NULL);
+ endif;
+
+ text
+ help = STRING_TOKEN (STR_SAVE_CHANGES_HELP),
+ text = STRING_TOKEN (STR_SAVE_CHANGES),
+ text = STRING_TOKEN (STR_NULL),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_CHANGES;
+
+ endform;
+
+ form formid = FORMID_MANUAL_CONFIG_FORM,
+ title = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM);
+
+ string varid = IP6_CONFIG_IFR_NVDATA.ManualAddress,
+ prompt = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS),
+ help = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_MANUAL_ADDRESS,
+ minsize = ADDRESS_STR_MIN_SIZE,
+ maxsize = ADDRESS_STR_MAX_SIZE,
+ endstring;
+
+ string varid = IP6_CONFIG_IFR_NVDATA.GatewayAddress,
+ prompt = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDRESS),
+ help = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDR_HELP),
+ flags = INTERACTIVE,
+ key = KEY_GATEWAY_ADDRESS,
+ minsize = ADDRESS_STR_MIN_SIZE,
+ maxsize = ADDRESS_STR_MAX_SIZE,
+ endstring;
+
+ string varid = IP6_CONFIG_IFR_NVDATA.DnsAddress,
+ prompt = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS),
+ help = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_DNS_ADDRESS,
+ minsize = ADDRESS_STR_MIN_SIZE,
+ maxsize = ADDRESS_STR_MAX_SIZE,
+ endstring;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_CONFIG_CHANGES;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_IGNORE_CONFIG_CHANGES;
+
+ endform;
+
+endformset;
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
new file mode 100644
index 0000000000..3cfd1f2104
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
@@ -0,0 +1,2369 @@
+/** @file
+ The implementation of EFI IPv6 Configuration Protocol.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+LIST_ENTRY mIp6ConfigInstanceList = {&mIp6ConfigInstanceList, &mIp6ConfigInstanceList};
+
+/**
+ The event process routine when the DHCPv6 service binding protocol is installed
+ in the system.
+
+ @param[in] Event Not used.
+ @param[in] Context Pointer to the IP6 config instance data.
+
+**/
+VOID
+EFIAPI
+Ip6ConfigOnDhcp6SbInstalled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Update the current policy to NewPolicy. During the transition
+ period, the default router list, on-link prefix list, autonomous prefix list
+ and address list in all interfaces will be released.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] NewPolicy The new policy to be updated to.
+
+**/
+VOID
+Ip6ConfigOnPolicyChanged (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_CONFIG_POLICY NewPolicy
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Entry2;
+ LIST_ENTRY *Next;
+ IP6_INTERFACE *IpIf;
+ IP6_DAD_ENTRY *DadEntry;
+
+ //
+ // Currently there are only two policies: Manual and Automatic. Regardless of
+ // what transition is going on, i.e., Manual -> Automatic and Automatic ->
+ // Manual, we have to free default router list, on-link prefix list, autonomous
+ // prefix list, address list in all the interfaces and destroy any IPv6 child
+ // instance whose local IP is neither 0 nor the link-local address.
+ //
+ Ip6CleanDefaultRouterList (IpSb);
+ Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);
+ Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);
+
+ //
+ // It's tricky... If the LinkLocal address is O.K., add back the link-local
+ // prefix to the on-link prefix table.
+ //
+ if (IpSb->LinkLocalOk) {
+ Ip6CreatePrefixListEntry (
+ IpSb,
+ TRUE,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ IP6_LINK_LOCAL_PREFIX_LENGTH,
+ &IpSb->LinkLocalAddr
+ );
+ }
+
+ //
+ // All IPv6 children that use global unicast address as it's source address
+ // should be destryoed now. The survivers are those use the link-local address
+ // or the unspecified address as the source address.
+ // TODO: Conduct a check here.
+ Ip6RemoveAddr (
+ IpSb,
+ &IpSb->DefaultInterface->AddressList,
+ &IpSb->DefaultInterface->AddressCount,
+ NULL,
+ 0
+ );
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ //
+ // remove all pending DAD entries for the global addresses.
+ //
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);
+
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {
+ DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);
+
+ if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) {
+ //
+ // Fail this DAD entry if the address is not link-local.
+ //
+ Ip6OnDADFinished (FALSE, IpIf, DadEntry);
+ }
+ }
+ }
+
+ if (NewPolicy == Ip6ConfigPolicyAutomatic) {
+ //
+ // Set paramters to trigger router solicitation sending in timer handler.
+ //
+ IpSb->RouterAdvertiseReceived = FALSE;
+ IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS;
+ //
+ // delay 1 second
+ //
+ IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS);
+ }
+
+}
+
+/**
+ The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.
+
+ @param[in] Instance Pointer to the IP6 config instance data.
+ @param[in] OtherInfoOnly If FALSE, get stateful address and other information
+ via DHCPv6. Otherwise, only get the other information.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_UNSUPPORTED The DHCP6 driver is not available.
+
+**/
+EFI_STATUS
+Ip6ConfigStartStatefulAutoConfig (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN BOOLEAN OtherInfoOnly
+ )
+{
+ EFI_STATUS Status;
+ IP6_SERVICE *IpSb;
+ EFI_DHCP6_CONFIG_DATA Dhcp6CfgData;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ EFI_DHCP6_PACKET_OPTION *OptList[1];
+ UINT16 OptBuf[4];
+ EFI_DHCP6_PACKET_OPTION *Oro;
+ EFI_DHCP6_RETRANSMISSION InfoReqReXmit;
+
+ //
+ // A host must not invoke stateful address configuration if it is already
+ // participating in the statuful protocol as a result of an earlier advertisement.
+ //
+ if (Instance->Dhcp6Handle != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+
+ Instance->OtherInfoOnly = OtherInfoOnly;
+
+ Status = NetLibCreateServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ &Instance->Dhcp6Handle
+ );
+
+ if (Status == EFI_UNSUPPORTED) {
+ //
+ // No DHCPv6 Service Binding protocol, register a notify.
+ //
+ if (Instance->Dhcp6SbNotifyEvent == NULL) {
+ Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent (
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ TPL_CALLBACK,
+ Ip6ConfigOnDhcp6SbInstalled,
+ (VOID *) Instance,
+ &Instance->Registration
+ );
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Instance->Dhcp6SbNotifyEvent != NULL) {
+ gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent);
+ }
+
+ Status = gBS->OpenProtocol (
+ Instance->Dhcp6Handle,
+ &gEfiDhcp6ProtocolGuid,
+ (VOID **) &Instance->Dhcp6,
+ IpSb->Image,
+ IpSb->Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Dhcp6 = Instance->Dhcp6;
+ Dhcp6->Configure (Dhcp6, NULL);
+
+ //
+ // Set the exta options to send. Here we only want the option request option
+ // with DNS SERVERS.
+ //
+ Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf;
+ Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);
+ Oro->OpLen = HTONS (2);
+ *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);
+ OptList[0] = Oro;
+
+ Status = EFI_SUCCESS;
+
+ if (!OtherInfoOnly) {
+ //
+ // Get stateful address and other information via DHCPv6.
+ //
+ Dhcp6CfgData.Dhcp6Callback = NULL;
+ Dhcp6CfgData.CallbackContext = NULL;
+ Dhcp6CfgData.OptionCount = 1;
+ Dhcp6CfgData.OptionList = &OptList[0];
+ Dhcp6CfgData.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;
+ Dhcp6CfgData.IaDescriptor.IaId = Instance->IaId;
+ Dhcp6CfgData.IaInfoEvent = Instance->Dhcp6Event;
+ Dhcp6CfgData.ReconfigureAccept = FALSE;
+ Dhcp6CfgData.RapidCommit = FALSE;
+ Dhcp6CfgData.SolicitRetransmission = NULL;
+
+ Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData);
+
+ if (!EFI_ERROR (Status)) {
+
+ if (IpSb->LinkLocalOk) {
+ Status = Dhcp6->Start (Dhcp6);
+ } else {
+ IpSb->Dhcp6NeedStart = TRUE;
+ }
+
+ }
+ } else {
+ //
+ // Only get other information via DHCPv6, this doesn't require a config
+ // action.
+ //
+ InfoReqReXmit.Irt = 4;
+ InfoReqReXmit.Mrc = 64;
+ InfoReqReXmit.Mrt = 60;
+ InfoReqReXmit.Mrd = 0;
+
+ if (IpSb->LinkLocalOk) {
+ Status = Dhcp6->InfoRequest (
+ Dhcp6,
+ TRUE,
+ Oro,
+ 0,
+ NULL,
+ &InfoReqReXmit,
+ Instance->Dhcp6Event,
+ Ip6ConfigOnDhcp6Reply,
+ Instance
+ );
+ } else {
+ IpSb->Dhcp6NeedInfoRequest = TRUE;
+ }
+
+ }
+
+ return Status;
+}
+
+/**
+ Signal the registered event. It is the callback routine for NetMapIterate.
+
+ @param[in] Map Points to the list of registered event.
+ @param[in] Item The registered event.
+ @param[in] Arg Not used.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ConfigSignalEvent (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ )
+{
+ gBS->SignalEvent ((EFI_EVENT) Item->Key);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read the configuration data from variable storage according to the VarName and
+ gEfiIp6ConfigProtocolGuid. It checks the integrity of variable data. If the
+ data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the
+ configuration data to IP6_CONFIG_INSTANCE.
+
+ @param[in] VarName The pointer to the variable name
+ @param[in, out] Instance The pointer to the IP6 config instance data.
+
+ @retval EFI_NOT_FOUND The variable can not be found or already corrupted.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.
+ @retval EFI_SUCCESS The configuration data was retrieved successfully.
+
+**/
+EFI_STATUS
+Ip6ConfigReadConfigData (
+ IN CHAR16 *VarName,
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ UINTN VarSize;
+ IP6_CONFIG_VARIABLE *Variable;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+ UINTN Index;
+ IP6_CONFIG_DATA_RECORD DataRecord;
+ CHAR8 *Data;
+
+ //
+ // Try to read the configuration variable.
+ //
+ VarSize = 0;
+ Status = gRT->GetVariable (
+ VarName,
+ &gEfiIp6ConfigProtocolGuid,
+ NULL,
+ &VarSize,
+ NULL
+ );
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Allocate buffer and read the config variable.
+ //
+ Variable = AllocatePool (VarSize);
+ if (Variable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gRT->GetVariable (
+ VarName,
+ &gEfiIp6ConfigProtocolGuid,
+ NULL,
+ &VarSize,
+ Variable
+ );
+ if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) {
+ //
+ // GetVariable still error or the variable is corrupted.
+ // Fall back to the default value.
+ //
+ FreePool (Variable);
+
+ //
+ // Remove the problematic variable and return EFI_NOT_FOUND, a new
+ // variable will be set again.
+ //
+ gRT->SetVariable (
+ VarName,
+ &gEfiIp6ConfigProtocolGuid,
+ IP6_CONFIG_VARIABLE_ATTRIBUTE,
+ 0,
+ NULL
+ );
+
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Get the IAID we use.
+ //
+ Instance->IaId = Variable->IaId;
+
+ for (Index = 0; Index < Variable->DataRecordCount; Index++) {
+
+ CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord));
+
+ DataItem = &Instance->DataItem[DataRecord.DataType];
+ if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) &&
+ (DataItem->DataSize != DataRecord.DataSize)
+ ) {
+ //
+ // Perhaps a corrupted data record...
+ //
+ continue;
+ }
+
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {
+ //
+ // This data item has variable length data.
+ //
+ DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize);
+ if (DataItem->Data.Ptr == NULL) {
+ //
+ // no memory resource
+ //
+ continue;
+ }
+ }
+
+ Data = (CHAR8 *) Variable + DataRecord.Offset;
+ CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize);
+
+ DataItem->DataSize = DataRecord.DataSize;
+ DataItem->Status = EFI_SUCCESS;
+ }
+
+ FreePool (Variable);
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Write the configuration data from IP6_CONFIG_INSTANCE to variable storage.
+
+ @param[in] VarName The pointer to the variable name.
+ @param[in] Instance The pointer to the IP6 configuration instance data.
+
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.
+ @retval EFI_SUCCESS The configuration data is written successfully.
+
+**/
+EFI_STATUS
+Ip6ConfigWriteConfigData (
+ IN CHAR16 *VarName,
+ IN IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ UINTN Index;
+ UINTN VarSize;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+ IP6_CONFIG_VARIABLE *Variable;
+ IP6_CONFIG_DATA_RECORD *DataRecord;
+ CHAR8 *Heap;
+ EFI_STATUS Status;
+
+ VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD);
+
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {
+
+ DataItem = &Instance->DataItem[Index];
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {
+
+ VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize;
+ }
+ }
+
+ Variable = AllocatePool (VarSize);
+ if (Variable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Variable->IaId = Instance->IaId;
+ Heap = (CHAR8 *) Variable + VarSize;
+ Variable->DataRecordCount = 0;
+
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {
+
+ DataItem = &Instance->DataItem[Index];
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) {
+
+ Heap -= DataItem->DataSize;
+ CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize);
+
+ DataRecord = &Variable->DataRecord[Variable->DataRecordCount];
+ DataRecord->DataType = (EFI_IP6_CONFIG_DATA_TYPE) Index;
+ DataRecord->DataSize = DataItem->DataSize;
+ DataRecord->Offset = (UINT16) (Heap - (CHAR8 *) Variable);
+
+ Variable->DataRecordCount++;
+ }
+ }
+
+ Variable->Checksum = 0;
+ Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize);
+
+ Status = gRT->SetVariable (
+ VarName,
+ &gEfiIp6ConfigProtocolGuid,
+ IP6_CONFIG_VARIABLE_ATTRIBUTE,
+ VarSize,
+ Variable
+ );
+
+ FreePool (Variable);
+
+ return Status;
+}
+
+/**
+ The work function for EfiIp6ConfigGetData() to get the interface information
+ of the communication device this IP6Config instance manages.
+
+ @param[in] Instance Pointer to the IP6 config instance data.
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in
+ bytes, the size of buffer required to store the specified
+ configuration data.
+ @param[in] Data The data buffer in which the configuration data is returned.
+ Ignored if DataSize is ZERO.
+
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified
+ configuration data, and the required size is
+ returned in DataSize.
+ @retval EFI_SUCCESS The specified configuration data was obtained.
+
+**/
+EFI_STATUS
+Ip6ConfigGetIfInfo (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN OUT UINTN *DataSize,
+ IN VOID *Data OPTIONAL
+ )
+{
+ IP6_SERVICE *IpSb;
+ UINTN Length;
+ IP6_CONFIG_DATA_ITEM *Item;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+ UINT32 AddressCount;
+ UINT32 RouteCount;
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO);
+
+ //
+ // Calculate the required length, add the buffer size for AddressInfo and
+ // RouteTable
+ //
+ Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL);
+ Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL);
+
+ Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE);
+
+ if (*DataSize < Length) {
+ *DataSize = Length;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Copy the fixed size part of the interface info.
+ //
+ Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];
+ IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;
+ CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO));
+
+ //
+ // AddressInfo
+ //
+ IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *) (IfInfo + 1);
+ Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo);
+
+ //
+ // RouteTable
+ //
+ IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *) (IfInfo->AddressInfo + IfInfo->AddressInfoCount);
+ Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable);
+
+ if (IfInfo->AddressInfoCount == 0) {
+ IfInfo->AddressInfo = NULL;
+ }
+
+ if (IfInfo->RouteCount == 0) {
+ IfInfo->RouteTable = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The work function for EfiIp6ConfigSetData() to set the alternative inteface ID
+ for the communication device managed by this IP6Config instance, if the link local
+ IPv6 addresses generated from the interface ID based on the default source the
+ EFI IPv6 Protocol uses is a duplicate address.
+
+ @param[in] Instance Pointer to the IP6 configuration instance data.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type,
+ 8 bytes.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip6ConfigSetAltIfId (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_IP6_CONFIG_INTERFACE_ID *OldIfId;
+ EFI_IP6_CONFIG_INTERFACE_ID *NewIfId;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+
+ if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];
+ OldIfId = DataItem->Data.AltIfId;
+ NewIfId = (EFI_IP6_CONFIG_INTERFACE_ID *) Data;
+
+ CopyMem (OldIfId, NewIfId, DataSize);
+ DataItem->Status = EFI_SUCCESS;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The work function for EfiIp6ConfigSetData() to set the general configuration
+ policy for the EFI IPv6 network stack that is running on the communication device
+ managed by this IP6Config instance. The policy will affect other configuration settings.
+
+ @param[in] Instance Pointer to the IP6 config instance data.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_INVALID_PARAMETER The to be set policy is invalid.
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_ABORTED The new policy equals the current policy.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip6ConfigSetPolicy (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_IP6_CONFIG_POLICY NewPolicy;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+ IP6_SERVICE *IpSb;
+
+ if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ NewPolicy = *((EFI_IP6_CONFIG_POLICY *) Data);
+
+ if (NewPolicy > Ip6ConfigPolicyAutomatic) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NewPolicy == Instance->Policy) {
+
+ return EFI_ABORTED;
+ } else {
+
+ if (NewPolicy == Ip6ConfigPolicyAutomatic) {
+ //
+ // Clean the ManualAddress, Gateway and DnsServers, shrink the variable
+ // data size, and fire up all the related events.
+ //
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ DataItem->Status = EFI_NOT_FOUND;
+ NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway];
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ DataItem->Status = EFI_NOT_FOUND;
+ NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ DataItem->Status = EFI_NOT_FOUND;
+ NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL);
+ } else {
+ //
+ // The policy is changed from automatic to manual. Stop the DHCPv6 process
+ // and destroy the DHCPv6 child.
+ //
+ if (Instance->Dhcp6Handle != NULL) {
+ Ip6ConfigDestroyDhcp6 (Instance);
+ }
+ }
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ Ip6ConfigOnPolicyChanged (IpSb, NewPolicy);
+
+ Instance->Policy = NewPolicy;
+
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ The work function for EfiIp6ConfigSetData() to set the number of consecutive
+ Neighbor Solicitation messages sent while performing Duplicate Address Detection
+ on a tentative address. A value of ZERO indicates that Duplicate Address Detection
+ will not be performed on a tentative address.
+
+ @param[in] The Instance Pointer to the IP6 config instance data.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_ABORTED The new transmit count equals the current configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip6ConfigSetDadXmits (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *OldDadXmits;
+
+ if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits;
+
+ if ((*(UINT32 *) Data) == OldDadXmits->DupAddrDetectTransmits) {
+
+ return EFI_ABORTED;
+ } else {
+
+ OldDadXmits->DupAddrDetectTransmits = *((UINT32 *) Data);
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ The callback function for Ip6SetAddr. The prototype is defined
+ as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed
+ for the manual address set by Ip6ConfigSetMaunualAddress.
+
+ @param[in] IsDadPassed If TRUE, Duplicate Address Detection passed.
+ @param[in] TargetAddress The tentative IPv6 address to be checked.
+ @param[in] Context Pointer to the IP6 configuration instance data.
+
+**/
+VOID
+Ip6ManualAddrDadCallback (
+ IN BOOLEAN IsDadPassed,
+ IN EFI_IPv6_ADDRESS *TargetAddress,
+ IN VOID *Context
+ )
+{
+ IP6_CONFIG_INSTANCE *Instance;
+ UINTN Index;
+ IP6_CONFIG_DATA_ITEM *Item;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddr;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *PassedAddr;
+ UINTN DadPassCount;
+ UINTN DadFailCount;
+ IP6_SERVICE *IpSb;
+
+ Instance = (IP6_CONFIG_INSTANCE *) Context;
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);
+ Item = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];
+ ManualAddr = NULL;
+
+ for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) {
+ //
+ // Find the original tag used to place into the NET_MAP.
+ //
+ ManualAddr = Item->Data.ManualAddress + Index;
+ if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) {
+ break;
+ }
+ }
+
+ ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
+
+ if (IsDadPassed) {
+ NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL);
+ } else {
+ NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL);
+ }
+
+ DadPassCount = NetMapGetCount (&Instance->DadPassedMap);
+ DadFailCount = NetMapGetCount (&Instance->DadFailedMap);
+
+ if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) {
+ //
+ // All addresses have finished the configuration process.
+ //
+ if (DadFailCount != 0) {
+ //
+ // There is at least one duplicate address.
+ //
+ FreePool (Item->Data.Ptr);
+
+ Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);
+ if (Item->DataSize == 0) {
+ //
+ // All failed, bad luck.
+ //
+ Item->Data.Ptr = NULL;
+ Item->Status = EFI_NOT_FOUND;
+ } else {
+ //
+ // Part of addresses are detected to be duplicates, so update the
+ // data with those passed.
+ //
+ PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AllocatePool (Item->DataSize);
+ ASSERT (PassedAddr != NULL);
+
+ Item->Data.Ptr = PassedAddr;
+ Item->Status = EFI_SUCCESS;
+
+ while (!NetMapIsEmpty (&Instance->DadPassedMap)) {
+ ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) NetMapRemoveHead (&Instance->DadPassedMap, NULL);
+ CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
+
+ PassedAddr++;
+ }
+
+ ASSERT ((UINTN) PassedAddr - (UINTN) Item->Data.Ptr == Item->DataSize);
+ }
+ } else {
+ //
+ // All addresses are valid.
+ //
+ Item->Status = EFI_SUCCESS;
+ }
+
+ //
+ // Remove the tags we put in the NET_MAPs.
+ //
+ while (!NetMapIsEmpty (&Instance->DadFailedMap)) {
+ NetMapRemoveHead (&Instance->DadFailedMap, NULL);
+ }
+
+ while (!NetMapIsEmpty (&Instance->DadPassedMap)) {
+ NetMapRemoveHead (&Instance->DadPassedMap, NULL);
+ }
+
+ //
+ // Signal the waiting events.
+ //
+ NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ Ip6ConfigWriteConfigData (IpSb->MacString, Instance);
+ }
+}
+
+/**
+ The work function for EfiIp6ConfigSetData() to set the station addresses manually
+ for the EFI IPv6 network stack. It is only configurable when the policy is
+ Ip6ConfigPolicyManual.
+
+ @param[in] Instance Pointer to the IP6 configuration instance data.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set
+ under the current policy.
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation.
+ @retval EFI_NOT_READY An asynchrous process is invoked to set the specified
+ configuration data, and the process is not finished.
+ @retval EFI_ABORTED The manual addresses to be set equal current
+ configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip6ConfigSetMaunualAddress (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *NewAddress;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *TmpAddress;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+ UINTN NewAddressCount;
+ UINTN Index1;
+ UINTN Index2;
+ IP6_SERVICE *IpSb;
+ IP6_ADDRESS_INFO *CurrentAddrInfo;
+ IP6_ADDRESS_INFO *Copy;
+ LIST_ENTRY CurrentSourceList;
+ UINT32 CurrentSourceCount;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Entry2;
+ IP6_INTERFACE *IpIf;
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;
+ EFI_STATUS Status;
+ BOOLEAN IsUpdated;
+
+ ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY);
+
+ if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip6ConfigPolicyManual) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);
+ NewAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) Data;
+
+ for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {
+
+ if (NetIp6IsLinkLocalAddr (&NewAddress->Address) ||
+ !NetIp6IsValidUnicast (&NewAddress->Address) ||
+ (NewAddress->PrefixLength > 128)
+ ) {
+ //
+ // make sure the IPv6 address is unicast and not link-local address &&
+ // the prefix length is valid.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TmpAddress = NewAddress + 1;
+ for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) {
+ //
+ // Any two addresses in the array can't be equal.
+ //
+ if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+
+ //
+ // Build the current source address list.
+ //
+ InitializeListHead (&CurrentSourceList);
+ CurrentSourceCount = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {
+ CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
+
+ Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo);
+ if (Copy == NULL) {
+ break;
+ }
+
+ InsertTailList (&CurrentSourceList, &Copy->Link);
+ CurrentSourceCount++;
+ }
+ }
+
+ //
+ // Update the value... a long journey starts
+ //
+ NewAddress = AllocateCopyPool (DataSize, Data);
+ if (NewAddress == NULL) {
+ Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0);
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Store the new data, and init the DataItem status to EFI_NOT_READY because
+ // we may have an asynchronous configuration process.
+ //
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NewAddress;
+ DataItem->DataSize = DataSize;
+ DataItem->Status = EFI_NOT_READY;
+
+ //
+ // Trigger DAD, it's an asynchronous process.
+ //
+ IsUpdated = FALSE;
+
+ for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) {
+ if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) {
+ ASSERT (CurrentAddrInfo != NULL);
+ //
+ // Remove this already existing source address from the CurrentSourceList
+ // built before.
+ //
+ Ip6RemoveAddr (
+ NULL,
+ &CurrentSourceList,
+ &CurrentSourceCount,
+ &CurrentAddrInfo->Address,
+ 128
+ );
+
+ //
+ // This manual address is already in use, see whether prefix length is changed.
+ //
+ if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) {
+ //
+ // Remove the on-link prefix table, the route entry will be removed
+ // implicitly.
+ //
+ PrefixEntry = Ip6FindPrefixListEntry (
+ IpSb,
+ TRUE,
+ CurrentAddrInfo->PrefixLength,
+ &CurrentAddrInfo->Address
+ );
+ if (PrefixEntry != NULL) {
+ Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);
+ }
+
+ //
+ // Save the prefix length.
+ //
+ CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength;
+ IsUpdated = TRUE;
+ }
+
+ //
+ // create a new on-link prefix entry.
+ //
+ PrefixEntry = Ip6FindPrefixListEntry (
+ IpSb,
+ TRUE,
+ NewAddress->PrefixLength,
+ &NewAddress->Address
+ );
+ if (PrefixEntry == NULL) {
+ Ip6CreatePrefixListEntry (
+ IpSb,
+ TRUE,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ NewAddress->PrefixLength,
+ &NewAddress->Address
+ );
+ }
+
+ CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast;
+ //
+ // Artificially mark this address passed DAD be'coz it is already in use.
+ //
+ Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance);
+ } else {
+ //
+ // A new address.
+ //
+ IsUpdated = TRUE;
+
+ //
+ // Set the new address, this will trigger DAD and activate the address if
+ // DAD succeeds.
+ //
+ Ip6SetAddress (
+ IpSb->DefaultInterface,
+ &NewAddress->Address,
+ NewAddress->IsAnycast,
+ NewAddress->PrefixLength,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ Ip6ManualAddrDadCallback,
+ Instance
+ );
+ }
+ }
+
+ //
+ // Check the CurrentSourceList, it now contains those addresses currently in
+ // use and will be removed.
+ //
+ IpIf = IpSb->DefaultInterface;
+
+ while (!IsListEmpty (&CurrentSourceList)) {
+ IsUpdated = TRUE;
+
+ CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link);
+
+ //
+ // This local address is going to be removed, the IP instances that are
+ // currently using it will be destroyed.
+ //
+ Ip6RemoveAddr (
+ IpSb,
+ &IpIf->AddressList,
+ &IpIf->AddressCount,
+ &CurrentAddrInfo->Address,
+ 128
+ );
+
+ //
+ // Remove the on-link prefix table, the route entry will be removed
+ // implicitly.
+ //
+ PrefixEntry = Ip6FindPrefixListEntry (
+ IpSb,
+ TRUE,
+ CurrentAddrInfo->PrefixLength,
+ &CurrentAddrInfo->Address
+ );
+ if (PrefixEntry != NULL) {
+ Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE);
+ }
+
+ RemoveEntryList (&CurrentAddrInfo->Link);
+ FreePool (CurrentAddrInfo);
+ }
+
+ if (IsUpdated) {
+ if (DataItem->Status == EFI_NOT_READY) {
+ //
+ // If DAD is disabled on this interface, the configuration process is
+ // actually synchronous, and the data item's status will be changed to
+ // the final status before we reach here, just check it.
+ //
+ Status = EFI_NOT_READY;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+ } else {
+ //
+ // No update is taken, reset the status to success and return EFI_ABORTED.
+ //
+ DataItem->Status = EFI_SUCCESS;
+ Status = EFI_ABORTED;
+ }
+
+ return Status;
+}
+
+/**
+ The work function for EfiIp6ConfigSetData() to set the gateway addresses manually
+ for the EFI IPv6 network stack that is running on the communication device that
+ this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is
+ Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses.
+
+ @param[in] Instance The pointer to the IP6 config instance data.
+ @param[in] DataSize The size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set. This points to an array of
+ EFI_IPv6_ADDRESS instances.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set
+ under the current policy.
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to complete the operation.
+ @retval EFI_ABORTED The manual gateway addresses to be set equal the
+ current configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip6ConfigSetGateway (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ UINTN Index1;
+ UINTN Index2;
+ EFI_IPv6_ADDRESS *OldGateway;
+ EFI_IPv6_ADDRESS *NewGateway;
+ UINTN OldGatewayCount;
+ UINTN NewGatewayCount;
+ IP6_CONFIG_DATA_ITEM *Item;
+ BOOLEAN OneRemoved;
+ BOOLEAN OneAdded;
+ IP6_SERVICE *IpSb;
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+ VOID *Tmp;
+
+ if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip6ConfigPolicyManual) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ NewGateway = (EFI_IPv6_ADDRESS *) Data;
+ NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS);
+ for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {
+
+ if (!NetIp6IsValidUnicast (NewGateway + Index1)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) {
+ if (EFI_IP6_EQUAL (NewGateway + Index1, NewGateway + Index2)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ Item = &Instance->DataItem[Ip6ConfigDataTypeGateway];
+ OldGateway = Item->Data.Gateway;
+ OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);
+ OneRemoved = FALSE;
+ OneAdded = FALSE;
+
+ if (NewGatewayCount != OldGatewayCount) {
+ Tmp = AllocatePool (DataSize);
+ if (Tmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ Tmp = NULL;
+ }
+
+ for (Index1 = 0; Index1 < OldGatewayCount; Index1++) {
+ //
+ // Find the gateways that are no long in the new setting and remove them.
+ //
+ for (Index2 = 0; Index2 < NewGatewayCount; Index2++) {
+ if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) {
+ OneRemoved = TRUE;
+ break;
+ }
+ }
+
+ if (Index2 == NewGatewayCount) {
+ //
+ // Remove this default router.
+ //
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1);
+ if (DefaultRouter != NULL) {
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
+ }
+ }
+ }
+
+ for (Index1 = 0; Index1 < NewGatewayCount; Index1++) {
+
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1);
+ if (DefaultRouter == NULL) {
+ Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME);
+ OneAdded = TRUE;
+ }
+ }
+
+ if (!OneRemoved && !OneAdded) {
+ Item->Status = EFI_SUCCESS;
+ return EFI_ABORTED;
+ } else {
+
+ if (Tmp != NULL) {
+ if (Item->Data.Ptr != NULL) {
+ FreePool (Item->Data.Ptr);
+ }
+ Item->Data.Ptr = Tmp;
+ }
+
+ CopyMem (Item->Data.Ptr, Data, DataSize);
+ Item->DataSize = DataSize;
+ Item->Status = EFI_SUCCESS;
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ The work function for EfiIp6ConfigSetData() to set the DNS server list for the
+ EFI IPv6 network stack running on the communication device that this EFI IPv6
+ Configuration Protocol manages. It is not configurable when the policy is
+ Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses.
+
+ @param[in] Instance The pointer to the IP6 config instance data.
+ @param[in] DataSize The size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set, points to an array of
+ EFI_IPv6_ADDRESS instances.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type.
+ @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set
+ under the current policy.
+ @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+ @retval EFI_ABORTED The DNS server addresses to be set equal the current
+ configuration.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set.
+
+**/
+EFI_STATUS
+Ip6ConfigSetDnsServer (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ UINTN OldIndex;
+ UINTN NewIndex;
+ UINTN Index1;
+ EFI_IPv6_ADDRESS *OldDns;
+ EFI_IPv6_ADDRESS *NewDns;
+ UINTN OldDnsCount;
+ UINTN NewDnsCount;
+ IP6_CONFIG_DATA_ITEM *Item;
+ BOOLEAN OneAdded;
+ VOID *Tmp;
+
+ if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip6ConfigPolicyManual) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];
+ NewDns = (EFI_IPv6_ADDRESS *) Data;
+ OldDns = Item->Data.DnsServers;
+ NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS);
+ OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);
+ OneAdded = FALSE;
+
+ if (NewDnsCount != OldDnsCount) {
+ Tmp = AllocatePool (DataSize);
+ if (Tmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ Tmp = NULL;
+ }
+
+ for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) {
+
+ if (!NetIp6IsValidUnicast (NewDns + NewIndex)) {
+ //
+ // The dns server address must be unicast.
+ //
+ FreePool (Tmp);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {
+ if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) {
+ FreePool (Tmp);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (OneAdded) {
+ //
+ // If any address in the new setting is not in the old settings, skip the
+ // comparision below.
+ //
+ continue;
+ }
+
+ for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) {
+ if (EFI_IP6_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) {
+ //
+ // If found break out.
+ //
+ break;
+ }
+ }
+
+ if (OldIndex == OldDnsCount) {
+ OneAdded = TRUE;
+ }
+ }
+
+ if (!OneAdded && (DataSize == Item->DataSize)) {
+ //
+ // No new item is added and the size is the same.
+ //
+ Item->Status = EFI_SUCCESS;
+ return EFI_ABORTED;
+ } else {
+ if (Tmp != NULL) {
+ if (Item->Data.Ptr != NULL) {
+ FreePool (Item->Data.Ptr);
+ }
+ Item->Data.Ptr = Tmp;
+ }
+
+ CopyMem (Item->Data.Ptr, Data, DataSize);
+ Item->DataSize = DataSize;
+ Item->Status = EFI_SUCCESS;
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Generate the operational state of the interface this IP6 config instance manages
+ and output in EFI_IP6_CONFIG_INTERFACE_INFO.
+
+ @param[in] IpSb The pointer to the IP6 service binding instance.
+ @param[out] IfInfo The pointer to the IP6 configuration interface information structure.
+
+**/
+VOID
+Ip6ConfigInitIfInfo (
+ IN IP6_SERVICE *IpSb,
+ OUT EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo
+ )
+{
+ IfInfo->Name[0] = L'e';
+ IfInfo->Name[1] = L't';
+ IfInfo->Name[2] = L'h';
+ IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex);
+ IfInfo->Name[4] = 0;
+
+ IfInfo->IfType = IpSb->SnpMode.IfType;
+ IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize;
+ CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize);
+}
+
+/**
+ Parse DHCPv6 reply packet to get the DNS server list.
+ It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event.
+
+ @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL instance.
+ @param[in, out] Instance The pointer to the IP6 configuration instance data.
+ @param[in] Reply The pointer to the DHCPv6 reply packet.
+
+ @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet.
+ @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or
+ the DNS server address is not valid.
+
+**/
+EFI_STATUS
+Ip6ConfigParseDhcpReply (
+ IN EFI_DHCP6_PROTOCOL *Dhcp6,
+ IN OUT IP6_CONFIG_INSTANCE *Instance,
+ IN EFI_DHCP6_PACKET *Reply
+ )
+{
+ EFI_STATUS Status;
+ UINT32 OptCount;
+ EFI_DHCP6_PACKET_OPTION **OptList;
+ UINT16 OpCode;
+ UINT16 Length;
+ UINTN Index;
+ UINTN Index2;
+ EFI_IPv6_ADDRESS *DnsServer;
+ IP6_CONFIG_DATA_ITEM *Item;
+
+ //
+ // A DHCPv6 reply packet is received as the response to our InfoRequest
+ // packet.
+ //
+ OptCount = 0;
+ Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_NOT_READY;
+ }
+
+ OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *));
+ if (OptList == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_NOT_READY;
+ goto ON_EXIT;
+ }
+
+ Status = EFI_SUCCESS;
+
+ for (Index = 0; Index < OptCount; Index++) {
+ //
+ // Go through all the options to check the ones we are interested in.
+ // The OpCode and Length are in network byte-order and may not be naturally
+ // aligned.
+ //
+ CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode));
+ OpCode = NTOHS (OpCode);
+
+ if (OpCode == IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS) {
+ CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length));
+ Length = NTOHS (Length);
+
+ if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) {
+ //
+ // The length should be a multiple of 16 bytes.
+ //
+ Status = EFI_NOT_READY;
+ break;
+ }
+
+ //
+ // Validate the DnsServers: whether they are unicast addresses.
+ //
+ DnsServer = (EFI_IPv6_ADDRESS *) OptList[Index]->Data;
+ for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) {
+ if (!NetIp6IsValidUnicast (DnsServer)) {
+ Status = EFI_NOT_READY;
+ goto ON_EXIT;
+ }
+
+ DnsServer++;
+ }
+
+ Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];
+
+ if (Item->DataSize != Length) {
+ if (Item->Data.Ptr != NULL) {
+ FreePool (Item->Data.Ptr);
+ }
+
+ Item->Data.Ptr = AllocatePool (Length);
+ ASSERT (Item->Data.Ptr != NULL);
+ }
+
+ CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length);
+ Item->DataSize = Length;
+ Item->Status = EFI_SUCCESS;
+
+ //
+ // Signal the waiting events.
+ //
+ NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL);
+
+ break;
+ }
+ }
+
+ON_EXIT:
+
+ FreePool (OptList);
+ return Status;
+}
+
+/**
+ The callback function for Ip6SetAddr. The prototype is defined
+ as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed
+ on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event().
+
+ @param[in] IsDadPassed If TRUE, Duplicate Address Detection passes.
+ @param[in] TargetAddress The tentative IPv6 address to be checked.
+ @param[in] Context Pointer to the IP6 configuration instance data.
+
+**/
+VOID
+Ip6ConfigSetStatefulAddrCallback (
+ IN BOOLEAN IsDadPassed,
+ IN EFI_IPv6_ADDRESS *TargetAddress,
+ IN VOID *Context
+ )
+{
+ IP6_CONFIG_INSTANCE *Instance;
+
+ Instance = (IP6_CONFIG_INSTANCE *) Context;
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);
+
+ //
+ // We should record the addresses that fail the DAD, and DECLINE them.
+ //
+ if (IsDadPassed) {
+ //
+ // Decrease the count, no interests in those passed DAD.
+ //
+ if (Instance->FailedIaAddressCount > 0 ) {
+ Instance->FailedIaAddressCount--;
+ }
+ } else {
+ //
+ // Record it.
+ //
+ IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress);
+ Instance->DeclineAddressCount++;
+ }
+
+ if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) {
+ //
+ // The checking on all addresses are finished.
+ //
+ if (Instance->DeclineAddressCount != 0) {
+ //
+ // Decline those duplicates.
+ //
+ Instance->Dhcp6->Decline (
+ Instance->Dhcp6,
+ Instance->DeclineAddressCount,
+ Instance->DeclineAddress
+ );
+ }
+
+ if (Instance->DeclineAddress != NULL) {
+ FreePool (Instance->DeclineAddress);
+ }
+ Instance->DeclineAddress = NULL;
+ Instance->DeclineAddressCount = 0;
+ }
+}
+
+/**
+ The event handle routine when DHCPv6 process is finished or is updated.
+
+ @param[in] Event Not used.
+ @param[in] Context The pointer to the IP6 configuration instance data.
+
+**/
+VOID
+EFIAPI
+Ip6ConfigOnDhcp6Event (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP6_CONFIG_INSTANCE *Instance;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ EFI_STATUS Status;
+ EFI_DHCP6_MODE_DATA Dhcp6ModeData;
+ EFI_DHCP6_IA *Ia;
+ EFI_DHCP6_IA_ADDRESS *IaAddr;
+ UINT32 Index;
+ IP6_SERVICE *IpSb;
+ IP6_ADDRESS_INFO *AddrInfo;
+ IP6_INTERFACE *IpIf;
+
+ Instance = (IP6_CONFIG_INSTANCE *) Context;
+
+ if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) {
+ //
+ // IPv6 is not operating in the automatic policy now or
+ // the DHCPv6 information request message exchange is aborted.
+ //
+ return ;
+ }
+
+ //
+ // The stateful address autoconfiguration is done or updated.
+ //
+ Dhcp6 = Instance->Dhcp6;
+
+ Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL);
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ IpIf = IpSb->DefaultInterface;
+ Ia = Dhcp6ModeData.Ia;
+ IaAddr = Ia->IaAddress;
+
+ if (Instance->DeclineAddress != NULL) {
+ FreePool (Instance->DeclineAddress);
+ }
+
+ Instance->DeclineAddress = (EFI_IPv6_ADDRESS *) AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS));
+ if (Instance->DeclineAddress == NULL) {
+ goto ON_EXIT;
+ }
+
+ Instance->FailedIaAddressCount = Ia->IaAddressCount;
+ Instance->DeclineAddressCount = 0;
+
+ for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) {
+ if (Ia->IaAddress[Index].ValidLifetime != 0 && Ia->State == Dhcp6Bound) {
+ //
+ // Set this address, either it's a new address or with updated lifetimes.
+ // An appropriate prefix length will be set.
+ //
+ Ip6SetAddress (
+ IpIf,
+ &IaAddr->IpAddress,
+ FALSE,
+ 0,
+ IaAddr->ValidLifetime,
+ IaAddr->PreferredLifetime,
+ Ip6ConfigSetStatefulAddrCallback,
+ Instance
+ );
+ } else {
+ //
+ // discard this address, artificially decrease the count as if this address
+ // passed DAD.
+ //
+ if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) {
+ ASSERT (AddrInfo != NULL);
+ Ip6RemoveAddr (
+ IpSb,
+ &IpIf->AddressList,
+ &IpIf->AddressCount,
+ &AddrInfo->Address,
+ AddrInfo->PrefixLength
+ );
+ }
+
+ if (Instance->FailedIaAddressCount > 0) {
+ Instance->FailedIaAddressCount--;
+ }
+ }
+ }
+
+ //
+ // Parse the Reply packet to get the options we need.
+ //
+ if (Dhcp6ModeData.Ia->ReplyPacket != NULL) {
+ Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket);
+ }
+
+ON_EXIT:
+
+ FreePool (Dhcp6ModeData.ClientId);
+ FreePool (Dhcp6ModeData.Ia);
+}
+
+/**
+ The event process routine when the DHCPv6 server is answered with a reply packet
+ for an information request.
+
+ @param[in] This Points to the EFI_DHCP6_PROTOCOL.
+ @param[in] Context The pointer to the IP6 configuration instance data.
+ @param[in] Packet The DHCPv6 reply packet.
+
+ @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet.
+ @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or
+ the DNS server address is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ConfigOnDhcp6Reply (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *) Context, Packet);
+}
+
+/**
+ The event process routine when the DHCPv6 service binding protocol is installed
+ in the system.
+
+ @param[in] Event Not used.
+ @param[in] Context The pointer to the IP6 config instance data.
+
+**/
+VOID
+EFIAPI
+Ip6ConfigOnDhcp6SbInstalled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP6_CONFIG_INSTANCE *Instance;
+
+ Instance = (IP6_CONFIG_INSTANCE *) Context;
+
+ if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) {
+ //
+ // The DHCP6 child is already created or the policy is no longer AUTOMATIC.
+ //
+ return ;
+ }
+
+ Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly);
+}
+
+/**
+ Set the configuration for the EFI IPv6 network stack running on the communication
+ device this EFI IPv6 Configuration Protocol instance manages.
+
+ This function is used to set the configuration data of type DataType for the EFI
+ IPv6 network stack that is running on the communication device that this EFI IPv6
+ Configuration Protocol instance manages.
+
+ DataSize is used to calculate the count of structure instances in the Data for
+ a DataType in which multiple structure instances are allowed.
+
+ This function is always non-blocking. When setting some type of configuration data,
+ an asynchronous process is invoked to check the correctness of the data, such as
+ performing Duplicate Address Detection on the manually set local IPv6 addresses.
+ EFI_NOT_READY is returned immediately to indicate that such an asynchronous process
+ is invoked, and the process is not finished yet. The caller wanting to get the result
+ of the asynchronous process is required to call RegisterDataNotify() to register an
+ event on the specified configuration data. Once the event is signaled, the caller
+ can call GetData() to obtain the configuration data and know the result.
+ For other types of configuration data that do not require an asynchronous configuration
+ process, the result of the operation is immediately returned.
+
+ @param[in] This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to set.
+ @param[in] DataSize Size of the buffer pointed to by Data in bytes.
+ @param[in] Data The data buffer to set. The type of the data buffer is
+ associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - Data is NULL.
+ - One or more fields in Data do not match the requirement of the
+ data type indicated by DataType.
+ @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified
+ configuration data cannot be set under the current policy.
+ @retval EFI_ACCESS_DENIED Another set operation on the specified configuration
+ data is already in process.
+ @retval EFI_NOT_READY An asynchronous process was invoked to set the specified
+ configuration data, and the process is not finished yet.
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type
+ indicated by DataType.
+ @retval EFI_UNSUPPORTED This DataType is not supported.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6ConfigSetData (
+ IN EFI_IP6_CONFIG_PROTOCOL *This,
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP6_CONFIG_INSTANCE *Instance;
+ IP6_SERVICE *IpSb;
+
+ if ((This == NULL) || (Data == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip6ConfigDataTypeMaximum) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Status = Instance->DataItem[DataType].Status;
+ if (Status != EFI_NOT_READY) {
+
+ if (Instance->DataItem[DataType].SetData == NULL) {
+ //
+ // This type of data is readonly.
+ //
+ Status = EFI_WRITE_PROTECTED;
+ } else {
+
+ Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Fire up the events registered with this type of data.
+ //
+ NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);
+ Ip6ConfigWriteConfigData (IpSb->MacString, Instance);
+ } else if (Status == EFI_ABORTED) {
+ //
+ // The SetData is aborted because the data to set is the same with
+ // the one maintained.
+ //
+ Status = EFI_SUCCESS;
+ NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL);
+ }
+ }
+ } else {
+ //
+ // Another asynchornous process is on the way.
+ //
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Get the configuration data for the EFI IPv6 network stack running on the communication
+ device that this EFI IPv6 Configuration Protocol instance manages.
+
+ This function returns the configuration data of type DataType for the EFI IPv6 network
+ stack running on the communication device that this EFI IPv6 Configuration Protocol instance
+ manages.
+
+ The caller is responsible for allocating the buffer used to return the specified
+ configuration data. The required size will be returned to the caller if the size of
+ the buffer is too small.
+
+ EFI_NOT_READY is returned if the specified configuration data is not ready due to an
+ asynchronous configuration process already in progress. The caller can call RegisterDataNotify()
+ to register an event on the specified configuration data. Once the asynchronous configuration
+ process is finished, the event will be signaled, and a subsequent GetData() call will return
+ the specified configuration data.
+
+ @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to get.
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in bytes, the
+ size of buffer required to store the specified configuration data.
+ @param[in] Data The data buffer in which the configuration data is returned. The
+ type of the data buffer is associated with the DataType.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:
+ - This is NULL.
+ - DataSize is NULL.
+ - Data is NULL if *DataSize is not zero.
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data,
+ and the required size is returned in DataSize.
+ @retval EFI_NOT_READY The specified configuration data is not ready due to an
+ asynchronous configuration process already in progress.
+ @retval EFI_NOT_FOUND The specified configuration data is not found.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6ConfigGetData (
+ IN EFI_IP6_CONFIG_PROTOCOL *This,
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,
+ IN OUT UINTN *DataSize,
+ IN VOID *Data OPTIONAL
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP6_CONFIG_INSTANCE *Instance;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+
+ if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip6ConfigDataTypeMaximum) {
+ return EFI_NOT_FOUND;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);
+ DataItem = &Instance->DataItem[DataType];
+
+ Status = Instance->DataItem[DataType].Status;
+ if (!EFI_ERROR (Status)) {
+
+ if (DataItem->GetData != NULL) {
+
+ Status = DataItem->GetData (Instance, DataSize, Data);
+ } else if (*DataSize < Instance->DataItem[DataType].DataSize) {
+ //
+ // Update the buffer length.
+ //
+ *DataSize = Instance->DataItem[DataType].DataSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+
+ *DataSize = Instance->DataItem[DataType].DataSize;
+ CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize);
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Register an event that is signaled whenever a configuration process on the specified
+ configuration data is done.
+
+ This function registers an event that is to be signaled whenever a configuration
+ process on the specified configuration data is performed. An event can be registered
+ for a different DataType simultaneously. The caller is responsible for determining
+ which type of configuration data causes the signaling of the event in such an event.
+
+ @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to unregister the event for.
+ @param[in] Event The event to register.
+
+ @retval EFI_SUCCESS The notification event for the specified configuration data is
+ registered.
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
+ @retval EFI_UNSUPPORTED The configuration data type specified by DataType is not
+ supported.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The Event is already registered for the DataType.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6ConfigRegisterDataNotify (
+ IN EFI_IP6_CONFIG_PROTOCOL *This,
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP6_CONFIG_INSTANCE *Instance;
+ NET_MAP *EventMap;
+ NET_MAP_ITEM *Item;
+
+ if ((This == NULL) || (Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip6ConfigDataTypeMaximum) {
+ return EFI_UNSUPPORTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);
+ EventMap = &Instance->DataItem[DataType].EventMap;
+
+ //
+ // Check whether this event is already registered for this DataType.
+ //
+ Item = NetMapFindKey (EventMap, Event);
+ if (Item == NULL) {
+
+ Status = NetMapInsertTail (EventMap, Event, NULL);
+
+ if (EFI_ERROR (Status)) {
+
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+ } else {
+
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Remove a previously registered event for the specified configuration data.
+
+ @param This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.
+ @param DataType The type of data to remove from the previously
+ registered event.
+ @param Event The event to be unregistered.
+
+ @retval EFI_SUCCESS The event registered for the specified
+ configuration data was removed.
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
+ @retval EFI_NOT_FOUND The Event has not been registered for the
+ specified DataType.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6ConfigUnregisterDataNotify (
+ IN EFI_IP6_CONFIG_PROTOCOL *This,
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP6_CONFIG_INSTANCE *Instance;
+ NET_MAP_ITEM *Item;
+
+ if ((This == NULL) || (Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= Ip6ConfigDataTypeMaximum) {
+ return EFI_NOT_FOUND;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This);
+
+ Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event);
+ if (Item != NULL) {
+
+ NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL);
+ Status = EFI_SUCCESS;
+ } else {
+
+ Status = EFI_NOT_FOUND;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+/**
+ Initialize an IP6_CONFIG_INSTANCE.
+
+ @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+ @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully.
+
+**/
+EFI_STATUS
+Ip6ConfigInitInstance (
+ OUT IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_CONFIG_INSTANCE *TmpInstance;
+ LIST_ENTRY *Entry;
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT16 IfIndex;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+
+ Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE;
+
+ //
+ // Determine the index of this interface.
+ //
+ IfIndex = 0;
+ NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) {
+ TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_INSTANCE_SIGNATURE);
+
+ if (TmpInstance->IfIndex > IfIndex) {
+ //
+ // There is a sequence hole because some interface is down.
+ //
+ break;
+ }
+
+ IfIndex++;
+ }
+
+ Instance->IfIndex = IfIndex;
+ NetListInsertBefore (Entry, &Instance->Link);
+
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {
+ //
+ // Initialize the event map for each data item.
+ //
+ NetMapInit (&Instance->DataItem[Index].EventMap);
+ }
+
+ //
+ // Initialize the NET_MAPs used for DAD on manually configured source addresses.
+ //
+ NetMapInit (&Instance->DadFailedMap);
+ NetMapInit (&Instance->DadPassedMap);
+
+ //
+ // Initialize each data type: associate storage and set data size for the
+ // fixed size data types, hook the SetData function, set the data attribute.
+ //
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo];
+ DataItem->GetData = Ip6ConfigGetIfInfo;
+ DataItem->Data.Ptr = &Instance->InterfaceInfo;
+ DataItem->DataSize = sizeof (Instance->InterfaceInfo);
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE);
+ Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo);
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId];
+ DataItem->SetData = Ip6ConfigSetAltIfId;
+ DataItem->Data.Ptr = &Instance->AltIfId;
+ DataItem->DataSize = sizeof (Instance->AltIfId);
+ DataItem->Status = EFI_NOT_FOUND;
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypePolicy];
+ DataItem->SetData = Ip6ConfigSetPolicy;
+ DataItem->Data.Ptr = &Instance->Policy;
+ DataItem->DataSize = sizeof (Instance->Policy);
+ Instance->Policy = Ip6ConfigPolicyAutomatic;
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits];
+ DataItem->SetData = Ip6ConfigSetDadXmits;
+ DataItem->Data.Ptr = &Instance->DadXmits;
+ DataItem->DataSize = sizeof (Instance->DadXmits);
+ Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS;
+ SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED);
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];
+ DataItem->SetData = Ip6ConfigSetMaunualAddress;
+ DataItem->Status = EFI_NOT_FOUND;
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway];
+ DataItem->SetData = Ip6ConfigSetGateway;
+ DataItem->Status = EFI_NOT_FOUND;
+
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];
+ DataItem->SetData = Ip6ConfigSetDnsServer;
+ DataItem->Status = EFI_NOT_FOUND;
+
+ //
+ // Create the event used for DHCP.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Ip6ConfigOnDhcp6Event,
+ Instance,
+ &Instance->Dhcp6Event
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Instance->Configured = TRUE;
+
+ //
+ // Try to read the config data from NV variable.
+ //
+ Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance);
+ if (Status == EFI_NOT_FOUND) {
+ //
+ // The NV variable is not set, so generate a random IAID, and write down the
+ // fresh new configuration as the NV variable now.
+ //
+ Instance->IaId = NET_RANDOM (NetRandomInitSeed ());
+
+ for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) {
+ Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31));
+ }
+
+ Ip6ConfigWriteConfigData (IpSb->MacString, Instance);
+ } else if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Instance->Ip6Config.SetData = EfiIp6ConfigSetData;
+ Instance->Ip6Config.GetData = EfiIp6ConfigGetData;
+ Instance->Ip6Config.RegisterDataNotify = EfiIp6ConfigRegisterDataNotify;
+ Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify;
+
+
+ //
+ // Publish the IP6 configuration form
+ //
+ return Ip6ConfigFormInit (Instance);
+}
+
+/**
+ Release an IP6_CONFIG_INSTANCE.
+
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.
+
+**/
+VOID
+Ip6ConfigCleanInstance (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ UINTN Index;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+
+ if (Instance->DeclineAddress != NULL) {
+ FreePool (Instance->DeclineAddress);
+ }
+
+ if (!Instance->Configured) {
+ return ;
+ }
+
+ if (Instance->Dhcp6Handle != NULL) {
+
+ Ip6ConfigDestroyDhcp6 (Instance);
+ }
+
+ //
+ // Close the event.
+ //
+ if (Instance->Dhcp6Event != NULL) {
+ gBS->CloseEvent (Instance->Dhcp6Event);
+ }
+
+ NetMapClean (&Instance->DadPassedMap);
+ NetMapClean (&Instance->DadFailedMap);
+
+ for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) {
+
+ DataItem = &Instance->DataItem[Index];
+
+ if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) {
+ if (DataItem->Data.Ptr != NULL) {
+ FreePool (DataItem->Data.Ptr);
+ }
+ DataItem->Data.Ptr = NULL;
+ DataItem->DataSize = 0;
+ }
+
+ NetMapClean (&Instance->DataItem[Index].EventMap);
+ }
+
+ Ip6ConfigFormUnload (Instance);
+
+ RemoveEntryList (&Instance->Link);
+}
+
+/**
+ Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.
+
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.
+
+ @retval EFI_SUCCESS The child was successfully destroyed.
+ @retval Others Failed to destory the child.
+
+**/
+EFI_STATUS
+Ip6ConfigDestroyDhcp6 (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ IP6_SERVICE *IpSb;
+ EFI_STATUS Status;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+
+ Dhcp6 = Instance->Dhcp6;
+ ASSERT (Dhcp6 != NULL);
+
+ Dhcp6->Stop (Dhcp6);
+ Dhcp6->Configure (Dhcp6, NULL);
+ Instance->Dhcp6 = NULL;
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+
+ //
+ // Close DHCPv6 protocol and destroy the child.
+ //
+ Status = gBS->CloseProtocol (
+ Instance->Dhcp6Handle,
+ &gEfiDhcp6ProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = NetLibDestroyServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ Instance->Dhcp6Handle
+ );
+
+ Instance->Dhcp6Handle = NULL;
+
+ return Status;
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
new file mode 100644
index 0000000000..5ae483931a
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
@@ -0,0 +1,295 @@
+/** @file
+ Definitions for EFI IPv6 Configuartion Protocol implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __IP6_CONFIG_IMPL_H__
+#define __IP6_CONFIG_IMPL_H__
+
+#define IP6_CONFIG_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'C')
+#define IP6_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I')
+#define IP6_CONFIG_VARIABLE_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)
+
+#define IP6_CONFIG_DEFAULT_DAD_XMITS 1
+#define IP6_CONFIG_DHCP6_OPTION_ORO 6
+#define IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS 23
+
+#define DATA_ATTRIB_SIZE_FIXED 0x1
+#define DATA_ATTRIB_VOLATILE 0x2
+
+#define DATA_ATTRIB_SET(Attrib, Bits) (BOOLEAN)((Attrib) & (Bits))
+#define SET_DATA_ATTRIB(Attrib, Bits) ((Attrib) |= (Bits))
+
+typedef struct _IP6_CONFIG_INSTANCE IP6_CONFIG_INSTANCE;
+
+#define IP6_CONFIG_INSTANCE_FROM_PROTOCOL(Proto) \
+ CR ((Proto), \
+ IP6_CONFIG_INSTANCE, \
+ Ip6Config, \
+ IP6_CONFIG_INSTANCE_SIGNATURE \
+ )
+
+
+#define IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK(Callback) \
+ CR ((Callback), \
+ IP6_CONFIG_INSTANCE, \
+ CallbackInfo, \
+ IP6_CONFIG_INSTANCE_SIGNATURE \
+ )
+
+#define IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE(Instance) \
+ CR ((Instance), \
+ IP6_SERVICE, \
+ Ip6ConfigInstance, \
+ IP6_SERVICE_SIGNATURE \
+ )
+
+#define IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \
+ CR ((ConfigAccess), \
+ IP6_FORM_CALLBACK_INFO, \
+ HiiConfigAccess, \
+ IP6_FORM_CALLBACK_INFO_SIGNATURE \
+ )
+
+/**
+ The prototype of work function for EfiIp6ConfigSetData().
+
+ @param[in] Instance The pointer to the IP6 config instance data.
+ @param[in] DataSize In bytes, the size of the buffer pointed to by Data.
+ @param[in] Data The data buffer to set.
+
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type,
+ 8 bytes.
+ @retval EFI_SUCCESS The specified configuration data for the EFI IPv6
+ network stack was set successfully.
+
+**/
+typedef
+EFI_STATUS
+(*IP6_CONFIG_SET_DATA) (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN UINTN DataSize,
+ IN VOID *Data
+ );
+
+/**
+ The prototype of work function for EfiIp6ConfigGetData().
+
+ @param[in] Instance The pointer to the IP6 config instance data.
+ @param[in, out] DataSize On input, in bytes, the size of Data. On output, in
+ bytes, the size of buffer required to store the specified
+ configuration data.
+ @param[in] Data The data buffer in which the configuration data is returned.
+ Ignored if DataSize is ZERO.
+
+ @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified
+ configuration data, and the required size is
+ returned in DataSize.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+typedef
+EFI_STATUS
+(*IP6_CONFIG_GET_DATA) (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN OUT UINTN *DataSize,
+ IN VOID *Data OPTIONAL
+ );
+
+typedef union {
+ VOID *Ptr;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+ EFI_IP6_CONFIG_INTERFACE_ID *AltIfId;
+ EFI_IP6_CONFIG_POLICY *Policy;
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress;
+ EFI_IPv6_ADDRESS *Gateway;
+ EFI_IPv6_ADDRESS *DnsServers;
+} IP6_CONFIG_DATA;
+
+typedef struct {
+ IP6_CONFIG_SET_DATA SetData;
+ IP6_CONFIG_GET_DATA GetData;
+ EFI_STATUS Status;
+ UINT8 Attribute;
+ NET_MAP EventMap;
+ IP6_CONFIG_DATA Data;
+ UINTN DataSize;
+} IP6_CONFIG_DATA_ITEM;
+
+typedef struct {
+ UINT16 Offset;
+ UINTN DataSize;
+ EFI_IP6_CONFIG_DATA_TYPE DataType;
+} IP6_CONFIG_DATA_RECORD;
+
+#pragma pack(1)
+
+//
+// heap data that contains the data for each data record.
+//
+// BOOLEAN IsAltIfIdSet;
+// EFI_IP6_CONFIG_POLICY Policy;
+// EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;
+// UINT32 ManualaddressCount;
+// UINT32 GatewayCount;
+// UINT32 DnsServersCount;
+// EFI_IP6_CONFIG_INTERFACE_ID AltIfId;
+// EFI_IP6_CONFIG_MANUAL_ADDRESS ManualAddress[];
+// EFI_IPv6_ADDRESS Gateway[];
+// EFI_IPv6_ADDRESS DnsServers[];
+//
+typedef struct {
+ UINT32 IaId;
+ UINT16 Checksum;
+ UINT16 DataRecordCount;
+ IP6_CONFIG_DATA_RECORD DataRecord[1];
+} IP6_CONFIG_VARIABLE;
+
+#pragma pack()
+
+typedef struct {
+ LIST_ENTRY Link;
+ EFI_IP6_ADDRESS_INFO AddrInfo;
+} IP6_ADDRESS_INFO_ENTRY;
+
+typedef struct {
+ EFI_IP6_CONFIG_POLICY Policy; ///< manual or automatic
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadTransmitCount; ///< dad transmits count
+ EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; ///< alternative interface id
+ LIST_ENTRY ManualAddress; ///< IP addresses
+ UINT32 ManualAddressCount; ///< IP addresses count
+ LIST_ENTRY GatewayAddress; ///< Gateway address
+ UINT32 GatewayAddressCount; ///< Gateway address count
+ LIST_ENTRY DnsAddress; ///< DNS server address
+ UINT32 DnsAddressCount; ///< DNS server address count
+} IP6_CONFIG_NVDATA;
+
+typedef struct _IP6_FORM_CALLBACK_INFO {
+ UINT32 Signature;
+ EFI_HANDLE ChildHandle;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL HiiConfigAccess;
+ EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath;
+ EFI_HII_HANDLE RegisteredHandle;
+} IP6_FORM_CALLBACK_INFO;
+
+struct _IP6_CONFIG_INSTANCE {
+ UINT32 Signature;
+ BOOLEAN Configured;
+ LIST_ENTRY Link;
+ UINT16 IfIndex;
+
+ EFI_IP6_CONFIG_INTERFACE_INFO InterfaceInfo;
+ EFI_IP6_CONFIG_INTERFACE_ID AltIfId;
+ EFI_IP6_CONFIG_POLICY Policy;
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;
+
+ IP6_CONFIG_DATA_ITEM DataItem[Ip6ConfigDataTypeMaximum];
+ NET_MAP DadFailedMap;
+ NET_MAP DadPassedMap;
+
+ EFI_IP6_CONFIG_PROTOCOL Ip6Config;
+
+ EFI_EVENT Dhcp6SbNotifyEvent;
+ VOID *Registration;
+ EFI_HANDLE Dhcp6Handle;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ BOOLEAN OtherInfoOnly;
+ UINT32 IaId;
+ EFI_EVENT Dhcp6Event;
+ UINT32 FailedIaAddressCount;
+ EFI_IPv6_ADDRESS *DeclineAddress;
+ UINT32 DeclineAddressCount;
+
+ IP6_FORM_CALLBACK_INFO CallbackInfo;
+ IP6_CONFIG_NVDATA Ip6NvData;
+};
+
+/**
+ The event process routine when the DHCPv6 server is answered with a reply packet
+ for an information request.
+
+ @param[in] This Points to the EFI_DHCP6_PROTOCOL.
+ @param[in] Context The pointer to the IP6 configuration instance data.
+ @param[in] Packet The DHCPv6 reply packet.
+
+ @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet.
+ @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or
+ the DNS server address is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ConfigOnDhcp6Reply (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP6_PACKET *Packet
+ );
+
+/**
+ The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration.
+
+ @param[in] Instance Pointer to the IP6 config instance data.
+ @param[in] OtherInfoOnly If FALSE, get stateful address and other information
+ via DHCPv6. Otherwise, only get the other information.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_UNSUPPORTED The DHCP6 driver is not available.
+
+**/
+EFI_STATUS
+Ip6ConfigStartStatefulAutoConfig (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN BOOLEAN OtherInfoOnly
+ );
+
+/**
+ Initialize an IP6_CONFIG_INSTANCE.
+
+ @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+ @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully.
+
+**/
+EFI_STATUS
+Ip6ConfigInitInstance (
+ OUT IP6_CONFIG_INSTANCE *Instance
+ );
+
+/**
+ Release an IP6_CONFIG_INSTANCE.
+
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.
+
+**/
+VOID
+Ip6ConfigCleanInstance (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ );
+
+/**
+ Destory the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources.
+
+ @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed.
+
+ @retval EFI_SUCCESS The child was successfully destroyed.
+ @retval Others Failed to destory the child.
+
+**/
+EFI_STATUS
+Ip6ConfigDestroyDhcp6 (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c
new file mode 100644
index 0000000000..9ec4886726
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c
@@ -0,0 +1,2116 @@
+/** @file
+ Helper functions for configuring or obtaining the parameters relating to IP6.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_GUID mIp6HiiVendorDevicePathGuid = IP6_HII_VENDOR_DEVICE_PATH_GUID;
+EFI_GUID mIp6ConfigNvDataGuid = IP6_CONFIG_NVDATA_GUID;
+CHAR16 mIp6ConfigStorageName[] = L"IP6_CONFIG_IFR_NVDATA";
+
+/**
+ The notify function of create event when performing a manual configuration.
+
+ @param[in] Event The pointer of Event.
+ @param[in] Context The pointer of Context.
+
+**/
+VOID
+EFIAPI
+Ip6ConfigManualAddressNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Get the configuration data for the EFI IPv6 network stack running on the
+ communication. It is a help function to the call EfiIp6ConfigGetData().
+
+ @param[in] Ip6Config The pointer to the EFI_IP6_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to get.
+ @param[out] DataSize The size of buffer required in bytes.
+ @param[out] Data The data buffer in which the configuration data is returned. The
+ type of the data buffer associated with the DataType.
+ It is the caller's responsibility to free the resource.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:
+ - Ip6Config is NULL or invalid.
+ - DataSize is NULL.
+ - Data is NULL.
+ @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resources.
+ @retval EFI_NOT_READY The specified configuration data is not ready due to an
+ asynchronous configuration process already in progress.
+ @retval EFI_NOT_FOUND The specified configuration data was not found.
+
+**/
+EFI_STATUS
+Ip6ConfigNvGetData (
+ IN EFI_IP6_CONFIG_PROTOCOL *Ip6Config,
+ IN EFI_IP6_CONFIG_DATA_TYPE DataType,
+ OUT UINTN *DataSize,
+ OUT VOID **Data
+ )
+{
+ UINTN BufferSize;
+ VOID *Buffer;
+ EFI_STATUS Status;
+
+ if ((Ip6Config == NULL) || (Data == NULL) || (DataSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BufferSize = 0;
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ DataType,
+ &BufferSize,
+ NULL
+ );
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+
+ Buffer = AllocateZeroPool (BufferSize);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ DataType,
+ &BufferSize,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ *DataSize = BufferSize;
+ *Data = Buffer;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Free all nodes in IP6_ADDRESS_INFO_ENTRY in the list array specified
+ with ListHead.
+
+ @param[in] ListHead The head of the list array in IP6_ADDRESS_INFO_ENTRY.
+
+**/
+VOID
+Ip6FreeAddressInfoList (
+ IN LIST_ENTRY *ListHead
+ )
+{
+ IP6_ADDRESS_INFO_ENTRY *Node;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ListHead) {
+ Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);
+ RemoveEntryList (&Node->Link);
+ FreePool (Node);
+ }
+}
+
+/**
+ Convert the IPv6 address into a formatted string.
+
+ @param[in] Ip6 The IPv6 address.
+ @param[out] Str The formatted IP string.
+
+**/
+VOID
+Ip6ToStr (
+ IN EFI_IPv6_ADDRESS *Ip6,
+ OUT CHAR16 *Str
+ )
+{
+ UINTN Index;
+ BOOLEAN Short;
+ UINTN Number;
+ CHAR16 FormatString[8];
+
+ Short = FALSE;
+
+ for (Index = 0; Index < 15; Index = Index + 2) {
+ if (!Short &&
+ Index % 2 == 0 &&
+ Ip6->Addr[Index] == 0 &&
+ Ip6->Addr[Index + 1] == 0
+ ) {
+ //
+ // Deal with the case of ::.
+ //
+ if (Index == 0) {
+ *Str = L':';
+ *(Str + 1) = L':';
+ Str = Str + 2;
+ } else {
+ *Str = L':';
+ Str = Str + 1;
+ }
+
+ while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) {
+ Index = Index + 2;
+ }
+
+ Short = TRUE;
+
+ if (Index == 16) {
+ //
+ // :: is at the end of the address.
+ //
+ *Str = L'\0';
+ break;
+ }
+ }
+
+ ASSERT (Index < 15);
+
+ if (Ip6->Addr[Index] == 0) {
+ Number = UnicodeSPrint (Str, 2 * IP6_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]);
+ } else {
+ if (Ip6->Addr[Index + 1] < 0x10) {
+ CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:"));
+ } else {
+ CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:"));
+ }
+
+ Number = UnicodeSPrint (
+ Str,
+ 2 * IP6_STR_MAX_SIZE,
+ (CONST CHAR16 *) FormatString,
+ (UINTN) Ip6->Addr[Index],
+ (UINTN) Ip6->Addr[Index + 1]
+ );
+ }
+
+ Str = Str + Number;
+
+ if (Index + 2 == 16) {
+ *Str = L'\0';
+ if (*(Str - 1) == L':') {
+ *(Str - 1) = L'\0';
+ }
+ }
+ }
+}
+
+/**
+ Convert EFI_IP6_CONFIG_INTERFACE_ID to string format.
+
+ @param[out] String The buffer to store the converted string.
+ @param[in] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID.
+
+ @retval EFI_SUCCESS The string converted successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+Ip6ConvertInterfaceIdToString (
+ OUT CHAR16 *String,
+ IN EFI_IP6_CONFIG_INTERFACE_ID *IfId
+ )
+{
+ UINT8 Index;
+ UINTN Number;
+
+ if ((String == NULL) || (IfId == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < 8; Index++) {
+ Number = UnicodeSPrint (
+ String,
+ 2 * INTERFACE_ID_STR_STORAGE,
+ L"%x:",
+ (UINTN) IfId->Id[Index]
+ );
+ String = String + Number;
+ }
+
+ *(String - 1) = '\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse InterfaceId in string format and convert it to EFI_IP6_CONFIG_INTERFACE_ID.
+
+ @param[in] String The buffer of the string to be parsed.
+ @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+Ip6ParseInterfaceIdFromString (
+ IN CONST CHAR16 *String,
+ OUT EFI_IP6_CONFIG_INTERFACE_ID *IfId
+ )
+{
+ UINT8 Index;
+ CHAR16 *IfIdStr;
+ CHAR16 *TempStr;
+ UINTN NodeVal;
+
+ if ((String == NULL) || (IfId == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IfIdStr = (CHAR16 *) String;
+
+ ZeroMem (IfId, sizeof (EFI_IP6_CONFIG_INTERFACE_ID));
+
+ for (Index = 0; Index < 8; Index++) {
+ TempStr = IfIdStr;
+
+ while ((*IfIdStr != L'\0') && (*IfIdStr != L':')) {
+ IfIdStr++;
+ }
+
+ //
+ // The InterfaceId format is X:X:X:X, the number of X should not exceed 8.
+ // If the number of X is less than 8, zero is appended to the InterfaceId.
+ //
+ if ((*IfIdStr == ':') && (Index == 7)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the string to interface id. AsciiStrHexToUintn stops at the
+ // first character that is not a valid hex character, ':' or '\0' here.
+ //
+ NodeVal = StrHexToUintn (TempStr);
+ if (NodeVal > 0xFF) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IfId->Id[Index] = (UINT8) NodeVal;
+
+ IfIdStr++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create Hii Extend Label OpCode as the start opcode and end opcode. It is
+ a help function.
+
+ @param[in] StartLabelNumber The number of start label.
+ @param[out] StartOpCodeHandle Points to the start opcode handle.
+ @param[out] StartLabel Points to the created start opcode.
+ @param[out] EndOpCodeHandle Points to the end opcode handle.
+ @param[out] EndLabel Points to the created end opcode.
+
+ @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this
+ operation.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_SUCCESS The operation completed successfully.
+
+**/
+EFI_STATUS
+Ip6CreateOpCode (
+ IN UINT16 StartLabelNumber,
+ OUT VOID **StartOpCodeHandle,
+ OUT EFI_IFR_GUID_LABEL **StartLabel,
+ OUT VOID **EndOpCodeHandle,
+ OUT EFI_IFR_GUID_LABEL **EndLabel
+ )
+{
+ EFI_STATUS Status;
+ EFI_IFR_GUID_LABEL *InternalStartLabel;
+ EFI_IFR_GUID_LABEL *InternalEndLabel;
+
+ if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *StartOpCodeHandle = NULL;
+ *EndOpCodeHandle = NULL;
+ Status = EFI_OUT_OF_RESOURCES;
+
+ //
+ // Initialize the container for dynamic opcodes.
+ //
+ *StartOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (*StartOpCodeHandle == NULL) {
+ return Status;
+ }
+
+ *EndOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (*EndOpCodeHandle == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Create Hii Extend Label OpCode as the start opcode.
+ //
+ InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ *StartOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ if (InternalStartLabel == NULL) {
+ goto Exit;
+ }
+
+ InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ InternalStartLabel->Number = StartLabelNumber;
+
+ //
+ // Create Hii Extend Label OpCode as the end opcode.
+ //
+ InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ *EndOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ if (InternalEndLabel == NULL) {
+ goto Exit;
+ }
+
+ InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ InternalEndLabel->Number = LABEL_END;
+
+ *StartLabel = InternalStartLabel;
+ *EndLabel = InternalEndLabel;
+
+ return EFI_SUCCESS;
+
+Exit:
+
+ if (*StartOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (*StartOpCodeHandle);
+ }
+
+ if (*EndOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (*EndOpCodeHandle);
+ }
+
+ return Status;
+}
+
+/**
+ This function converts the different format of address list to string format and
+ then generates the corresponding text opcode to illustarate the address info in
+ IP6 configuration page. Currently, the following formats are supported:
+ EFI_IP6_ADDRESS_INFO AddressType: Ip6ConfigNvHostAddress;
+ EFI_IPv6_ADDRESS AddressType: Ip6ConfigNvGatewayAddress and Ip6ConfigNvDnsAddress;
+ EFI_IP6_ROUTE_TABLE AddressType: Ip6ConfigNvRouteTable.
+
+ @param[in, out] String The pointer to the buffer to store the converted
+ string.
+ @param[in] HiiHandle A handle that was previously registered in the
+ HII Database.
+ @param[in] AddressType The address type.
+ @param[in] AddressInfo Pointer to the address list.
+ @param[in] AddressCount The address count of the address list.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_UNSUPPORTED The AddressType is not supported.
+
+
+**/
+EFI_STATUS
+Ip6ConvertAddressListToString (
+ IN OUT CHAR16 *String,
+ IN EFI_HII_HANDLE HiiHandle,
+ IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType,
+ IN VOID *AddressInfo,
+ IN UINTN AddressCount
+ )
+{
+ UINTN Index;
+ UINTN Number;
+ CHAR16 *TempStr;
+ EFI_STATUS Status;
+ VOID *StartOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *EndLabel;
+ UINT16 StartLabelNumber;
+ EFI_STRING_ID TextTwo;
+ UINT8 *AddressHead;
+ UINT8 PrefixLength;
+ EFI_IPv6_ADDRESS *Address;
+
+ if ((String == NULL) || (HiiHandle == NULL) || (AddressInfo == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (AddressType == Ip6ConfigNvHostAddress) {
+ StartLabelNumber = HOST_ADDRESS_LABEL;
+ } else if (AddressType == Ip6ConfigNvGatewayAddress) {
+ StartLabelNumber = GATEWAY_ADDRESS_LABEL;
+ } else if (AddressType == Ip6ConfigNvDnsAddress) {
+ StartLabelNumber = DNS_ADDRESS_LABEL;
+ } else if (AddressType == Ip6ConfigNvRouteTable) {
+ StartLabelNumber = ROUTE_TABLE_LABEL;
+ } else {
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = Ip6CreateOpCode (
+ StartLabelNumber,
+ &StartOpCodeHandle,
+ &StartLabel,
+ &EndOpCodeHandle,
+ &EndLabel
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ AddressHead = (UINT8 *) AddressInfo;
+
+ for (Index = 0; Index < AddressCount; Index++) {
+ if (AddressType == Ip6ConfigNvHostAddress) {
+ AddressInfo = AddressHead + sizeof (EFI_IP6_ADDRESS_INFO) * Index;
+ Address = &((EFI_IP6_ADDRESS_INFO *) AddressInfo)->Address;
+ } else if (AddressType == Ip6ConfigNvRouteTable) {
+ AddressInfo = AddressHead + sizeof (EFI_IP6_ROUTE_TABLE) * Index;
+ Address = &((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Destination;
+ } else {
+ AddressInfo = AddressHead + sizeof (EFI_IPv6_ADDRESS) * Index;
+ Address = AddressInfo;
+ }
+
+ //
+ // Convert the IP address info to string.
+ //
+ Ip6ToStr (Address, String);
+ TempStr = String + StrLen (String);
+
+ if ((AddressType == Ip6ConfigNvHostAddress) || (AddressType == Ip6ConfigNvRouteTable)) {
+ if (AddressType == Ip6ConfigNvHostAddress) {
+ PrefixLength = ((EFI_IP6_ADDRESS_INFO *) AddressInfo)->PrefixLength;
+ } else {
+ PrefixLength = ((EFI_IP6_ROUTE_TABLE *) AddressInfo)->PrefixLength;
+ }
+
+ //
+ // Append the prefix length to the string.
+ //
+ *TempStr = L'/';
+ TempStr++;
+ Number = UnicodeSPrint (TempStr, 6, L"%d", PrefixLength);
+ TempStr = TempStr + Number;
+ }
+
+ if (AddressType == Ip6ConfigNvRouteTable) {
+ //
+ // Append " >> " to the string.
+ //
+ Number = UnicodeSPrint (TempStr, 8, L" >> ");
+ TempStr = TempStr + Number;
+
+ //
+ // Append the gateway address to the string.
+ //
+ Ip6ToStr (&((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Gateway, TempStr);
+ TempStr = TempStr + StrLen (TempStr);
+ }
+
+ //
+ // Generate a text opcode and update the UI.
+ //
+ TextTwo = HiiSetString (HiiHandle, 0, String, NULL);
+ if (TextTwo == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ HiiCreateTextOpCode (StartOpCodeHandle, STR_NULL, STR_NULL, TextTwo);
+
+ String = TempStr;
+ *String = IP6_ADDRESS_DELIMITER;
+ String++;
+ }
+
+ *(String - 1) = '\0';
+
+ Status = HiiUpdateForm (
+ HiiHandle, // HII handle
+ &mIp6ConfigNvDataGuid, // Formset GUID
+ FORMID_MAIN_FORM, // Form ID
+ StartOpCodeHandle, // Label for where to insert opcodes
+ EndOpCodeHandle // Replace data
+ );
+
+Exit:
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+
+ return Status;
+}
+
+/**
+ Parse address list in string format and convert it to a list array of node in
+ IP6_ADDRESS_INFO_ENTRY.
+
+ @param[in] String The buffer to string to be parsed.
+ @param[out] ListHead The list head of array.
+ @param[out] AddressCount The number of list nodes in the array.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of resource.
+
+**/
+EFI_STATUS
+Ip6ParseAddressListFromString (
+ IN CONST CHAR16 *String,
+ OUT LIST_ENTRY *ListHead,
+ OUT UINT32 *AddressCount
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *LocalString;
+ CHAR16 *Temp;
+ CHAR16 *TempStr;
+ EFI_IP6_ADDRESS_INFO AddressInfo;
+ IP6_ADDRESS_INFO_ENTRY *Node;
+ BOOLEAN Last;
+ UINT32 Count;
+
+ if ((String == NULL) || (ListHead == NULL) || (AddressCount == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ LocalString = (CHAR16 *) AllocateCopyPool (StrSize (String), String);
+ if (LocalString == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Clean the original address list.
+ //
+ Ip6FreeAddressInfoList (ListHead);
+
+ Temp = LocalString;
+ Last = FALSE;
+ Count = 0;
+
+ while (*LocalString != L'\0') {
+ TempStr = LocalString;
+ while ((*LocalString != L'\0') && (*LocalString != IP6_ADDRESS_DELIMITER)) {
+ LocalString++;
+ }
+
+ if (*LocalString == L'\0') {
+ Last = TRUE;
+ }
+
+ *LocalString = L'\0';
+
+ Status = NetLibStrToIp6andPrefix (TempStr, &AddressInfo.Address, &AddressInfo.PrefixLength);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ if (AddressInfo.PrefixLength == 0xFF) {
+ AddressInfo.PrefixLength = 0;
+ }
+
+ if (!NetIp6IsValidUnicast (&AddressInfo.Address)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Error;
+ }
+
+ Node = AllocatePool (sizeof (IP6_ADDRESS_INFO_ENTRY));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ CopyMem (&Node->AddrInfo, &AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO));
+ InsertTailList (ListHead, &Node->Link);
+ Count++;
+
+ if (Last) {
+ break;
+ }
+
+ LocalString++;
+ }
+
+ FreePool (Temp);
+ *AddressCount = Count;
+ return EFI_SUCCESS;
+
+Error:
+ Ip6FreeAddressInfoList (ListHead);
+ FreePool (Temp);
+ return Status;
+}
+
+/**
+ This function converts the interface info to string and draws it to the IP6 UI.
+ The interface information includes interface name, interface type, hardware address,
+ address info, and route table information. The address information is also used as the
+ content of manual addresses in IP6 UI.
+
+ @param[in] IfInfo The pointer of EFI_IP6_CONFIG_INTERFACE_INFO.
+ @param[in] HiiHandle The handle that was previously registered in the
+ HII Database.
+ @param[in, out] IfrNvData Points to IP6_CONFIG_IFR_NVDATA.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources.
+
+**/
+EFI_STATUS
+Ip6ConvertInterfaceInfoToString (
+ IN EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo,
+ IN EFI_HII_HANDLE HiiHandle,
+ IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+ UINT32 Index;
+ UINTN Number;
+ CHAR16 *String;
+ CHAR16 *LinkLocalStr;
+ CHAR16 PortString[ADDRESS_STR_MAX_SIZE];
+ CHAR16 FormatString[8];
+ EFI_STRING_ID StringId;
+ EFI_STATUS Status;
+
+ if ((IfInfo == NULL) || (HiiHandle == NULL) || (IfrNvData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Print the interface name.
+ //
+ StringId = HiiSetString (
+ HiiHandle,
+ STRING_TOKEN (STR_IP6_INTERFACE_NAME_CONTENT),
+ IfInfo->Name,
+ NULL
+ );
+ if (StringId == 0) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Print the interface type.
+ //
+ if (IfInfo->IfType == Ip6InterfaceTypeEthernet) {
+ StrCpy (PortString, IP6_ETHERNET);
+ } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) {
+ StrCpy (PortString, IP6_EXPERIMENTAL_ETHERNET);
+ } else {
+ //
+ // Refer to RFC1700, chapter Number Hardware Type.
+ //
+ UnicodeSPrint (PortString, 6, L"%d", IfInfo->IfType);
+ }
+
+ StringId = HiiSetString (
+ HiiHandle,
+ STRING_TOKEN (STR_IP6_INTERFACE_TYPE_CONTENT),
+ PortString,
+ NULL
+ );
+ if (StringId == 0) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Convert the hardware address.
+ //
+ String = PortString;
+ ASSERT (IfInfo->HwAddressSize <= 32);
+
+ for (Index = 0; Index < IfInfo->HwAddressSize; Index++) {
+
+ if (IfInfo->HwAddress.Addr[Index] < 0x10) {
+ StrCpy (FormatString, L"0%x-");
+ } else {
+ StrCpy (FormatString, L"%x-");
+ }
+
+ Number = UnicodeSPrint (
+ String,
+ 8,
+ (CONST CHAR16 *) FormatString,
+ (UINTN) IfInfo->HwAddress.Addr[Index]
+ );
+ String = String + Number;
+ }
+
+ if (Index != 0) {
+ ASSERT (String > PortString);
+ String--;
+ *String = '\0';
+ }
+
+ //
+ // Print the hardware address.
+ //
+ StringId = HiiSetString (
+ HiiHandle,
+ STRING_TOKEN (STR_IP6_MAC_ADDRESS_CONTENT),
+ PortString,
+ NULL
+ );
+ if (StringId == 0) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Print the host address Information.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvHostAddress,
+ IfInfo->AddressInfo,
+ IfInfo->AddressInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Copy the Host Address Info to manual address field.
+ // Do not copy the link local address.
+ //
+ LinkLocalStr = StrStr (PortString, IP6_LINK_LOCAL_PREFIX);
+ if (LinkLocalStr != NULL) {
+ Number = LinkLocalStr - PortString;
+ if (Number > 0) {
+ CopyMem (IfrNvData->ManualAddress, PortString, Number * sizeof (CHAR16));
+ }
+
+ while ((*LinkLocalStr != L' ') && (*LinkLocalStr != L'\0')) {
+ LinkLocalStr++;
+ }
+
+ if (*LinkLocalStr != L'\0') {
+ LinkLocalStr++;
+ StrCat (IfrNvData->ManualAddress, LinkLocalStr);
+ }
+ } else {
+ StrCpy (IfrNvData->ManualAddress, PortString);
+ }
+
+ //
+ // Print the route table information.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvRouteTable,
+ IfInfo->RouteTable,
+ IfInfo->RouteCount
+ );
+ return Status;
+}
+
+/**
+ Build the address info list from list array of node in IP6_ADDRESS_INFO_ENTRY.
+
+ @param[in] Instance Points to IP6 config instance data.
+ @param[in] AddressType The address type.
+ @param[out] AddressInfo The pointer to the buffer to store the address list.
+ @param[out] AddressSize The address size of the address list.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_UNSUPPORTED The AddressType is not supported.
+
+**/
+EFI_STATUS
+Ip6BuildNvAddressInfo (
+ IN IP6_CONFIG_INSTANCE *Instance,
+ IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType,
+ OUT VOID **AddressInfo,
+ OUT UINTN *AddressSize
+ )
+{
+ IP6_CONFIG_NVDATA *Ip6NvData;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *ListHead;
+ IP6_ADDRESS_INFO_ENTRY *Node;
+ VOID *AddressList;
+ VOID *TmpStr;
+ UINTN DataSize;
+ EFI_IPv6_ADDRESS *Ip6Address;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress;
+
+ if ((Instance == NULL) || (AddressInfo == NULL) || (AddressSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);
+
+ Ip6NvData = &Instance->Ip6NvData;
+
+ if (AddressType == Ip6ConfigNvHostAddress) {
+ ListHead = &Ip6NvData->ManualAddress;
+ DataSize = sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS) * Ip6NvData->ManualAddressCount;
+ } else if (AddressType == Ip6ConfigNvGatewayAddress) {
+ ListHead = &Ip6NvData->GatewayAddress;
+ DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->GatewayAddressCount;
+ } else if (AddressType == Ip6ConfigNvDnsAddress) {
+ ListHead = &Ip6NvData->DnsAddress;
+ DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->DnsAddressCount;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ AddressList = AllocateZeroPool (DataSize);
+ if (AddressList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TmpStr = AddressList;
+
+ NET_LIST_FOR_EACH (Entry, ListHead) {
+ Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link);
+ if (AddressType == Ip6ConfigNvHostAddress) {
+ ManualAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AddressList;
+ IP6_COPY_ADDRESS (&ManualAddress->Address, &Node->AddrInfo.Address);
+ ManualAddress->PrefixLength = Node->AddrInfo.PrefixLength;
+ AddressList = (UINT8 *) AddressList + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS);
+ } else {
+ Ip6Address = (EFI_IPv6_ADDRESS *) AddressList;
+ IP6_COPY_ADDRESS (Ip6Address, &Node->AddrInfo.Address);
+ AddressList = (UINT8 *) AddressList + sizeof (EFI_IPv6_ADDRESS);
+ }
+ }
+
+ *AddressInfo = TmpStr;
+ *AddressSize = DataSize;
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert the IP6 configuration data into the IFR data.
+
+ @param[in, out] IfrNvData The IFR NV data.
+ @param[in] Instance The IP6 config instance data.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_UNSUPPORTED The policy is not supported in the current implementation.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip6ConvertConfigNvDataToIfrNvData (
+ IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData,
+ IN IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ UINTN DataSize;
+ VOID *Data;
+ EFI_STATUS Status;
+ EFI_IP6_CONFIG_INTERFACE_ID InterfaceId;
+ EFI_IP6_CONFIG_POLICY Policy;
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;
+ EFI_HII_HANDLE HiiHandle;
+
+ if ((IfrNvData == NULL) || (Instance == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);
+
+ Ip6Config = &Instance->Ip6Config;
+ Data = NULL;
+ DataSize = 0;
+ HiiHandle = Instance->CallbackInfo.RegisteredHandle;
+
+ //
+ // Get the current interface info.
+ //
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &DataSize,
+ (VOID **) &Data
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Convert the interface info to string and print.
+ //
+ Status = Ip6ConvertInterfaceInfoToString (
+ (EFI_IP6_CONFIG_INTERFACE_INFO *) Data,
+ HiiHandle,
+ IfrNvData
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get the interface id.
+ //
+ DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID);
+ ZeroMem (&InterfaceId, DataSize);
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ Ip6ConfigDataTypeAltInterfaceId,
+ &DataSize,
+ &InterfaceId
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &InterfaceId);
+
+ //
+ // Get current policy.
+ //
+ DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ Ip6ConfigDataTypePolicy,
+ &DataSize,
+ &Policy
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (Policy == Ip6ConfigPolicyManual) {
+ IfrNvData->Policy = IP6_POLICY_MANUAL;
+ } else if (Policy == Ip6ConfigPolicyAutomatic) {
+ IfrNvData->Policy = IP6_POLICY_AUTO;
+ } else {
+ ASSERT (FALSE);
+ Status = EFI_UNSUPPORTED;
+ goto Exit;
+ }
+
+ //
+ // Get Duplicate Address Detection Transmits count.
+ //
+ DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDupAddrDetectTransmits,
+ &DataSize,
+ &DadXmits
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ IfrNvData->DadTransmitCount = DadXmits.DupAddrDetectTransmits;
+
+ //
+ // Get DNS server list.
+ //
+ FreePool (Data);
+ Data = NULL;
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDnsServer,
+ &DataSize,
+ (VOID **) &Data
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ if (DataSize > 0) {
+ //
+ // Convert the DNS server address to string and draw it to UI.
+ //
+ Status = Ip6ConvertAddressListToString (
+ IfrNvData->DnsAddress,
+ HiiHandle,
+ Ip6ConfigNvDnsAddress,
+ Data,
+ DataSize / sizeof (EFI_IPv6_ADDRESS)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ FreePool (Data);
+ Data = NULL;
+ }
+
+ //
+ // Get gateway adderss list.
+ //
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeGateway,
+ &DataSize,
+ (VOID **) &Data
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ if (DataSize > 0) {
+ //
+ // Convert the gateway address to string and draw it to UI.
+ //
+ Status = Ip6ConvertAddressListToString (
+ IfrNvData->GatewayAddress,
+ HiiHandle,
+ Ip6ConfigNvGatewayAddress,
+ Data,
+ DataSize / sizeof (EFI_IPv6_ADDRESS)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+
+ return Status;
+}
+
+/**
+ Convert IFR data into IP6 configuration data. The policy, alternative interface
+ ID, and DAD transmit counts, and will be saved. If under manual policy, the configured
+ manual address, gateway address, and DNS server address will be saved.
+
+ @param[in] IfrNvData The IFR NV data.
+ @param[in, out] Instance The IP6 config instance data.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip6ConvertIfrNvDataToConfigNvData (
+ IN IP6_CONFIG_IFR_NVDATA *IfrNvData,
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ IP6_CONFIG_NVDATA *Ip6NvData;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ EFI_STATUS Status;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress;
+ EFI_IPv6_ADDRESS *Address;
+ BOOLEAN IsAddressOk;
+ EFI_EVENT SetAddressEvent;
+ EFI_EVENT TimeoutEvent;
+ UINTN DataSize;
+
+ if ((IfrNvData == NULL) || (Instance == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE);
+ Ip6NvData = &Instance->Ip6NvData;
+ Ip6Config = &Instance->Ip6Config;
+
+ //
+ // Update those fields which don't have INTERACTIVE attribute.
+ //
+ if (IfrNvData->Policy == IP6_POLICY_AUTO) {
+ Ip6NvData->Policy = Ip6ConfigPolicyAutomatic;
+ } else if (IfrNvData->Policy == IP6_POLICY_MANUAL) {
+ Ip6NvData->Policy = Ip6ConfigPolicyManual;
+ }
+
+ Ip6NvData->DadTransmitCount.DupAddrDetectTransmits = IfrNvData->DadTransmitCount;
+
+ //
+ // Set the configured policy.
+ //
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypePolicy,
+ sizeof (EFI_IP6_CONFIG_POLICY),
+ &Ip6NvData->Policy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Set the duplicate address detection transmits count.
+ //
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDupAddrDetectTransmits,
+ sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS),
+ &Ip6NvData->DadTransmitCount
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Set the alternative interface ID
+ //
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeAltInterfaceId,
+ sizeof (EFI_IP6_CONFIG_INTERFACE_ID),
+ &Ip6NvData->InterfaceId
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+
+ if (Ip6NvData->Policy == Ip6ConfigPolicyAutomatic) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Create events & timers for asynchronous settings.
+ //
+ SetAddressEvent = NULL;
+ TimeoutEvent = NULL;
+ ManualAddress = NULL;
+ Address = NULL;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip6ConfigManualAddressNotify,
+ &IsAddressOk,
+ &SetAddressEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Set the manual address list. This is an asynchronous process.
+ //
+ if (!IsListEmpty (&Ip6NvData->ManualAddress) && (Ip6NvData->ManualAddressCount != 0)) {
+ Status = Ip6BuildNvAddressInfo (
+ Instance,
+ Ip6ConfigNvHostAddress,
+ (VOID **) &ManualAddress,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ IsAddressOk = FALSE;
+
+ Status = Ip6Config->RegisterDataNotify (
+ Ip6Config,
+ Ip6ConfigDataTypeManualAddress,
+ SetAddressEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeManualAddress,
+ DataSize,
+ (VOID *) ManualAddress
+ );
+ if (Status == EFI_NOT_READY) {
+ gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000);
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ if (IsAddressOk) {
+ Status = EFI_SUCCESS;
+ }
+ break;
+ }
+ }
+
+ Status = Ip6Config->UnregisterDataNotify (
+ Ip6Config,
+ Ip6ConfigDataTypeManualAddress,
+ SetAddressEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ //
+ // Set gateway address list.
+ //
+ if (!IsListEmpty (&Ip6NvData->GatewayAddress) && (Ip6NvData->GatewayAddressCount != 0)) {
+ Status = Ip6BuildNvAddressInfo (
+ Instance,
+ Ip6ConfigNvGatewayAddress,
+ (VOID **) &Address,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeGateway,
+ DataSize,
+ (VOID *) Address
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ FreePool (Address);
+ Address = NULL;
+ }
+
+ //
+ // Set DNS server address list.
+ //
+ if (!IsListEmpty (&Ip6NvData->DnsAddress) && (Ip6NvData->DnsAddressCount != 0)) {
+ Status = Ip6BuildNvAddressInfo (
+ Instance,
+ Ip6ConfigNvDnsAddress,
+ (VOID **) &Address,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDnsServer,
+ DataSize,
+ (VOID *) Address
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ if (SetAddressEvent != NULL) {
+ gBS->CloseEvent (SetAddressEvent);
+ }
+
+ if (TimeoutEvent != NULL) {
+ gBS->CloseEvent (TimeoutEvent);
+ }
+
+ if (ManualAddress != NULL) {
+ FreePool (ManualAddress);
+ }
+
+ if (Address != NULL) {
+ FreePool (Address);
+ }
+
+ return Status;
+}
+
+/**
+ This function allows the caller to request the current
+ configuration for one or more named elements. The resulting
+ string is in <ConfigAltResp> format. Any and all alternative
+ configuration strings shall also be appended to the end of the
+ current configuration string. If they are, they must appear
+ after the current configuration. They must contain the same
+ routing (GUID, NAME, PATH) as the current configuration string.
+ They must have an additional description indicating the type of
+ alternative configuration the string represents,
+ "ALTCFG=<StringToken>". That <StringToken> (when
+ converted from Hex UNICODE to binary) is a reference to a
+ string in the associated string pack.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Request A null-terminated Unicode string in
+ <ConfigRequest> format. Note that this
+ includes the routing information as well as
+ the configurable name / value pairs. It is
+ invalid for this string to be in
+ <MultiConfigRequest> format.
+ @param[out] Progress On return, points to a character in the
+ Request string. Points to the string's null
+ terminator if request was successful. Points
+ to the most recent "&" before the first
+ failing name / value pair (or the beginning
+ of the string if the failure is in the first
+ name / value pair) if the request was not
+ successful.
+ @param[out] Results A null-terminated Unicode string in
+ <ConfigAltResp> format which has all values
+ filled in for the names in the Request string.
+ String to be allocated by the called function.
+
+ @retval EFI_SUCCESS The Results string is filled with the
+ values corresponding to all requested
+ names.
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+ @retval EFI_INVALID_PARAMETER For example, passing in a NULL
+ for the Request parameter
+ would result in this type of
+ error. In this case, the
+ Progress parameter would be
+ set to NULL.
+ @retval EFI_NOT_FOUND Routing data doesn't match any
+ known driver. Progress set to the
+ first character in the routing header.
+ Note: There is no requirement that the
+ driver validate the routing data. It
+ must skip the <ConfigHdr> in order to
+ process the names.
+ @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set
+ to most recent & before the
+ error or the beginning of the
+ string.
+ @retval EFI_INVALID_PARAMETER Unknown name. Progress points
+ to the & before the name in
+ question. Currently not implemented.
+**/
+EFI_STATUS
+EFIAPI
+Ip6FormExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ )
+{
+
+ EFI_STATUS Status;
+ IP6_FORM_CALLBACK_INFO *Private;
+ IP6_CONFIG_INSTANCE *Ip6ConfigInstance;
+ IP6_CONFIG_IFR_NVDATA *IfrNvData;
+ EFI_STRING ConfigRequestHdr;
+ EFI_STRING ConfigRequest;
+ BOOLEAN AllocatedRequest;
+ UINTN Size;
+ UINTN BufferSize;
+
+ if (This == NULL || Progress == NULL || Results == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = Request;
+ if ((Request != NULL) &&
+ !HiiIsConfigHdrMatch (Request, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ ConfigRequestHdr = NULL;
+ ConfigRequest = NULL;
+ AllocatedRequest = FALSE;
+ Size = 0;
+
+ Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+ Ip6ConfigInstance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);
+ BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);
+
+ IfrNvData = (IP6_CONFIG_IFR_NVDATA *) AllocateZeroPool (BufferSize);
+ if (IfrNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Ip6ConvertConfigNvDataToIfrNvData (IfrNvData, Ip6ConfigInstance);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ ConfigRequest = Request;
+ if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
+ //
+ // Request has no request element, construct full request string.
+ // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
+ // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator.
+ //
+ ConfigRequestHdr = HiiConstructConfigHdr (
+ &mIp6ConfigNvDataGuid,
+ mIp6ConfigStorageName,
+ Private->ChildHandle
+ );
+ Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
+ ConfigRequest = AllocateZeroPool (Size);
+ ASSERT (ConfigRequest != NULL);
+ AllocatedRequest = TRUE;
+ UnicodeSPrint (
+ ConfigRequest,
+ Size,
+ L"%s&OFFSET=0&WIDTH=%016LX",
+ ConfigRequestHdr,
+ (UINT64) BufferSize
+ );
+ FreePool (ConfigRequestHdr);
+ }
+
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
+ //
+ Status = gHiiConfigRouting->BlockToConfig (
+ gHiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) IfrNvData,
+ BufferSize,
+ Results,
+ Progress
+ );
+
+Exit:
+ FreePool (IfrNvData);
+ //
+ // Free the allocated config request string.
+ //
+ if (AllocatedRequest) {
+ FreePool (ConfigRequest);
+ ConfigRequest = NULL;
+ }
+ //
+ // Set Progress string to the original request string.
+ //
+ if (Request == NULL) {
+ *Progress = NULL;
+ } else if (StrStr (Request, L"OFFSET") == NULL) {
+ *Progress = Request + StrLen (Request);
+ }
+
+ return Status;
+}
+
+/**
+ This function applies changes in a driver's configuration.
+ Input is a Configuration, which has the routing data for this
+ driver followed by name / value configuration pairs. The driver
+ must apply those pairs to its configurable storage. If the
+ driver's configuration is stored in a linear block of data
+ and the driver's name / value pairs are in <BlockConfig>
+ format, it may use the ConfigToBlock helper function (above) to
+ simplify the job. Currently not implemented.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Configuration A null-terminated Unicode string in
+ <ConfigString> format.
+ @param[out] Progress A pointer to a string filled in with the
+ offset of the most recent '&' before the
+ first failing name / value pair (or the
+ beginn ing of the string if the failure
+ is in the first name / value pair) or
+ the terminating NULL if all was
+ successful.
+
+ @retval EFI_SUCCESS The results have been distributed or are
+ awaiting distribution.
+ @retval EFI_OUT_OF_MEMORY Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+ @retval EFI_INVALID_PARAMETERS Passing in a NULL for the
+ Results parameter would result
+ in this type of error.
+ @retval EFI_NOT_FOUND Target for the specified routing data
+ was not found.
+**/
+EFI_STATUS
+EFIAPI
+Ip6FormRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ )
+{
+ if (This == NULL || Configuration == NULL || Progress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check routing data in <ConfigHdr>.
+ // Note: if only one Storage is used, then this checking could be skipped.
+ //
+ if (!HiiIsConfigHdrMatch (Configuration, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {
+ *Progress = Configuration;
+ return EFI_NOT_FOUND;
+ }
+
+ *Progress = Configuration + StrLen (Configuration);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called to provide results data to the driver.
+ This data consists of a unique key that is used to identify
+ which data is either being passed back or being asked for.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Action Specifies the type of action taken by the browser.
+ @param[in] QuestionId A unique value which is sent to the original
+ exporting driver so that it can identify the type
+ of data to expect. The format of the data tends to
+ vary based on the opcode that generated the callback.
+ @param[in] Type The type of value for the question.
+ @param[in] Value A pointer to the data being sent to the original
+ exporting driver.
+ @param[out] ActionRequest On return, points to the action requested by the
+ callback function.
+
+ @retval EFI_SUCCESS The callback successfully handled the action.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
+ variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be saved.
+ @retval EFI_UNSUPPORTED The specified Action is not supported by the
+ callback. Currently not implemented.
+ @retval EFI_INVALID_PARAMETER Passed in the wrong parameter.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6FormCallback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ IP6_FORM_CALLBACK_INFO *Private;
+ UINTN BufferSize;
+ IP6_CONFIG_IFR_NVDATA *IfrNvData;
+ IP6_CONFIG_IFR_NVDATA OldIfrNvData;
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ IP6_CONFIG_INSTANCE *Instance;
+ IP6_CONFIG_NVDATA *Ip6NvData;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ EFI_IP6_CONFIG_INTERFACE_INFO *Data;
+ UINTN DataSize;
+ CHAR16 PortString[ADDRESS_STR_MAX_SIZE];
+ EFI_HII_HANDLE HiiHandle;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+ Instance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private);
+ Ip6NvData = &Instance->Ip6NvData;
+
+ if (Action == EFI_BROWSER_ACTION_FORM_OPEN) {
+ //
+ // Update main Form when main Form is opened.
+ // This will be done only in FORM_OPEN CallBack of question with KEY_INTERFACE_ID from main Form.
+ //
+ if (QuestionId != KEY_INTERFACE_ID) {
+ return EFI_SUCCESS;
+ }
+
+ Ip6Config = &Instance->Ip6Config;
+ HiiHandle = Instance->CallbackInfo.RegisteredHandle;
+
+ //
+ // Get the current interface info.
+ //
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &DataSize,
+ (VOID **) &Data
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Generate the dynamic text opcode for host address and draw it.
+ //
+ IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data;
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvHostAddress,
+ IfInfo->AddressInfo,
+ IfInfo->AddressInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Generate the dynamic text opcode for route table and draw it.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvRouteTable,
+ IfInfo->RouteTable,
+ IfInfo->RouteCount
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get DNS server list.
+ //
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDnsServer,
+ &DataSize,
+ (VOID **) &Data
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ if (DataSize > 0) {
+ //
+ // Generate the dynamic text opcode for DNS server and draw it.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvDnsAddress,
+ Data,
+ DataSize / sizeof (EFI_IPv6_ADDRESS)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ //
+ // Get gateway adderss list.
+ //
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeGateway,
+ &DataSize,
+ (VOID **) &Data
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ if (DataSize > 0) {
+ //
+ // Generate the dynamic text opcode for gateway and draw it.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvGatewayAddress,
+ Data,
+ DataSize / sizeof (EFI_IPv6_ADDRESS)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+Exit:
+ FreePool (Data);
+ return Status;
+ }
+
+ if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) {
+ //
+ // Do nothing for UEFI FORM_CLOSE action
+ //
+ return EFI_SUCCESS;
+ }
+
+ if ((Value == NULL) || (ActionRequest == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve uncommitted data from Browser
+ //
+
+ BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);
+ IfrNvData = AllocateZeroPool (BufferSize);
+ if (IfrNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_SUCCESS;
+
+ ZeroMem (&OldIfrNvData, BufferSize);
+
+ HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);
+
+ CopyMem (&OldIfrNvData, IfrNvData, BufferSize);
+
+ switch (QuestionId) {
+ case KEY_INTERFACE_ID:
+ Status = Ip6ParseInterfaceIdFromString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Interface ID!",
+ NULL
+ );
+ }
+
+ break;
+
+ case KEY_MANUAL_ADDRESS:
+ Status = Ip6ParseAddressListFromString (
+ IfrNvData->ManualAddress,
+ &Ip6NvData->ManualAddress,
+ &Ip6NvData->ManualAddressCount
+ );
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Host Addresses!",
+ NULL
+ );
+ }
+
+ break;
+
+ case KEY_GATEWAY_ADDRESS:
+ Status = Ip6ParseAddressListFromString (
+ IfrNvData->GatewayAddress,
+ &Ip6NvData->GatewayAddress,
+ &Ip6NvData->GatewayAddressCount
+ );
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Gateway Addresses!",
+ NULL
+ );
+ }
+
+ break;
+
+ case KEY_DNS_ADDRESS:
+ Status = Ip6ParseAddressListFromString (
+ IfrNvData->DnsAddress,
+ &Ip6NvData->DnsAddress,
+ &Ip6NvData->DnsAddressCount
+ );
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid DNS Addresses!",
+ NULL
+ );
+ }
+
+ break;
+
+ case KEY_SAVE_CONFIG_CHANGES:
+ CopyMem (&OldIfrNvData, IfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_IGNORE_CONFIG_CHANGES:
+ CopyMem (IfrNvData, &OldIfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_SAVE_CHANGES:
+ Status = Ip6ConvertIfrNvDataToConfigNvData (IfrNvData, Instance);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Pass changed uncommitted data back to Form Browser.
+ //
+ BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA);
+ HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL);
+ }
+
+ FreePool (IfrNvData);
+ return Status;
+}
+
+/**
+ Install HII Config Access protocol for network device and allocate resources.
+
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form.
+
+ @retval EFI_SUCCESS The HII Config Access protocol is installed.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip6ConfigFormInit (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ IP6_SERVICE *IpSb;
+ IP6_FORM_CALLBACK_INFO *CallbackInfo;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
+ VENDOR_DEVICE_PATH VendorDeviceNode;
+ EFI_SERVICE_BINDING_PROTOCOL *MnpSb;
+ CHAR16 *MacString;
+ CHAR16 MenuString[128];
+ CHAR16 PortString[128];
+ CHAR16 *OldMenuString;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ ASSERT (IpSb != NULL);
+
+ Status = gBS->HandleProtocol (
+ IpSb->Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ParentDevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CallbackInfo = &Instance->CallbackInfo;
+ CallbackInfo->Signature = IP6_FORM_CALLBACK_INFO_SIGNATURE;
+
+ //
+ // Construct device path node for EFI HII Config Access protocol,
+ // which consists of controller physical device path and one hardware
+ // vendor guid node.
+ //
+ ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));
+ VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH;
+ VendorDeviceNode.Header.SubType = HW_VENDOR_DP;
+
+ CopyGuid (&VendorDeviceNode.Guid, &mIp6HiiVendorDevicePathGuid);
+
+ SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));
+ CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (
+ ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode
+ );
+ if (CallbackInfo->HiiVendorDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ ConfigAccess = &CallbackInfo->HiiConfigAccess;
+ ConfigAccess->ExtractConfig = Ip6FormExtractConfig;
+ ConfigAccess->RouteConfig = Ip6FormRouteConfig;
+ ConfigAccess->Callback = Ip6FormCallback;
+
+ //
+ // Install Device Path Protocol and Config Access protocol on new handle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ ConfigAccess,
+ NULL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Open the Parent Handle for the child
+ //
+ Status = gBS->OpenProtocol (
+ IpSb->Controller,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ (VOID **) &MnpSb,
+ IpSb->Image,
+ CallbackInfo->ChildHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // Publish our HII data
+ //
+ CallbackInfo->RegisteredHandle = HiiAddPackages (
+ &mIp6ConfigNvDataGuid,
+ CallbackInfo->ChildHandle,
+ Ip6DxeStrings,
+ Ip6ConfigBin,
+ NULL
+ );
+ if (CallbackInfo->RegisteredHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Append MAC string in the menu string and tile string
+ //
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString);
+ if (!EFI_ERROR (Status)) {
+ OldMenuString = HiiGetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),
+ NULL)
+ ;
+ UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),
+ MenuString,
+ NULL
+ );
+ UnicodeSPrint (PortString, 128, L"MAC:%s", MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP6_DEVICE_FORM_TITLE),
+ PortString,
+ NULL
+ );
+
+ FreePool (MacString);
+ FreePool (OldMenuString);
+
+ InitializeListHead (&Instance->Ip6NvData.ManualAddress);
+ InitializeListHead (&Instance->Ip6NvData.GatewayAddress);
+ InitializeListHead (&Instance->Ip6NvData.DnsAddress);
+
+ return EFI_SUCCESS;
+ }
+
+Error:
+ Ip6ConfigFormUnload (Instance);
+ return Status;
+}
+
+/**
+ Uninstall the HII Config Access protocol for network devices and free up the resources.
+
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form.
+
+**/
+VOID
+Ip6ConfigFormUnload (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_FORM_CALLBACK_INFO *CallbackInfo;
+ IP6_CONFIG_NVDATA *Ip6NvData;
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ ASSERT (IpSb != NULL);
+
+ CallbackInfo = &Instance->CallbackInfo;
+
+ if (CallbackInfo->ChildHandle != NULL) {
+
+ //
+ // Close the child handle
+ //
+ gBS->CloseProtocol (
+ IpSb->Controller,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ IpSb->Image,
+ CallbackInfo->ChildHandle
+ );
+ //
+ // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->HiiConfigAccess,
+ NULL
+ );
+ }
+
+ if (CallbackInfo->HiiVendorDevicePath != NULL) {
+ FreePool (CallbackInfo->HiiVendorDevicePath);
+ }
+
+ if (CallbackInfo->RegisteredHandle != NULL) {
+ //
+ // Remove HII package list
+ //
+ HiiRemovePackages (CallbackInfo->RegisteredHandle);
+ }
+
+ Ip6NvData = &Instance->Ip6NvData;
+
+ Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress);
+ Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress);
+ Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress);
+
+ Ip6NvData->ManualAddressCount = 0;
+ Ip6NvData->GatewayAddressCount = 0;
+ Ip6NvData->DnsAddressCount = 0;
+}
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h
new file mode 100644
index 0000000000..d184776707
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h
@@ -0,0 +1,73 @@
+/** @file
+ The header file of Ip6ConfigNv.c.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IP6_CONFIGNV_H_
+#define _IP6_CONFIGNV_H_
+
+#include "Ip6NvData.h"
+#include "Ip6ConfigImpl.h"
+
+extern UINT8 Ip6ConfigBin[];
+extern UINT8 Ip6DxeStrings[];
+
+#define IP6_HII_VENDOR_DEVICE_PATH_GUID \
+ { \
+ 0x13288098, 0xb11f, 0x45b9, { 0xbc, 0x4f, 0x91, 0xb5, 0x4b, 0xa3, 0x39, 0xb9 } \
+ }
+
+#define IP6_ETHERNET L"Ethernet"
+#define IP6_EXPERIMENTAL_ETHERNET L"Experimental Ethernet"
+#define IP6_ADDRESS_DELIMITER L' '
+#define IP6_LINK_LOCAL_PREFIX L"FE80::"
+
+typedef enum {
+ Ip6InterfaceTypeEthernet = 1,
+ Ip6InterfaceTypeExperimentalEthernet
+} IP6_INTERFACE_TYPE;
+
+typedef enum {
+ Ip6ConfigNvHostAddress,
+ Ip6ConfigNvGatewayAddress,
+ Ip6ConfigNvDnsAddress,
+ Ip6ConfigNvRouteTable
+} IP6_CONFIG_NV_ADDRESS_TYPE;
+
+/**
+ Install HII Config Access protocol for network device and allocate resources.
+
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form.
+
+ @retval EFI_SUCCESS The HII Config Access protocol is installed.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+Ip6ConfigFormInit (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ );
+
+/**
+ Uninstall HII Config Access protocol for network device and free resource.
+
+ @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form.
+
+**/
+VOID
+Ip6ConfigFormUnload (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.c b/NetworkPkg/Ip6Dxe/Ip6Driver.c
new file mode 100644
index 0000000000..388dadef8d
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Driver.c
@@ -0,0 +1,930 @@
+/** @file
+ The driver binding and service binding protocol for IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = {
+ Ip6DriverBindingSupported,
+ Ip6DriverBindingStart,
+ Ip6DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ The entry point for IP6 driver which installs the driver
+ binding and component name protocol on its image.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gIp6DriverBinding,
+ ImageHandle,
+ &gIp6ComponentName,
+ &gIp6ComponentName2
+ );
+}
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ //
+ // Test for the MNP service binding Protocol
+ //
+ return gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+}
+
+/**
+ Clean up an IP6 service binding instance. It releases all
+ the resource allocated by the instance. The instance may be
+ partly initialized, or partly destroyed. If a resource is
+ destroyed, it is marked as that in case the destory failed and
+ being called again later.
+
+ @param[in] IpSb The IP6 service binding instance to clean up.
+
+ @retval EFI_SUCCESS The resource used by the instance are cleaned up.
+ @retval Others Failed to clean up some of the resources.
+
+**/
+EFI_STATUS
+Ip6CleanService (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS AllNodes;
+ IP6_NEIGHBOR_ENTRY *NeighborCache;
+
+ Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance);
+
+ //
+ // Leave link-scope all-nodes multicast address (FF02::1)
+ //
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
+
+ Status = Ip6LeaveGroup (IpSb, &AllNodes);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (IpSb->DefaultInterface != NULL) {
+ Ip6CleanInterface (IpSb->DefaultInterface, NULL);
+ IpSb->DefaultInterface = NULL;
+ }
+
+ Ip6CleanDefaultRouterList (IpSb);
+
+ Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);
+ Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);
+
+ if (IpSb->RouteTable != NULL) {
+ Ip6CleanRouteTable (IpSb->RouteTable);
+ IpSb->RouteTable = NULL;
+ }
+
+ if (IpSb->InterfaceId != NULL) {
+ FreePool (IpSb->InterfaceId);
+ }
+
+ IpSb->InterfaceId = NULL;
+
+ Ip6CleanAssembleTable (&IpSb->Assemble);
+
+ if (IpSb->MnpChildHandle != NULL) {
+ if (IpSb->Mnp != NULL) {
+ IpSb->Mnp->Cancel (IpSb->Mnp, NULL);
+ IpSb->Mnp->Configure (IpSb->Mnp, NULL);
+ gBS->CloseProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ IpSb->Image,
+ IpSb->Controller
+ );
+
+ IpSb->Mnp = NULL;
+ }
+
+ NetLibDestroyServiceChild (
+ IpSb->Controller,
+ IpSb->Image,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ IpSb->MnpChildHandle
+ );
+
+ IpSb->MnpChildHandle = NULL;
+ }
+
+ if (IpSb->RecvRequest.MnpToken.Event != NULL) {
+ gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event);
+ }
+
+ if (IpSb->Timer != NULL) {
+ gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
+ gBS->CloseEvent (IpSb->Timer);
+
+ IpSb->Timer = NULL;
+ }
+
+ if (IpSb->FasterTimer != NULL) {
+ gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);
+ gBS->CloseEvent (IpSb->FasterTimer);
+
+ IpSb->FasterTimer = NULL;
+ }
+ //
+ // Free the Neighbor Discovery resources
+ //
+ while (!IsListEmpty (&IpSb->NeighborTable)) {
+ NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link);
+ Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create a new IP6 driver service binding protocol.
+
+ @param[in] Controller The controller that has MNP service binding
+ installed.
+ @param[in] ImageHandle The IP6 driver's image handle.
+ @param[out] Service The variable to receive the newly created IP6
+ service.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_SUCCESS A new IP6 service binding private is created.
+
+**/
+EFI_STATUS
+Ip6CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ OUT IP6_SERVICE **Service
+ )
+{
+ IP6_SERVICE *IpSb;
+ EFI_STATUS Status;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *Config;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+
+ ASSERT (Service != NULL);
+
+ *Service = NULL;
+
+ //
+ // allocate a service private data then initialize all the filed to
+ // empty resources, so if any thing goes wrong when allocating
+ // resources, Ip6CleanService can be called to clean it up.
+ //
+ IpSb = AllocateZeroPool (sizeof (IP6_SERVICE));
+
+ if (IpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IpSb->Signature = IP6_SERVICE_SIGNATURE;
+ IpSb->ServiceBinding.CreateChild = Ip6ServiceBindingCreateChild;
+ IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild;
+ IpSb->State = IP6_SERVICE_UNSTARTED;
+ IpSb->InDestroy = FALSE;
+
+ IpSb->NumChildren = 0;
+ InitializeListHead (&IpSb->Children);
+
+ InitializeListHead (&IpSb->Interfaces);
+ IpSb->DefaultInterface = NULL;
+ IpSb->RouteTable = NULL;
+
+ IpSb->RecvRequest.Signature = IP6_LINK_RX_SIGNATURE;
+ IpSb->RecvRequest.CallBack = NULL;
+ IpSb->RecvRequest.Context = NULL;
+ MnpToken = &IpSb->RecvRequest.MnpToken;
+ MnpToken->Event = NULL;
+ MnpToken->Status = EFI_NOT_READY;
+ MnpToken->Packet.RxData = NULL;
+
+ Ip6CreateAssembleTable (&IpSb->Assemble);
+
+ IpSb->MldCtrl.Mldv1QuerySeen = 0;
+ InitializeListHead (&IpSb->MldCtrl.Groups);
+
+ ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS));
+ IpSb->LinkLocalOk = FALSE;
+ IpSb->LinkLocalDadFail = FALSE;
+ IpSb->Dhcp6NeedStart = FALSE;
+ IpSb->Dhcp6NeedInfoRequest = FALSE;
+
+ IpSb->CurHopLimit = IP6_HOP_LIMIT;
+ IpSb->LinkMTU = IP6_MIN_LINK_MTU;
+ IpSb->BaseReachableTime = IP6_REACHABLE_TIME;
+ Ip6UpdateReachableTime (IpSb);
+ //
+ // RFC4861 RETRANS_TIMER: 1,000 milliseconds
+ //
+ IpSb->RetransTimer = IP6_RETRANS_TIMER;
+
+ IpSb->RoundRobin = 0;
+
+ InitializeListHead (&IpSb->NeighborTable);
+ InitializeListHead (&IpSb->DefaultRouterList);
+ InitializeListHead (&IpSb->OnlinkPrefix);
+ InitializeListHead (&IpSb->AutonomousPrefix);
+
+ IpSb->InterfaceIdLen = IP6_IF_ID_LEN;
+ IpSb->InterfaceId = NULL;
+
+ IpSb->RouterAdvertiseReceived = FALSE;
+ IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS;
+ IpSb->Ticks = 0;
+
+ IpSb->Image = ImageHandle;
+ IpSb->Controller = Controller;
+
+ IpSb->MnpChildHandle = NULL;
+ IpSb->Mnp = NULL;
+
+ Config = &IpSb->MnpConfigData;
+ Config->ReceivedQueueTimeoutValue = 0;
+ Config->TransmitQueueTimeoutValue = 0;
+ Config->ProtocolTypeFilter = IP6_ETHER_PROTO;
+ Config->EnableUnicastReceive = TRUE;
+ Config->EnableMulticastReceive = TRUE;
+ Config->EnableBroadcastReceive = TRUE;
+ Config->EnablePromiscuousReceive = FALSE;
+ Config->FlushQueuesOnReset = TRUE;
+ Config->EnableReceiveTimestamps = FALSE;
+ Config->DisableBackgroundPolling = FALSE;
+
+ ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));
+
+ IpSb->Timer = NULL;
+ IpSb->FasterTimer = NULL;
+
+ ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE));
+
+ IpSb->MacString = NULL;
+
+ //
+ // Create various resources. First create the route table, timer
+ // event, MNP token event and MNP child.
+ //
+
+ IpSb->RouteTable = Ip6CreateRouteTable ();
+ if (IpSb->RouteTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Ip6TimerTicking,
+ IpSb,
+ &IpSb->Timer
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Ip6NdFasterTimerTicking,
+ IpSb,
+ &IpSb->FasterTimer
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = NetLibCreateServiceChild (
+ Controller,
+ ImageHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &IpSb->MnpChildHandle
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) (&IpSb->Mnp),
+ ImageHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip6ServiceConfigMnp (IpSb, TRUE);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER);
+ if (NetLibGetVlanId (IpSb->Controller) != 0) {
+ //
+ // This is a VLAN device, reduce MTU by VLAN tag length
+ //
+ IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN;
+ }
+ IpSb->OldMaxPacketSize = IpSb->MaxPacketSize;
+
+ //
+ // Currently only ETHERNET is supported in IPv6 stack, since
+ // link local address requires an IEEE 802 48-bit MACs for
+ // EUI-64 format interface identifier mapping.
+ //
+ if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_ERROR;
+ }
+
+ Status = Ip6InitMld (IpSb);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds.
+ //
+ Status = gBS->SetTimer (IpSb->FasterTimer, TimerPeriodic, TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds.
+ //
+ Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_MS * IP6_ONE_SECOND_IN_MS);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip6OnFrameReceived,
+ &IpSb->RecvRequest,
+ &MnpToken->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE);
+ if (IpSb->DefaultInterface == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ //
+ // If there is any manual address, set it.
+ //
+ DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress];
+ if (DataItem->Data.Ptr != NULL) {
+ DataItem->SetData (
+ &IpSb->Ip6ConfigInstance,
+ DataItem->DataSize,
+ DataItem->Data.Ptr
+ );
+ }
+
+ InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);
+
+ *Service = IpSb;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Ip6CleanService (IpSb);
+ FreePool (IpSb);
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ IP6_SERVICE *IpSb;
+ EFI_STATUS Status;
+
+ //
+ // Test for the Ip6 service binding protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (IpSb != NULL);
+
+ //
+ // Install the Ip6ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding,
+ &gEfiIp6ConfigProtocolGuid,
+ &IpSb->Ip6ConfigInstance.Ip6Config,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ Ip6CleanService (IpSb);
+ FreePool (IpSb);
+ } else {
+ //
+ // Initialize the IP6 ID
+ //
+ mIp6Id = NET_RANDOM (NetRandomInitSeed ());
+
+ Ip6SetVariableData (IpSb);
+ }
+
+ return Status;
+}
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ IP6_SERVICE *IpSb;
+ IP6_PROTOCOL *IpInstance;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ BOOLEAN IsDhcp6;
+ EFI_TPL OldTpl;
+ INTN State;
+
+ IsDhcp6 = FALSE;
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
+
+ if (NicHandle != NULL) {
+ //
+ // DriverBindingStop is triggered by the uninstallation of the EFI DHCPv6
+ // Protocol used by Ip6Config.
+ //
+ IsDhcp6 = TRUE;
+ } else {
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpSb->InDestroy) {
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ if (IsDhcp6) {
+
+ Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance);
+ gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event);
+ IpSb->Ip6ConfigInstance.Dhcp6Event = NULL;
+ } else if (NumberOfChildren == 0) {
+
+ IpSb->InDestroy = TRUE;
+ State = IpSb->State;
+ IpSb->State = IP6_SERVICE_DESTROY;
+
+ //
+ // Clear the variable data.
+ //
+ Ip6ClearVariableData (IpSb);
+
+ Status = Ip6CleanService (IpSb);
+ if (EFI_ERROR (Status)) {
+ IpSb->State = State;
+ goto Exit;
+ }
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ ServiceBinding,
+ &gEfiIp6ConfigProtocolGuid,
+ &IpSb->Ip6ConfigInstance.Ip6Config,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ FreePool (IpSb);
+ } else {
+ //
+ // NumberOfChildren is not zero, destroy all IP6 children instances.
+ //
+ while (!IsListEmpty (&IpSb->Children)) {
+ IpInstance = NET_LIST_HEAD (&IpSb->Children, IP6_PROTOCOL, Link);
+ ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle);
+ }
+
+ if (IpSb->NumChildren != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+Exit:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it
+ is not NULL, then the I/O services are added to
+ the existing child handle.
+
+ @retval EFI_SUCCES The child handle was created with the I/O services.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_PROTOCOL *IpInstance;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ VOID *Mnp;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpSb = IP6_SERVICE_FROM_PROTOCOL (This);
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ IpInstance = AllocatePool (sizeof (IP6_PROTOCOL));
+
+ if (IpInstance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ip6InitProtocol (IpSb, IpInstance);
+
+ //
+ // Install Ip6 onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ &IpInstance->Ip6Proto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ IpInstance->Handle = *ChildHandle;
+
+ //
+ // Open the Managed Network protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &Mnp,
+ gIp6DriverBinding.DriverBindingHandle,
+ IpInstance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ &IpInstance->Ip6Proto,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ //
+ // Insert it into the service binding instance.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&IpSb->Children, &IpInstance->Link);
+ IpSb->NumChildren++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ON_ERROR:
+
+ if (EFI_ERROR (Status)) {
+
+ Ip6CleanProtocol (IpInstance);
+
+ FreePool (IpInstance);
+ }
+
+ return Status;
+}
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle.
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ IP6_SERVICE *IpSb;
+ IP6_PROTOCOL *IpInstance;
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_TPL OldTpl;
+ INTN State;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ IpSb = IP6_SERVICE_FROM_PROTOCOL (This);
+
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ (VOID **) &Ip6,
+ gIp6DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6);
+
+ if (IpInstance->Service != IpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // A child can be destroyed more than once. For example,
+ // Ip6DriverBindingStop will destory all of its children.
+ // when UDP driver is being stopped, it will destory all
+ // the IP child it opens.
+ //
+ if (IpInstance->State == IP6_STATE_DESTROY) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ State = IpInstance->State;
+ IpInstance->State = IP6_STATE_DESTROY;
+
+ //
+ // Close the Managed Network protocol.
+ //
+ gBS->CloseProtocol (
+ IpSb->MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ gIp6DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the IP6 protocol first. Many thing happens during
+ // this:
+ // 1. The consumer of the IP6 protocol will be stopped if it
+ // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is
+ // stopped, IP driver's stop function will be called, and uninstall
+ // EFI_IP6_PROTOCOL will trigger the UDP's stop function. This
+ // makes it possible to create the network stack bottom up, and
+ // stop it top down.
+ // 2. the upper layer will recycle the received packet. The recycle
+ // event's TPL is higher than this function. The recycle events
+ // will be called back before preceeding. If any packets not recycled,
+ // that means there is a resource leak.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ &IpInstance->Ip6Proto
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip6CleanProtocol (IpInstance);
+
+ Ip6SetVariableData (IpSb);
+
+ if (EFI_ERROR (Status)) {
+ gBS->InstallMultipleProtocolInterfaces (
+ &ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ Ip6,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ RemoveEntryList (&IpInstance->Link);
+ ASSERT (IpSb->NumChildren > 0);
+ IpSb->NumChildren--;
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (IpInstance);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ IpInstance->State = State;
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.h b/NetworkPkg/Ip6Dxe/Ip6Driver.h
new file mode 100644
index 0000000000..fcb92ab946
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Driver.h
@@ -0,0 +1,185 @@
+/** @file
+ The driver binding and service binding protocol for IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_DRIVER_H__
+#define __EFI_IP6_DRIVER_H__
+
+extern EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2;
+
+/**
+ Clean up an IP6 service binding instance. It releases all
+ the resource allocated by the instance. The instance may be
+ partly initialized, or partly destroyed. If a resource is
+ destroyed, it is marked as that in case the destory failed and
+ being called again later.
+
+ @param[in] IpSb The IP6 service binding instance to clean up.
+
+ @retval EFI_SUCCESS The resource used by the instance are cleaned up.
+ @retval Others Failed to clean up some of the resources.
+
+**/
+EFI_STATUS
+Ip6CleanService (
+ IN IP6_SERVICE *IpSb
+ );
+
+//
+// Function prototype for the driver's entry point
+//
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ The entry point for IP6 driver which installs the driver
+ binding and component name protocol on its image.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+//
+// Function prototypes for the Drivr Binding Protocol
+//
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+//
+// Function ptototypes for the ServiceBinding Prococol
+//
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it
+ is not NULL, then the I/O services are added to
+ the existing child handle.
+
+ @retval EFI_SUCCES The child handle was created with the I/O services.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle.
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Dxe.inf b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf
new file mode 100644
index 0000000000..aeb341cdc3
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf
@@ -0,0 +1,100 @@
+## @file
+# Component description file for Ip6 module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Ip6Dxe
+ FILE_GUID = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Ip6DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gIp6DriverBinding
+# COMPONENT_NAME = gIp6ComponentName
+# COMPONENT_NAME2 = gIp6ComponentName2
+#
+
+[Sources]
+ Ip6Output.h
+ Ip6Option.h
+ Ip6Input.h
+ Ip6Nd.h
+ Ip6Mld.h
+ Ip6Impl.c
+ Ip6Driver.c
+ ComponentName.c
+ Ip6Nd.c
+ Ip6Input.c
+ Ip6ConfigImpl.c
+ Ip6ConfigImpl.h
+ Ip6Impl.h
+ Ip6Option.c
+ Ip6If.h
+ Ip6Icmp.h
+ Ip6Mld.c
+ Ip6Common.c
+ Ip6Route.c
+ Ip6If.c
+ Ip6Driver.h
+ Ip6Output.c
+ Ip6Icmp.c
+ Ip6Common.h
+ Ip6Route.h
+ Ip6DxeStrings.uni
+ Ip6NvData.h
+ Ip6ConfigNv.c
+ Ip6ConfigNv.h
+ Ip6Config.vfr
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DevicePathLib
+ HiiLib
+ UefiHiiServicesLib
+ PrintLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ UefiLib
+ DebugLib
+ NetLib
+ DpcLib
+
+[Protocols]
+ gEfiManagedNetworkServiceBindingProtocolGuid
+ gEfiManagedNetworkProtocolGuid
+ gEfiIp6ServiceBindingProtocolGuid
+ gEfiIp6ProtocolGuid
+ gEfiIp6ConfigProtocolGuid
+ gEfiDhcp6ServiceBindingProtocolGuid
+ gEfiDhcp6ProtocolGuid
+ gEfiIpSecProtocolGuid
+ gEfiHiiConfigAccessProtocolGuid
+
+[Guids]
+ gEfiIfrTianoGuid ## CONSUMES ## GUID
diff --git a/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
new file mode 100644
index 0000000000..cf85e08795
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
Binary files differ
diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/NetworkPkg/Ip6Dxe/Ip6Icmp.c
new file mode 100644
index 0000000000..db40b81d5e
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.c
@@ -0,0 +1,684 @@
+/** @file
+ The ICMPv6 handle routines to process the ICMPv6 control messages.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = {
+
+ {
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_NO_ROUTE_TO_DEST
+ },
+ {
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_COMM_PROHIBITED
+ },
+ {
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_BEYOND_SCOPE
+ },
+ {
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_ADDR_UNREACHABLE
+ },
+ {
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_PORT_UNREACHABLE
+ },
+ {
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_SOURCE_ADDR_FAILED
+ },
+ {
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_ROUTE_REJECTED
+ },
+
+ {
+ ICMP_V6_PACKET_TOO_BIG,
+ ICMP_V6_DEFAULT_CODE
+ },
+
+ {
+ ICMP_V6_TIME_EXCEEDED,
+ ICMP_V6_TIMEOUT_HOP_LIMIT
+ },
+ {
+ ICMP_V6_TIME_EXCEEDED,
+ ICMP_V6_TIMEOUT_REASSEMBLE
+ },
+
+ {
+ ICMP_V6_PARAMETER_PROBLEM,
+ ICMP_V6_ERRONEOUS_HEADER
+ },
+ {
+ ICMP_V6_PARAMETER_PROBLEM,
+ ICMP_V6_UNRECOGNIZE_NEXT_HDR
+ },
+ {
+ ICMP_V6_PARAMETER_PROBLEM,
+ ICMP_V6_UNRECOGNIZE_OPTION
+ },
+
+ {
+ ICMP_V6_ECHO_REQUEST,
+ ICMP_V6_DEFAULT_CODE
+ },
+ {
+ ICMP_V6_ECHO_REPLY,
+ ICMP_V6_DEFAULT_CODE
+ },
+
+ {
+ ICMP_V6_LISTENER_QUERY,
+ ICMP_V6_DEFAULT_CODE
+ },
+ {
+ ICMP_V6_LISTENER_REPORT,
+ ICMP_V6_DEFAULT_CODE
+ },
+ {
+ ICMP_V6_LISTENER_REPORT_2,
+ ICMP_V6_DEFAULT_CODE
+ },
+ {
+ ICMP_V6_LISTENER_DONE,
+ ICMP_V6_DEFAULT_CODE
+ },
+
+ {
+ ICMP_V6_ROUTER_SOLICIT,
+ ICMP_V6_DEFAULT_CODE
+ },
+ {
+ ICMP_V6_ROUTER_ADVERTISE,
+ ICMP_V6_DEFAULT_CODE
+ },
+ {
+ ICMP_V6_NEIGHBOR_SOLICIT,
+ ICMP_V6_DEFAULT_CODE
+ },
+ {
+ ICMP_V6_NEIGHBOR_ADVERTISE,
+ ICMP_V6_DEFAULT_CODE
+ },
+};
+
+/**
+ Reply an ICMPv6 echo request.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the ICMPv6 informational message.
+ @param[in] Packet The content of the ICMPv6 message with the IP head
+ removed.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_SUCCESS Successfully answered the ICMPv6 Echo request.
+ @retval Others Failed to answer the ICMPv6 Echo request.
+
+**/
+EFI_STATUS
+Ip6IcmpReplyEcho (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_INFORMATION_HEAD *Icmp;
+ NET_BUF *Data;
+ EFI_STATUS Status;
+ EFI_IP6_HEADER ReplyHead;
+
+ Status = EFI_OUT_OF_RESOURCES;
+ //
+ // make a copy the packet, it is really a bad idea to
+ // send the MNP's buffer back to MNP.
+ //
+ Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN);
+ if (Data == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Change the ICMP type to echo reply, exchange the source
+ // and destination, then send it. The source is updated to
+ // use specific destination. See RFC1122. SRR/RR option
+ // update is omitted.
+ //
+ Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL);
+ if (Icmp == NULL) {
+ NetbufFree (Data);
+ goto Exit;
+ }
+
+ Icmp->Head.Type = ICMP_V6_ECHO_REPLY;
+ Icmp->Head.Checksum = 0;
+
+ //
+ // Generate the IPv6 basic header
+ // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address,
+ // the Source address of the Echo Reply must be the same address.
+ //
+ ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER));
+
+ ReplyHead.PayloadLength = HTONS ((UINT16) (Packet->TotalSize));
+ ReplyHead.NextHeader = IP6_ICMP;
+ ReplyHead.HopLimit = IpSb->CurHopLimit;
+ IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress);
+
+ if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
+ IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress);
+ }
+
+ //
+ // If source is unspecified, Ip6Output will select a source for us
+ //
+ Status = Ip6Output (
+ IpSb,
+ NULL,
+ NULL,
+ Data,
+ &ReplyHead,
+ NULL,
+ 0,
+ Ip6SysPacketSent,
+ NULL
+ );
+
+Exit:
+ NetbufFree (Packet);
+ return Status;
+}
+
+/**
+ Process Packet Too Big message sent by a router in response to a packet that
+ it cannot forward because the packet is larger than the MTU of outgoing link.
+ Since this driver already uses IPv6 minimum link MTU as the maximum packet size,
+ if Packet Too Big message is still received, do not reduce the packet size, but
+ rather include a Fragment header in the subsequent packets.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the ICMPv6 error packet.
+ @param[in] Packet The content of the ICMPv6 error with the IP head
+ removed.
+
+ @retval EFI_SUCCESS The ICMPv6 error processed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of
+ resource.
+ @retval EFI_NOT_FOUND The packet too big message is not sent to us.
+
+**/
+EFI_STATUS
+Ip6ProcessPacketTooBig (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_ERROR_HEAD Icmp;
+ UINT32 Mtu;
+ IP6_ROUTE_ENTRY *RouteEntry;
+ EFI_IPv6_ADDRESS *DestAddress;
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+ Mtu = NTOHL (Icmp.Fourth);
+ DestAddress = &Icmp.IpHead.DestinationAddress;
+
+ if (Mtu < IP6_MIN_LINK_MTU) {
+ //
+ // Normally the multicast address is considered to be on-link and not recorded
+ // in route table. Here it is added into the table since the MTU information
+ // need be recorded.
+ //
+ if (IP6_IS_MULTICAST (DestAddress)) {
+ RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL);
+ if (RouteEntry == NULL) {
+ NetbufFree (Packet);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG;
+ InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link);
+ IpSb->RouteTable->TotalNum++;
+ } else {
+ RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL);
+ if (RouteEntry == NULL) {
+ NetbufFree (Packet);
+ return EFI_NOT_FOUND;
+ }
+
+ RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG;
+
+ Ip6FreeRouteEntry (RouteEntry);
+ }
+ }
+
+ NetbufFree (Packet);
+ return EFI_SUCCESS;
+}
+
+/**
+ Process the ICMPv6 error packet, and deliver the packet to upper layer.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the ICMPv6 error packet.
+ @param[in] Packet The content of the ICMPv6 error with the IP head
+ removed.
+
+ @retval EFI_SUCCESS The ICMPv6 error processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessIcmpError (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_ERROR_HEAD Icmp;
+
+ //
+ // Check the validity of the packet
+ //
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ goto DROP;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+ if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) {
+ return Ip6ProcessPacketTooBig (IpSb, Head, Packet);
+ }
+
+ //
+ // Notify the upper-layer process that an ICMPv6 eror message is received.
+ //
+ IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
+ return Ip6Demultiplex (IpSb, Head, Packet);
+
+DROP:
+ NetbufFree (Packet);
+ Packet = NULL;
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ Process the ICMPv6 informational messages. If it is an ICMPv6 echo
+ request, answer it. If it is a MLD message, trigger MLD routines to
+ process it. If it is a ND message, trigger ND routines to process it.
+ Otherwise, deliver it to upper layer.
+
+ @param[in] IpSb The IP service that receivd the packet.
+ @param[in] Head The IP head of the ICMPv6 informational packet.
+ @param[in] Packet The content of the ICMPv6 informational packet
+ with IP head removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval EFI_SUCCESS The ICMPv6 informational message processed.
+ @retval Others Failed to process ICMPv6 informational message.
+
+**/
+EFI_STATUS
+Ip6ProcessIcmpInformation (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_INFORMATION_HEAD Icmp;
+ EFI_STATUS Status;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);
+ ASSERT (Head != NULL);
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+ Status = EFI_INVALID_PARAMETER;
+
+ switch (Icmp.Head.Type) {
+ case ICMP_V6_ECHO_REQUEST:
+ //
+ // If ICMPv6 echo, reply it
+ //
+ if (Icmp.Head.Code == 0) {
+ Status = Ip6IcmpReplyEcho (IpSb, Head, Packet);
+ }
+ break;
+ case ICMP_V6_LISTENER_QUERY:
+ Status = Ip6ProcessMldQuery (IpSb, Head, Packet);
+ break;
+ case ICMP_V6_LISTENER_REPORT:
+ case ICMP_V6_LISTENER_REPORT_2:
+ Status = Ip6ProcessMldReport (IpSb, Head, Packet);
+ break;
+ case ICMP_V6_NEIGHBOR_SOLICIT:
+ Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet);
+ break;
+ case ICMP_V6_NEIGHBOR_ADVERTISE:
+ Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet);
+ break;
+ case ICMP_V6_ROUTER_ADVERTISE:
+ Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet);
+ break;
+ case ICMP_V6_REDIRECT:
+ Status = Ip6ProcessRedirect (IpSb, Head, Packet);
+ break;
+ case ICMP_V6_ECHO_REPLY:
+ Status = Ip6Demultiplex (IpSb, Head, Packet);
+ break;
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ return Status;
+}
+
+/**
+ Handle the ICMPv6 packet. First validate the message format,
+ then, according to the message types, process it as an informational packet or
+ an error packet.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the ICMPv6 packet.
+ @param[in] Packet The content of the ICMPv6 packet with IP head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_SUCCESS The ICMPv6 message successfully processed.
+ @retval Others Failed to handle the ICMPv6 packet.
+
+**/
+EFI_STATUS
+Ip6IcmpHandle (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_HEAD Icmp;
+ UINT16 PseudoCheckSum;
+ UINT16 CheckSum;
+
+ //
+ // Check the validity of the incoming packet.
+ //
+ if (Packet->TotalSize < sizeof (Icmp)) {
+ goto DROP;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ //
+ // Make sure checksum is valid.
+ //
+ PseudoCheckSum = NetIp6PseudoHeadChecksum (
+ &Head->SourceAddress,
+ &Head->DestinationAddress,
+ IP6_ICMP,
+ Packet->TotalSize
+ );
+ CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet));
+ if (CheckSum != 0) {
+ goto DROP;
+ }
+
+ //
+ // According to the packet type, call corresponding process
+ //
+ if (Icmp.Type <= ICMP_V6_ERROR_MAX) {
+ return Ip6ProcessIcmpError (IpSb, Head, Packet);
+ } else {
+ return Ip6ProcessIcmpInformation (IpSb, Head, Packet);
+ }
+
+DROP:
+ NetbufFree (Packet);
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ Retrieve the Prefix address according to the PrefixLength by clear the useless
+ bits.
+
+ @param[in] PrefixLength The prefix length of the prefix.
+ @param[in, out] Prefix On input, points to the original prefix address
+ with dirty bits; on output, points to the updated
+ address with useless bit clear.
+
+**/
+VOID
+Ip6GetPrefix (
+ IN UINT8 PrefixLength,
+ IN OUT EFI_IPv6_ADDRESS *Prefix
+ )
+{
+ UINT8 Byte;
+ UINT8 Bit;
+ UINT8 Mask;
+ UINT8 Value;
+
+ ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_NUM));
+
+ if (PrefixLength == 0) {
+ ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));
+ return ;
+ }
+
+ if (PrefixLength == IP6_PREFIX_NUM - 1) {
+ return ;
+ }
+
+ Byte = (UINT8) (PrefixLength / 8);
+ Bit = (UINT8) (PrefixLength % 8);
+ Value = Prefix->Addr[Byte];
+
+ if ((Byte > 0) && (Byte < 16)) {
+ ZeroMem (Prefix->Addr + Byte, 16 - Byte);
+ }
+
+ if (Bit > 0) {
+ Mask = (UINT8) (0xFF << (8 - Bit));
+ Prefix->Addr[Byte] = (UINT8) (Value & Mask);
+ }
+
+}
+
+/**
+ Check whether the DestinationAddress is an anycast address.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] DestinationAddress Points to the Destination Address of the packet.
+
+ @retval TRUE The DestinationAddress is anycast address.
+ @retval FALSE The DestinationAddress is not anycast address.
+
+**/
+BOOLEAN
+Ip6IsAnycast (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *DestinationAddress
+ )
+{
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;
+ EFI_IPv6_ADDRESS Prefix;
+ BOOLEAN Flag;
+
+ ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS));
+
+ Flag = FALSE;
+
+ //
+ // If the address is known as on-link or autonomous prefix, record it as
+ // anycast address.
+ //
+ do {
+ PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress);
+ if (PrefixEntry != NULL) {
+ IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix);
+ Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix);
+ if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) {
+ return TRUE;
+ }
+ }
+
+ Flag = (BOOLEAN) !Flag;
+ } while (Flag);
+
+ return FALSE;
+}
+
+/**
+ Generate ICMPv6 error message and send it out to DestinationAddress. Currently
+ Destination Unreachable message, Time Exceeded message and Parameter Problem
+ message are supported.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Packet The packet which invoking ICMPv6 error.
+ @param[in] SourceAddress If not NULL, points to the SourceAddress.
+ Otherwise, the IP layer will select a source address
+ according to the DestinationAddress.
+ @param[in] DestinationAddress Points to the Destination Address of the ICMPv6
+ error message.
+ @param[in] Type The type of the ICMPv6 message.
+ @param[in] Code The additional level of the ICMPv6 message.
+ @param[in] Pointer If not NULL, identifies the octet offset within
+ the invoking packet where the error was detected.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the
+ operation.
+ @retval EFI_SUCCESS The ICMPv6 message was successfully sent out.
+ @retval Others Failed to generate the ICMPv6 packet.
+
+**/
+EFI_STATUS
+Ip6SendIcmpError (
+ IN IP6_SERVICE *IpSb,
+ IN NET_BUF *Packet,
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,
+ IN EFI_IPv6_ADDRESS *DestinationAddress,
+ IN UINT8 Type,
+ IN UINT8 Code,
+ IN UINT32 *Pointer OPTIONAL
+ )
+{
+ UINT32 PacketLen;
+ NET_BUF *ErrorMsg;
+ UINT16 PayloadLen;
+ EFI_IP6_HEADER Head;
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;
+ UINT8 *ErrorBody;
+
+ if (DestinationAddress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // An ICMPv6 error message must not be originated as a result of receiving
+ // a packet whose source address does not uniquely identify a single node --
+ // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address
+ // known by the ICMP message originator to be an IPv6 anycast address.
+ //
+ if (NetIp6IsUnspecifiedAddr (DestinationAddress) ||
+ IP6_IS_MULTICAST (DestinationAddress) ||
+ Ip6IsAnycast (IpSb, DestinationAddress)
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (Type) {
+ case ICMP_V6_DEST_UNREACHABLE:
+ case ICMP_V6_TIME_EXCEEDED:
+ break;
+
+ case ICMP_V6_PARAMETER_PROBLEM:
+ if (Pointer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ break;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize;
+
+ if (PacketLen > IpSb->MaxPacketSize) {
+ PacketLen = IpSb->MaxPacketSize;
+ }
+
+ ErrorMsg = NetbufAlloc (PacketLen);
+ if (ErrorMsg == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER));
+
+ //
+ // Create the basic IPv6 header.
+ //
+ ZeroMem (&Head, sizeof (EFI_IP6_HEADER));
+
+ Head.PayloadLength = HTONS (PayloadLen);
+ Head.NextHeader = IP6_ICMP;
+ Head.HopLimit = IpSb->CurHopLimit;
+
+ if (SourceAddress != NULL) {
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
+ } else {
+ ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
+
+ NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER));
+
+ //
+ // Fill in the ICMP error message head
+ //
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
+ if (IcmpHead == NULL) {
+ NetbufFree (ErrorMsg);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
+ IcmpHead->Head.Type = Type;
+ IcmpHead->Head.Code = Code;
+
+ if (Pointer != NULL) {
+ IcmpHead->Fourth = HTONL (*Pointer);
+ }
+
+ //
+ // Fill in the ICMP error message body
+ //
+ PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD);
+ ErrorBody = NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE);
+ if (ErrorBody != NULL) {
+ ZeroMem (ErrorBody, PayloadLen);
+ NetbufCopy (Packet, 0, PayloadLen, ErrorBody);
+ }
+
+ //
+ // Transmit the packet
+ //
+ return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL);
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.h b/NetworkPkg/Ip6Dxe/Ip6Icmp.h
new file mode 100644
index 0000000000..6852ae5490
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.h
@@ -0,0 +1,108 @@
+/** @file
+ Header file for ICMPv6 protocol.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_ICMP_H__
+#define __EFI_IP6_ICMP_H__
+
+#define ICMP_V6_DEFAULT_CODE 0
+
+#define ICMP_V6_ERROR_MAX 127
+
+//
+// ICMPv6 message classes, each class of ICMPv6 message shares
+// a common message format. INVALID_MESSAGE is only a flag.
+//
+#define ICMP_V6_INVALID_MESSAGE 0
+#define ICMP_V6_ERROR_MESSAGE 1
+#define ICMP_V6_INFORMATION_MESSAGE 2
+
+
+extern EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[];
+
+/**
+ Handle the ICMPv6 packet. First validate the message format,
+ then, according to the message types, process it as an informational packet or
+ an error packet.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the ICMPv6 packet.
+ @param[in] Packet The content of the ICMPv6 packet with IP head
+ removed.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_SUCCESS The ICMPv6 message successfully processed.
+ @retval Others Failed to handle the ICMPv6 packet.
+
+**/
+EFI_STATUS
+Ip6IcmpHandle (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Check whether the DestinationAddress is an anycast address.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] DestinationAddress Points to the Destination Address of the packet.
+
+ @retval TRUE The DestinationAddress is anycast address.
+ @retval FALSE The DestinationAddress is not anycast address.
+
+**/
+BOOLEAN
+Ip6IsAnycast (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *DestinationAddress
+ );
+
+/**
+ Generate ICMPv6 error message and send it out to DestinationAddress. Currently
+ Destination Unreachable message, Time Exceeded message and Parameter Problem
+ message are supported.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Packet The packet which invoking ICMPv6 error.
+ @param[in] SourceAddress If not NULL, points to the SourceAddress.
+ Otherwise, the IP layer will select a source address
+ according to the DestinationAddress.
+ @param[in] DestinationAddress Points to the Destination Address of the ICMPv6
+ error message.
+ @param[in] Type The type of the ICMPv6 message.
+ @param[in] Code The additional level of the ICMPv6 message.
+ @param[in] Pointer If not NULL, identifies the octet offset within
+ the invoking packet where the error was detected.
+
+ @retval EFI_INVALID_PARAMETER The packet is malformated.
+ @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the
+ operation.
+ @retval EFI_SUCCESS The ICMPv6 message was successfully sent out.
+ @retval Others Failed to generate the ICMPv6 packet.
+
+**/
+EFI_STATUS
+Ip6SendIcmpError (
+ IN IP6_SERVICE *IpSb,
+ IN NET_BUF *Packet,
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,
+ IN EFI_IPv6_ADDRESS *DestinationAddress,
+ IN UINT8 Type,
+ IN UINT8 Code,
+ IN UINT32 *Pointer OPTIONAL
+ );
+
+#endif
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6If.c b/NetworkPkg/Ip6Dxe/Ip6If.c
new file mode 100644
index 0000000000..198b547ed2
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6If.c
@@ -0,0 +1,802 @@
+/** @file
+ Implement IP6 pesudo interface.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+/**
+ Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The transmit token's event.
+ @param[in] Context The Context which is pointed to the token.
+
+**/
+VOID
+EFIAPI
+Ip6OnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Fileter function to cancel all the frame related to an IP instance.
+
+ @param[in] Frame The transmit request to test whether to cancel.
+ @param[in] Context The context which is the Ip instance that issued
+ the transmit.
+
+ @retval TRUE The frame belongs to this instance and is to be
+ removed.
+ @retval FALSE The frame doesn't belong to this instance.
+
+**/
+BOOLEAN
+Ip6CancelInstanceFrame (
+ IN IP6_LINK_TX_TOKEN *Frame,
+ IN VOID *Context
+ )
+{
+ if (Frame->IpInstance == (IP6_PROTOCOL *) Context) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Set the interface's address. This will trigger the DAD process for the
+ address to set. To set an already set address, the lifetimes wil be
+ updated to the new value passed in.
+
+ @param[in] Interface The interface to set the address.
+ @param[in] Ip6Addr The interface's to be assigned IPv6 address.
+ @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast.
+ Otherwise, it is not anycast.
+ @param[in] PrefixLength The prefix length of the Ip6Addr.
+ @param[in] ValidLifetime The valid lifetime for this address.
+ @param[in] PreferredLifetime The preferred lifetime for this address.
+ @param[in] DadCallback The caller's callback to trigger when DAD finishes.
+ This is an optional parameter that may be NULL.
+ @param[in] Context The context that will be passed to DadCallback.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The interface is scheduled to be configured with
+ the specified address.
+ @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to
+ lack of resources.
+
+**/
+EFI_STATUS
+Ip6SetAddress (
+ IN IP6_INTERFACE *Interface,
+ IN EFI_IPv6_ADDRESS *Ip6Addr,
+ IN BOOLEAN IsAnycast,
+ IN UINT8 PrefixLength,
+ IN UINT32 ValidLifetime,
+ IN UINT32 PreferredLifetime,
+ IN IP6_DAD_CALLBACK DadCallback OPTIONAL,
+ IN VOID *Context OPTIONAL
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_ADDRESS_INFO *AddressInfo;
+ LIST_ENTRY *Entry;
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;
+ UINT64 Delay;
+ IP6_DELAY_JOIN_LIST *DelayNode;
+
+ NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);
+
+ IpSb = Interface->Service;
+
+ if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) {
+ ASSERT (AddressInfo != NULL);
+ //
+ // Update the lifetime.
+ //
+ AddressInfo->ValidLifetime = ValidLifetime;
+ AddressInfo->PreferredLifetime = PreferredLifetime;
+
+ if (DadCallback != NULL) {
+ DadCallback (TRUE, Ip6Addr, Context);
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO));
+ if (AddressInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AddressInfo->Signature = IP6_ADDR_INFO_SIGNATURE;
+ IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr);
+ AddressInfo->IsAnycast = IsAnycast;
+ AddressInfo->PrefixLength = PrefixLength;
+ AddressInfo->ValidLifetime = ValidLifetime;
+ AddressInfo->PreferredLifetime = PreferredLifetime;
+
+ if (AddressInfo->PrefixLength == 0) {
+ //
+ // Find an appropriate prefix from on-link prefixes and update the prefixlength.
+ // Longest prefix match is used here.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
+ PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
+
+ if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {
+ AddressInfo->PrefixLength = PrefixEntry->PrefixLength;
+ break;
+ }
+ }
+ }
+
+ if (AddressInfo->PrefixLength == 0) {
+ //
+ // If the prefix length is still zero, try the autonomous prefixes.
+ // Longest prefix match is used here.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {
+ PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
+
+ if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) {
+ AddressInfo->PrefixLength = PrefixEntry->PrefixLength;
+ break;
+ }
+ }
+ }
+
+ if (AddressInfo->PrefixLength == 0) {
+ //
+ // BUGBUG: Stil fail, use 64 as the default prefix length.
+ //
+ AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;
+ }
+
+
+ //
+ // Node should delay joining the solicited-node mulitcast address by a random delay
+ // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second).
+ // Thus queue the address to be processed in Duplicate Address Detection module
+ // after the delay time (in milliseconds).
+ //
+ Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ());
+ Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS);
+ Delay = RShiftU64 (Delay, 32);
+
+ DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST));
+ if (DelayNode == NULL) {
+ FreePool (AddressInfo);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DelayNode->DelayTime = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS));
+ DelayNode->Interface = Interface;
+ DelayNode->AddressInfo = AddressInfo;
+ DelayNode->DadCallback = DadCallback;
+ DelayNode->Context = Context;
+
+ InsertTailList (&Interface->DelayJoinList, &DelayNode->Link);
+ return EFI_SUCCESS;
+}
+
+/**
+ Create an IP6_INTERFACE.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] LinkLocal If TRUE, the instance is created for link-local address.
+ Otherwise, it is not for a link-local address.
+
+ @return Point to the created IP6_INTERFACE, otherwise NULL.
+
+**/
+IP6_INTERFACE *
+Ip6CreateInterface (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN LinkLocal
+ )
+{
+ EFI_STATUS Status;
+ IP6_INTERFACE *Interface;
+ EFI_IPv6_ADDRESS *Ip6Addr;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ Interface = AllocatePool (sizeof (IP6_INTERFACE));
+ if (Interface == NULL) {
+ return NULL;
+ }
+
+ Interface->Signature = IP6_INTERFACE_SIGNATURE;
+ Interface->RefCnt = 1;
+
+ InitializeListHead (&Interface->AddressList);
+ Interface->AddressCount = 0;
+ Interface->Configured = FALSE;
+
+ Interface->Service = IpSb;
+ Interface->Controller = IpSb->Controller;
+ Interface->Image = IpSb->Image;
+
+ InitializeListHead (&Interface->ArpQues);
+ InitializeListHead (&Interface->SentFrames);
+
+ Interface->DupAddrDetect = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits;
+ InitializeListHead (&Interface->DupAddrDetectList);
+
+ InitializeListHead (&Interface->DelayJoinList);
+
+ InitializeListHead (&Interface->IpInstances);
+ Interface->PromiscRecv = FALSE;
+
+ if (!LinkLocal) {
+ return Interface;
+ }
+
+ //
+ // Get the link local addr
+ //
+ Ip6Addr = Ip6CreateLinkLocalAddr (IpSb);
+ if (Ip6Addr == NULL) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Perform DAD - Duplicate Address Detection.
+ //
+ Status = Ip6SetAddress (
+ Interface,
+ Ip6Addr,
+ FALSE,
+ IP6_LINK_LOCAL_PREFIX_LENGTH,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ NULL,
+ NULL
+ );
+
+ FreePool (Ip6Addr);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return Interface;
+
+ON_ERROR:
+
+ FreePool (Interface);
+ return NULL;
+}
+
+/**
+ Free the interface used by IpInstance. All the IP instance with
+ the same Ip/prefix pair share the same interface. It is reference
+ counted. All the frames that haven't been sent will be cancelled.
+ Because the IpInstance is optional, the caller must remove
+ IpInstance from the interface's instance list.
+
+ @param[in] Interface The interface used by the IpInstance.
+ @param[in] IpInstance The IP instance that free the interface. NULL if
+ the IP driver is releasing the default interface.
+
+**/
+VOID
+Ip6CleanInterface (
+ IN IP6_INTERFACE *Interface,
+ IN IP6_PROTOCOL *IpInstance OPTIONAL
+ )
+{
+ IP6_DAD_ENTRY *Duplicate;
+ IP6_DELAY_JOIN_LIST *Delay;
+
+ NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE);
+ ASSERT (Interface->RefCnt > 0);
+
+ //
+ // Remove all the pending transmit token related to this IP instance.
+ //
+ Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance);
+
+ if (--Interface->RefCnt > 0) {
+ return;
+ }
+
+ //
+ // Destory the interface if this is the last IP instance.
+ // Remove all the system transmitted packets
+ // from this interface, cancel the receive request if exists.
+ //
+ Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL);
+
+ ASSERT (IsListEmpty (&Interface->IpInstances));
+ ASSERT (IsListEmpty (&Interface->ArpQues));
+ ASSERT (IsListEmpty (&Interface->SentFrames));
+
+ while (!IsListEmpty (&Interface->DupAddrDetectList)) {
+ Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link);
+ NetListRemoveHead (&Interface->DupAddrDetectList);
+ FreePool (Duplicate);
+ }
+
+ while (!IsListEmpty (&Interface->DelayJoinList)) {
+ Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link);
+ NetListRemoveHead (&Interface->DelayJoinList);
+ FreePool (Delay);
+ }
+
+ Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0);
+
+ RemoveEntryList (&Interface->Link);
+ FreePool (Interface);
+}
+
+/**
+ Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN.
+
+ @param[in] Interface The interface to send out from.
+ @param[in] IpInstance The IpInstance that transmit the packet. NULL if
+ the packet is sent by the IP6 driver itself.
+ @param[in] Packet The packet to transmit
+ @param[in] CallBack Call back function to execute if transmission
+ finished.
+ @param[in] Context Opaque parameter to the callback.
+
+ @return The wrapped token if succeed or NULL.
+
+**/
+IP6_LINK_TX_TOKEN *
+Ip6CreateLinkTxToken (
+ IN IP6_INTERFACE *Interface,
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN IP6_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData;
+ IP6_LINK_TX_TOKEN *Token;
+ EFI_STATUS Status;
+ UINT32 Count;
+
+ Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));
+
+ if (Token == NULL) {
+ return NULL;
+ }
+
+ Token->Signature = IP6_LINK_TX_SIGNATURE;
+ InitializeListHead (&Token->Link);
+
+ Token->IpInstance = IpInstance;
+ Token->CallBack = CallBack;
+ Token->Packet = Packet;
+ Token->Context = Context;
+ ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS));
+ IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress);
+
+ MnpToken = &(Token->MnpToken);
+ MnpToken->Status = EFI_NOT_READY;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip6OnFrameSent,
+ Token,
+ &MnpToken->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Token);
+ return NULL;
+ }
+
+ MnpTxData = &Token->MnpTxData;
+ MnpToken->Packet.TxData = MnpTxData;
+
+ MnpTxData->DestinationAddress = &Token->DstMac;
+ MnpTxData->SourceAddress = &Token->SrcMac;
+ MnpTxData->ProtocolType = IP6_ETHER_PROTO;
+ MnpTxData->DataLength = Packet->TotalSize;
+ MnpTxData->HeaderLength = 0;
+
+ Count = Packet->BlockOpNum;
+
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);
+ MnpTxData->FragmentCount = (UINT16)Count;
+
+ return Token;
+}
+
+/**
+ Free the link layer transmit token. It will close the event,
+ then free the memory used.
+
+ @param[in] Token Token to free.
+
+**/
+VOID
+Ip6FreeLinkTxToken (
+ IN IP6_LINK_TX_TOKEN *Token
+ )
+{
+ NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);
+
+ gBS->CloseEvent (Token->MnpToken.Event);
+ FreePool (Token);
+}
+
+/**
+ Callback function when the received packet is freed.
+ Check Ip6OnFrameReceived for information.
+
+ @param[in] Context Points to EFI_MANAGED_NETWORK_RECEIVE_DATA.
+
+**/
+VOID
+EFIAPI
+Ip6RecycleFrame (
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData;
+
+ RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context;
+
+ gBS->SignalEvent (RxData->RecycleEvent);
+}
+
+/**
+ Received a frame from MNP. Wrap it in net buffer then deliver
+ it to IP's input function. The ownship of the packet also
+ is transferred to IP. When Ip is finished with this packet, it
+ will call NetbufFree to release the packet, NetbufFree will
+ again call the Ip6RecycleFrame to signal MNP's event and free
+ the token used.
+
+ @param[in] Context Context for the callback.
+
+**/
+VOID
+EFIAPI
+Ip6OnFrameReceivedDpc (
+ IN VOID *Context
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData;
+ IP6_LINK_RX_TOKEN *Token;
+ NET_FRAGMENT Netfrag;
+ NET_BUF *Packet;
+ UINT32 Flag;
+ IP6_SERVICE *IpSb;
+
+ Token = (IP6_LINK_RX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE);
+
+ //
+ // First clear the interface's receive request in case the
+ // caller wants to call Ip6ReceiveFrame in the callback.
+ //
+ IpSb = (IP6_SERVICE *) Token->Context;
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+
+ MnpToken = &Token->MnpToken;
+ MnpRxData = MnpToken->Packet.RxData;
+
+ if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {
+ Token->CallBack (NULL, MnpToken->Status, 0, Token->Context);
+ return ;
+ }
+
+ //
+ // Wrap the frame in a net buffer then deliever it to IP input.
+ // IP will reassemble the packet, and deliver it to upper layer
+ //
+ Netfrag.Len = MnpRxData->DataLength;
+ Netfrag.Bulk = MnpRxData->PacketData;
+
+ Packet = NetbufFromExt (&Netfrag, 1, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData);
+
+ if (Packet == NULL) {
+ gBS->SignalEvent (MnpRxData->RecycleEvent);
+
+ Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);
+
+ return ;
+ }
+
+ Flag = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0);
+ Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0);
+ Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0);
+
+ Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context);
+}
+
+/**
+ Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK.
+
+ @param Event The receive event delivered to MNP for receive.
+ @param Context Context for the callback.
+
+**/
+VOID
+EFIAPI
+Ip6OnFrameReceived (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context);
+}
+
+/**
+ Request to receive the packet from the interface.
+
+ @param[in] CallBack Function to call when receive finished.
+ @param[in] IpSb Points to IP6 service binding instance.
+
+ @retval EFI_ALREADY_STARTED There is already a pending receive request.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive.
+ @retval EFI_SUCCESS The recieve request has been started.
+
+**/
+EFI_STATUS
+Ip6ReceiveFrame (
+ IN IP6_FRAME_CALLBACK CallBack,
+ IN IP6_SERVICE *IpSb
+ )
+{
+ EFI_STATUS Status;
+ IP6_LINK_RX_TOKEN *Token;
+
+ if (IpSb->InDestroy) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ Token = &IpSb->RecvRequest;
+ Token->CallBack = CallBack;
+ Token->Context = (VOID *) IpSb;
+
+ Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Callback funtion when frame transmission is finished. It will
+ call the frame owner's callback function to tell it the result.
+
+ @param[in] Context Context which points to the token.
+
+**/
+VOID
+EFIAPI
+Ip6OnFrameSentDpc (
+ IN VOID *Context
+ )
+{
+ IP6_LINK_TX_TOKEN *Token;
+
+ Token = (IP6_LINK_TX_TOKEN *) Context;
+ NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE);
+
+ RemoveEntryList (&Token->Link);
+
+ Token->CallBack (
+ Token->Packet,
+ Token->MnpToken.Status,
+ 0,
+ Token->Context
+ );
+
+ Ip6FreeLinkTxToken (Token);
+}
+
+/**
+ Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The transmit token's event.
+ @param[in] Context Context which points to the token.
+
+**/
+VOID
+EFIAPI
+Ip6OnFrameSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context);
+}
+
+/**
+ Send a frame from the interface. If the next hop is a multicast address,
+ it is transmitted immediately. If the next hop is a unicast,
+ and the NextHop's MAC is not known, it will perform address resolution.
+ If an error occurred, the CallBack won't be called. So, the caller
+ must test the return value, and take action when there is an error.
+
+ @param[in] Interface The interface to send the frame from
+ @param[in] IpInstance The IP child that request the transmission.
+ NULL if it is the IP6 driver itself.
+ @param[in] Packet The packet to transmit.
+ @param[in] NextHop The immediate destination to transmit the packet to.
+ @param[in] CallBack Function to call back when transmit finished.
+ @param[in] Context Opaque parameter to the callback.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame.
+ @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop.
+ @retval EFI_SUCCESS The packet successfully transmitted.
+
+**/
+EFI_STATUS
+Ip6SendFrame (
+ IN IP6_INTERFACE *Interface,
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN EFI_IPv6_ADDRESS *NextHop,
+ IN IP6_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_LINK_TX_TOKEN *Token;
+ EFI_STATUS Status;
+ IP6_NEIGHBOR_ENTRY *NeighborCache;
+ LIST_ENTRY *Entry;
+ IP6_NEIGHBOR_ENTRY *ArpQue;
+
+ IpSb = Interface->Service;
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ //
+ // Only when link local address is performing DAD, the interface could be used in unconfigured.
+ //
+ if (IpSb->LinkLocalOk) {
+ ASSERT (Interface->Configured);
+ }
+
+ Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);
+
+ if (Token == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (IP6_IS_MULTICAST (NextHop)) {
+ Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ goto SendNow;
+ }
+
+ //
+ // If send to itself, directly send out
+ //
+ if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) {
+ IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress);
+ goto SendNow;
+ }
+
+ //
+ // If unicast, check the neighbor state.
+ //
+
+ NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop);
+ ASSERT (NeighborCache != NULL);
+
+ if (NeighborCache->Interface == NULL) {
+ NeighborCache->Interface = Interface;
+ }
+
+ switch (NeighborCache->State) {
+ case EfiNeighborStale:
+ NeighborCache->State = EfiNeighborDelay;
+ NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);
+ //
+ // Fall through
+ //
+ case EfiNeighborReachable:
+ case EfiNeighborDelay:
+ case EfiNeighborProbe:
+ IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress);
+ goto SendNow;
+ break;
+
+ default:
+ break;
+ }
+
+ //
+ // Have to do asynchronous ARP resolution. First check whether there is
+ // already a pending request.
+ //
+ NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);
+ if (ArpQue == NeighborCache) {
+ InsertTailList (&NeighborCache->Frames, &Token->Link);
+ NeighborCache->ArpFree = TRUE;
+ return EFI_SUCCESS;
+ }
+ }
+
+ //
+ // First frame requires ARP.
+ //
+ InsertTailList (&NeighborCache->Frames, &Token->Link);
+ InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList);
+
+ NeighborCache->ArpFree = TRUE;
+
+ return EFI_SUCCESS;
+
+SendNow:
+ //
+ // Insert the tx token into the SentFrames list before calling Mnp->Transmit.
+ // Remove it if the returned status is not EFI_SUCCESS.
+ //
+ InsertTailList (&Interface->SentFrames, &Token->Link);
+ Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);
+ if (EFI_ERROR (Status)) {
+ RemoveEntryList (&Token->Link);
+ goto Error;
+ }
+
+ return EFI_SUCCESS;
+
+Error:
+ Ip6FreeLinkTxToken (Token);
+ return Status;
+}
+
+/**
+ The heartbeat timer of IP6 service instance. It times out
+ all of its IP6 children's received-but-not-delivered and
+ transmitted-but-not-recycle packets.
+
+ @param[in] Event The IP6 service instance's heartbeat timer.
+ @param[in] Context The IP6 service instance.
+
+**/
+VOID
+EFIAPI
+Ip6TimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP6_SERVICE *IpSb;
+
+ IpSb = (IP6_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ Ip6PacketTimerTicking (IpSb);
+ Ip6NdTimerTicking (IpSb);
+ Ip6MldTimerTicking (IpSb);
+}
diff --git a/NetworkPkg/Ip6Dxe/Ip6If.h b/NetworkPkg/Ip6Dxe/Ip6If.h
new file mode 100644
index 0000000000..736035ec39
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6If.h
@@ -0,0 +1,267 @@
+/** @file
+ Definition for IP6 pesudo interface structure.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_IF_H__
+#define __EFI_IP6_IF_H__
+
+#define IP6_LINK_RX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'R')
+#define IP6_LINK_TX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'T')
+#define IP6_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'I')
+#define IP6_ADDR_INFO_SIGNATURE SIGNATURE_32 ('I', 'P', 'A', 'I')
+
+//
+// This prototype is used by both receive and transmission.
+// When receiving Netbuf is allocated by IP6_INTERFACE, and
+// released by IP6. Flag shows whether the frame is received
+// as unicast/multicast/anycast...
+//
+// When transmitting, the Netbuf is from IP6, and provided
+// to the callback as a reference. Flag isn't used.
+//
+// IpInstance can be NULL which means that it is the IP6 driver
+// itself sending the packets. IP6 driver may send packets that
+// don't belong to any instance, such as ICMP errors, ICMP
+// informational packets. IpInstance is used as a tag in
+// this module.
+//
+typedef
+VOID
+(*IP6_FRAME_CALLBACK) (
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 LinkFlag,
+ VOID *Context
+ );
+
+//
+// Each receive request is wrapped in an IP6_LINK_RX_TOKEN.
+// Upon completion, the Callback will be called. Only one
+// receive request is send to MNP. IpInstance is always NULL.
+// Reference MNP's spec for information.
+//
+typedef struct {
+ UINT32 Signature;
+ IP6_FRAME_CALLBACK CallBack;
+ VOID *Context;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;
+} IP6_LINK_RX_TOKEN;
+
+//
+// Each transmit request is wrapped in an IP6_LINK_TX_TOKEN.
+// Upon completion, the Callback will be called.
+//
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ IP6_PROTOCOL *IpInstance;
+ IP6_FRAME_CALLBACK CallBack;
+ NET_BUF *Packet;
+ VOID *Context;
+
+ EFI_MAC_ADDRESS DstMac;
+ EFI_MAC_ADDRESS SrcMac;
+
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData;
+} IP6_LINK_TX_TOKEN;
+
+struct _IP6_ADDRESS_INFO {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ EFI_IPv6_ADDRESS Address;
+ BOOLEAN IsAnycast;
+ UINT8 PrefixLength;
+ UINT32 ValidLifetime;
+ UINT32 PreferredLifetime;
+};
+
+//
+// Callback to select which frame to cancel. Caller can cancel a
+// single frame, or all the frame from an IP instance.
+//
+typedef
+BOOLEAN
+(*IP6_FRAME_TO_CANCEL) (
+ IP6_LINK_TX_TOKEN *Frame,
+ VOID *Context
+ );
+
+struct _IP6_INTERFACE {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ INTN RefCnt;
+
+ //
+ // IP address and prefix length of the interface. The fileds
+ // are invalid if (Configured == FALSE)
+ //
+ LIST_ENTRY AddressList;
+ UINT32 AddressCount;
+ BOOLEAN Configured;
+
+ IP6_SERVICE *Service;
+
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+
+ //
+ // Queues to keep the frames sent and waiting ARP request.
+ //
+ LIST_ENTRY ArpQues;
+ LIST_ENTRY SentFrames;
+
+
+ //
+ // The interface's configuration variables
+ //
+ UINT32 DupAddrDetect;
+ LIST_ENTRY DupAddrDetectList;
+ LIST_ENTRY DelayJoinList;
+
+ //
+ // All the IP instances that have the same IP/SubnetMask are linked
+ // together through IpInstances. If any of the instance enables
+ // promiscuous receive, PromiscRecv is true.
+ //
+ LIST_ENTRY IpInstances;
+ BOOLEAN PromiscRecv;
+};
+
+/**
+ Create an IP6_INTERFACE.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] LinkLocal If TRUE, the instance is created for link-local address.
+ Otherwise, it is not for a link-local address.
+
+ @return Point to the created IP6_INTERFACE, otherwise NULL.
+
+**/
+IP6_INTERFACE *
+Ip6CreateInterface (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN LinkLocal
+ );
+
+/**
+ Free the interface used by IpInstance. All the IP instance with
+ the same Ip/prefix pair share the same interface. It is reference
+ counted. All the frames that haven't been sent will be cancelled.
+ Because the IpInstance is optional, the caller must remove
+ IpInstance from the interface's instance list.
+
+ @param[in] Interface The interface used by the IpInstance.
+ @param[in] IpInstance The IP instance that free the interface. NULL if
+ the IP driver is releasing the default interface.
+
+**/
+VOID
+Ip6CleanInterface (
+ IN IP6_INTERFACE *Interface,
+ IN IP6_PROTOCOL *IpInstance OPTIONAL
+ );
+
+/**
+ Free the link layer transmit token. It will close the event
+ then free the memory used.
+
+ @param[in] Token Token to free.
+
+**/
+VOID
+Ip6FreeLinkTxToken (
+ IN IP6_LINK_TX_TOKEN *Token
+ );
+
+/**
+ Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK
+
+ @param Event The receive event delivered to MNP for receive.
+ @param Context Context for the callback.
+
+**/
+VOID
+EFIAPI
+Ip6OnFrameReceived (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Request to receive the packet from the interface.
+
+ @param[in] CallBack Function to call when the receive finished.
+ @param[in] IpSb Points to the IP6 service binding instance.
+
+ @retval EFI_ALREADY_STARTED There is already a pending receive request.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to receive.
+ @retval EFI_SUCCESS The recieve request has been started.
+
+**/
+EFI_STATUS
+Ip6ReceiveFrame (
+ IN IP6_FRAME_CALLBACK CallBack,
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ Send a frame from the interface. If the next hop is multicast address,
+ it is transmitted immediately. If the next hop is a unicast,
+ and the NextHop's MAC is not known, it will perform address resolution.
+ If some error happened, the CallBack won't be called. So, the caller
+ must test the return value, and take action when there is an error.
+
+ @param[in] Interface The interface to send the frame from
+ @param[in] IpInstance The IP child that request the transmission.
+ NULL if it is the IP6 driver itself.
+ @param[in] Packet The packet to transmit.
+ @param[in] NextHop The immediate destination to transmit the packet to.
+ @param[in] CallBack Function to call back when transmit finished.
+ @param[in] Context Opaque parameter to the call back.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame.
+ @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop.
+ @retval EFI_SUCCESS The packet successfully transmitted.
+
+**/
+EFI_STATUS
+Ip6SendFrame (
+ IN IP6_INTERFACE *Interface,
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN EFI_IPv6_ADDRESS *NextHop,
+ IN IP6_FRAME_CALLBACK CallBack,
+ IN VOID *Context
+ );
+
+/**
+ The heartbeat timer of IP6 service instance. It times out
+ all of its IP6 children's received-but-not-delivered and
+ transmitted-but-not-recycle packets.
+
+ @param[in] Event The IP6 service instance's heart beat timer.
+ @param[in] Context The IP6 service instance.
+
+**/
+VOID
+EFIAPI
+Ip6TimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.c b/NetworkPkg/Ip6Dxe/Ip6Impl.c
new file mode 100644
index 0000000000..9b34eceeb7
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Impl.c
@@ -0,0 +1,1857 @@
+/** @file
+ Implementation of EFI_IP6_PROTOCOL protocol interfaces.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_IPSEC_PROTOCOL *mIpSec = NULL;
+
+EFI_IP6_PROTOCOL mEfiIp6ProtocolTemplete = {
+ EfiIp6GetModeData,
+ EfiIp6Configure,
+ EfiIp6Groups,
+ EfiIp6Routes,
+ EfiIp6Neighbors,
+ EfiIp6Transmit,
+ EfiIp6Receive,
+ EfiIp6Cancel,
+ EfiIp6Poll
+};
+
+/**
+ Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.
+
+ The GetModeData() function returns the current operational mode data for this driver instance.
+ The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to
+ retrieve the operational mode data of underlying networks or drivers.
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[out] Ip6ModeData Pointer to the EFI IPv6 Protocol mode data structure.
+ @param[out] MnpConfigData Pointer to the managed network configuration data structure.
+ @param[out] SnpModeData Pointer to the simple network mode data structure.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6GetModeData (
+ IN EFI_IP6_PROTOCOL *This,
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ IP6_SERVICE *IpSb;
+ IP6_INTERFACE *IpIf;
+ EFI_IP6_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+ IpIf = IpInstance->Interface;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Ip6ModeData != NULL) {
+ //
+ // IsStarted is "whether the EfiIp6Configure has been called".
+ // IsConfigured is "whether the station address has been configured"
+ //
+ Ip6ModeData->IsStarted = (BOOLEAN) (IpInstance->State == IP6_STATE_CONFIGED);
+ Ip6ModeData->MaxPacketSize = IpSb->MaxPacketSize;
+ CopyMem (&Ip6ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP6_CONFIG_DATA));
+ Ip6ModeData->IsConfigured = FALSE;
+
+ Ip6ModeData->AddressCount = 0;
+ Ip6ModeData->AddressList = NULL;
+
+ Ip6ModeData->GroupCount = IpInstance->GroupCount;
+ Ip6ModeData->GroupTable = NULL;
+
+ Ip6ModeData->RouteCount = 0;
+ Ip6ModeData->RouteTable = NULL;
+
+ Ip6ModeData->NeighborCount = 0;
+ Ip6ModeData->NeighborCache = NULL;
+
+ Ip6ModeData->PrefixCount = 0;
+ Ip6ModeData->PrefixTable = NULL;
+
+ Ip6ModeData->IcmpTypeCount = 23;
+ Ip6ModeData->IcmpTypeList = AllocateCopyPool (
+ Ip6ModeData->IcmpTypeCount * sizeof (EFI_IP6_ICMP_TYPE),
+ mIp6SupportedIcmp
+ );
+ if (Ip6ModeData->IcmpTypeList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Return the currently configured IPv6 addresses and corresponding prefix lengths.
+ //
+ Status = Ip6BuildEfiAddressList (
+ IpSb,
+ &Ip6ModeData->AddressCount,
+ &Ip6ModeData->AddressList
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // Return the current station address for this IP child.
+ // If UseAnyStationAddress is set to TRUE, IP6 driver will
+ // select a source address from its address list. Otherwise use the
+ // StationAddress in config data.
+ //
+ if (Ip6ModeData->IsStarted) {
+ Config = &Ip6ModeData->ConfigData;
+
+ if (IpIf->Configured || NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {
+ Ip6ModeData->IsConfigured = TRUE;
+ } else {
+ Ip6ModeData->IsConfigured = FALSE;
+ }
+
+ //
+ // Build a EFI route table for user from the internal route table.
+ //
+ Status = Ip6BuildEfiRouteTable (
+ IpSb->RouteTable,
+ &Ip6ModeData->RouteCount,
+ &Ip6ModeData->RouteTable
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ }
+
+ if (Ip6ModeData->IsConfigured) {
+ //
+ // Return the joined multicast group addresses.
+ //
+ if (IpInstance->GroupCount != 0) {
+ Ip6ModeData->GroupTable = AllocateCopyPool (
+ IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS),
+ IpInstance->GroupList
+ );
+ if (Ip6ModeData->GroupTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+ }
+ //
+ // Return the neighbor cache entries
+ //
+ Status = Ip6BuildEfiNeighborCache (
+ IpInstance,
+ &Ip6ModeData->NeighborCount,
+ &Ip6ModeData->NeighborCache
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // Return the prefix table entries
+ //
+ Status = Ip6BuildPrefixTable (
+ IpInstance,
+ &Ip6ModeData->PrefixCount,
+ &Ip6ModeData->PrefixTable
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ }
+ }
+
+ //
+ // Get fresh mode data from MNP, since underlying media status may change
+ //
+ Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData);
+
+ goto Exit;
+
+Error:
+ if (Ip6ModeData != NULL) {
+ if (Ip6ModeData->AddressList != NULL) {
+ FreePool (Ip6ModeData->AddressList);
+ }
+
+ if (Ip6ModeData->GroupTable != NULL) {
+ FreePool (Ip6ModeData->GroupTable);
+ }
+
+ if (Ip6ModeData->RouteTable != NULL) {
+ FreePool (Ip6ModeData->RouteTable);
+ }
+
+ if (Ip6ModeData->NeighborCache != NULL) {
+ FreePool (Ip6ModeData->NeighborCache);
+ }
+
+ if (Ip6ModeData->PrefixTable != NULL) {
+ FreePool (Ip6ModeData->PrefixTable);
+ }
+
+ if (Ip6ModeData->IcmpTypeList != NULL) {
+ FreePool (Ip6ModeData->IcmpTypeList);
+ }
+ }
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Validate that Ipv6 address is OK to be used as station address or next hop address/ neighbor.
+
+ @param[in] IpSb The IP6 service instance.
+ @param[in] Ip The IPv6 address to validate.
+ @param[in] Flag If TRUE, validate if the address is OK to be used
+ as station address. If FALSE, validate if the
+ address is OK to be used as the next hop address/
+ neighbor.
+
+ @retval TRUE The Ip address is valid and could be used.
+ @retval FALSE Invalid Ip address.
+
+**/
+BOOLEAN
+Ip6IsValidAddress (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Ip,
+ IN BOOLEAN Flag
+ )
+{
+ if (!NetIp6IsUnspecifiedAddr (Ip)) {
+ if (!NetIp6IsValidUnicast(Ip)) {
+ return FALSE;
+ }
+ if (Ip6IsOneOfSetAddress (IpSb, Ip, NULL, NULL)) {
+ return Flag;
+ }
+ } else {
+ return Flag;
+ }
+
+ return (BOOLEAN) !Flag;
+}
+
+/**
+ Validate whether the value of protocol is illegal or not. Protocol is the 'Next Header' field
+ in the last IPv6 extension header, or basic IPv6 header is there's no extension header.
+
+ @param[in] Protocol Default value of 'Next Header'
+
+ @retval TRUE The protocol is illegal.
+ @retval FALSE The protocol is legal.
+
+**/
+BOOLEAN
+Ip6IsIllegalProtocol (
+ IN UINT8 Protocol
+ )
+{
+ if (Protocol == IP6_HOP_BY_HOP || Protocol == EFI_IP_PROTO_ICMP || Protocol == IP4_PROTO_IGMP) {
+ return TRUE;
+ }
+
+ if (Protocol == 41 || Protocol == 43 || Protocol == 44 || Protocol == 59 || Protocol == 60 || Protocol == 124) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Intiialize the IP6_PROTOCOL structure to the unconfigured states.
+
+ @param[in] IpSb The IP6 service instance.
+ @param[in, out] IpInstance The IP6 child instance.
+
+**/
+VOID
+Ip6InitProtocol (
+ IN IP6_SERVICE *IpSb,
+ IN OUT IP6_PROTOCOL *IpInstance
+ )
+{
+ ASSERT ((IpSb != NULL) && (IpInstance != NULL));
+
+ ZeroMem (IpInstance, sizeof (IP6_PROTOCOL));
+
+ IpInstance->Signature = IP6_PROTOCOL_SIGNATURE;
+ IpInstance->State = IP6_STATE_UNCONFIGED;
+ IpInstance->Service = IpSb;
+ IpInstance->GroupList = NULL;
+ CopyMem (&IpInstance->Ip6Proto, &mEfiIp6ProtocolTemplete, sizeof (EFI_IP6_PROTOCOL));
+
+ NetMapInit (&IpInstance->RxTokens);
+ NetMapInit (&IpInstance->TxTokens);
+ InitializeListHead (&IpInstance->Received);
+ InitializeListHead (&IpInstance->Delivered);
+
+ EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY);
+}
+
+/**
+ Configure the IP6 child. If the child is already configured,
+ change the configuration parameter. Otherwise, configure it
+ for the first time. The caller should validate the configuration
+ before deliver them to it. It also don't do configure NULL.
+
+ @param[in, out] IpInstance The IP6 child to configure.
+ @param[in] Config The configure data.
+
+ @retval EFI_SUCCESS The IP6 child is successfully configured.
+ @retval EFI_DEVICE_ERROR Failed to free the pending transive or to
+ configure underlying MNP, or other errors.
+ @retval EFI_NO_MAPPING The IP6 child is configured to use the default
+ address, but the default address hasn't been
+ configured. The IP6 child doesn't need to be
+ reconfigured when the default address is configured.
+ @retval EFI_OUT_OF_RESOURCES No more memory space is available.
+ @retval other Other error occurs.
+
+**/
+EFI_STATUS
+Ip6ConfigProtocol (
+ IN OUT IP6_PROTOCOL *IpInstance,
+ IN EFI_IP6_CONFIG_DATA *Config
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_INTERFACE *IpIf;
+ EFI_STATUS Status;
+ EFI_IP6_CONFIG_DATA *Current;
+ IP6_ADDRESS_INFO *AddressInfo;
+ BOOLEAN StationZero;
+ BOOLEAN DestZero;
+ EFI_IPv6_ADDRESS Source;
+ BOOLEAN AddrOk;
+
+ IpSb = IpInstance->Service;
+ Current = &IpInstance->ConfigData;
+
+ //
+ // User is changing packet filters. It must be stopped
+ // before the station address can be changed.
+ //
+ if (IpInstance->State == IP6_STATE_CONFIGED) {
+ //
+ // Cancel all the pending transmit/receive from upper layer
+ //
+ Status = Ip6Cancel (IpInstance, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Set up the interface.
+ //
+ StationZero = NetIp6IsUnspecifiedAddr (&Config->StationAddress);
+ DestZero = NetIp6IsUnspecifiedAddr (&Config->DestinationAddress);
+
+ if (StationZero && DestZero) {
+ //
+ // StationAddress is still zero.
+ //
+
+ NET_GET_REF (IpSb->DefaultInterface);
+ IpInstance->Interface = IpSb->DefaultInterface;
+ InsertTailList (&IpSb->DefaultInterface->IpInstances, &IpInstance->AddrLink);
+
+ CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));
+ IpInstance->State = IP6_STATE_CONFIGED;
+
+ return EFI_SUCCESS;
+ }
+
+ if (StationZero && !DestZero) {
+ Status = Ip6SelectSourceAddress (IpSb, &Config->DestinationAddress, &Source);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ IP6_COPY_ADDRESS (&Source, &Config->StationAddress);
+ }
+
+ AddrOk = Ip6IsOneOfSetAddress (IpSb, &Source, &IpIf, &AddressInfo);
+ if (AddrOk) {
+ if (AddressInfo != NULL) {
+ IpInstance->PrefixLength = AddressInfo->PrefixLength;
+ } else {
+ IpInstance->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;
+ }
+ } else {
+ //
+ // The specified source address is not one of the addresses IPv6 maintains.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+
+ NET_GET_REF (IpIf);
+ IpInstance->Interface = IpIf;
+ InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink);
+
+ CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA));
+ IP6_COPY_ADDRESS (&Current->StationAddress, &Source);
+ IpInstance->State = IP6_STATE_CONFIGED;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Clean up the IP6 child, and release all the resources used by it.
+
+ @param[in, out] IpInstance The IP6 child to clean up.
+
+ @retval EFI_SUCCESS The IP6 child is cleaned up.
+ @retval EFI_DEVICE_ERROR Some resources failed to be released.
+
+**/
+EFI_STATUS
+Ip6CleanProtocol (
+ IN OUT IP6_PROTOCOL *IpInstance
+ )
+{
+ if (EFI_ERROR (Ip6Cancel (IpInstance, NULL))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (EFI_ERROR (Ip6Groups (IpInstance, FALSE, NULL))) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Some packets haven't been recycled. It is because either the
+ // user forgets to recycle the packets, or because the callback
+ // hasn't been called. Just leave it alone.
+ //
+ if (!IsListEmpty (&IpInstance->Delivered)) {
+ ;
+ }
+
+ if (IpInstance->Interface != NULL) {
+ RemoveEntryList (&IpInstance->AddrLink);
+ Ip6CleanInterface (IpInstance->Interface, IpInstance);
+ IpInstance->Interface = NULL;
+ }
+
+ if (IpInstance->GroupList != NULL) {
+ FreePool (IpInstance->GroupList);
+ IpInstance->GroupList = NULL;
+ IpInstance->GroupCount = 0;
+ }
+
+ NetMapClean (&IpInstance->TxTokens);
+
+ NetMapClean (&IpInstance->RxTokens);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Configure the MNP parameter used by IP. The IP driver uses one MNP
+ child to transmit/receive frames. By default, it configures MNP
+ to receive unicast/multicast/broadcast. Also, it will enable/disable
+ the promiscuous receive according to whether there is IP child
+ enable that or not. If Force is FALSE, it will iterate through
+ all the IP children to check whether the promiscuous receive
+ setting has been changed. If it hasn't been changed, it won't
+ reconfigure the MNP. If Force is TRUE, the MNP is configured
+ whether that is changed or not.
+
+ @param[in] IpSb The IP6 service instance that is to be changed.
+ @param[in] Force Force the configuration or not.
+
+ @retval EFI_SUCCESS The MNP successfully configured/reconfigured.
+ @retval Others Configuration failed.
+
+**/
+EFI_STATUS
+Ip6ServiceConfigMnp (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN Force
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *ProtoEntry;
+ IP6_INTERFACE *IpIf;
+ IP6_PROTOCOL *IpInstance;
+ BOOLEAN Reconfig;
+ BOOLEAN PromiscReceive;
+ EFI_STATUS Status;
+
+ Reconfig = FALSE;
+ PromiscReceive = FALSE;
+
+ if (!Force) {
+ //
+ // Iterate through the IP children to check whether promiscuous
+ // receive setting has been changed. Update the interface's receive
+ // filter also.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
+ IpIf->PromiscRecv = FALSE;
+
+ NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) {
+ IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP6_PROTOCOL, AddrLink);
+
+ if (IpInstance->ConfigData.AcceptPromiscuous) {
+ IpIf->PromiscRecv = TRUE;
+ PromiscReceive = TRUE;
+ }
+ }
+ }
+
+ //
+ // If promiscuous receive isn't changed, it isn't necessary to reconfigure.
+ //
+ if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) {
+ return EFI_SUCCESS;
+ }
+
+ Reconfig = TRUE;
+ IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive;
+ }
+
+ Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData);
+
+ //
+ // recover the original configuration if failed to set the configure.
+ //
+ if (EFI_ERROR (Status) && Reconfig) {
+ IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive;
+ }
+
+ return Status;
+}
+
+/**
+ Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.
+
+ The Configure() function is used to set, change, or reset the operational parameters and filter
+ settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic
+ can be sent or received by this instance. Once the parameters have been reset (by calling this
+ function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these
+ parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped
+ independently of each other by enabling or disabling their receive filter settings with the
+ Configure() function.
+
+ If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required
+ to be one of the currently configured IPv6 addresses listed in the EFI IPv6 drivers, or else
+ EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is
+ unspecified, the IPv6 driver will bind a source address according to the source address selection
+ algorithm. Clients could frequently call GetModeData() to check get currently configured IPv6
+ address list in the EFI IPv6 driver. If both Ip6ConfigData.StationAddress and
+ Ip6ConfigData.Destination are unspecified, when transmitting the packet afterwards, the
+ source address filled in each outgoing IPv6 packet is decided based on the destination of this packet.
+
+ If operational parameters are reset or changed, any pending transmit and receive requests will be
+ cancelled. Their completion token status will be set to EFI_ABORTED and their events will be
+ signaled.
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Ip6ConfigData Pointer to the EFI IPv6 Protocol configuration data structure.
+ If NULL, reset the configuration data.
+
+ @retval EFI_SUCCESS The driver instance was successfully opened.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Ip6ConfigData.StationAddress is neither zero nor
+ a unicast IPv6 address.
+ - Ip6ConfigData.StationAddress is neither zero nor
+ one of the configured IP addresses in the EFI IPv6 driver.
+ - Ip6ConfigData.DefaultProtocol is illegal.
+ @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated.
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for
+ this instance, but no source address was available for use.
+ @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6
+ address or prefix length can be changed.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6
+ Protocol driver instance was not opened.
+ @retval EFI_UNSUPPORTED Default protocol specified through
+ Ip6ConfigData.DefaulProtocol isn't supported.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Configure (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ EFI_IP6_CONFIG_DATA *Current;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP6_SERVICE *IpSb;
+
+ //
+ // First, validate the parameters
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Status = EFI_INVALID_PARAMETER;
+
+ //
+ // Validate the configuration first.
+ //
+ if (Ip6ConfigData != NULL) {
+ //
+ // Check whether the station address is valid.
+ //
+ if (!Ip6IsValidAddress (IpSb, &Ip6ConfigData->StationAddress, TRUE)) {
+ goto Exit;
+ }
+ //
+ // Check whether the default protocol is valid.
+ //
+ if (Ip6IsIllegalProtocol (Ip6ConfigData->DefaultProtocol)) {
+ goto Exit;
+ }
+
+ //
+ // User can only update packet filters when already configured.
+ // If it wants to change the station address, it must configure(NULL)
+ // the instance firstly.
+ //
+ if (IpInstance->State == IP6_STATE_CONFIGED) {
+ Current = &IpInstance->ConfigData;
+
+ if (!EFI_IP6_EQUAL (&Current->StationAddress, &Ip6ConfigData->StationAddress)) {
+ Status = EFI_ALREADY_STARTED;
+ goto Exit;
+ }
+
+ if (NetIp6IsUnspecifiedAddr (&Current->StationAddress) && IP6_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+ }
+ }
+
+ //
+ // Configure the instance or clean it up.
+ //
+ if (Ip6ConfigData != NULL) {
+ Status = Ip6ConfigProtocol (IpInstance, Ip6ConfigData);
+ } else {
+ Status = Ip6CleanProtocol (IpInstance);
+
+ //
+ // Don't change the state if it is DESTORY, consider the following
+ // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped,
+ // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED,
+ // the unload fails miserably.
+ //
+ if (IpInstance->State == IP6_STATE_CONFIGED) {
+ IpInstance->State = IP6_STATE_UNCONFIGED;
+ }
+ }
+
+ //
+ // Update the MNP's configure data. Ip6ServiceConfigMnp will check
+ // whether it is necessary to reconfigure the MNP.
+ //
+ Ip6ServiceConfigMnp (IpInstance->Service, FALSE);
+
+ //
+ // Update the variable data.
+ //
+ Ip6SetVariableData (IpInstance->Service);
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Joins and leaves multicast groups.
+
+ The Groups() function is used to join and leave multicast group sessions. Joining a group will
+ enable reception of matching multicast packets. Leaving a group will disable reception of matching
+ multicast packets. Source-Specific Multicast isn't required to be supported.
+
+ If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join the multicast group session, and FALSE to leave.
+ @param[in] GroupAddress Pointer to the IPv6 multicast address.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:
+ - This is NULL.
+ - JoinFlag is TRUE and GroupAddress is NULL.
+ - GroupAddress is not NULL and *GroupAddress is
+ not a multicast IPv6 address.
+ - GroupAddress is not NULL and *GroupAddress is in the
+ range of SSM destination address.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_OUT_OF_RESOURCES System resources could not be allocated.
+ @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when
+ JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Groups (
+ IN EFI_IP6_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP6_PROTOCOL *IpInstance;
+ IP6_SERVICE *IpSb;
+
+ if ((This == NULL) || (JoinFlag && GroupAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GroupAddress != NULL && !IP6_IS_MULTICAST (GroupAddress)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP6_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ Status = Ip6Groups (IpInstance, JoinFlag, GroupAddress);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Adds and deletes routing table entries.
+
+ The Routes() function adds a route to, or deletes a route from, the routing table.
+
+ Routes are determined by comparing the leftmost PrefixLength bits of Destination with
+ the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the
+ configured station address.
+
+ The default route is added with Destination and PrefixLegth both set to all zeros. The
+ default route matches all destination IPv6 addresses that do not match any other routes.
+
+ All EFI IPv6 Protocol instances share a routing table.
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to
+ FALSE to add this route to the routing table. Destination,
+ PrefixLength and Gateway are used as the key to each
+ route entry.
+ @param[in] Destination The address prefix of the subnet that needs to be routed.
+ This is an optional parameter that may be NULL.
+ @param[in] PrefixLength The prefix length of Destination. Ignored if Destination
+ is NULL.
+ @param[in] GatewayAddress The unicast gateway IPv6 address for this route.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The driver instance has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - When DeleteRoute is TRUE, both Destination and
+ GatewayAddress are NULL.
+ - When DeleteRoute is FALSE, either Destination or
+ GatewayAddress is NULL.
+ - *GatewayAddress is not a valid unicast IPv6 address.
+ - *GatewayAddress is one of the local configured IPv6
+ addresses.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE).
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when
+ DeleteRoute is FALSE).
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Routes (
+ IN EFI_IP6_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ IP6_SERVICE *IpSb;
+
+ if ((This == NULL) || (PrefixLength >= IP6_PREFIX_NUM)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (IpInstance->State != IP6_STATE_CONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (DeleteRoute && (Destination == NULL) && (GatewayAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!DeleteRoute && (Destination == NULL || GatewayAddress == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GatewayAddress != NULL) {
+ if (!Ip6IsValidAddress (IpSb, GatewayAddress, FALSE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!NetIp6IsUnspecifiedAddr (GatewayAddress) &&
+ !NetIp6IsNetEqual (GatewayAddress, &IpInstance->ConfigData.StationAddress, PrefixLength)
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Update the route table
+ //
+ if (DeleteRoute) {
+ Status = Ip6DelRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);
+ } else {
+ Status = Ip6AddRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Add or delete Neighbor cache entries.
+
+ The Neighbors() function is used to add, update, or delete an entry from neighbor cache.
+ IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as
+ network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network
+ traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not
+ timeout) or dynamic (will timeout).
+
+ The implementation should follow the neighbor cache timeout mechanism which is defined in
+ RFC4861. The default neighbor cache timeout value should be tuned for the expected network
+ environment
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] DeleteFlag Set to TRUE to delete the specified cache entry, set to FALSE to
+ add (or update, if it already exists and Override is TRUE) the
+ specified cache entry. TargetIp6Address is used as the key
+ to find the requested cache entry.
+ @param[in] TargetIp6Address Pointer to the Target IPv6 address.
+ @param[in] TargetLinkAddress Pointer to the link-layer address of the target. Ignored if NULL.
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
+ cache, it will be deleted after Timeout. A value of zero means that
+ the entry is permanent. A non-zero value means that the entry is
+ dynamic.
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will
+ be overridden and updated; if FALSE, EFI_ACCESS_DENIED
+ will be returned if a corresponding cache entry already existed.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - TargetIpAddress is NULL.
+ - *TargetLinkAddress is invalid when not NULL.
+ - *TargetIpAddress is not a valid unicast IPv6 address.
+ - *TargetIpAddress is one of the local configured IPv6
+ addresses.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache.
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is
+ TRUE or when DeleteFlag is FALSE while
+ TargetLinkAddress is NULL.).
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,
+ and that entry is tagged as un-overridden (when Override
+ is FALSE).
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Neighbors (
+ IN EFI_IP6_PROTOCOL *This,
+ IN BOOLEAN DeleteFlag,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
+ IN UINT32 Timeout,
+ IN BOOLEAN Override
+ )
+{
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ IP6_PROTOCOL *IpInstance;
+ IP6_SERVICE *IpSb;
+
+ if (This == NULL || TargetIp6Address == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NetIp6IsUnspecifiedAddr (TargetIp6Address)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (!Ip6IsValidAddress (IpSb, TargetIp6Address, FALSE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TargetLinkAddress != NULL) {
+ if (!Ip6IsValidLinkAddress (IpSb, TargetLinkAddress)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (Ip6IsOneOfSetAddress (IpSb, TargetIp6Address, NULL, NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (IpInstance->State != IP6_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (DeleteFlag) {
+ Status = Ip6DelNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);
+ } else {
+ Status = Ip6AddNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override);
+ }
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Check whether the user's token or event has already
+ been enqueue on IP6's list.
+
+ @param[in] Map The container of either user's transmit or receive
+ token.
+ @param[in] Item Current item to check against.
+ @param[in] Context The Token to check againist.
+
+ @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP
+ @retval EFI_SUCCESS The current item isn't the same token/event as the
+ context.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP6_COMPLETION_TOKEN *Token;
+ EFI_IP6_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_IP6_COMPLETION_TOKEN *) Context;
+ TokenInItem = (EFI_IP6_COMPLETION_TOKEN *) Item->Key;
+
+ if (Token == TokenInItem || Token->Event == TokenInItem->Event) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Validate the user's token against the current station address.
+
+ @param[in] Token User's token to validate.
+
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid.
+ @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long.
+ @retval EFI_SUCCESS The token is OK.
+
+**/
+EFI_STATUS
+Ip6TxTokenValid (
+ IN EFI_IP6_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_IP6_TRANSMIT_DATA *TxData;
+ UINT32 Index;
+ UINT32 DataLength;
+
+ if (Token == NULL || Token->Event == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TxData = Token->Packet.TxData;
+
+ if (TxData == NULL || (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TxData->FragmentCount == 0 || TxData->DataLength == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (DataLength = 0, Index = 0; Index < TxData->FragmentCount; Index++) {
+ if (TxData->FragmentTable[Index].FragmentLength == 0 || TxData->FragmentTable[Index].FragmentBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DataLength += TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ if (TxData->DataLength != DataLength) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // TODO: Token.Packet.TxData.DataLength is too short to transmit.
+ // return EFI_BUFFER_TOO_SMALL;
+ //
+
+ //
+ // If Token.Packet.TxData.DataLength is beyond the maximum that which can be
+ // described through the Fragment Offset field in Fragment header when performing
+ // fragmentation.
+ //
+ if (TxData->DataLength > 64 * 1024) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The callback function for the net buffer which wraps the user's
+ transmit token. Although this function seems simple, there
+ are some subtle aspects.
+ When user requests the IP to transmit a packet by passing it a
+ token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data
+ is wrapped in an net buffer. The net buffer's Free function is
+ set to Ip6FreeTxToken. The Token and token wrap are added to the
+ IP child's TxToken map. Then the buffer is passed to Ip6Output for
+ transmission. If an error happened before that, the buffer
+ is freed, which in turn frees the token wrap. The wrap may
+ have been added to the TxToken map or not, and the user's event
+ shouldn't be fired because we are still in the EfiIp6Transmit. If
+ the buffer has been sent by Ip6Output, it should be removed from
+ the TxToken map and user's event signaled. The token wrap and buffer
+ are bound together. Check the comments in Ip6Output for information
+ about IP fragmentation.
+
+ @param[in] Context The token's wrap.
+
+**/
+VOID
+EFIAPI
+Ip6FreeTxToken (
+ IN VOID *Context
+ )
+{
+ IP6_TXTOKEN_WRAP *Wrap;
+ NET_MAP_ITEM *Item;
+
+ Wrap = (IP6_TXTOKEN_WRAP *) Context;
+
+ //
+ // Signal IpSecRecycleEvent to inform IPsec free the memory
+ //
+ if (Wrap->IpSecRecycleSignal != NULL) {
+ gBS->SignalEvent (Wrap->IpSecRecycleSignal);
+ }
+
+ //
+ // Find the token in the instance's map. EfiIp6Transmit put the
+ // token to the map. If that failed, NetMapFindKey will return NULL.
+ //
+ Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token);
+
+ if (Item != NULL) {
+ NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL);
+ }
+
+ if (Wrap->Sent) {
+ gBS->SignalEvent (Wrap->Token->Event);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.
+ //
+ DispatchDpc ();
+ }
+
+ FreePool (Wrap);
+}
+
+
+/**
+ The callback function to Ip6Output to update the transmit status.
+
+ @param[in] Packet The user's transmit packet.
+ @param[in] IoStatus The result of the transmission.
+ @param[in] Flag Not used during transmission.
+ @param[in] Context The token's wrap.
+
+**/
+VOID
+Ip6OnPacketSent (
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 Flag,
+ IN VOID *Context
+ )
+{
+ IP6_TXTOKEN_WRAP *Wrap;
+
+ //
+ // This is the transmission request from upper layer,
+ // not the IP6 driver itself.
+ //
+ Wrap = (IP6_TXTOKEN_WRAP *) Context;
+ Wrap->Token->Status = IoStatus;
+
+ NetbufFree (Wrap->Packet);
+}
+
+/**
+ Places outgoing data packets into the transmit queue.
+
+ The Transmit() function places a sending request in the transmit queue of this
+ EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some
+ errors occur, the event in the token will be signaled, and the status is updated.
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Token Pointer to the transmit token.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing
+ a source address for this transmission,
+ but no source address was available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Event is NULL.
+ - Token.Packet.TxData is NULL.
+ - Token.Packet.ExtHdrsLength is not zero and
+ Token.Packet.ExtHdrs is NULL.
+ - Token.Packet.FragmentCount is zero.
+ - One or more of the Token.Packet.TxData.
+ FragmentTable[].FragmentLength fields is zero.
+ - One or more of the Token.Packet.TxData.
+ FragmentTable[].FragmentBuffer fields is NULL.
+ - Token.Packet.TxData.DataLength is zero or not
+ equal to the sum of fragment lengths.
+ - Token.Packet.TxData.DestinationAddress is non
+ zero when DestinationAddress is configured as
+ non-zero when doing Configure() for this
+ EFI IPv6 protocol instance.
+ - Token.Packet.TxData.DestinationAddress is
+ unspecified when DestinationAddress is unspecified
+ when doing Configure() for this EFI IPv6 protocol
+ instance.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.
+ Event was already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_NOT_FOUND Not route is found to destination address.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too
+ short to transmit.
+ @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the
+ maximum that which can be described through the
+ Fragment Offset field in Fragment header when
+ performing fragmentation.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Transmit (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_COMPLETION_TOKEN *Token
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_PROTOCOL *IpInstance;
+ EFI_IP6_CONFIG_DATA *Config;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_IP6_HEADER Head;
+ EFI_IP6_TRANSMIT_DATA *TxData;
+ EFI_IP6_OVERRIDE_DATA *Override;
+ IP6_TXTOKEN_WRAP *Wrap;
+ UINT8 *ExtHdrs;
+
+ //
+ // Check input parameters.
+ //
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ExtHdrs = NULL;
+
+ Status = Ip6TxTokenValid (Token);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP6_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Config = &IpInstance->ConfigData;
+
+ //
+ // Check whether the token or signal already existed.
+ //
+ if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip6TokenExist, Token))) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ //
+ // Build the IP header, fill in the information from ConfigData or OverrideData
+ //
+ ZeroMem (&Head, sizeof(EFI_IP6_HEADER));
+ TxData = Token->Packet.TxData;
+ IP6_COPY_ADDRESS (&Head.SourceAddress, &Config->StationAddress);
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, &Config->DestinationAddress);
+
+ Status = EFI_INVALID_PARAMETER;
+
+ if (NetIp6IsUnspecifiedAddr (&TxData->DestinationAddress)) {
+ if (NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {
+ goto Exit;
+ }
+
+ ASSERT (!NetIp6IsUnspecifiedAddr (&Config->StationAddress));
+
+ } else {
+ //
+ // StationAddress is unspecified only when ConfigData.Dest is unspecified.
+ // Use TxData.Dest to override the DestinationAddress.
+ //
+ if (!NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) {
+ goto Exit;
+ }
+
+ if (NetIp6IsUnspecifiedAddr (&Config->StationAddress)) {
+ Status = Ip6SelectSourceAddress (
+ IpSb,
+ &TxData->DestinationAddress,
+ &Head.SourceAddress
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, &TxData->DestinationAddress);
+ }
+
+ //
+ // Fill in Head infos.
+ //
+ Head.NextHeader = Config->DefaultProtocol;
+ if (TxData->ExtHdrsLength != 0) {
+ Head.NextHeader = TxData->NextHeader;
+ }
+
+ if (TxData->OverrideData != NULL) {
+ Override = TxData->OverrideData;
+ Head.NextHeader = Override->Protocol;
+ Head.HopLimit = Override->HopLimit;
+ Head.FlowLabelL = HTONS ((UINT16) Override->FlowLabel);
+ Head.FlowLabelH = (UINT8) ((Override->FlowLabel >> 16) & 0x0F);
+ } else {
+ Head.HopLimit = Config->HopLimit;
+ Head.FlowLabelL = HTONS ((UINT16) Config->FlowLabel);
+ Head.FlowLabelH = (UINT8) ((Config->FlowLabel >> 16) & 0x0F);
+ }
+
+ Head.PayloadLength = HTONS ((UINT16) (TxData->ExtHdrsLength + TxData->DataLength));
+
+ //
+ // OK, it survives all the validation check. Wrap the token in
+ // a IP6_TXTOKEN_WRAP and the data in a netbuf
+ //
+ Status = EFI_OUT_OF_RESOURCES;
+ Wrap = AllocateZeroPool (sizeof (IP6_TXTOKEN_WRAP));
+ if (Wrap == NULL) {
+ goto Exit;
+ }
+
+ Wrap->IpInstance = IpInstance;
+ Wrap->Token = Token;
+ Wrap->Sent = FALSE;
+ Wrap->Life = IP6_US_TO_SEC (Config->TransmitTimeout);
+ Wrap->Packet = NetbufFromExt (
+ (NET_FRAGMENT *) TxData->FragmentTable,
+ TxData->FragmentCount,
+ IP6_MAX_HEADLEN,
+ 0,
+ Ip6FreeTxToken,
+ Wrap
+ );
+
+ if (Wrap->Packet == NULL) {
+ FreePool (Wrap);
+ goto Exit;
+ }
+
+ Token->Status = EFI_NOT_READY;
+
+ Status = NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap);
+ if (EFI_ERROR (Status)) {
+ //
+ // NetbufFree will call Ip6FreeTxToken, which in turn will
+ // free the IP6_TXTOKEN_WRAP. Now, the token wrap hasn't been
+ // enqueued.
+ //
+ NetbufFree (Wrap->Packet);
+ goto Exit;
+ }
+
+ //
+ // Allocate a new buffer to store IPv6 extension headers to avoid updating
+ // the original data in EFI_IP6_COMPLETION_TOKEN.
+ //
+ if (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs != NULL) {
+ ExtHdrs = (UINT8 *) AllocateCopyPool (TxData->ExtHdrsLength, TxData->ExtHdrs);
+ if (ExtHdrs == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ }
+
+ //
+ // Mark the packet sent before output it. Mark it not sent again if the
+ // returned status is not EFI_SUCCESS;
+ //
+ Wrap->Sent = TRUE;
+
+ Status = Ip6Output (
+ IpSb,
+ NULL,
+ IpInstance,
+ Wrap->Packet,
+ &Head,
+ ExtHdrs,
+ TxData->ExtHdrsLength,
+ Ip6OnPacketSent,
+ Wrap
+ );
+ if (EFI_ERROR (Status)) {
+ Wrap->Sent = FALSE;
+ NetbufFree (Wrap->Packet);
+ }
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+
+ if (ExtHdrs != NULL) {
+ FreePool (ExtHdrs);
+ }
+
+ return Status;
+}
+
+/**
+ Places a receiving request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous.
+
+ The Token.Event field in the completion token must be filled in by the caller
+ and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event
+ is signaled.
+
+ Current Udp implementation creates an IP child for each Udp child.
+ It initates a asynchronous receive immediately no matter whether
+ there is no mapping or not. Therefore, disable the returning EFI_NO_MAPPING for now.
+ To enable it, the following check must be performed:
+
+ if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance,
+ while no source address is available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system
+ resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI IPv6 Protocol instance has been reset to startup defaults.
+ @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already
+ in the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Receive (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_COMPLETION_TOKEN *Token
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ IP6_SERVICE *IpSb;
+
+ if (This == NULL || Token == NULL || Token->Event == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP6_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ //
+ // Check whether the toke is already on the receive queue.
+ //
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip6TokenExist, Token);
+
+ if (EFI_ERROR (Status)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ //
+ // Queue the token then check whether there is pending received packet.
+ //
+ Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL);
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = Ip6InstanceDeliverPacket (IpInstance);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of this instane's receive
+ // event.
+ //
+ DispatchDpc ();
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Cancel the transmitted but not recycled packet. If a matching
+ token is found, it will call Ip6CancelPacket to cancel the
+ packet. Ip6CancelPacket cancels all the fragments of the
+ packet. When all the fragments are freed, the IP6_TXTOKEN_WRAP
+ is deleted from the Map, and user's event is signalled.
+ Because Ip6CancelPacket and other functions are all called in
+ line, after Ip6CancelPacket returns, the Item has been freed.
+
+ @param[in] Map The IP6 child's transmit queue.
+ @param[in] Item The current transmitted packet to test.
+ @param[in] Context The user's token to cancel.
+
+ @retval EFI_SUCCESS Continue to check the next Item.
+ @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6CancelTxTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP6_COMPLETION_TOKEN *Token;
+ IP6_TXTOKEN_WRAP *Wrap;
+
+ Token = (EFI_IP6_COMPLETION_TOKEN *) Context;
+
+ //
+ // Return EFI_SUCCESS to check the next item in the map if
+ // this one doesn't match.
+ //
+ if ((Token != NULL) && (Token != Item->Key)) {
+ return EFI_SUCCESS;
+ }
+
+ Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;
+ ASSERT (Wrap != NULL);
+
+ //
+ // Don't access the Item, Wrap and Token's members after this point.
+ // Item and wrap has been freed. And we no longer own the Token.
+ //
+ Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
+
+ //
+ // If only one item is to be cancel, return EFI_ABORTED to stop
+ // iterating the map any more.
+ //
+ if (Token != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cancel the receive request. This is simple, because
+ it is only enqueued in our local receive map.
+
+ @param[in] Map The IP6 child's receive queue.
+ @param[in] Item Current receive request to cancel.
+ @param[in] Context The user's token to cancel.
+
+
+ @retval EFI_SUCCESS Continue to check the next receive request on the
+ queue.
+ @retval EFI_ABORTED The user's token (token != NULL) has been
+ cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6CancelRxTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_IP6_COMPLETION_TOKEN *Token;
+ EFI_IP6_COMPLETION_TOKEN *This;
+
+ Token = (EFI_IP6_COMPLETION_TOKEN *) Context;
+ This = Item->Key;
+
+ if ((Token != NULL) && (Token != This)) {
+ return EFI_SUCCESS;
+ }
+
+ NetMapRemoveItem (Map, Item, NULL);
+
+ This->Status = EFI_ABORTED;
+ This->Packet.RxData = NULL;
+ gBS->SignalEvent (This->Event);
+
+ if (Token != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Cancel the user's receive/transmit request. It is the worker function of
+ EfiIp6Cancel API.
+
+ @param[in] IpInstance The IP6 child.
+ @param[in] Token The token to cancel. If NULL, all token will be
+ cancelled.
+
+ @retval EFI_SUCCESS The token is cancelled.
+ @retval EFI_NOT_FOUND The token isn't found on either the
+ transmit/receive queue.
+ @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL.
+
+**/
+EFI_STATUS
+Ip6Cancel (
+ IN IP6_PROTOCOL *IpInstance,
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // First check the transmitted packet. Ip6CancelTxTokens returns
+ // EFI_ABORTED to mean that the token has been cancelled when
+ // token != NULL. So, return EFI_SUCCESS for this condition.
+ //
+ Status = NetMapIterate (&IpInstance->TxTokens, Ip6CancelTxTokens, Token);
+ if (EFI_ERROR (Status)) {
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ //
+ // Check the receive queue. Ip6CancelRxTokens also returns EFI_ABORT
+ // for Token!=NULL and it is cancelled.
+ //
+ Status = NetMapIterate (&IpInstance->RxTokens, Ip6CancelRxTokens, Token);
+ //
+ // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's
+ // events.
+ //
+ DispatchDpc ();
+ if (EFI_ERROR (Status)) {
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ //
+ // OK, if the Token is found when Token != NULL, the NetMapIterate
+ // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS.
+ //
+ if (Token != NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If Token == NULL, cancel all the tokens. return error if not
+ // all of them are cancelled.
+ //
+ if (!NetMapIsEmpty (&IpInstance->TxTokens) || !NetMapIsEmpty (&IpInstance->RxTokens)) {
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Abort an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token->Status will be set to EFI_ABORTED, and then Token->Event will
+ be signaled. If the token is not in one of the queues, which usually means the
+ asynchronous operation has completed, this function will not signal the token,
+ and EFI_NOT_FOUND is returned.
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_IP6_PROTOCOL.Transmit() or
+ EFI_IP6_PROTOCOL.Receive(). If NULL, all pending
+ tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is
+ defined in EFI_IP6_PROTOCOL.Transmit().
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and
+ Token->Event was signaled. When Token is NULL, all
+ pending requests were aborted, and their events were signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was
+ not found in the transmit or receive queue. It has either completed
+ or was not issued by Transmit() and Receive().
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Cancel (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ IP6_SERVICE *IpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpInstance->State != IP6_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Status = Ip6Cancel (IpInstance, Token);
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Polls for incoming data packets, and processes outgoing data packets.
+
+ The Poll() function polls for incoming data packets and processes outgoing data
+ packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()
+ function to increase the rate that data packets are moved between the communications
+ device and the transmit and receive queues.
+
+ In some systems the periodic timer event may not poll the underlying communications
+ device fast enough to transmit and/or receive all data packets without missing
+ incoming packets or dropping outgoing packets. Drivers and applications that are
+ experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function
+ more often.
+
+ @param[in] This Pointer to the EFI_IP6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Poll (
+ IN EFI_IP6_PROTOCOL *This
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ IP6_SERVICE *IpSb;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This);
+ IpSb = IpInstance->Service;
+
+ if (IpSb->LinkLocalDadFail) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (IpInstance->State == IP6_STATE_UNCONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ Mnp = IpInstance->Service->Mnp;
+
+ //
+ // Don't lock the Poll function to enable the deliver of
+ // the packet polled up.
+ //
+ return Mnp->Poll (Mnp);
+
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Impl.h b/NetworkPkg/Ip6Dxe/Ip6Impl.h
new file mode 100644
index 0000000000..524de5e256
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Impl.h
@@ -0,0 +1,751 @@
+/** @file
+ Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_IMPL_H__
+#define __EFI_IP6_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/IpSec.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/HiiConfigRouting.h>
+#include <Protocol/HiiConfigAccess.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DpcLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiHiiServicesLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+
+#include <Guid/MdeModuleHii.h>
+
+#include "Ip6Common.h"
+#include "Ip6Driver.h"
+#include "Ip6Icmp.h"
+#include "Ip6If.h"
+#include "Ip6Input.h"
+#include "Ip6Mld.h"
+#include "Ip6Nd.h"
+#include "Ip6Option.h"
+#include "Ip6Output.h"
+#include "Ip6Route.h"
+#include "Ip6ConfigNv.h"
+#include "Ip6ConfigImpl.h"
+
+#define IP6_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'P')
+#define IP6_SERVICE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'S')
+
+//
+// The state of IP6 protocol. It starts from UNCONFIGED. if it is
+// successfully configured, it goes to CONFIGED. if configure NULL
+// is called, it becomes UNCONFIGED again. If (partly) destroyed, it
+// becomes DESTROY.
+//
+#define IP6_STATE_UNCONFIGED 0
+#define IP6_STATE_CONFIGED 1
+#define IP6_STATE_DESTROY 2
+
+//
+// The state of IP6 service. It starts from UNSTARTED. It transits
+// to STARTED if autoconfigure is started. If default address is
+// configured, it becomes CONFIGED. and if partly destroyed, it goes
+// to DESTROY.
+//
+#define IP6_SERVICE_UNSTARTED 0
+#define IP6_SERVICE_STARTED 1
+#define IP6_SERVICE_CONFIGED 2
+#define IP6_SERVICE_DESTROY 3
+
+#define IP6_INSTANCE_FROM_PROTOCOL(Ip6) \
+ CR ((Ip6), IP6_PROTOCOL, Ip6Proto, IP6_PROTOCOL_SIGNATURE)
+
+#define IP6_SERVICE_FROM_PROTOCOL(Sb) \
+ CR ((Sb), IP6_SERVICE, ServiceBinding, IP6_SERVICE_SIGNATURE)
+
+#define IP6_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured)
+
+extern EFI_IPSEC_PROTOCOL *mIpSec;
+
+//
+// IP6_TXTOKEN_WRAP wraps the upper layer's transmit token.
+// The user's data is kept in the Packet. When fragment is
+// needed, each fragment of the Packet has a reference to the
+// Packet, no data is actually copied. The Packet will be
+// released when all the fragments of it have been recycled by
+// MNP. Upon then, the IP6_TXTOKEN_WRAP will be released, and
+// user's event signalled.
+//
+typedef struct {
+ IP6_PROTOCOL *IpInstance;
+ EFI_IP6_COMPLETION_TOKEN *Token;
+ EFI_EVENT IpSecRecycleSignal;
+ NET_BUF *Packet;
+ BOOLEAN Sent;
+ INTN Life;
+} IP6_TXTOKEN_WRAP;
+
+typedef struct {
+ EFI_EVENT IpSecRecycleSignal;
+ NET_BUF *Packet;
+} IP6_IPSEC_WRAP;
+
+//
+// IP6_RXDATA_WRAP wraps the data IP6 child delivers to the
+// upper layers. The received packet is kept in the Packet.
+// The Packet itself may be constructured from some fragments.
+// All the fragments of the Packet is organized by a
+// IP6_ASSEMBLE_ENTRY structure. If the Packet is recycled by
+// the upper layer, the assemble entry and its associated
+// fragments will be freed at last.
+//
+typedef struct {
+ LIST_ENTRY Link;
+ IP6_PROTOCOL *IpInstance;
+ NET_BUF *Packet;
+ EFI_IP6_RECEIVE_DATA RxData;
+} IP6_RXDATA_WRAP;
+
+struct _IP6_PROTOCOL {
+ UINT32 Signature;
+
+ EFI_IP6_PROTOCOL Ip6Proto;
+ EFI_HANDLE Handle;
+ INTN State;
+
+ IP6_SERVICE *Service;
+ LIST_ENTRY Link; // Link to all the IP protocol from the service
+
+ UINT8 PrefixLength; // PrefixLength of the configured station address.
+ //
+ // User's transmit/receive tokens, and received/deliverd packets
+ //
+ NET_MAP RxTokens;
+ NET_MAP TxTokens; // map between (User's Token, IP6_TXTOKE_WRAP)
+ LIST_ENTRY Received; // Received but not delivered packet
+ LIST_ENTRY Delivered; // Delivered and to be recycled packets
+ EFI_LOCK RecycleLock;
+
+ IP6_INTERFACE *Interface;
+ LIST_ENTRY AddrLink; // Ip instances with the same IP address.
+
+ EFI_IPv6_ADDRESS *GroupList; // stored in network order.
+ UINT32 GroupCount;
+
+ EFI_IP6_CONFIG_DATA ConfigData;
+};
+
+struct _IP6_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ INTN State;
+ BOOLEAN InDestroy;
+
+ //
+ // List of all the IP instances and interfaces, and default
+ // interface and route table and caches.
+ //
+ UINTN NumChildren;
+ LIST_ENTRY Children;
+
+ LIST_ENTRY Interfaces;
+
+ IP6_INTERFACE *DefaultInterface;
+ IP6_ROUTE_TABLE *RouteTable;
+
+ IP6_LINK_RX_TOKEN RecvRequest;
+
+ //
+ // Ip reassemble utilities and MLD data
+ //
+ IP6_ASSEMBLE_TABLE Assemble;
+ IP6_MLD_SERVICE_DATA MldCtrl;
+
+ EFI_IPv6_ADDRESS LinkLocalAddr;
+ BOOLEAN LinkLocalOk;
+ BOOLEAN LinkLocalDadFail;
+ BOOLEAN Dhcp6NeedStart;
+ BOOLEAN Dhcp6NeedInfoRequest;
+
+ //
+ // ND data
+ //
+ UINT8 CurHopLimit;
+ UINT32 LinkMTU;
+ UINT32 BaseReachableTime;
+ UINT32 ReachableTime;
+ UINT32 RetransTimer;
+ LIST_ENTRY NeighborTable;
+
+ LIST_ENTRY OnlinkPrefix;
+ LIST_ENTRY AutonomousPrefix;
+
+ LIST_ENTRY DefaultRouterList;
+ UINT32 RoundRobin;
+
+ UINT8 InterfaceIdLen;
+ UINT8 *InterfaceId;
+
+ BOOLEAN RouterAdvertiseReceived;
+ UINT8 SolicitTimer;
+ UINT32 Ticks;
+
+ //
+ // Low level protocol used by this service instance
+ //
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+
+ EFI_HANDLE MnpChildHandle;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+
+ EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+
+ EFI_EVENT Timer;
+ EFI_EVENT FasterTimer;
+
+ //
+ // IPv6 Configuration Protocol instance
+ //
+ IP6_CONFIG_INSTANCE Ip6ConfigInstance;
+
+ //
+ // The string representation of the current mac address of the
+ // NIC this IP6_SERVICE works on.
+ //
+ CHAR16 *MacString;
+ UINT32 MaxPacketSize;
+ UINT32 OldMaxPacketSize;
+};
+
+/**
+ The callback function for the net buffer which wraps the user's
+ transmit token. Although this function seems simple,
+ there are some subtle aspects.
+ When a user requests the IP to transmit a packet by passing it a
+ token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data
+ is wrapped in a net buffer. The net buffer's Free function is
+ set to Ip6FreeTxToken. The Token and token wrap are added to the
+ IP child's TxToken map. Then the buffer is passed to Ip6Output for
+ transmission. If an error occurs before that, the buffer
+ is freed, which in turn frees the token wrap. The wrap may
+ have been added to the TxToken map or not, and the user's event
+ shouldn't be signaled because we are still in the EfiIp6Transmit. If
+ the buffer has been sent by Ip6Output, it should be removed from
+ the TxToken map and the user's event signaled. The token wrap and buffer
+ are bound together. Refer to the comments in Ip6Output for information
+ about IP fragmentation.
+
+ @param[in] Context The token's wrap.
+
+**/
+VOID
+EFIAPI
+Ip6FreeTxToken (
+ IN VOID *Context
+ );
+
+/**
+ Config the MNP parameter used by IP. The IP driver use one MNP
+ child to transmit/receive frames. By default, it configures MNP
+ to receive unicast/multicast/broadcast. And it will enable/disable
+ the promiscuous receive according to whether there is IP child
+ enable that or not. If Force is FALSE, it will iterate through
+ all the IP children to check whether the promiscuous receive
+ setting has been changed. If it hasn't been changed, it won't
+ reconfigure the MNP. If Force is TRUE, the MNP is configured
+ whether that is changed or not.
+
+ @param[in] IpSb The IP6 service instance that is to be changed.
+ @param[in] Force Force the configuration or not.
+
+ @retval EFI_SUCCESS The MNP successfully configured/reconfigured.
+ @retval Others The configuration failed.
+
+**/
+EFI_STATUS
+Ip6ServiceConfigMnp (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN Force
+ );
+
+/**
+ Cancel the user's receive/transmit request. It is the worker function of
+ EfiIp6Cancel API.
+
+ @param[in] IpInstance The IP6 child.
+ @param[in] Token The token to cancel. If NULL, all tokens will be
+ cancelled.
+
+ @retval EFI_SUCCESS The token was cancelled.
+ @retval EFI_NOT_FOUND The token isn't found on either the
+ transmit or receive queue.
+ @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL.
+
+**/
+EFI_STATUS
+Ip6Cancel (
+ IN IP6_PROTOCOL *IpInstance,
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Initialize the IP6_PROTOCOL structure to the unconfigured states.
+
+ @param[in] IpSb The IP6 service instance.
+ @param[in, out] IpInstance The IP6 child instance.
+
+**/
+VOID
+Ip6InitProtocol (
+ IN IP6_SERVICE *IpSb,
+ IN OUT IP6_PROTOCOL *IpInstance
+ );
+
+/**
+ Clean up the IP6 child, release all the resources used by it.
+
+ @param[in, out] IpInstance The IP6 child to clean up.
+
+ @retval EFI_SUCCESS The IP6 child was cleaned up
+ @retval EFI_DEVICE_ERROR Some resources failed to be released.
+
+**/
+EFI_STATUS
+Ip6CleanProtocol (
+ IN OUT IP6_PROTOCOL *IpInstance
+ );
+
+//
+// EFI_IP6_PROTOCOL interface prototypes
+//
+
+/**
+ Gets the current operational settings for this instance of the EFI IPv6 Protocol driver.
+
+ The GetModeData() function returns the current operational mode data for this driver instance.
+ The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to
+ retrieve the operational mode data of underlying networks or drivers.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[out] Ip6ModeData The pointer to the EFI IPv6 Protocol mode data structure.
+ @param[out] MnpConfigData The pointer to the managed network configuration data structure.
+ @param[out] SnpModeData The pointer to the simple network mode data structure.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6GetModeData (
+ IN EFI_IP6_PROTOCOL *This,
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+/**
+ Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance.
+
+ The Configure() function is used to set, change, or reset the operational parameters and filter
+ settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic
+ can be sent or received by this instance. Once the parameters have been reset (by calling this
+ function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these
+ parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped
+ independently of each other by enabling or disabling their receive filter settings with the
+ Configure() function.
+
+ If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required
+ to be one of the currently configured IPv6 addresses list in the EFI IPv6 drivers, or else
+ EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is
+ unspecified, the IPv6 driver will bind a source address according to the source address selection
+ algorithm. Clients could frequently call GetModeData() to check get a currently configured IPv6.
+ If both Ip6ConfigData.StationAddress and Ip6ConfigData.Destination are unspecified, when
+ transmitting the packet afterwards, the source address filled in each outgoing IPv6 packet
+ is decided based on the destination of this packet.
+
+ If operational parameters are reset or changed, any pending transmit and receive requests will be
+ cancelled. Their completion token status will be set to EFI_ABORTED, and their events will be
+ signaled.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Ip6ConfigData The pointer to the EFI IPv6 Protocol configuration data structure.
+ If NULL, reset the configuration data.
+
+ @retval EFI_SUCCESS The driver instance was successfully opened.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Ip6ConfigData.StationAddress is neither zero nor
+ a unicast IPv6 address.
+ - Ip6ConfigData.StationAddress is neither zero nor
+ one of the configured IP addresses in the EFI IPv6 driver.
+ - Ip6ConfigData.DefaultProtocol is illegal.
+ @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated.
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for
+ this instance, but no source address was available for use.
+ @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6
+ address or prefix length can be changed.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6
+ Protocol driver instance was not opened.
+ @retval EFI_UNSUPPORTED Default protocol specified through
+ Ip6ConfigData.DefaulProtocol isn't supported.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Configure (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL
+ );
+
+/**
+ Joins and leaves multicast groups.
+
+ The Groups() function is used to join and leave multicast group sessions. Joining a group will
+ enable reception of matching multicast packets. Leaving a group will disable reception of matching
+ multicast packets. Source-Specific Multicast isn't required to be supported.
+
+ If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave.
+ @param[in] GroupAddress The pointer to the IPv6 multicast address.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:
+ - This is NULL.
+ - JoinFlag is TRUE and GroupAddress is NULL.
+ - GroupAddress is not NULL and *GroupAddress is
+ not a multicast IPv6 address.
+ - GroupAddress is not NULL and *GroupAddress is in the
+ range of SSM destination address.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_OUT_OF_RESOURCES System resources could not be allocated.
+ @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table (when
+ JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Groups (
+ IN EFI_IP6_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL
+ );
+
+/**
+ Adds and deletes routing table entries.
+
+ The Routes() function adds a route to or deletes a route from the routing table.
+
+ Routes are determined by comparing the leftmost PrefixLength bits of Destination with
+ the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the
+ configured station address.
+
+ The default route is added with Destination and PrefixLegth both set to all zeros. The
+ default route matches all destination IPv6 addresses that do not match any other routes.
+
+ All EFI IPv6 Protocol instances share a routing table.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to
+ FALSE to add this route to the routing table. Destination,
+ PrefixLength and Gateway are used as the key to each
+ route entry.
+ @param[in] Destination The address prefix of the subnet that needs to be routed.
+ This is an optional parameter that may be NULL.
+ @param[in] PrefixLength The prefix length of Destination. Ignored if Destination
+ is NULL.
+ @param[in] GatewayAddress The unicast gateway IPv6 address for this route.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The driver instance has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - When DeleteRoute is TRUE, both Destination and
+ GatewayAddress are NULL.
+ - When DeleteRoute is FALSE, either Destination or
+ GatewayAddress is NULL.
+ - *GatewayAddress is not a valid unicast IPv6 address.
+ - *GatewayAddress is one of the local configured IPv6
+ addresses.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE).
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when
+ DeleteRoute is FALSE).
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Routes (
+ IN EFI_IP6_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL
+ );
+
+/**
+ Add or delete Neighbor cache entries.
+
+ The Neighbors() function is used to add, update, or delete an entry from a neighbor cache.
+ IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as
+ network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network
+ traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not
+ timeout) or dynamic (will timeout).
+
+ The implementation should follow the neighbor cache timeout mechanism defined in
+ RFC4861. The default neighbor cache timeout value should be tuned for the expected network
+ environment.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] DeleteFlag Set to TRUE to delete the specified cache entry. Set to FALSE to
+ add (or update, if it already exists and Override is TRUE) the
+ specified cache entry. TargetIp6Address is used as the key
+ to find the requested cache entry.
+ @param[in] TargetIp6Address The pointer to the Target IPv6 address.
+ @param[in] TargetLinkAddress The pointer to link-layer address of the target. Ignored if NULL.
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
+ cache, it will be deleted after Timeout. A value of zero means that
+ the entry is permanent. A non-zero value means that the entry is
+ dynamic.
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will
+ be overridden and updated; if FALSE, EFI_ACCESS_DENIED
+ will be returned if a corresponding cache entry already exists.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - TargetIpAddress is NULL.
+ - *TargetLinkAddress is invalid when not NULL.
+ - *TargetIpAddress is not a valid unicast IPv6 address.
+ - *TargetIpAddress is one of the local configured IPv6
+ addresses.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache.
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is
+ TRUE or when DeleteFlag is FALSE while
+ TargetLinkAddress is NULL.).
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,
+ and that entry is tagged as un-overridden (when Override
+ is FALSE).
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Neighbors (
+ IN EFI_IP6_PROTOCOL *This,
+ IN BOOLEAN DeleteFlag,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
+ IN UINT32 Timeout,
+ IN BOOLEAN Override
+ );
+
+/**
+ Places outgoing data packets into the transmit queue.
+
+ The Transmit() function places a sending request in the transmit queue of this
+ EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some
+ errors occur, the event in the token will be signaled and the status is updated.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Token The pointer to the transmit token.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing
+ a source address for this transmission,
+ but no source address was available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Event is NULL.
+ - Token.Packet.TxData is NULL.
+ - Token.Packet.ExtHdrsLength is not zero and
+ Token.Packet.ExtHdrs is NULL.
+ - Token.Packet.FragmentCount is zero.
+ - One or more of the Token.Packet.TxData.
+ FragmentTable[].FragmentLength fields is zero.
+ - One or more of the Token.Packet.TxData.
+ FragmentTable[].FragmentBuffer fields is NULL.
+ - Token.Packet.TxData.DataLength is zero or not
+ equal to the sum of fragment lengths.
+ - Token.Packet.TxData.DestinationAddress is non-
+ zero when DestinationAddress is configured as
+ non-zero when doing Configure() for this
+ EFI IPv6 protocol instance.
+ - Token.Packet.TxData.DestinationAddress is
+ unspecified when DestinationAddress is unspecified
+ when doing Configure() for this EFI IPv6 protocol
+ instance.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.
+ The event was already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_NOT_FOUND Not route is found to the destination address.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too
+ short to transmit.
+ @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the
+ maximum that which can be described through the
+ Fragment Offset field in Fragment header when
+ performing fragmentation.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Transmit (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Places a receiving request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue.
+ This function is always asynchronous.
+
+ The Token.Event field in the completion token must be filled in by the caller
+ and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol
+ driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event
+ is signaled.
+
+ Current Udp implementation creates an IP child for each Udp child.
+ It initates a asynchronous receive immediately whether or not
+ there is no mapping. Therefore, disable the returning EFI_NO_MAPPING for now.
+ To enable it, the following check must be performed:
+
+ if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Token The pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance,
+ while no source address is available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system
+ resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI IPv6 Protocol instance has been reset to startup defaults.
+ @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already
+ in the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Receive (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Abort an asynchronous transmit or receive request.
+
+ The Cancel() function is used to abort a pending transmit or receive request.
+ If the token is in the transmit or receive request queues, after calling this
+ function, Token->Status will be set to EFI_ABORTED, and then Token->Event will
+ be signaled. If the token is not in one of the queues, which usually means the
+ asynchronous operation has completed, this function will not signal the token,
+ and EFI_NOT_FOUND is returned.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+ @param[in] Token The pointer to a token that has been issued by
+ EFI_IP6_PROTOCOL.Transmit() or
+ EFI_IP6_PROTOCOL.Receive(). If NULL, all pending
+ tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is
+ defined in EFI_IP6_PROTOCOL.Transmit().
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and
+ Token->Event was signaled. When Token is NULL, all
+ pending requests were aborted, and their events were signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was
+ not found in the transmit or receive queue. It has either completed
+ or was not issued by Transmit() and Receive().
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Cancel (
+ IN EFI_IP6_PROTOCOL *This,
+ IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function polls for incoming data packets and processes outgoing data
+ packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll()
+ function to increase the rate that data packets are moved between the communications
+ device and the transmit and receive queues.
+
+ In some systems the periodic timer event may not poll the underlying communications
+ device fast enough to transmit and/or receive all data packets without missing
+ incoming packets or dropping outgoing packets. Drivers and applications that are
+ experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function
+ more often.
+
+ @param[in] This The pointer to the EFI_IP6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIp6Poll (
+ IN EFI_IP6_PROTOCOL *This
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.c b/NetworkPkg/Ip6Dxe/Ip6Input.c
new file mode 100644
index 0000000000..c18811b611
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Input.c
@@ -0,0 +1,1710 @@
+/** @file
+ IP6 internal functions to process the incoming packets.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+/**
+ Create an empty assemble entry for the packet identified by
+ (Dst, Src, Id). The default life for the packet is 60 seconds.
+
+ @param[in] Dst The destination address.
+ @param[in] Src The source address.
+ @param[in] Id The ID field in the IP header.
+
+ @return NULL if failed to allocate memory for the entry. Otherwise,
+ the pointer to the just created reassemble entry.
+
+**/
+IP6_ASSEMBLE_ENTRY *
+Ip6CreateAssembleEntry (
+ IN EFI_IPv6_ADDRESS *Dst,
+ IN EFI_IPv6_ADDRESS *Src,
+ IN UINT32 Id
+ )
+{
+ IP6_ASSEMBLE_ENTRY *Assemble;
+
+ Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY));
+ if (Assemble == NULL) {
+ return NULL;
+ }
+
+ IP6_COPY_ADDRESS (&Assemble->Dst, Dst);
+ IP6_COPY_ADDRESS (&Assemble->Src, Src);
+ InitializeListHead (&Assemble->Fragments);
+
+ Assemble->Id = Id;
+ Assemble->Life = IP6_FRAGMENT_LIFE + 1;
+
+ Assemble->TotalLen = 0;
+ Assemble->CurLen = 0;
+ Assemble->Head = NULL;
+ Assemble->Info = NULL;
+ Assemble->Packet = NULL;
+
+ return Assemble;
+}
+
+/**
+ Release all the fragments of a packet, then free the assemble entry.
+
+ @param[in] Assemble The assemble entry to free.
+
+**/
+VOID
+Ip6FreeAssembleEntry (
+ IN IP6_ASSEMBLE_ENTRY *Assemble
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ NET_BUF *Fragment;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {
+ Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ RemoveEntryList (Entry);
+ NetbufFree (Fragment);
+ }
+
+ if (Assemble->Packet != NULL) {
+ NetbufFree (Assemble->Packet);
+ }
+
+ FreePool (Assemble);
+}
+
+/**
+ Release all the fragments of the packet. This is the callback for
+ the assembled packet's OnFree. It will free the assemble entry,
+ which in turn frees all the fragments of the packet.
+
+ @param[in] Arg The assemble entry to free.
+
+**/
+VOID
+EFIAPI
+Ip6OnFreeFragments (
+ IN VOID *Arg
+ )
+{
+ Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg);
+}
+
+/**
+ Trim the packet to fit in [Start, End), and update per the
+ packet information.
+
+ @param[in, out] Packet Packet to trim.
+ @param[in] Start The sequence of the first byte to fit in.
+ @param[in] End One beyond the sequence of last byte to fit in.
+
+**/
+VOID
+Ip6TrimPacket (
+ IN OUT NET_BUF *Packet,
+ IN INTN Start,
+ IN INTN End
+ )
+{
+ IP6_CLIP_INFO *Info;
+ INTN Len;
+
+ Info = IP6_GET_CLIP_INFO (Packet);
+
+ ASSERT (Info->Start + Info->Length == Info->End);
+ ASSERT ((Info->Start < End) && (Start < Info->End));
+
+ if (Info->Start < Start) {
+ Len = Start - Info->Start;
+
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);
+ Info->Start = (UINT32) Start;
+ Info->Length -= (UINT32) Len;
+ }
+
+ if (End < Info->End) {
+ Len = End - Info->End;
+
+ NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);
+ Info->End = (UINT32) End;
+ Info->Length -= (UINT32) Len;
+ }
+}
+
+/**
+ Reassemble the IP fragments. If all the fragments of the packet
+ have been received, it will wrap the packet in a net buffer then
+ return it to caller. If the packet can't be assembled, NULL is
+ returned.
+
+ @param[in, out] Table The assemble table used. A new assemble entry will be created
+ if the Packet is from a new chain of fragments.
+ @param[in] Packet The fragment to assemble. It might be freed if the fragment
+ can't be re-assembled.
+
+ @return NULL if the packet can't be reassembled. The pointer to the just assembled
+ packet if all the fragments of the packet have arrived.
+
+**/
+NET_BUF *
+Ip6Reassemble (
+ IN OUT IP6_ASSEMBLE_TABLE *Table,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_IP6_HEADER *Head;
+ IP6_CLIP_INFO *This;
+ IP6_CLIP_INFO *Node;
+ IP6_ASSEMBLE_ENTRY *Assemble;
+ IP6_ASSEMBLE_ENTRY *Entry;
+ LIST_ENTRY *ListHead;
+ LIST_ENTRY *Prev;
+ LIST_ENTRY *Cur;
+ NET_BUF *Fragment;
+ NET_BUF *TmpPacket;
+ NET_BUF *NewPacket;
+ NET_BUF *Duplicate;
+ UINT8 *DupHead;
+ INTN Index;
+ UINT16 UnFragmentLen;
+ UINT8 *NextHeader;
+
+ Head = Packet->Ip.Ip6;
+ This = IP6_GET_CLIP_INFO (Packet);
+
+ ASSERT (Head != NULL);
+
+ //
+ // Find the corresponding assemble entry by (Dst, Src, Id)
+ //
+ Assemble = NULL;
+ Index = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id);
+
+ NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {
+ Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link);
+
+ if (Entry->Id == This->Id &&
+ EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) &&
+ EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress)
+ ) {
+ Assemble = Entry;
+ break;
+ }
+ }
+
+ //
+ // Create a new entry if can not find an existing one, insert it to assemble table
+ //
+ if (Assemble == NULL) {
+ Assemble = Ip6CreateAssembleEntry (
+ &Head->DestinationAddress,
+ &Head->SourceAddress,
+ This->Id
+ );
+
+ if (Assemble == NULL) {
+ goto Error;
+ }
+
+ InsertHeadList (&Table->Bucket[Index], &Assemble->Link);
+ }
+
+ //
+ // Find the point to insert the packet: before the first
+ // fragment with THIS.Start < CUR.Start. the previous one
+ // has PREV.Start <= THIS.Start < CUR.Start.
+ //
+ ListHead = &Assemble->Fragments;
+
+ NET_LIST_FOR_EACH (Cur, ListHead) {
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) {
+ break;
+ }
+ }
+
+ //
+ // Check whether the current fragment overlaps with the previous one.
+ // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to
+ // check whether THIS.Start < PREV.End for overlap. If two fragments
+ // overlaps, trim the overlapped part off THIS fragment.
+ //
+ if ((Cur != ListHead) && ((Prev = Cur->BackLink) != ListHead)) {
+ Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
+ Node = IP6_GET_CLIP_INFO (Fragment);
+
+ if (This->Start < Node->End) {
+ if (This->End <= Node->End) {
+ goto Error;
+ }
+
+ //
+ // Trim the previous fragment from tail.
+ //
+ Ip6TrimPacket (Fragment, Node->Start, This->Start);
+ }
+ }
+
+ //
+ // Insert the fragment into the packet. The fragment may be removed
+ // from the list by the following checks.
+ //
+ NetListInsertBefore (Cur, &Packet->List);
+
+ //
+ // Check the packets after the insert point. It holds that:
+ // THIS.Start <= NODE.Start < NODE.End. The equality holds
+ // if PREV and NEXT are continuous. THIS fragment may fill
+ // several holes. Remove the completely overlapped fragments
+ //
+ while (Cur != ListHead) {
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Node = IP6_GET_CLIP_INFO (Fragment);
+
+ //
+ // Remove fragments completely overlapped by this fragment
+ //
+ if (Node->End <= This->End) {
+ Cur = Cur->ForwardLink;
+
+ RemoveEntryList (&Fragment->List);
+ Assemble->CurLen -= Node->Length;
+
+ NetbufFree (Fragment);
+ continue;
+ }
+
+ //
+ // The conditions are: THIS.Start <= NODE.Start, and THIS.End <
+ // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.
+ // If two fragments start at the same offset, remove THIS fragment
+ // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).
+ //
+ if (Node->Start < This->End) {
+ if (This->Start == Node->Start) {
+ RemoveEntryList (&Packet->List);
+ goto Error;
+ }
+
+ Ip6TrimPacket (Packet, This->Start, Node->Start);
+ }
+
+ break;
+ }
+
+ //
+ // Update the assemble info: increase the current length. If it is
+ // the frist fragment, update the packet's IP head and per packet
+ // info. If it is the last fragment, update the total length.
+ //
+ Assemble->CurLen += This->Length;
+
+ if (This->Start == 0) {
+ //
+ // Once the first fragment is enqueued, it can't be removed
+ // from the fragment list. So, Assemble->Head always point
+ // to valid memory area.
+ //
+ if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) {
+ goto Error;
+ }
+
+ //
+ // Backup the first fragment in case the reasembly of that packet fail.
+ //
+ Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));
+ if (Duplicate == NULL) {
+ goto Error;
+ }
+
+ //
+ // Revert IP head to network order.
+ //
+ DupHead = NetbufGetByte (Duplicate, 0, NULL);
+ ASSERT (DupHead != NULL);
+ Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead);
+ Assemble->Packet = Duplicate;
+
+ //
+ // Adjust the unfragmentable part in first fragment
+ //
+ UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER));
+ if (UnFragmentLen == 0) {
+ //
+ // There is not any unfragmentable extension header.
+ //
+ ASSERT (Head->NextHeader == IP6_FRAGMENT);
+ Head->NextHeader = This->NextHeader;
+ } else {
+ NextHeader = NetbufGetByte (
+ Packet,
+ This->FormerNextHeader + sizeof (EFI_IP6_HEADER),
+ 0
+ );
+ if (NextHeader == NULL) {
+ goto Error;
+ }
+
+ *NextHeader = This->NextHeader;
+ }
+
+ Assemble->Head = Head;
+ Assemble->Info = IP6_GET_CLIP_INFO (Packet);
+ }
+
+ //
+ // Don't update the length more than once.
+ //
+ if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) {
+ Assemble->TotalLen = This->End;
+ }
+
+ //
+ // Deliver the whole packet if all the fragments received.
+ // All fragments received if:
+ // 1. received the last one, so, the totoal length is know
+ // 2. received all the data. If the last fragment on the
+ // queue ends at the total length, all data is received.
+ //
+ if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {
+
+ RemoveEntryList (&Assemble->Link);
+
+ //
+ // If the packet is properly formated, the last fragment's End
+ // equals to the packet's total length. Otherwise, the packet
+ // is a fake, drop it now.
+ //
+ Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List);
+ if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) {
+ Ip6FreeAssembleEntry (Assemble);
+ goto Error;
+ }
+
+ Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List);
+ This = Assemble->Info;
+
+ //
+ // This TmpPacket is used to hold the unfragmentable part, i.e.,
+ // the IPv6 header and the unfragmentable extension headers. Be noted that
+ // the Fragment Header is exluded.
+ //
+ TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0);
+ ASSERT (TmpPacket != NULL);
+
+ NET_LIST_FOR_EACH (Cur, ListHead) {
+ //
+ // Trim off the unfragment part plus the fragment header from all fragments.
+ //
+ Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE);
+ }
+
+ InsertHeadList (ListHead, &TmpPacket->List);
+
+ //
+ // Wrap the packet in a net buffer then deliver it up
+ //
+ NewPacket = NetbufFromBufList (
+ &Assemble->Fragments,
+ 0,
+ 0,
+ Ip6OnFreeFragments,
+ Assemble
+ );
+
+ if (NewPacket == NULL) {
+ Ip6FreeAssembleEntry (Assemble);
+ goto Error;
+ }
+
+ NewPacket->Ip.Ip6 = Assemble->Head;
+
+ CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO));
+
+ return NewPacket;
+ }
+
+ return NULL;
+
+Error:
+ NetbufFree (Packet);
+ return NULL;
+}
+
+
+/**
+ The callback function for the net buffer that wraps the packet processed by
+ IPsec. It releases the wrap packet and also signals IPsec to free the resources.
+
+ @param[in] Arg The wrap context.
+
+**/
+VOID
+EFIAPI
+Ip6IpSecFree (
+ IN VOID *Arg
+ )
+{
+ IP6_IPSEC_WRAP *Wrap;
+
+ Wrap = (IP6_IPSEC_WRAP *) Arg;
+
+ if (Wrap->IpSecRecycleSignal != NULL) {
+ gBS->SignalEvent (Wrap->IpSecRecycleSignal);
+ }
+
+ NetbufFree (Wrap->Packet);
+
+ FreePool (Wrap);
+
+ return;
+}
+
+/**
+ The work function to locate the IPsec protocol to process the inbound or
+ outbound IP packets. The process routine handles the packet with the following
+ actions: bypass the packet, discard the packet, or protect the packet.
+
+ @param[in] IpSb The IP6 service instance.
+ @param[in] Head The caller-supplied IP6 header.
+ @param[in, out] LastHead The next header field of last IP header.
+ @param[in, out] Netbuf The IP6 packet to be processed by IPsec.
+ @param[in] ExtHdrs The caller-supplied options.
+ @param[in] ExtHdrsLen The length of the option.
+ @param[in] Direction The directionality in an SPD entry,
+ EfiIPsecInBound, or EfiIPsecOutBound.
+ @param[in] Context The token's wrap.
+
+ @retval EFI_SUCCESS The IPsec protocol is not available or disabled.
+ @retval EFI_SUCCESS The packet was bypassed, and all buffers remain the same.
+ @retval EFI_SUCCESS The packet was protected.
+ @retval EFI_ACCESS_DENIED The packet was discarded.
+ @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation.
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the
+ number of input data blocks when building a fragment table.
+
+**/
+EFI_STATUS
+Ip6IpSecProcessPacket (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN OUT UINT8 *LastHead,
+ IN OUT NET_BUF **Netbuf,
+ IN VOID *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,
+ IN VOID *Context
+ )
+{
+ NET_FRAGMENT *FragmentTable;
+ UINT32 FragmentCount;
+ EFI_EVENT RecycleEvent;
+ NET_BUF *Packet;
+ IP6_TXTOKEN_WRAP *TxWrap;
+ IP6_IPSEC_WRAP *IpSecWrap;
+ EFI_STATUS Status;
+ EFI_IP6_HEADER *PacketHead;
+ UINT8 *Buf;
+
+ Status = EFI_SUCCESS;
+ Packet = *Netbuf;
+ RecycleEvent = NULL;
+ IpSecWrap = NULL;
+ FragmentTable = NULL;
+ PacketHead = NULL;
+ Buf = NULL;
+ TxWrap = (IP6_TXTOKEN_WRAP *) Context;
+ FragmentCount = Packet->BlockOpNum;
+
+ if (mIpSec == NULL) {
+ gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &mIpSec);
+
+ //
+ // Check whether the ipsec protocol is available.
+ //
+ if (mIpSec == NULL) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Check whether the ipsec enable variable is set.
+ //
+ if (mIpSec->DisabledFlag) {
+ //
+ // If IPsec is disabled, restore the original MTU
+ //
+ IpSb->MaxPacketSize = IpSb->OldMaxPacketSize;
+ goto ON_EXIT;
+ } else {
+ //
+ // If IPsec is enabled, use the MTU which reduce the IPsec header length.
+ //
+ IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN;
+ }
+
+
+ //
+ // Bypass all multicast inbound or outbound traffic.
+ //
+ if (IP6_IS_MULTICAST (&Head->DestinationAddress) || IP6_IS_MULTICAST (&Head->SourceAddress)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Rebuild fragment table from netbuf to ease ipsec process.
+ //
+ FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT));
+
+ if (FragmentTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);
+
+ if (EFI_ERROR(Status)) {
+ FreePool (FragmentTable);
+ goto ON_EXIT;
+ }
+
+ //
+ // Convert host byte order to network byte order
+ //
+ Ip6NtohHead (Head);
+
+ Status = mIpSec->Process (
+ mIpSec,
+ IpSb->Controller,
+ IP_VERSION_6,
+ (VOID *) Head,
+ LastHead,
+ NULL,
+ 0,
+ (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable),
+ &FragmentCount,
+ Direction,
+ &RecycleEvent
+ );
+ //
+ // Convert back to host byte order
+ //
+ Ip6NtohHead (Head);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (Direction == EfiIPsecOutBound && TxWrap != NULL) {
+
+ TxWrap->IpSecRecycleSignal = RecycleEvent;
+ TxWrap->Packet = NetbufFromExt (
+ FragmentTable,
+ FragmentCount,
+ IP6_MAX_HEADLEN,
+ 0,
+ Ip6FreeTxToken,
+ TxWrap
+ );
+ if (TxWrap->Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ *Netbuf = TxWrap->Packet;
+
+ } else {
+
+ IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP));
+
+ if (IpSecWrap == NULL) {
+ goto ON_EXIT;
+ }
+
+ IpSecWrap->IpSecRecycleSignal = RecycleEvent;
+ IpSecWrap->Packet = Packet;
+ Packet = NetbufFromExt (
+ FragmentTable,
+ FragmentCount,
+ IP6_MAX_HEADLEN,
+ 0,
+ Ip6IpSecFree,
+ IpSecWrap
+ );
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ if (Direction == EfiIPsecInBound) {
+
+ PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (
+ Packet,
+ sizeof (EFI_IP6_HEADER) + ExtHdrsLen,
+ NET_BUF_HEAD
+ );
+ if (PacketHead == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
+ Packet->Ip.Ip6 = PacketHead;
+
+ if (ExtHdrs != NULL) {
+ Buf = (UINT8 *) (PacketHead + 1);
+ CopyMem (Buf, ExtHdrs, ExtHdrsLen);
+ }
+
+ NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);
+ CopyMem (
+ IP6_GET_CLIP_INFO (Packet),
+ IP6_GET_CLIP_INFO (IpSecWrap->Packet),
+ sizeof (IP6_CLIP_INFO)
+ );
+ }
+
+ *Netbuf = Packet;
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ The IP6 input routine. It is called by the IP6_INTERFACE when an
+ IP6 fragment is received from MNP.
+
+ @param[in] Packet The IP6 packet received.
+ @param[in] IoStatus The return status of receive request.
+ @param[in] Flag The link layer flag for the packet received, such
+ as multicast.
+ @param[in] Context The IP6 service instance that owns the MNP.
+
+**/
+VOID
+Ip6AcceptFrame (
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 Flag,
+ IN VOID *Context
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_CLIP_INFO *Info;
+ EFI_IP6_HEADER *Head;
+ UINT16 PayloadLen;
+ UINT8 *Payload;
+ UINT16 TotalLen;
+ UINT8 *LastHead;
+ UINT32 FormerHeadOffset;
+ UINT32 UnFragmentLen;
+ UINT32 ExtHdrsLen;
+ UINT32 HeadLen;
+ BOOLEAN Fragmented;
+ IP6_FRAGMENT_HEADER *FragmentHead;
+ UINT16 FragmentOffset;
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS Loopback;
+
+ IpSb = (IP6_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ Payload = NULL;
+
+ //
+ // Check input parameters
+ //
+ if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) {
+ goto Drop;
+ }
+
+ //
+ // Check whether the input packet is a valid packet
+ //
+ if (Packet->TotalSize < IP6_MIN_HEADLEN) {
+ goto Restart;
+ }
+
+ //
+ // Get header information of the packet.
+ //
+ Head = (EFI_IP6_HEADER *) NetbufGetByte (Packet, 0, NULL);
+ if (Head == NULL) {
+ goto Restart;
+ }
+
+ //
+ // Multicast addresses must not be used as source addresses in IPv6 packets.
+ //
+ if ((Head->Version != 6) || (IP6_IS_MULTICAST (&Head->SourceAddress))) {
+ goto Restart;
+ }
+
+ //
+ // A packet with a destination address of loopback ::1/128 or unspecified must be dropped.
+ //
+ ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS));
+ Loopback.Addr[15] = 0x1;
+ if ((CompareMem (&Loopback, &Head->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) ||
+ (NetIp6IsUnspecifiedAddr (&Head->DestinationAddress))) {
+ goto Restart;
+ }
+
+ //
+ // Convert the IP header to host byte order.
+ //
+ Packet->Ip.Ip6 = Ip6NtohHead (Head);
+
+ //
+ // Get the per packet info.
+ //
+ Info = IP6_GET_CLIP_INFO (Packet);
+ Info->LinkFlag = Flag;
+ Info->CastType = 0;
+
+ if (IpSb->MnpConfigData.EnablePromiscuousReceive) {
+ Info->CastType = Ip6Promiscuous;
+ }
+
+ if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
+ Info->CastType = Ip6Unicast;
+ } else if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {
+ if (Ip6FindMldEntry (IpSb, &Head->DestinationAddress) != NULL) {
+ Info->CastType = Ip6Multicast;
+ }
+ }
+
+ //
+ // Drop the packet that is not delivered to us.
+ //
+ if (Info->CastType == 0) {
+ goto Restart;
+ }
+
+
+ PayloadLen = Head->PayloadLength;
+
+ Info->Start = 0;
+ Info->Length = PayloadLen;
+ Info->End = Info->Start + Info->Length;
+ Info->HeadLen = (UINT16) sizeof (EFI_IP6_HEADER);
+ Info->Status = EFI_SUCCESS;
+ Info->LastFrag = FALSE;
+
+ TotalLen = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER));
+
+ //
+ // Mnp may deliver frame trailer sequence up, trim it off.
+ //
+ if (TotalLen < Packet->TotalSize) {
+ NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);
+ }
+
+ if (TotalLen != Packet->TotalSize) {
+ goto Restart;
+ }
+
+ //
+ // Check the extension headers, if exist validate them
+ //
+ if (PayloadLen != 0) {
+ Payload = AllocatePool ((UINTN) PayloadLen);
+ if (Payload == NULL) {
+ goto Restart;
+ }
+
+ NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);
+ }
+
+ LastHead = NULL;
+ if (!Ip6IsExtsValid (
+ IpSb,
+ Packet,
+ &Head->NextHeader,
+ Payload,
+ (UINT32) PayloadLen,
+ TRUE,
+ &FormerHeadOffset,
+ &LastHead,
+ &ExtHdrsLen,
+ &UnFragmentLen,
+ &Fragmented
+ )) {
+ goto Restart;
+ }
+
+ HeadLen = sizeof (EFI_IP6_HEADER) + UnFragmentLen;
+
+ if (Fragmented) {
+ //
+ // Get the fragment offset from the Fragment header
+ //
+ FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (Packet, HeadLen, NULL);
+ if (FragmentHead == NULL) {
+ goto Restart;
+ }
+
+ FragmentOffset = NTOHS (FragmentHead->FragmentOffset);
+
+ if ((FragmentOffset & 0x1) == 0) {
+ Info->LastFrag = TRUE;
+ }
+
+ FragmentOffset &= (~0x1);
+
+ //
+ // This is the first fragment of the packet
+ //
+ if (FragmentOffset == 0) {
+ Info->NextHeader = FragmentHead->NextHeader;
+ }
+
+ Info->HeadLen = (UINT16) HeadLen;
+ HeadLen += sizeof (IP6_FRAGMENT_HEADER);
+ Info->Start = FragmentOffset;
+ Info->Length = TotalLen - (UINT16) HeadLen;
+ Info->End = Info->Start + Info->Length;
+ Info->Id = FragmentHead->Identification;
+ Info->FormerNextHeader = FormerHeadOffset;
+
+ //
+ // Fragments should in the unit of 8 octets long except the last one.
+ //
+ if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) {
+ goto Restart;
+ }
+
+ //
+ // Reassemble the packet.
+ //
+ Packet = Ip6Reassemble (&IpSb->Assemble, Packet);
+ if (Packet == NULL) {
+ goto Restart;
+ }
+
+ //
+ // Re-check the assembled packet to get the right values.
+ //
+ Head = Packet->Ip.Ip6;
+ PayloadLen = Head->PayloadLength;
+ if (PayloadLen != 0) {
+ if (Payload != NULL) {
+ FreePool (Payload);
+ }
+
+ Payload = AllocatePool ((UINTN) PayloadLen);
+ if (Payload == NULL) {
+ goto Restart;
+ }
+
+ NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);
+ }
+
+ if (!Ip6IsExtsValid (
+ IpSb,
+ Packet,
+ &Head->NextHeader,
+ Payload,
+ (UINT32) PayloadLen,
+ TRUE,
+ NULL,
+ &LastHead,
+ &ExtHdrsLen,
+ &UnFragmentLen,
+ &Fragmented
+ )) {
+ goto Restart;
+ }
+ }
+
+ //
+ // Trim the head off, after this point, the packet is headless.
+ // and Packet->TotalLen == Info->Length.
+ //
+ NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);
+
+ //
+ // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer,
+ // and no need consider any other ahead ext headers.
+ //
+ Status = Ip6IpSecProcessPacket (
+ IpSb,
+ Head,
+ LastHead, // need get the lasthead value for input
+ &Packet,
+ NULL,
+ 0,
+ EfiIPsecInBound,
+ NULL
+ );
+
+ if (EFI_ERROR(Status)) {
+ goto Restart;
+ }
+
+ //
+ // TODO: may check the last head again, the same as the output routine
+ //
+
+ //
+ // Packet may have been changed. The ownership of the packet
+ // is transfered to the packet process logic.
+ //
+ Head = Packet->Ip.Ip6;
+ IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;
+
+ switch (*LastHead) {
+ case IP6_ICMP:
+ Ip6IcmpHandle (IpSb, Head, Packet);
+ break;
+ default:
+ Ip6Demultiplex (IpSb, Head, Packet);
+ }
+
+ Packet = NULL;
+
+ //
+ // Dispatch the DPCs queued by the NotifyFunction of the rx token's events
+ // which are signaled with received data.
+ //
+ DispatchDpc ();
+
+Restart:
+ if (Payload != NULL) {
+ FreePool (Payload);
+ }
+
+ Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);
+
+Drop:
+ if (Packet != NULL) {
+ NetbufFree (Packet);
+ }
+
+ return ;
+}
+
+/**
+ Initialize an already allocated assemble table. This is generally
+ the assemble table embedded in the IP6 service instance.
+
+ @param[in, out] Table The assemble table to initialize.
+
+**/
+VOID
+Ip6CreateAssembleTable (
+ IN OUT IP6_ASSEMBLE_TABLE *Table
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
+ InitializeListHead (&Table->Bucket[Index]);
+ }
+}
+
+/**
+ Clean up the assemble table by removing all of the fragments
+ and assemble entries.
+
+ @param[in, out] Table The assemble table to clean up.
+
+**/
+VOID
+Ip6CleanAssembleTable (
+ IN OUT IP6_ASSEMBLE_TABLE *Table
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_ASSEMBLE_ENTRY *Assemble;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);
+
+ RemoveEntryList (Entry);
+ Ip6FreeAssembleEntry (Assemble);
+ }
+ }
+}
+
+
+/**
+ The signal handle of IP6's recycle event. It is called back
+ when the upper layer releases the packet.
+
+ @param[in] Event The IP6's recycle event.
+ @param[in] Context The context of the handle, which is a IP6_RXDATA_WRAP.
+
+**/
+VOID
+EFIAPI
+Ip6OnRecyclePacket (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IP6_RXDATA_WRAP *Wrap;
+
+ Wrap = (IP6_RXDATA_WRAP *) Context;
+
+ EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);
+ RemoveEntryList (&Wrap->Link);
+ EfiReleaseLock (&Wrap->IpInstance->RecycleLock);
+
+ ASSERT (!NET_BUF_SHARED (Wrap->Packet));
+ NetbufFree (Wrap->Packet);
+
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);
+ FreePool (Wrap);
+}
+
+/**
+ Wrap the received packet to a IP6_RXDATA_WRAP, which will be
+ delivered to the upper layer. Each IP6 child that accepts the
+ packet will get a not-shared copy of the packet which is wrapped
+ in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed
+ to the upper layer. The upper layer will signal the recycle event in
+ it when it is done with the packet.
+
+ @param[in] IpInstance The IP6 child to receive the packet.
+ @param[in] Packet The packet to deliver up.
+
+ @return NULL if it failed to wrap the packet; otherwise, the wrapper.
+
+**/
+IP6_RXDATA_WRAP *
+Ip6WrapRxData (
+ IN IP6_PROTOCOL *IpInstance,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_RXDATA_WRAP *Wrap;
+ EFI_IP6_RECEIVE_DATA *RxData;
+ EFI_STATUS Status;
+
+ Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum));
+
+ if (Wrap == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Wrap->Link);
+
+ Wrap->IpInstance = IpInstance;
+ Wrap->Packet = Packet;
+ RxData = &Wrap->RxData;
+
+ ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip6OnRecyclePacket,
+ Wrap,
+ &RxData->RecycleSignal
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Wrap);
+ return NULL;
+ }
+
+ ASSERT (Packet->Ip.Ip6 != NULL);
+
+ //
+ // The application expects a network byte order header.
+ //
+ RxData->HeaderLength = sizeof (EFI_IP6_HEADER);
+ RxData->Header = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6);
+ RxData->DataLength = Packet->TotalSize;
+
+ //
+ // Build the fragment table to be delivered up.
+ //
+ RxData->FragmentCount = Packet->BlockOpNum;
+ NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);
+
+ return Wrap;
+}
+
+/**
+ Check whether this IP child accepts the packet.
+
+ @param[in] IpInstance The IP child to check.
+ @param[in] Head The IP header of the packet.
+ @param[in] Packet The data of the packet.
+
+ @retval TRUE The child wants to receive the packet.
+ @retval FALSE The child does not want to receive the packet.
+
+**/
+BOOLEAN
+Ip6InstanceFrameAcceptable (
+ IN IP6_PROTOCOL *IpInstance,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_ERROR_HEAD Icmp;
+ EFI_IP6_CONFIG_DATA *Config;
+ IP6_CLIP_INFO *Info;
+ UINT8 *Proto;
+ UINT32 Index;
+ UINT8 *ExtHdrs;
+ UINT16 ErrMsgPayloadLen;
+ UINT8 *ErrMsgPayload;
+
+ Config = &IpInstance->ConfigData;
+ Proto = NULL;
+
+ //
+ // Dirty trick for the Tiano UEFI network stack implmentation. If
+ // ReceiveTimeout == -1, the receive of the packet for this instance
+ // is disabled. The UEFI spec don't have such captibility. We add
+ // this to improve the performance because IP will make a copy of
+ // the received packet for each accepting instance. Some IP instances
+ // used by UDP/TCP only send packets, they don't wants to receive.
+ //
+ if (Config->ReceiveTimeout == (UINT32)(-1)) {
+ return FALSE;
+ }
+
+ if (Config->AcceptPromiscuous) {
+ return TRUE;
+ }
+
+ //
+ // Check whether the protocol is acceptable.
+ //
+ ExtHdrs = NetbufGetByte (Packet, 0, NULL);
+
+ if (!Ip6IsExtsValid (
+ IpInstance->Service,
+ Packet,
+ &Head->NextHeader,
+ ExtHdrs,
+ (UINT32) Head->PayloadLength,
+ TRUE,
+ NULL,
+ &Proto,
+ NULL,
+ NULL,
+ NULL
+ )) {
+ return FALSE;
+ }
+
+ //
+ // The upper layer driver may want to receive the ICMPv6 error packet
+ // invoked by its packet, like UDP.
+ //
+ if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) {
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) {
+ if (!Config->AcceptIcmpErrors) {
+ return FALSE;
+ }
+
+ //
+ // Get the protocol of the invoking packet of ICMPv6 error packet.
+ //
+ ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength);
+ ErrMsgPayload = NetbufGetByte (Packet, sizeof (Icmp), NULL);
+
+ if (!Ip6IsExtsValid (
+ NULL,
+ NULL,
+ &Icmp.IpHead.NextHeader,
+ ErrMsgPayload,
+ ErrMsgPayloadLen,
+ TRUE,
+ NULL,
+ &Proto,
+ NULL,
+ NULL,
+ NULL
+ )) {
+ return FALSE;
+ }
+ }
+ }
+
+ //
+ // Match the protocol
+ //
+ if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) {
+ return FALSE;
+ }
+
+ //
+ // Check for broadcast, the caller has computed the packet's
+ // cast type for this child's interface.
+ //
+ Info = IP6_GET_CLIP_INFO (Packet);
+
+ //
+ // If it is a multicast packet, check whether we are in the group.
+ //
+ if (Info->CastType == Ip6Multicast) {
+ //
+ // Receive the multicast if the instance wants to receive all packets.
+ //
+ if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) {
+ return TRUE;
+ }
+
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {
+ if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) {
+ break;
+ }
+ }
+
+ return (BOOLEAN)(Index < IpInstance->GroupCount);
+ }
+
+ return TRUE;
+}
+
+/**
+ Enqueue a shared copy of the packet to the IP6 child if the
+ packet is acceptable to it. Here the data of the packet is
+ shared, but the net buffer isn't.
+
+ @param IpInstance The IP6 child to enqueue the packet to.
+ @param Head The IP header of the received packet.
+ @param Packet The data of the received packet.
+
+ @retval EFI_NOT_STARTED The IP child hasn't been configured.
+ @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources
+ @retval EFI_SUCCESS A shared copy the packet is enqueued to the child.
+
+**/
+EFI_STATUS
+Ip6InstanceEnquePacket (
+ IN IP6_PROTOCOL *IpInstance,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_CLIP_INFO *Info;
+ NET_BUF *Clone;
+
+ //
+ // Check whether the packet is acceptable to this instance.
+ //
+ if (IpInstance->State != IP6_STATE_CONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Enque a shared copy of the packet.
+ //
+ Clone = NetbufClone (Packet);
+
+ if (Clone == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Set the receive time out for the assembled packet. If it expires,
+ // packet will be removed from the queue.
+ //
+ Info = IP6_GET_CLIP_INFO (Clone);
+ Info->Life = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);
+
+ InsertTailList (&IpInstance->Received, &Clone->List);
+ return EFI_SUCCESS;
+}
+
+/**
+ Deliver the received packets to the upper layer if there are both received
+ requests and enqueued packets. If the enqueued packet is shared, it will
+ duplicate it to a non-shared packet, release the shared packet, then
+ deliver the non-shared packet up.
+
+ @param[in] IpInstance The IP child to deliver the packet up.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the
+ packets.
+ @retval EFI_SUCCESS All the enqueued packets that can be delivered
+ are delivered up.
+
+**/
+EFI_STATUS
+Ip6InstanceDeliverPacket (
+ IN IP6_PROTOCOL *IpInstance
+ )
+{
+ EFI_IP6_COMPLETION_TOKEN *Token;
+ IP6_RXDATA_WRAP *Wrap;
+ NET_BUF *Packet;
+ NET_BUF *Dup;
+ UINT8 *Head;
+
+ //
+ // Deliver a packet if there are both a packet and a receive token.
+ //
+ while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) {
+
+ Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);
+
+ if (!NET_BUF_SHARED (Packet)) {
+ //
+ // If this is the only instance that wants the packet, wrap it up.
+ //
+ Wrap = Ip6WrapRxData (IpInstance, Packet);
+
+ if (Wrap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RemoveEntryList (&Packet->List);
+
+ } else {
+ //
+ // Create a duplicated packet if this packet is shared
+ //
+ Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));
+
+ if (Dup == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the IP head over. The packet to deliver up is
+ // headless. Trim the head off after copy. The IP head
+ // may be not continuous before the data.
+ //
+ Head = NetbufAllocSpace (Dup, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD);
+ ASSERT (Head != NULL);
+ Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head;
+
+ CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER));
+ NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE);
+
+ Wrap = Ip6WrapRxData (IpInstance, Dup);
+
+ if (Wrap == NULL) {
+ NetbufFree (Dup);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RemoveEntryList (&Packet->List);
+ NetbufFree (Packet);
+
+ Packet = Dup;
+ }
+
+ //
+ // Insert it into the delivered packet, then get a user's
+ // receive token, pass the wrapped packet up.
+ //
+ EfiAcquireLockOrFail (&IpInstance->RecycleLock);
+ InsertHeadList (&IpInstance->Delivered, &Wrap->Link);
+ EfiReleaseLock (&IpInstance->RecycleLock);
+
+ Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL);
+ Token->Status = IP6_GET_CLIP_INFO (Packet)->Status;
+ Token->Packet.RxData = &Wrap->RxData;
+
+ gBS->SignalEvent (Token->Event);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Enqueue a received packet to all the IP children that share
+ the same interface.
+
+ @param[in] IpSb The IP6 service instance that receive the packet.
+ @param[in] Head The header of the received packet.
+ @param[in] Packet The data of the received packet.
+ @param[in] IpIf The interface to enqueue the packet to.
+
+ @return The number of the IP6 children that accepts the packet.
+
+**/
+INTN
+Ip6InterfaceEnquePacket (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet,
+ IN IP6_INTERFACE *IpIf
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ IP6_CLIP_INFO *Info;
+ LIST_ENTRY *Entry;
+ INTN Enqueued;
+ INTN LocalType;
+ INTN SavedType;
+
+ //
+ // First, check that the packet is acceptable to this interface
+ // and find the local cast type for the interface.
+ //
+ LocalType = 0;
+ Info = IP6_GET_CLIP_INFO (Packet);
+
+ if (IpIf->PromiscRecv) {
+ LocalType = Ip6Promiscuous;
+ } else {
+ LocalType = Info->CastType;
+ }
+
+ //
+ // Iterate through the ip instances on the interface, enqueue
+ // the packet if filter passed. Save the original cast type,
+ // and pass the local cast type to the IP children on the
+ // interface. The global cast type will be restored later.
+ //
+ SavedType = Info->CastType;
+ Info->CastType = (UINT32) LocalType;
+
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
+
+ if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {
+ Enqueued++;
+ }
+ }
+
+ Info->CastType = (UINT32) SavedType;
+ return Enqueued;
+}
+
+/**
+ Deliver the packet for each IP6 child on the interface.
+
+ @param[in] IpSb The IP6 service instance that received the packet.
+ @param[in] IpIf The IP6 interface to deliver the packet.
+
+**/
+VOID
+Ip6InterfaceDeliverPacket (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *IpIf
+ )
+{
+ IP6_PROTOCOL *IpInstance;
+ LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
+ IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);
+ Ip6InstanceDeliverPacket (IpInstance);
+ }
+}
+
+/**
+ De-multiplex the packet. the packet delivery is processed in two
+ passes. The first pass will enqueue a shared copy of the packet
+ to each IP6 child that accepts the packet. The second pass will
+ deliver a non-shared copy of the packet to each IP6 child that
+ has pending receive requests. Data is copied if more than one
+ child wants to consume the packet, because each IP child needs
+ its own copy of the packet to make changes.
+
+ @param[in] IpSb The IP6 service instance that received the packet.
+ @param[in] Head The header of the received packet.
+ @param[in] Packet The data of the received packet.
+
+ @retval EFI_NOT_FOUND No IP child accepts the packet.
+ @retval EFI_SUCCESS The packet is enqueued or delivered to some IP
+ children.
+
+**/
+EFI_STATUS
+Ip6Demultiplex (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+
+ LIST_ENTRY *Entry;
+ IP6_INTERFACE *IpIf;
+ INTN Enqueued;
+
+ //
+ // Two pass delivery: first, enque a shared copy of the packet
+ // to each instance that accept the packet.
+ //
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
+
+ if (IpIf->Configured) {
+ Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf);
+ }
+ }
+
+ //
+ // Second: deliver a duplicate of the packet to each instance.
+ // Release the local reference first, so that the last instance
+ // getting the packet will not copy the data.
+ //
+ NetbufFree (Packet);
+ Packet = NULL;
+
+ if (Enqueued == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
+
+ if (IpIf->Configured) {
+ Ip6InterfaceDeliverPacket (IpSb, IpIf);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Decrease the life of the transmitted packets. If it is
+ decreased to zero, cancel the packet. This function is
+ called by Ip6packetTimerTicking that provides timeout for both the
+ received-but-not-delivered and transmitted-but-not-recycle
+ packets.
+
+ @param[in] Map The IP6 child's transmit map.
+ @param[in] Item Current transmitted packet.
+ @param[in] Context Not used.
+
+ @retval EFI_SUCCESS Always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+Ip6SentPacketTicking (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ IP6_TXTOKEN_WRAP *Wrap;
+
+ Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;
+ ASSERT (Wrap != NULL);
+
+ if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {
+ Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Timeout the fragments, and the enqueued, and transmitted packets.
+
+ @param[in] IpSb The IP6 service instance to timeout.
+
+**/
+VOID
+Ip6PacketTimerTicking (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ LIST_ENTRY *InstanceEntry;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_PROTOCOL *IpInstance;
+ IP6_ASSEMBLE_ENTRY *Assemble;
+ NET_BUF *Packet;
+ IP6_CLIP_INFO *Info;
+ UINT32 Index;
+
+ //
+ // First, time out the fragments. The packet's life is counting down
+ // once the first-arriving fragment of that packet was received.
+ //
+ for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) {
+ Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);
+
+ if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {
+ //
+ // If the first fragment (the one with a Fragment Offset of zero)
+ // has been received, an ICMP Time Exceeded - Fragment Reassembly
+ // Time Exceeded message should be sent to the source of that fragment.
+ //
+ if ((Assemble->Packet != NULL) &&
+ !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ Assemble->Packet,
+ NULL,
+ &Assemble->Head->SourceAddress,
+ ICMP_V6_TIME_EXCEEDED,
+ ICMP_V6_TIMEOUT_REASSEMBLE,
+ NULL
+ );
+ }
+
+ //
+ // If reassembly of a packet is not completed within 60 seconds of
+ // the reception of the first-arriving fragment of that packet, the
+ // reassembly must be abandoned and all the fragments that have been
+ // received for that packet must be discarded.
+ //
+ RemoveEntryList (Entry);
+ Ip6FreeAssembleEntry (Assemble);
+ }
+ }
+ }
+
+ NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link);
+
+ //
+ // Second, time out the assembled packets enqueued on each IP child.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {
+ Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Info = IP6_GET_CLIP_INFO (Packet);
+
+ if ((Info->Life > 0) && (--Info->Life == 0)) {
+ RemoveEntryList (Entry);
+ NetbufFree (Packet);
+ }
+ }
+
+ //
+ // Third: time out the transmitted packets.
+ //
+ NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL);
+ }
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Input.h b/NetworkPkg/Ip6Dxe/Ip6Input.h
new file mode 100644
index 0000000000..8594896521
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Input.h
@@ -0,0 +1,235 @@
+/** @file
+ IP6 internal functions and definitions to process the incoming packets.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_INPUT_H__
+#define __EFI_IP6_INPUT_H__
+
+#define IP6_MIN_HEADLEN 40
+#define IP6_MAX_HEADLEN 120
+///
+/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54
+///
+#define IP6_MAX_IPSEC_HEADLEN 54
+
+
+#define IP6_ASSEMLE_HASH_SIZE 127
+///
+/// Lift time in seconds.
+///
+#define IP6_FRAGMENT_LIFE 60
+#define IP6_MAX_PACKET_SIZE 65535
+
+
+#define IP6_GET_CLIP_INFO(Packet) ((IP6_CLIP_INFO *) ((Packet)->ProtoData))
+
+#define IP6_ASSEMBLE_HASH(Dst, Src, Id) \
+ ((*((UINT32 *) (Dst)) + *((UINT32 *) (Src)) + (Id)) % IP6_ASSEMLE_HASH_SIZE)
+
+#define IP6_RXDATA_WRAP_SIZE(NumFrag) \
+ (sizeof (IP6_RXDATA_WRAP) + sizeof (EFI_IP6_FRAGMENT_DATA) * ((NumFrag) - 1))
+
+//
+// Per packet information for input process. LinkFlag specifies whether
+// the packet is received as Link layer unicast, multicast or broadcast.
+// The CastType is the IP layer cast type, such as IP multicast or unicast.
+// Start, End and Length are staffs used to assemble the packets. Start
+// is the sequence number of the first byte of data in the packet. Length
+// is the number of bytes of data. End = Start + Length, that is, the
+// sequence number of last byte + 1. Each assembled packet has a count down
+// life. If it isn't consumed before Life reaches zero, the packet is released.
+//
+typedef struct {
+ UINT32 LinkFlag;
+ INT32 CastType;
+ INT32 Start;
+ INT32 End;
+ INT32 Length;
+ UINT32 Life;
+ EFI_STATUS Status;
+ UINT32 Id;
+ UINT16 HeadLen;
+ UINT8 NextHeader;
+ UINT8 LastFrag;
+ UINT32 FormerNextHeader;
+} IP6_CLIP_INFO;
+
+//
+// Structure used to assemble IP packets.
+//
+typedef struct {
+ LIST_ENTRY Link;
+ LIST_ENTRY Fragments; // List of all the fragments of this packet
+
+ //
+ // Identity of one IP6 packet. Each fragment of a packet has
+ // the same (Dst, Src, Id).
+ //
+ EFI_IPv6_ADDRESS Dst;
+ EFI_IPv6_ADDRESS Src;
+ UINT32 Id;
+
+ UINT32 TotalLen;
+ UINT32 CurLen;
+ UINT32 Life; // Count down life for the packet.
+
+ EFI_IP6_HEADER *Head; // IP head of the first fragment
+ IP6_CLIP_INFO *Info; // Per packet information of the first fragment
+ NET_BUF *Packet; // The first fragment of the packet
+} IP6_ASSEMBLE_ENTRY;
+
+//
+// Each Ip service instance has an assemble table to reassemble
+// the packets before delivery to its children. It is organized
+// as hash table.
+//
+typedef struct {
+ LIST_ENTRY Bucket[IP6_ASSEMLE_HASH_SIZE];
+} IP6_ASSEMBLE_TABLE;
+
+/**
+ The IP6 input routine. It is called by the IP6_INTERFACE when an
+ IP6 fragment is received from MNP.
+
+ @param[in] Packet The IP6 packet received.
+ @param[in] IoStatus The return status of receive request.
+ @param[in] Flag The link layer flag for the packet received, such
+ as multicast.
+ @param[in] Context The IP6 service instance that own the MNP.
+
+**/
+VOID
+Ip6AcceptFrame (
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus,
+ IN UINT32 Flag,
+ IN VOID *Context
+ );
+
+/**
+ Deliver the received packets to upper layer if there are both received
+ requests and enqueued packets. If the enqueued packet is shared, it will
+ duplicate it to a non-shared packet, release the shared packet, then
+ deliver the non-shared packet up.
+
+ @param[in] IpInstance The IP child to deliver the packet up.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the
+ packets.
+ @retval EFI_SUCCESS All the enqueued packets that can be delivered
+ are delivered up.
+
+**/
+EFI_STATUS
+Ip6InstanceDeliverPacket (
+ IN IP6_PROTOCOL *IpInstance
+ );
+
+/**
+ The work function to locate IPsec protocol to process the inbound or
+ outbound IP packets. The process routine handls the packet with the following
+ actions: bypass the packet, discard the packet, or protect the packet.
+
+ @param[in] IpSb The IP6 service instance.
+ @param[in] Head The caller supplied IP6 header.
+ @param[in, out] LastHead The next header field of last IP header.
+ @param[in, out] Netbuf The IP6 packet to be processed by IPsec.
+ @param[in] ExtHdrs The caller supplied options.
+ @param[in] ExtHdrsLen The length of the option.
+ @param[in] Direction The directionality in an SPD entry,
+ EfiIPsecInBound or EfiIPsecOutBound.
+ @param[in] Context The token's wrap.
+
+ @retval EFI_SUCCESS The IPsec protocol is not available or disabled.
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.
+ @retval EFI_SUCCESS The packet was protected.
+ @retval EFI_ACCESS_DENIED The packet was discarded.
+ @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation.
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the
+ number of input data blocks when building a fragment table.
+
+**/
+EFI_STATUS
+Ip6IpSecProcessPacket (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN OUT UINT8 *LastHead,
+ IN OUT NET_BUF **Netbuf,
+ IN VOID *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,
+ IN VOID *Context
+ );
+
+/**
+ Initialize an already allocated assemble table. This is generally
+ the assemble table embedded in the IP6 service instance.
+
+ @param[in, out] Table The assemble table to initialize.
+
+**/
+VOID
+Ip6CreateAssembleTable (
+ IN OUT IP6_ASSEMBLE_TABLE *Table
+ );
+
+/**
+ Clean up the assemble table: remove all the fragments
+ and assemble entries.
+
+ @param[in, out] Table The assemble table to clean up.
+
+**/
+VOID
+Ip6CleanAssembleTable (
+ IN OUT IP6_ASSEMBLE_TABLE *Table
+ );
+
+/**
+ Demultiple the packet. the packet delivery is processed in two
+ passes. The first pass will enque a shared copy of the packet
+ to each IP6 child that accepts the packet. The second pass will
+ deliver a non-shared copy of the packet to each IP6 child that
+ has pending receive requests. Data is copied if more than one
+ child wants to consume the packet bacause each IP child need
+ its own copy of the packet to make changes.
+
+ @param[in] IpSb The IP6 service instance that received the packet.
+ @param[in] Head The header of the received packet.
+ @param[in] Packet The data of the received packet.
+
+ @retval EFI_NOT_FOUND No IP child accepts the packet.
+ @retval EFI_SUCCESS The packet is enqueued or delivered to some IP
+ children.
+
+**/
+EFI_STATUS
+Ip6Demultiplex (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Timeout the fragmented, enqueued, and transmitted packets.
+
+ @param[in] IpSb The IP6 service instance to timeout.
+
+**/
+VOID
+Ip6PacketTimerTicking (
+ IN IP6_SERVICE *IpSb
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.c b/NetworkPkg/Ip6Dxe/Ip6Mld.c
new file mode 100644
index 0000000000..4a418fade5
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Mld.c
@@ -0,0 +1,908 @@
+/** @file
+ Multicast Listener Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+/**
+ Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data.
+
+ @param[in, out] IpSb Points to IP6 service binding instance.
+ @param[in] MulticastAddr The IPv6 multicast address to be recorded.
+ @param[in] DelayTimer The maximum allowed delay before sending a responding
+ report, in units of milliseconds.
+ @return The created IP6_ML_GROUP list entry or NULL.
+
+**/
+IP6_MLD_GROUP *
+Ip6CreateMldEntry (
+ IN OUT IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *MulticastAddr,
+ IN UINT32 DelayTimer
+ )
+{
+ IP6_MLD_GROUP *Entry;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
+
+ Entry = AllocatePool (sizeof (IP6_MLD_GROUP));
+ if (Entry != NULL) {
+ Entry->RefCnt = 1;
+ Entry->DelayTimer = DelayTimer;
+ Entry->SendByUs = FALSE;
+ IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr);
+ InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link);
+ }
+
+ return Entry;
+}
+
+/**
+ Search a IP6_MLD_GROUP list entry node from a list array.
+
+ @param[in] IpSb Points to IP6 service binding instance.
+ @param[in] MulticastAddr The IPv6 multicast address to be searched.
+
+ @return The found IP6_ML_GROUP list entry or NULL.
+
+**/
+IP6_MLD_GROUP *
+Ip6FindMldEntry (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *MulticastAddr
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_MLD_GROUP *Group;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
+ if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) {
+ return Group;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Count the number of IP6 multicast groups that are mapped to the
+ same MAC address. Several IP6 multicast address may be mapped to
+ the same MAC address.
+
+ @param[in] MldCtrl The MLD control block to search in.
+ @param[in] Mac The MAC address to search.
+
+ @return The number of the IP6 multicast group that mapped to the same
+ multicast group Mac.
+
+**/
+INTN
+Ip6FindMac (
+ IN IP6_MLD_SERVICE_DATA *MldCtrl,
+ IN EFI_MAC_ADDRESS *Mac
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_MLD_GROUP *Group;
+ INTN Count;
+
+ Count = 0;
+
+ NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
+
+ if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {
+ Count++;
+ }
+ }
+
+ return Count;
+}
+
+/**
+ Generate MLD report message and send it out to MulticastAddr.
+
+ @param[in] IpSb The IP service to send the packet.
+ @param[in] Interface The IP interface to send the packet.
+ If NULL, a system interface will be selected.
+ @param[in] MulticastAddr The specific IPv6 multicast address to which
+ the message sender is listening.
+
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the
+ operation.
+ @retval EFI_SUCCESS The MLD report message was successfully sent out.
+
+**/
+EFI_STATUS
+Ip6SendMldReport (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *Interface OPTIONAL,
+ IN EFI_IPv6_ADDRESS *MulticastAddr
+ )
+{
+ IP6_MLD_HEAD *MldHead;
+ NET_BUF *Packet;
+ EFI_IP6_HEADER Head;
+ UINT16 PayloadLen;
+ UINTN OptionLen;
+ UINT8 *Options;
+ EFI_STATUS Status;
+ UINT16 HeadChecksum;
+ UINT16 PseudoChecksum;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
+
+ //
+ // Generate the packet to be sent
+ // IPv6 basic header + Hop by Hop option + MLD message
+ //
+
+ OptionLen = 0;
+ Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+
+ PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create the basic IPv6 header.
+ // RFC3590: Use link-local address as source address if it is available,
+ // otherwise use the unspecified address.
+ //
+ Head.FlowLabelL = 0;
+ Head.FlowLabelH = 0;
+ Head.PayloadLength = HTONS (PayloadLen);
+ Head.NextHeader = IP6_HOP_BY_HOP;
+ Head.HopLimit = 1;
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr);
+
+ //
+ // If Link-Local address is not ready, we use unspecified address.
+ //
+ IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);
+
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
+
+ //
+ // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header
+ //
+ Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);
+ ASSERT (Options != NULL);
+ Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Packet);
+ Packet = NULL;
+ return Status;
+ }
+
+ //
+ // Fill in MLD message - Report
+ //
+ MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);
+ ASSERT (MldHead != NULL);
+ ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));
+ MldHead->Head.Type = ICMP_V6_LISTENER_REPORT;
+ MldHead->Head.Code = 0;
+ IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);
+
+ HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));
+ PseudoChecksum = NetIp6PseudoHeadChecksum (
+ &Head.SourceAddress,
+ &Head.DestinationAddress,
+ IP6_ICMP,
+ sizeof (IP6_MLD_HEAD)
+ );
+
+ MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);
+
+ //
+ // Transmit the packet
+ //
+ return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
+}
+
+/**
+ Generate MLD Done message and send it out to MulticastAddr.
+
+ @param[in] IpSb The IP service to send the packet.
+ @param[in] MulticastAddr The specific IPv6 multicast address to which
+ the message sender is ceasing to listen.
+
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the
+ operation.
+ @retval EFI_SUCCESS The MLD report message was successfully sent out.
+
+**/
+EFI_STATUS
+Ip6SendMldDone (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *MulticastAddr
+ )
+{
+ IP6_MLD_HEAD *MldHead;
+ NET_BUF *Packet;
+ EFI_IP6_HEADER Head;
+ UINT16 PayloadLen;
+ UINTN OptionLen;
+ UINT8 *Options;
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS Destination;
+ UINT16 HeadChecksum;
+ UINT16 PseudoChecksum;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
+
+ //
+ // Generate the packet to be sent
+ // IPv6 basic header + Hop by Hop option + MLD message
+ //
+
+ OptionLen = 0;
+ Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+
+ PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create the basic IPv6 header.
+ //
+ Head.FlowLabelL = 0;
+ Head.FlowLabelH = 0;
+ Head.PayloadLength = HTONS (PayloadLen);
+ Head.NextHeader = IP6_HOP_BY_HOP;
+ Head.HopLimit = 1;
+
+ //
+ // If Link-Local address is not ready, we use unspecified address.
+ //
+ IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);
+
+ Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination);
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination);
+
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
+
+ //
+ // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header
+ //
+ Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);
+ ASSERT (Options != NULL);
+ Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Packet);
+ Packet = NULL;
+ return Status;
+ }
+
+ //
+ // Fill in MLD message - Done
+ //
+ MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);
+ ASSERT (MldHead != NULL);
+ ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));
+ MldHead->Head.Type = ICMP_V6_LISTENER_DONE;
+ MldHead->Head.Code = 0;
+ IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);
+
+ HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));
+ PseudoChecksum = NetIp6PseudoHeadChecksum (
+ &Head.SourceAddress,
+ &Head.DestinationAddress,
+ IP6_ICMP,
+ sizeof (IP6_MLD_HEAD)
+ );
+
+ MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);
+
+ //
+ // Transmit the packet
+ //
+ return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
+}
+
+/**
+ Init the MLD data of the IP6 service instance. Configure
+ MNP to receive ALL SYSTEM multicast.
+
+ @param[in] IpSb The IP6 service whose MLD is to be initialized.
+
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the
+ operation.
+ @retval EFI_SUCCESS The MLD module successfully initialized.
+
+**/
+EFI_STATUS
+Ip6InitMld (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ EFI_IPv6_ADDRESS AllNodes;
+ IP6_MLD_GROUP *Group;
+ EFI_STATUS Status;
+
+ //
+ // Join the link-scope all-nodes multicast address (FF02::1).
+ // This address is started in Idle Listener state and never transitions to
+ // another state, and never sends a Report or Done for that address.
+ //
+
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
+
+ Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME);
+ if (Group == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ //
+ // Configure MNP to receive all-nodes multicast
+ //
+ Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ERROR:
+ RemoveEntryList (&Group->Link);
+ FreePool (Group);
+ return Status;
+}
+
+/**
+ Add a group address to the array of group addresses.
+ The caller should make sure that no duplicated address
+ existed in the array.
+
+ @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.
+ @param[in] Group The IP6 multicast address to add.
+
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete
+ the operation.
+ @retval EFI_SUCESS The address is added to the group address array.
+
+**/
+EFI_STATUS
+Ip6CombineGroups (
+ IN OUT IP6_PROTOCOL *IpInstance,
+ IN EFI_IPv6_ADDRESS *Group
+ )
+{
+ EFI_IPv6_ADDRESS *GroupList;
+
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
+ ASSERT (Group != NULL && IP6_IS_MULTICAST (Group));
+
+ IpInstance->GroupCount++;
+
+ GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS));
+ if (GroupList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (IpInstance->GroupCount > 1) {
+ ASSERT (IpInstance->GroupList != NULL);
+
+ CopyMem (
+ GroupList,
+ IpInstance->GroupList,
+ (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ FreePool (IpInstance->GroupList);
+ }
+
+ IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group);
+
+ IpInstance->GroupList = GroupList;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Remove a group address from the array of group addresses.
+ Although the function doesn't assume the byte order of Group,
+ the network byte order is used by the caller.
+
+ @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.
+ @param[in] Group The IP6 multicast address to remove.
+
+ @retval EFI_NOT_FOUND Cannot find the to be removed group address.
+ @retval EFI_SUCCESS The group address was successfully removed.
+
+**/
+EFI_STATUS
+Ip6RemoveGroup (
+ IN OUT IP6_PROTOCOL *IpInstance,
+ IN EFI_IPv6_ADDRESS *Group
+ )
+{
+ UINT32 Index;
+ UINT32 Count;
+
+ Count = IpInstance->GroupCount;
+
+ for (Index = 0; Index < Count; Index++) {
+ if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) {
+ break;
+ }
+ }
+
+ if (Index == Count) {
+ return EFI_NOT_FOUND;
+ }
+
+ while (Index < Count - 1) {
+ IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1);
+ Index++;
+ }
+
+ ASSERT (IpInstance->GroupCount > 0);
+ IpInstance->GroupCount--;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Join the multicast group on behalf of this IP6 service binding instance.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] Interface Points to an IP6_INTERFACE structure.
+ @param[in] Address The group address to join.
+
+ @retval EFI_SUCCESS Successfully join the multicast group.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval Others Failed to join the multicast group.
+
+**/
+EFI_STATUS
+Ip6JoinGroup (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *Interface,
+ IN EFI_IPv6_ADDRESS *Address
+ )
+{
+ IP6_MLD_GROUP *Group;
+ EFI_STATUS Status;
+
+ Group = Ip6FindMldEntry (IpSb, Address);
+ if (Group != NULL) {
+ Group->RefCnt++;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s)
+ // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss.
+ //
+ Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL);
+ if (Group == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Group->SendByUs = TRUE;
+
+ Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);
+ if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+ goto ERROR;
+ }
+
+ //
+ // Send unsolicited report when a node starts listening to a multicast address
+ //
+ Status = Ip6SendMldReport (IpSb, Interface, Address);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ERROR:
+ RemoveEntryList (&Group->Link);
+ FreePool (Group);
+ return Status;
+}
+
+/**
+ Leave the IP6 multicast group.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] Address The group address to leave.
+
+ @retval EFI_NOT_FOUND The IP6 service instance isn't in the group.
+ @retval EFI_SUCCESS Successfully leave the multicast group..
+ @retval Others Failed to leave the multicast group.
+
+**/
+EFI_STATUS
+Ip6LeaveGroup (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Address
+ )
+{
+ IP6_MLD_GROUP *Group;
+ EFI_STATUS Status;
+
+ Group = Ip6FindMldEntry (IpSb, Address);
+ if (Group == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If more than one instance is in the group, decrease
+ // the RefCnt then return.
+ //
+ if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // If multiple IP6 group addresses are mapped to the same
+ // multicast MAC address, don't configure the MNP to leave
+ // the MAC.
+ //
+ if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) {
+ Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac);
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ return Status;
+ }
+ }
+
+ //
+ // Send a leave report if we are the last node to report
+ //
+ if (Group->SendByUs) {
+ Status = Ip6SendMldDone (IpSb, Address);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ RemoveEntryList (&Group->Link);
+ FreePool (Group);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Worker function for EfiIp6Groups(). The caller
+ should make sure that the parameters are valid.
+
+ @param[in] IpInstance The IP6 child to change the setting.
+ @param[in] JoinFlag TRUE to join the group, otherwise leave it.
+ @param[in] GroupAddress The target group address. If NULL, leave all
+ the group addresses.
+
+ @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient resources.
+ @retval EFI_DEVICE_ERROR Failed to set the group configuraton.
+ @retval EFI_SUCCESS Successfully updated the group setting.
+ @retval EFI_NOT_FOUND Try to leave the group which it isn't a member.
+
+**/
+EFI_STATUS
+Ip6Groups (
+ IN IP6_PROTOCOL *IpInstance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ IP6_SERVICE *IpSb;
+ UINT32 Index;
+ EFI_IPv6_ADDRESS *Group;
+
+ IpSb = IpInstance->Service;
+
+ if (JoinFlag) {
+ ASSERT (GroupAddress != NULL);
+
+ for (Index = 0; Index < IpInstance->GroupCount; Index++) {
+ if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) {
+ return EFI_ALREADY_STARTED;
+ }
+ }
+
+ Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress);
+ if (!EFI_ERROR (Status)) {
+ return Ip6CombineGroups (IpInstance, GroupAddress);
+ }
+
+ return Status;
+ }
+
+ //
+ // Leave the group. Leave all the groups if GroupAddress is NULL.
+ //
+ for (Index = IpInstance->GroupCount; Index > 0; Index--) {
+ Group = IpInstance->GroupList + (Index - 1);
+
+ if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) {
+ Status = Ip6LeaveGroup (IpInstance->Service, Group);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Ip6RemoveGroup (IpInstance, Group);
+
+ if (IpInstance->GroupCount == 0) {
+ ASSERT (Index == 1);
+ FreePool (IpInstance->GroupList);
+ IpInstance->GroupList = NULL;
+ }
+
+ if (GroupAddress != NULL) {
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);
+}
+
+/**
+ Set a random value of the delay timer for the multicast address from the range
+ [0, Maximum Response Delay]. If a timer for any address is already
+ running, it is reset to the new random value only if the requested
+ Maximum Response Delay is less than the remaining value of the
+ running timer. If the Query packet specifies a Maximum Response
+ Delay of zero, each timer is effectively set to zero, and the action
+ specified below for timer expiration is performed immediately.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds.
+ @param[in] MulticastAddr The multicast address.
+ @param[in, out] Group Points to a IP6_MLD_GROUP list entry node.
+
+ @retval EFI_SUCCESS The delay timer is successfully updated or
+ timer expiration is performed immediately.
+ @retval Others Failed to send out MLD report message.
+
+**/
+EFI_STATUS
+Ip6UpdateDelayTimer (
+ IN IP6_SERVICE *IpSb,
+ IN UINT16 MaxRespDelay,
+ IN EFI_IPv6_ADDRESS *MulticastAddr,
+ IN OUT IP6_MLD_GROUP *Group
+ )
+{
+ UINT32 Delay;
+
+ //
+ // If the Query packet specifies a Maximum Response Delay of zero, perform timer
+ // expiration immediately.
+ //
+ if (MaxRespDelay == 0) {
+ Group->DelayTimer = 0;
+ return Ip6SendMldReport (IpSb, NULL, MulticastAddr);
+ }
+
+ Delay = (UINT32) (MaxRespDelay / 1000);
+
+ //
+ // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay]
+ // If a timer is already running, resets it if the request Maximum Response Delay
+ // is less than the remaining value of the running timer.
+ //
+ if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) {
+ Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ());
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Process the Multicast Listener Query message.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the MLD query packet.
+ @param[in] Packet The content of the MLD query packet with IP head
+ removed.
+
+ @retval EFI_SUCCESS The MLD query packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessMldQuery (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_IPv6_ADDRESS AllNodes;
+ IP6_MLD_GROUP *Group;
+ IP6_MLD_HEAD MldPacket;
+ LIST_ENTRY *Entry;
+ EFI_STATUS Status;
+
+ Status = EFI_INVALID_PARAMETER;
+
+ //
+ // Check the validity of the packet, generic query or specific query
+ //
+ if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
+ goto Exit;
+ }
+
+ if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {
+ goto Exit;
+ }
+
+ //
+ // The Packet points to MLD report raw data without Hop-By-Hop option.
+ //
+ NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);
+ MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay);
+
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
+ if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) {
+ //
+ // Receives a Multicast-Address-Specific Query, check it firstly
+ //
+ if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {
+ goto Exit;
+ }
+ //
+ // The node is not listening but it receives the specific query. Just return.
+ //
+ Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);
+ if (Group == NULL) {
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ Status = Ip6UpdateDelayTimer (
+ IpSb,
+ MldPacket.MaxRespDelay,
+ &MldPacket.Group,
+ Group
+ );
+ goto Exit;
+ }
+
+ //
+ // Receives a General Query, sets a delay timer for each multicast address it is listening
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
+ Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ NetbufFree (Packet);
+ return Status;
+}
+
+/**
+ Process the Multicast Listener Report message.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the MLD report packet.
+ @param[in] Packet The content of the MLD report packet with IP head
+ removed.
+
+ @retval EFI_SUCCESS The MLD report packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+
+**/
+EFI_STATUS
+Ip6ProcessMldReport (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_MLD_HEAD MldPacket;
+ IP6_MLD_GROUP *Group;
+ EFI_STATUS Status;
+
+ Status = EFI_INVALID_PARAMETER;
+
+ //
+ // Validate the incoming message, if invalid, drop it.
+ //
+ if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
+ goto Exit;
+ }
+
+ if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {
+ goto Exit;
+ }
+
+ //
+ // The Packet points to MLD report raw data without Hop-By-Hop option.
+ //
+ NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);
+ if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {
+ goto Exit;
+ }
+
+ Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);
+ if (Group == NULL) {
+ goto Exit;
+ }
+
+ //
+ // The report is sent by another node, stop its own timer relates to the multicast address and clear
+ //
+
+ if (!Group->SendByUs) {
+ Group->DelayTimer = 0;
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ NetbufFree (Packet);
+ return Status;
+}
+
+/**
+ The heartbeat timer of MLD module. It sends out a solicited MLD report when
+ DelayTimer expires.
+
+ @param[in] IpSb The IP6 service binding instance.
+
+**/
+VOID
+Ip6MldTimerTicking (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ IP6_MLD_GROUP *Group;
+ LIST_ENTRY *Entry;
+
+ //
+ // Send solicited report when timer expires
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {
+ Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
+ if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) {
+ Ip6SendMldReport (IpSb, NULL, &Group->Address);
+ }
+ }
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Mld.h b/NetworkPkg/Ip6Dxe/Ip6Mld.h
new file mode 100644
index 0000000000..e69f5d56b5
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Mld.h
@@ -0,0 +1,198 @@
+/** @file
+ Multicast Listener Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_MLD_H__
+#define __EFI_IP6_MLD_H__
+
+#define IP6_UNSOLICITED_REPORT_INTERVAL 10
+
+#pragma pack(1)
+typedef struct {
+ IP6_ICMP_HEAD Head;
+ UINT16 MaxRespDelay;
+ UINT16 Reserved;
+ EFI_IPv6_ADDRESS Group;
+} IP6_MLD_HEAD;
+#pragma pack()
+
+//
+// The status of multicast group. It isn't necessary to maintain
+// explicit state of host state diagram. A group with finity
+// DelayTime (less than 0xffffffff) is in "delaying listener" state. otherwise, it is in
+// "idle listener" state.
+//
+typedef struct {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ EFI_IPv6_ADDRESS Address;
+ UINT32 DelayTimer;
+ BOOLEAN SendByUs;
+ EFI_MAC_ADDRESS Mac;
+} IP6_MLD_GROUP;
+
+//
+// The MLD status. Each IP6 service instance has a MLD_SERVICE_DATA
+// attached. The Mldv1QuerySeen remember whether the server on this
+// connected network is v1 or v2.
+//
+typedef struct {
+ INTN Mldv1QuerySeen;
+ LIST_ENTRY Groups;
+} IP6_MLD_SERVICE_DATA;
+
+/**
+ Search a IP6_MLD_GROUP list entry node from a list array.
+
+ @param[in] IpSb Points to an IP6 service binding instance.
+ @param[in] MulticastAddr The IPv6 multicast address to be searched.
+
+ @return The found IP6_ML_GROUP list entry or NULL.
+
+**/
+IP6_MLD_GROUP *
+Ip6FindMldEntry (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *MulticastAddr
+ );
+
+/**
+ Init the MLD data of the IP6 service instance, configure
+ MNP to receive ALL SYSTEM multicasts.
+
+ @param[in] IpSb The IP6 service whose MLD is to be initialized.
+
+ @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the
+ operation.
+ @retval EFI_SUCCESS The MLD module successfully initialized.
+
+**/
+EFI_STATUS
+Ip6InitMld (
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ Join the multicast group on behalf of this IP6 service binding instance.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] Interface Points to an IP6_INTERFACE structure.
+ @param[in] Address The group address to join.
+
+ @retval EFI_SUCCESS Successfully joined the multicast group.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval Others Failed to join the multicast group.
+
+**/
+EFI_STATUS
+Ip6JoinGroup (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *Interface,
+ IN EFI_IPv6_ADDRESS *Address
+ );
+
+/**
+ Leave the IP6 multicast group.
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] Address The group address to leave.
+
+ @retval EFI_NOT_FOUND The IP6 service instance isn't in the group.
+ @retval EFI_SUCCESS Successfully left the multicast group.
+ @retval Others Failed to leave the multicast group.
+
+**/
+EFI_STATUS
+Ip6LeaveGroup (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Address
+ );
+
+/**
+ Worker function for EfiIp6Groups(). The caller
+ should verify that the parameters are valid.
+
+ @param[in] IpInstance The IP6 child to change the setting.
+ @param[in] JoinFlag TRUE to join the group, otherwise leave it.
+ @param[in] GroupAddress The target group address. If NULL, leave all
+ the group addresses.
+
+ @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_DEVICE_ERROR Failed to set the group configuraton.
+ @retval EFI_SUCCESS Successfully updated the group setting.
+ @retval EFI_NOT_FOUND Tried to leave a group of whom it isn't a member.
+
+**/
+EFI_STATUS
+Ip6Groups (
+ IN IP6_PROTOCOL *IpInstance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL
+ );
+
+/**
+ Process the Multicast Listener Query message.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the MLD query packet.
+ @param[in] Packet The content of the MLD query packet with IP head
+ removed.
+
+ @retval EFI_SUCCESS The MLD query packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessMldQuery (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Process the Multicast Listener Report message.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the MLD report packet.
+ @param[in] Packet The content of the MLD report packet with IP head
+ removed.
+
+ @retval EFI_SUCCESS The MLD report packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+
+**/
+EFI_STATUS
+Ip6ProcessMldReport (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+
+/**
+ The heartbeat timer of the MLD module. It sends out solicited MLD report when
+ DelayTimer expires.
+
+ @param[in] IpSb The IP6 service binding instance.
+
+**/
+VOID
+Ip6MldTimerTicking (
+ IN IP6_SERVICE *IpSb
+ );
+
+#endif
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.c b/NetworkPkg/Ip6Dxe/Ip6Nd.c
new file mode 100644
index 0000000000..f2a47a8073
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Nd.c
@@ -0,0 +1,3141 @@
+/** @file
+ Implementation of Neighbor Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_MAC_ADDRESS mZeroMacAddress;
+
+/**
+ Update the ReachableTime in IP6 service binding instance data, in milliseconds.
+
+ @param[in, out] IpSb Points to the IP6_SERVICE.
+
+**/
+VOID
+Ip6UpdateReachableTime (
+ IN OUT IP6_SERVICE *IpSb
+ )
+{
+ UINT32 Random;
+
+ Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE;
+ Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED;
+ IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE;
+}
+
+/**
+ Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number
+ of EFI_IP6_NEIGHBOR_CACHE is also returned.
+
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.
+ @param[out] NeighborCount The number of returned neighbor cache entries.
+ @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.
+
+ @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.
+
+**/
+EFI_STATUS
+Ip6BuildEfiNeighborCache (
+ IN IP6_PROTOCOL *IpInstance,
+ OUT UINT32 *NeighborCount,
+ OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache
+ )
+{
+ IP6_NEIGHBOR_ENTRY *Neighbor;
+ LIST_ENTRY *Entry;
+ IP6_SERVICE *IpSb;
+ UINT32 Count;
+ EFI_IP6_NEIGHBOR_CACHE *EfiNeighborCache;
+ EFI_IP6_NEIGHBOR_CACHE *NeighborCacheTmp;
+
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
+ ASSERT (NeighborCount != NULL && NeighborCache != NULL);
+
+ IpSb = IpInstance->Service;
+ Count = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {
+ Count++;
+ }
+
+ if (Count == 0) {
+ return EFI_SUCCESS;
+ }
+
+ NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE));
+ if (NeighborCacheTmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *NeighborCount = Count;
+ Count = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {
+ Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
+
+ EfiNeighborCache = NeighborCacheTmp + Count;
+
+ EfiNeighborCache->State = Neighbor->State;
+ IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor);
+ IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress);
+
+ Count++;
+ }
+
+ ASSERT (*NeighborCount == Count);
+ *NeighborCache = NeighborCacheTmp;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number
+ of prefix entries is also returned.
+
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.
+ @param[out] PrefixCount The number of returned prefix entries.
+ @param[out] PrefixTable The pointer to the array of PrefixTable.
+
+ @retval EFI_SUCCESS The prefix table successfully built.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table.
+
+**/
+EFI_STATUS
+Ip6BuildPrefixTable (
+ IN IP6_PROTOCOL *IpInstance,
+ OUT UINT32 *PrefixCount,
+ OUT EFI_IP6_ADDRESS_INFO **PrefixTable
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_SERVICE *IpSb;
+ UINT32 Count;
+ IP6_PREFIX_LIST_ENTRY *PrefixList;
+ EFI_IP6_ADDRESS_INFO *EfiPrefix;
+ EFI_IP6_ADDRESS_INFO *PrefixTableTmp;
+
+ NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
+ ASSERT (PrefixCount != NULL && PrefixTable != NULL);
+
+ IpSb = IpInstance->Service;
+ Count = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
+ Count++;
+ }
+
+ if (Count == 0) {
+ return EFI_SUCCESS;
+ }
+
+ PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO));
+ if (PrefixTableTmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *PrefixCount = Count;
+ Count = 0;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
+ PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
+ EfiPrefix = PrefixTableTmp + Count;
+ IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix);
+ EfiPrefix->PrefixLength = PrefixList->PrefixLength;
+
+ Count++;
+ }
+
+ ASSERT (*PrefixCount == Count);
+ *PrefixTable = PrefixTableTmp;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate and initialize a IP6 prefix list entry.
+
+ @param[in] IpSb The pointer to IP6_SERVICE instance.
+ @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list.
+ Otherwise, it is created for the autoconfiguration prefix list.
+ @param[in] ValidLifetime The length of time in seconds that the prefix
+ is valid for the purpose of on-link determination.
+ @param[in] PreferredLifetime The length of time in seconds that addresses
+ generated from the prefix via stateless address
+ autoconfiguration remain preferred.
+ @param[in] PrefixLength The prefix length of the Prefix.
+ @param[in] Prefix The prefix address.
+
+ @return NULL if it failed to allocate memory for the prefix node. Otherwise, point
+ to the created or existing prefix list entry.
+
+**/
+IP6_PREFIX_LIST_ENTRY *
+Ip6CreatePrefixListEntry (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN OnLinkOrAuto,
+ IN UINT32 ValidLifetime,
+ IN UINT32 PreferredLifetime,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *Prefix
+ )
+{
+ IP6_PREFIX_LIST_ENTRY *PrefixEntry;
+ IP6_ROUTE_ENTRY *RtEntry;
+ LIST_ENTRY *ListHead;
+ LIST_ENTRY *Entry;
+ IP6_PREFIX_LIST_ENTRY *TmpPrefixEntry;
+
+ if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength >= IP6_PREFIX_NUM) {
+ return NULL;
+ }
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ PrefixEntry = Ip6FindPrefixListEntry (
+ IpSb,
+ OnLinkOrAuto,
+ PrefixLength,
+ Prefix
+ );
+ if (PrefixEntry != NULL) {
+ PrefixEntry->RefCnt ++;
+ return PrefixEntry;
+ }
+
+ PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY));
+ if (PrefixEntry == NULL) {
+ return NULL;
+ }
+
+ PrefixEntry->RefCnt = 1;
+ PrefixEntry->ValidLifetime = ValidLifetime;
+ PrefixEntry->PreferredLifetime = PreferredLifetime;
+ PrefixEntry->PrefixLength = PrefixLength;
+ IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix);
+
+ ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix;
+
+ //
+ // Create a direct route entry for on-link prefix and insert to route area.
+ //
+ if (OnLinkOrAuto) {
+ RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL);
+ if (RtEntry == NULL) {
+ FreePool (PrefixEntry);
+ return NULL;
+ }
+
+ RtEntry->Flag = IP6_DIRECT_ROUTE;
+ InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link);
+ IpSb->RouteTable->TotalNum++;
+ }
+
+ //
+ // Insert the prefix entry in the order that a prefix with longer prefix length
+ // is put ahead in the list.
+ //
+ NET_LIST_FOR_EACH (Entry, ListHead) {
+ TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link);
+
+ if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) {
+ break;
+ }
+ }
+
+ NetListInsertBefore (Entry, &PrefixEntry->Link);
+
+ return PrefixEntry;
+}
+
+/**
+ Destory a IP6 prefix list entry.
+
+ @param[in] IpSb The pointer to IP6_SERVICE instance.
+ @param[in] PrefixEntry The to be destroyed prefix list entry.
+ @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list.
+ Otherwise remove from autoconfiguration prefix list.
+ @param[in] ImmediateDelete If TRUE, remove the entry directly.
+ Otherwise, check the reference count to see whether
+ it should be removed.
+
+**/
+VOID
+Ip6DestroyPrefixListEntry (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_PREFIX_LIST_ENTRY *PrefixEntry,
+ IN BOOLEAN OnLinkOrAuto,
+ IN BOOLEAN ImmediateDelete
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_INTERFACE *IpIf;
+ EFI_STATUS Status;
+
+ if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) {
+ return ;
+ }
+
+ if (OnLinkOrAuto) {
+ //
+ // Remove the direct route for onlink prefix from route table.
+ //
+ do {
+ Status = Ip6DelRoute (
+ IpSb->RouteTable,
+ &PrefixEntry->Prefix,
+ PrefixEntry->PrefixLength,
+ NULL
+ );
+ } while (Status != EFI_NOT_FOUND);
+ } else {
+ //
+ // Remove the corresponding addresses generated from this autonomous prefix.
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);
+
+ Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength);
+ }
+ }
+
+ RemoveEntryList (&PrefixEntry->Link);
+ FreePool (PrefixEntry);
+}
+
+/**
+ Search the list array to find an IP6 prefix list entry.
+
+ @param[in] IpSb The pointer to IP6_SERVICE instance.
+ @param[in] OnLinkOrAuto If TRUE, the search the link prefix list,
+ Otherwise search the autoconfiguration prefix list.
+ @param[in] PrefixLength The prefix length of the Prefix
+ @param[in] Prefix The prefix address.
+
+ @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the
+ pointer to the IP6 prefix list entry.
+
+**/
+IP6_PREFIX_LIST_ENTRY *
+Ip6FindPrefixListEntry (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN OnLinkOrAuto,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *Prefix
+ )
+{
+ IP6_PREFIX_LIST_ENTRY *PrefixList;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *ListHead;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (Prefix != NULL);
+
+ if (OnLinkOrAuto) {
+ ListHead = &IpSb->OnlinkPrefix;
+ } else {
+ ListHead = &IpSb->AutonomousPrefix;
+ }
+
+ NET_LIST_FOR_EACH (Entry, ListHead) {
+ PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
+ if (PrefixLength != 255) {
+ //
+ // Perform exactly prefix match.
+ //
+ if (PrefixList->PrefixLength == PrefixLength &&
+ NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) {
+ return PrefixList;
+ }
+ } else {
+ //
+ // Perform the longest prefix match. The list is already sorted with
+ // the longest length prefix put at the head of the list.
+ //
+ if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) {
+ return PrefixList;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Release the resource in the prefix list table, and destroy the list entry and
+ corresponding addresses or route entries.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] ListHead The list entry head of the prefix list table.
+
+**/
+VOID
+Ip6CleanPrefixListTable (
+ IN IP6_SERVICE *IpSb,
+ IN LIST_ENTRY *ListHead
+ )
+{
+ IP6_PREFIX_LIST_ENTRY *PrefixList;
+ BOOLEAN OnLink;
+
+ OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix);
+
+ while (!IsListEmpty (ListHead)) {
+ PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link);
+ Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);
+ }
+}
+
+/**
+ Callback function when address resolution is finished. It will cancel
+ all the queued frames if the address resolution failed, or transmit them
+ if the request succeeded.
+
+ @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.
+
+**/
+VOID
+Ip6OnArpResolved (
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_NEIGHBOR_ENTRY *ArpQue;
+ IP6_SERVICE *IpSb;
+ IP6_LINK_TX_TOKEN *Token;
+ EFI_STATUS Status;
+ BOOLEAN Sent;
+
+ ArpQue = (IP6_NEIGHBOR_ENTRY *) Context;
+ if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) {
+ return ;
+ }
+
+ IpSb = ArpQue->Interface->Service;
+ if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) {
+ return ;
+ }
+
+ //
+ // ARP resolve failed for some reason. Release all the frame
+ // and ARP queue itself. Ip6FreeArpQue will call the frame's
+ // owner back.
+ //
+ if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) {
+ Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL);
+ return ;
+ }
+
+ //
+ // ARP resolve succeeded, Transmit all the frame.
+ //
+ Sent = FALSE;
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
+ RemoveEntryList (Entry);
+
+ Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
+ IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress);
+
+ //
+ // Insert the tx token before transmitting it via MNP as the FrameSentDpc
+ // may be called before Mnp->Transmit returns which will remove this tx
+ // token from the SentFrames list. Remove it from the list if the returned
+ // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the
+ // FrameSentDpc won't be queued.
+ //
+ InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link);
+
+ Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);
+ if (EFI_ERROR (Status)) {
+ RemoveEntryList (&Token->Link);
+ Token->CallBack (Token->Packet, Status, 0, Token->Context);
+
+ Ip6FreeLinkTxToken (Token);
+ continue;
+ } else {
+ Sent = TRUE;
+ }
+ }
+
+ //
+ // Free the ArpQue only but not the whole neighbor entry.
+ //
+ Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL);
+
+ if (Sent && (ArpQue->State == EfiNeighborStale)) {
+ ArpQue->State = EfiNeighborDelay;
+ ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);
+ }
+}
+
+/**
+ Allocate and initialize an IP6 neighbor cache entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] CallBack The callback function to be called when
+ address resolution is finished.
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.
+ @param[in] LinkAddress Points to the MAC address of the neighbor.
+ Ignored if NULL.
+
+ @return NULL if failed to allocate memory for the neighbor cache entry.
+ Otherwise, point to the created neighbor cache entry.
+
+**/
+IP6_NEIGHBOR_ENTRY *
+Ip6CreateNeighborEntry (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_ARP_CALLBACK CallBack,
+ IN EFI_IPv6_ADDRESS *Ip6Address,
+ IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL
+ )
+{
+ IP6_NEIGHBOR_ENTRY *Entry;
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (Ip6Address!= NULL);
+
+ Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY));
+ if (Entry == NULL) {
+ return NULL;
+ }
+
+ Entry->RefCnt = 1;
+ Entry->IsRouter = FALSE;
+ Entry->ArpFree = FALSE;
+ Entry->Dynamic = FALSE;
+ Entry->State = EfiNeighborInComplete;
+ Entry->Transmit = IP6_MAX_MULTICAST_SOLICIT + 1;
+ Entry->CallBack = CallBack;
+ Entry->Interface = NULL;
+
+ InitializeListHead (&Entry->Frames);
+
+ IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address);
+
+ if (LinkAddress != NULL) {
+ IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress);
+ } else {
+ IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress);
+ }
+
+ InsertHeadList (&IpSb->NeighborTable, &Entry->Link);
+
+ //
+ // If corresponding default router entry exists, establish the relationship.
+ //
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address);
+ if (DefaultRouter != NULL) {
+ DefaultRouter->NeighborCache = Entry;
+ }
+
+ return Entry;
+}
+
+/**
+ Search a IP6 neighbor cache entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.
+
+ @return NULL if it failed to find the matching neighbor cache entry.
+ Otherwise, point to the found neighbor cache entry.
+
+**/
+IP6_NEIGHBOR_ENTRY *
+Ip6FindNeighborEntry (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_NEIGHBOR_ENTRY *Neighbor;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (Ip6Address != NULL);
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {
+ Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
+ if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) {
+ RemoveEntryList (Entry);
+ InsertHeadList (&IpSb->NeighborTable, Entry);
+
+ return Neighbor;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Free a IP6 neighbor cache entry and remove all the frames on the address
+ resolution queue that pass the FrameToCancel. That is, either FrameToCancel
+ is NULL, or it returns true for the frame.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] NeighborCache The to be free neighbor cache entry.
+ @param[in] SendIcmpError If TRUE, send out ICMP error.
+ @param[in] FullFree If TRUE, remove the neighbor cache entry.
+ Otherwise remove the pending frames.
+ @param[in] IoStatus The status returned to the cancelled frames'
+ callback function.
+ @param[in] FrameToCancel Function to select which frame to cancel.
+ This is an optional parameter that may be NULL.
+ @param[in] Context Opaque parameter to the FrameToCancel.
+ Ignored if FrameToCancel is NULL.
+
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval EFI_SUCCESS The operation finished successfully.
+
+**/
+EFI_STATUS
+Ip6FreeNeighborEntry (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_NEIGHBOR_ENTRY *NeighborCache,
+ IN BOOLEAN SendIcmpError,
+ IN BOOLEAN FullFree,
+ IN EFI_STATUS IoStatus,
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context OPTIONAL
+ )
+{
+ IP6_LINK_TX_TOKEN *TxToken;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+
+ //
+ // If FrameToCancel fails, the token will not be released.
+ // To avoid the memory leak, stop this usage model.
+ //
+ if (FullFree && FrameToCancel != NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) {
+ TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
+
+ if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ TxToken->Packet,
+ NULL,
+ &TxToken->Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_DEST_UNREACHABLE,
+ ICMP_V6_ADDR_UNREACHABLE,
+ NULL
+ );
+ }
+
+ if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) {
+ RemoveEntryList (Entry);
+ TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context);
+ Ip6FreeLinkTxToken (TxToken);
+ }
+ }
+
+ if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) {
+ RemoveEntryList (&NeighborCache->ArpList);
+ NeighborCache->ArpFree = FALSE;
+ }
+
+ if (FullFree) {
+ if (NeighborCache->IsRouter) {
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor);
+ if (DefaultRouter != NULL) {
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
+ }
+ }
+
+ RemoveEntryList (&NeighborCache->Link);
+ FreePool (NeighborCache);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate and initialize an IP6 default router entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Ip6Address The IPv6 address of the default router.
+ @param[in] RouterLifetime The lifetime associated with the default
+ router, in units of seconds.
+
+ @return NULL if it failed to allocate memory for the default router node.
+ Otherwise, point to the created default router node.
+
+**/
+IP6_DEFAULT_ROUTER *
+Ip6CreateDefaultRouter (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Ip6Address,
+ IN UINT16 RouterLifetime
+ )
+{
+ IP6_DEFAULT_ROUTER *Entry;
+ IP6_ROUTE_ENTRY *RtEntry;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (Ip6Address != NULL);
+
+ Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER));
+ if (Entry == NULL) {
+ return NULL;
+ }
+
+ Entry->RefCnt = 1;
+ Entry->Lifetime = RouterLifetime;
+ Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address);
+ IP6_COPY_ADDRESS (&Entry->Router, Ip6Address);
+
+ //
+ // Add a default route into route table with both Destination and PrefixLength set to zero.
+ //
+ RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address);
+ if (RtEntry == NULL) {
+ FreePool (Entry);
+ return NULL;
+ }
+
+ InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link);
+ IpSb->RouteTable->TotalNum++;
+
+ InsertTailList (&IpSb->DefaultRouterList, &Entry->Link);
+
+ return Entry;
+}
+
+/**
+ Destroy an IP6 default router entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.
+
+**/
+VOID
+Ip6DestroyDefaultRouter (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_DEFAULT_ROUTER *DefaultRouter
+ )
+{
+ EFI_STATUS Status;
+
+ RemoveEntryList (&DefaultRouter->Link);
+
+ //
+ // Update the Destination Cache - all entries using the time-out router as next-hop
+ // should perform next-hop determination again.
+ //
+ do {
+ Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router);
+ } while (Status != EFI_NOT_FOUND);
+
+ FreePool (DefaultRouter);
+}
+
+/**
+ Clean an IP6 default router list.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.
+
+**/
+VOID
+Ip6CleanDefaultRouterList (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+
+ while (!IsListEmpty (&IpSb->DefaultRouterList)) {
+ DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link);
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
+ }
+}
+
+/**
+ Search a default router node from an IP6 default router list.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Ip6Address The IPv6 address of the to be searched default router node.
+
+ @return NULL if it failed to find the matching default router node.
+ Otherwise, point to the found default router node.
+
+**/
+IP6_DEFAULT_ROUTER *
+Ip6FindDefaultRouter (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (Ip6Address != NULL);
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) {
+ DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);
+ if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) {
+ return DefaultRouter;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ The function to be called after DAD (Duplicate Address Detection) is performed.
+
+ @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.
+ @param[in] IpIf Points to the IP6_INTERFACE.
+ @param[in] DadEntry The DAD entry which already performed DAD.
+
+**/
+VOID
+Ip6OnDADFinished (
+ IN BOOLEAN IsDadPassed,
+ IN IP6_INTERFACE *IpIf,
+ IN IP6_DAD_ENTRY *DadEntry
+ )
+{
+ IP6_SERVICE *IpSb;
+ IP6_ADDRESS_INFO *AddrInfo;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ UINT16 OptBuf[4];
+ EFI_DHCP6_PACKET_OPTION *Oro;
+ EFI_DHCP6_RETRANSMISSION InfoReqReXmit;
+
+ IpSb = IpIf->Service;
+ AddrInfo = DadEntry->AddressInfo;
+
+ if (IsDadPassed) {
+ //
+ // DAD succeed.
+ //
+ if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
+ ASSERT (!IpSb->LinkLocalOk);
+
+ IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address);
+ IpSb->LinkLocalOk = TRUE;
+ IpIf->Configured = TRUE;
+
+ //
+ // Check whether DHCP6 need to be started.
+ //
+ Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6;
+
+ if (IpSb->Dhcp6NeedStart) {
+ Dhcp6->Start (Dhcp6);
+ IpSb->Dhcp6NeedStart = FALSE;
+ }
+
+ if (IpSb->Dhcp6NeedInfoRequest) {
+ //
+ // Set the exta options to send. Here we only want the option request option
+ // with DNS SERVERS.
+ //
+ Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf;
+ Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);
+ Oro->OpLen = HTONS (2);
+ *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);
+
+ InfoReqReXmit.Irt = 4;
+ InfoReqReXmit.Mrc = 64;
+ InfoReqReXmit.Mrt = 60;
+ InfoReqReXmit.Mrd = 0;
+
+ Dhcp6->InfoRequest (
+ Dhcp6,
+ TRUE,
+ Oro,
+ 0,
+ NULL,
+ &InfoReqReXmit,
+ IpSb->Ip6ConfigInstance.Dhcp6Event,
+ Ip6ConfigOnDhcp6Reply,
+ &IpSb->Ip6ConfigInstance
+ );
+ }
+
+ //
+ // Add an on-link prefix for link-local address.
+ //
+ Ip6CreatePrefixListEntry (
+ IpSb,
+ TRUE,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ (UINT32) IP6_INFINIT_LIFETIME,
+ IP6_LINK_LOCAL_PREFIX_LENGTH,
+ &IpSb->LinkLocalAddr
+ );
+
+ } else {
+ //
+ // Global scope unicast address.
+ //
+ Ip6AddAddr (IpIf, AddrInfo);
+
+ //
+ // Add an on-link prefix for this address.
+ //
+ Ip6CreatePrefixListEntry (
+ IpSb,
+ TRUE,
+ AddrInfo->ValidLifetime,
+ AddrInfo->PreferredLifetime,
+ AddrInfo->PrefixLength,
+ &AddrInfo->Address
+ );
+
+ IpIf->Configured = TRUE;
+ }
+ } else {
+ //
+ // Leave the group we joined before.
+ //
+ Ip6LeaveGroup (IpSb, &DadEntry->Destination);
+ }
+
+ if (DadEntry->Callback != NULL) {
+ DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context);
+ }
+
+ if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
+ FreePool (AddrInfo);
+ RemoveEntryList (&DadEntry->Link);
+ FreePool (DadEntry);
+ //
+ // Disable IP operation since link-local address is a duplicate address.
+ //
+ IpSb->LinkLocalDadFail = TRUE;
+ IpSb->Mnp->Configure (IpSb->Mnp, NULL);
+ gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
+ gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);
+ return ;
+ }
+
+ if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
+ //
+ // Free the AddressInfo we hold if DAD fails or it is a link-local address.
+ //
+ FreePool (AddrInfo);
+ }
+
+ RemoveEntryList (&DadEntry->Link);
+ FreePool (DadEntry);
+}
+
+/**
+ Create a DAD (Duplicate Address Detection) entry and queue it to be performed.
+
+ @param[in] IpIf Points to the IP6_INTERFACE.
+ @param[in] AddressInfo The address information which needs DAD performed.
+ @param[in] Callback The callback routine that will be called after DAD
+ is performed. This is an optional parameter that
+ may be NULL.
+ @param[in] Context The opaque parameter for a DAD callback routine.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The DAD entry was created and queued.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the
+ operation.
+
+
+**/
+EFI_STATUS
+Ip6InitDADProcess (
+ IN IP6_INTERFACE *IpIf,
+ IN IP6_ADDRESS_INFO *AddressInfo,
+ IN IP6_DAD_CALLBACK Callback OPTIONAL,
+ IN VOID *Context OPTIONAL
+ )
+{
+ IP6_DAD_ENTRY *Entry;
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits;
+ IP6_SERVICE *IpSb;
+ EFI_STATUS Status;
+ UINT32 MaxDelayTick;
+
+ NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE);
+ ASSERT (AddressInfo != NULL);
+
+ Status = EFI_SUCCESS;
+ IpSb = IpIf->Service;
+ DadXmits = &IpSb->Ip6ConfigInstance.DadXmits;
+
+ //
+ // Allocate the resources and insert info
+ //
+ Entry = AllocatePool (sizeof (IP6_DAD_ENTRY));
+ if (Entry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Map the incoming unicast address to solicited-node multicast address
+ //
+ Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination);
+
+ //
+ // Join in the solicited-node multicast address.
+ //
+ Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination);
+ if (EFI_ERROR (Status)) {
+ FreePool (Entry);
+ return Status;
+ }
+
+ Entry->Signature = IP6_DAD_ENTRY_SIGNATURE;
+ Entry->MaxTransmit = DadXmits->DupAddrDetectTransmits;
+ Entry->Transmit = 0;
+ Entry->Receive = 0;
+ MaxDelayTick = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS;
+ Entry->RetransTick = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5;
+ Entry->AddressInfo = AddressInfo;
+ Entry->Callback = Callback;
+ Entry->Context = Context;
+ InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link);
+
+ if (Entry->MaxTransmit == 0) {
+ //
+ // DAD is disabled on this interface, immediately mark this DAD successful.
+ //
+ Ip6OnDADFinished (TRUE, IpIf, Entry);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Search IP6_DAD_ENTRY from the Duplicate Address Detection List.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Target The address information which needs DAD performed .
+ @param[out] Interface If not NULL, output the IP6 interface that configures
+ the tentative address.
+
+ @return NULL if failed to find the matching DAD entry.
+ Otherwise, point to the found DAD entry.
+
+**/
+IP6_DAD_ENTRY *
+Ip6FindDADEntry (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Target,
+ OUT IP6_INTERFACE **Interface OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Entry2;
+ IP6_INTERFACE *IpIf;
+ IP6_DAD_ENTRY *DupAddrDetect;
+ IP6_ADDRESS_INFO *AddrInfo;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
+
+ NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) {
+ DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);
+ AddrInfo = DupAddrDetect->AddressInfo;
+ if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) {
+ if (Interface != NULL) {
+ *Interface = IpIf;
+ }
+ return DupAddrDetect;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Generate router solicit message and send it out to Destination Address or
+ All Router Link Local scope multicast address.
+
+ @param[in] IpSb The IP service to send the packet.
+ @param[in] Interface If not NULL, points to the IP6 interface to send
+ the packet.
+ @param[in] SourceAddress If not NULL, the source address of the message.
+ @param[in] DestinationAddress If not NULL, the destination address of the message.
+ @param[in] SourceLinkAddress If not NULL, the MAC address of the source.
+ A source link-layer address option will be appended
+ to the message.
+
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
+ operation.
+ @retval EFI_SUCCESS The router solicit message was successfully sent.
+
+**/
+EFI_STATUS
+Ip6SendRouterSolicit (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *Interface OPTIONAL,
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,
+ IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL,
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL
+ )
+{
+ NET_BUF *Packet;
+ EFI_IP6_HEADER Head;
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;
+ UINT16 PayloadLen;
+ IP6_INTERFACE *IpIf;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ IpIf = Interface;
+ if (IpIf == NULL && IpSb->DefaultInterface != NULL) {
+ IpIf = IpSb->DefaultInterface;
+ }
+
+ //
+ // Generate the packet to be sent
+ //
+
+ PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD);
+ if (SourceLinkAddress != NULL) {
+ PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION);
+ }
+
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create the basic IPv6 header.
+ //
+ Head.FlowLabelL = 0;
+ Head.FlowLabelH = 0;
+ Head.PayloadLength = HTONS (PayloadLen);
+ Head.NextHeader = IP6_ICMP;
+ Head.HopLimit = IP6_HOP_LIMIT;
+
+ if (SourceAddress != NULL) {
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
+ } else {
+ ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
+ }
+
+
+ if (DestinationAddress != NULL) {
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
+ } else {
+ Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress);
+ }
+
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
+
+ //
+ // Fill in the ICMP header, and Source link-layer address if contained.
+ //
+
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
+ ASSERT (IcmpHead != NULL);
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
+ IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT;
+ IcmpHead->Head.Code = 0;
+
+ LinkLayerOption = NULL;
+ if (SourceLinkAddress != NULL) {
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
+ Packet,
+ sizeof (IP6_ETHER_ADDR_OPTION),
+ FALSE
+ );
+ ASSERT (LinkLayerOption != NULL);
+ LinkLayerOption->Type = Ip6OptionEtherSource;
+ LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION);
+ CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
+ }
+
+ //
+ // Transmit the packet
+ //
+ return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
+}
+
+/**
+ Generate a Neighbor Advertisement message and send it out to Destination Address.
+
+ @param[in] IpSb The IP service to send the packet.
+ @param[in] SourceAddress The source address of the message.
+ @param[in] DestinationAddress The destination address of the message.
+ @param[in] TargetIp6Address The target address field in the Neighbor Solicitation
+ message that prompted this advertisement.
+ @param[in] TargetLinkAddress The MAC address for the target, i.e. the sender
+ of the advertisement.
+ @param[in] IsRouter If TRUE, indicates the sender is a router.
+ @param[in] Override If TRUE, indicates the advertisement should override
+ an existing cache entry and update the MAC address.
+ @param[in] Solicited If TRUE, indicates the advertisement was sent
+ in response to a Neighbor Solicitation from
+ the Destination address.
+
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
+ operation.
+ @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.
+
+**/
+EFI_STATUS
+Ip6SendNeighborAdvertise (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *SourceAddress,
+ IN EFI_IPv6_ADDRESS *DestinationAddress,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *TargetLinkAddress,
+ IN BOOLEAN IsRouter,
+ IN BOOLEAN Override,
+ IN BOOLEAN Solicited
+ )
+{
+ NET_BUF *Packet;
+ EFI_IP6_HEADER Head;
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;
+ EFI_IPv6_ADDRESS *Target;
+ UINT16 PayloadLen;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ //
+ // The Neighbor Advertisement message must include a Target link-layer address option
+ // when responding to multicast solicitation and should include such option when
+ // responding to unicast solicitation. It also must include such option as unsolicited
+ // advertisement.
+ //
+ ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL);
+
+ PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION));
+
+ //
+ // Generate the packet to be sent
+ //
+
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create the basic IPv6 header.
+ //
+ Head.FlowLabelL = 0;
+ Head.FlowLabelH = 0;
+ Head.PayloadLength = HTONS (PayloadLen);
+ Head.NextHeader = IP6_ICMP;
+ Head.HopLimit = IP6_HOP_LIMIT;
+
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
+
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
+
+ //
+ // Fill in the ICMP header, Target address, and Target link-layer address.
+ // Set the Router flag, Solicited flag and Override flag.
+ //
+
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
+ ASSERT (IcmpHead != NULL);
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
+ IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE;
+ IcmpHead->Head.Code = 0;
+
+ if (IsRouter) {
+ IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG;
+ }
+
+ if (Solicited) {
+ IcmpHead->Fourth |= IP6_SOLICITED_FLAG;
+ }
+
+ if (Override) {
+ IcmpHead->Fourth |= IP6_OVERRIDE_FLAG;
+ }
+
+ Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
+ ASSERT (Target != NULL);
+ IP6_COPY_ADDRESS (Target, TargetIp6Address);
+
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
+ Packet,
+ sizeof (IP6_ETHER_ADDR_OPTION),
+ FALSE
+ );
+ ASSERT (LinkLayerOption != NULL);
+ LinkLayerOption->Type = Ip6OptionEtherTarget;
+ LinkLayerOption->Length = 1;
+ CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6);
+
+ //
+ // Transmit the packet
+ //
+ return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
+}
+
+/**
+ Generate the Neighbor Solicitation message and send it to the Destination Address.
+
+ @param[in] IpSb The IP service to send the packet
+ @param[in] SourceAddress The source address of the message.
+ @param[in] DestinationAddress The destination address of the message.
+ @param[in] TargetIp6Address The IP address of the target of the solicitation.
+ It must not be a multicast address.
+ @param[in] SourceLinkAddress The MAC address for the sender. If not NULL,
+ a source link-layer address option will be appended
+ to the message.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
+ operation.
+ @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.
+
+**/
+EFI_STATUS
+Ip6SendNeighborSolicit (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *SourceAddress,
+ IN EFI_IPv6_ADDRESS *DestinationAddress,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL
+ )
+{
+ NET_BUF *Packet;
+ EFI_IP6_HEADER Head;
+ IP6_ICMP_INFORMATION_HEAD *IcmpHead;
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;
+ EFI_IPv6_ADDRESS *Target;
+ BOOLEAN IsDAD;
+ UINT16 PayloadLen;
+ IP6_NEIGHBOR_ENTRY *Neighbor;
+
+ //
+ // Check input parameters
+ //
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ if (DestinationAddress == NULL || TargetIp6Address == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IsDAD = FALSE;
+
+ if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) {
+ IsDAD = TRUE;
+ }
+
+ //
+ // The Neighbor Solicitation message should include a source link-layer address option
+ // if the solicitation is not sent by performing DAD - Duplicate Address Detection.
+ // Otherwise must not include it.
+ //
+ PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS));
+
+ if (!IsDAD) {
+ if (SourceLinkAddress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION));
+ }
+
+ //
+ // Generate the packet to be sent
+ //
+
+ Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create the basic IPv6 header
+ //
+ Head.FlowLabelL = 0;
+ Head.FlowLabelH = 0;
+ Head.PayloadLength = HTONS (PayloadLen);
+ Head.NextHeader = IP6_ICMP;
+ Head.HopLimit = IP6_HOP_LIMIT;
+
+ if (SourceAddress != NULL) {
+ IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
+ } else {
+ ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
+
+ NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
+
+ //
+ // Fill in the ICMP header, Target address, and Source link-layer address.
+ //
+ IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
+ ASSERT (IcmpHead != NULL);
+ ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
+ IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT;
+ IcmpHead->Head.Code = 0;
+
+ Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
+ ASSERT (Target != NULL);
+ IP6_COPY_ADDRESS (Target, TargetIp6Address);
+
+ LinkLayerOption = NULL;
+ if (!IsDAD) {
+
+ //
+ // Fill in the source link-layer address option
+ //
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
+ Packet,
+ sizeof (IP6_ETHER_ADDR_OPTION),
+ FALSE
+ );
+ ASSERT (LinkLayerOption != NULL);
+ LinkLayerOption->Type = Ip6OptionEtherSource;
+ LinkLayerOption->Length = 1;
+ CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
+ }
+
+ //
+ // Create a Neighbor Cache entry in the INCOMPLETE state when performing
+ // address resolution.
+ //
+ if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) {
+ Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
+ if (Neighbor == NULL) {
+ Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL);
+ ASSERT (Neighbor != NULL);
+ }
+ }
+
+ //
+ // Transmit the packet
+ //
+ return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
+}
+
+/**
+ Process the Neighbor Solicitation message. The message may be sent for Duplicate
+ Address Detection or Address Resolution.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the message.
+ @param[in] Packet The content of the message with IP head removed.
+
+ @retval EFI_SUCCESS The packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessNeighborSolicit (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_INFORMATION_HEAD Icmp;
+ EFI_IPv6_ADDRESS Target;
+ IP6_ETHER_ADDR_OPTION LinkLayerOption;
+ BOOLEAN IsDAD;
+ BOOLEAN IsUnicast;
+ BOOLEAN IsMaintained;
+ IP6_DAD_ENTRY *DupAddrDetect;
+ IP6_INTERFACE *IpIf;
+ IP6_NEIGHBOR_ENTRY *Neighbor;
+ BOOLEAN Solicited;
+ BOOLEAN UpdateCache;
+ EFI_IPv6_ADDRESS Dest;
+ UINT16 OptionLen;
+ UINT8 *Option;
+ BOOLEAN Provided;
+ EFI_STATUS Status;
+ VOID *MacAddress;
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+ NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
+
+ //
+ // Perform Message Validation:
+ // The IP Hop Limit field has a value of 255, i.e., the packet
+ // could not possibly have been forwarded by a router.
+ // ICMP Code is 0.
+ // Target Address is not a multicast address.
+ //
+ Status = EFI_INVALID_PARAMETER;
+
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {
+ goto Exit;
+ }
+
+ //
+ // ICMP length is 24 or more octets.
+ //
+ OptionLen = 0;
+ if (Head->PayloadLength < IP6_ND_LENGTH) {
+ goto Exit;
+ } else {
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);
+ Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
+
+ //
+ // All included options should have a length that is greater than zero.
+ //
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {
+ goto Exit;
+ }
+ }
+
+ IsDAD = NetIp6IsUnspecifiedAddr (&Head->SourceAddress);
+ IsUnicast = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress);
+ IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL);
+
+ Provided = FALSE;
+ if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
+ NetbufCopy (
+ Packet,
+ IP6_ND_LENGTH,
+ sizeof (IP6_ETHER_ADDR_OPTION),
+ (UINT8 *) &LinkLayerOption
+ );
+ //
+ // The solicitation for neighbor discovery should include a source link-layer
+ // address option. If the option is not recognized, silently ignore it.
+ //
+ if (LinkLayerOption.Type == Ip6OptionEtherSource) {
+ if (IsDAD) {
+ //
+ // If the IP source address is the unspecified address, the source
+ // link-layer address option must not be included in the message.
+ //
+ goto Exit;
+ }
+
+ Provided = TRUE;
+ }
+ }
+
+ //
+ // If the IP source address is the unspecified address, the IP
+ // destination address is a solicited-node multicast address.
+ //
+ if (IsDAD && IsUnicast) {
+ goto Exit;
+ }
+
+ //
+ // If the target address is tentative, and the source address is a unicast address,
+ // the solicitation's sender is performing address resolution on the target;
+ // the solicitation should be silently ignored.
+ //
+ if (!IsDAD && !IsMaintained) {
+ goto Exit;
+ }
+
+ //
+ // If received unicast neighbor solicitation but destination is not this node,
+ // drop the packet.
+ //
+ if (IsUnicast && !IsMaintained) {
+ goto Exit;
+ }
+
+ //
+ // In DAD, when target address is a tentative address,
+ // process the received neighbor solicitation message but not send out response.
+ //
+ if (IsDAD && !IsMaintained) {
+ DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
+ if (DupAddrDetect != NULL) {
+ if (DupAddrDetect->Transmit == 0) {
+ //
+ // The NS is from another node to performing DAD on the same address since
+ // we haven't send out any NS yet. Fail DAD for the tentative address.
+ //
+ Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
+ Status = EFI_ICMP_ERROR;
+ goto Exit;
+ }
+
+ //
+ // Check the MAC address of the incoming packet.
+ //
+ if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) {
+ goto Exit;
+ }
+
+ MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress;
+ if (MacAddress != NULL) {
+ if (CompareMem (
+ MacAddress,
+ &IpSb->SnpMode.CurrentAddress,
+ IpSb->SnpMode.HwAddressSize
+ ) != 0) {
+ //
+ // The NS is from another node to performing DAD on the same address.
+ // Fail DAD for the tentative address.
+ //
+ Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
+ Status = EFI_ICMP_ERROR;
+ } else {
+ //
+ // The below layer loopback the NS we sent. Record it and wait for more.
+ //
+ DupAddrDetect->Receive++;
+ Status = EFI_SUCCESS;
+ }
+ }
+ }
+ goto Exit;
+ }
+
+ //
+ // If the solicitation does not contain a link-layer address, DO NOT create or
+ // update the neighbor cache entries.
+ //
+ if (Provided) {
+ Neighbor = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
+ UpdateCache = FALSE;
+
+ if (Neighbor == NULL) {
+ Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL);
+ if (Neighbor == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ UpdateCache = TRUE;
+ } else {
+ if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) {
+ UpdateCache = TRUE;
+ }
+ }
+
+ if (UpdateCache) {
+ Neighbor->State = EfiNeighborStale;
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
+ //
+ // Send queued packets if exist.
+ //
+ Neighbor->CallBack ((VOID *) Neighbor);
+ }
+ }
+
+ //
+ // Sends a Neighbor Advertisement as response.
+ // Set the Router flag to zero since the node is a host.
+ // If the source address of the solicitation is unspeicifed, and target address
+ // is one of the maintained address, reply a unsolicited multicast advertisement.
+ //
+ if (IsDAD && IsMaintained) {
+ Solicited = FALSE;
+ Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest);
+ } else {
+ Solicited = TRUE;
+ IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress);
+ }
+
+ Status = Ip6SendNeighborAdvertise (
+ IpSb,
+ &Target,
+ &Dest,
+ &Target,
+ &IpSb->SnpMode.CurrentAddress,
+ FALSE,
+ TRUE,
+ Solicited
+ );
+Exit:
+ NetbufFree (Packet);
+ return Status;
+}
+
+/**
+ Process the Neighbor Advertisement message.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the message.
+ @param[in] Packet The content of the message with IP head removed.
+
+ @retval EFI_SUCCESS The packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessNeighborAdvertise (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_INFORMATION_HEAD Icmp;
+ EFI_IPv6_ADDRESS Target;
+ IP6_ETHER_ADDR_OPTION LinkLayerOption;
+ BOOLEAN Provided;
+ INTN Compare;
+ IP6_NEIGHBOR_ENTRY *Neighbor;
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+ BOOLEAN Solicited;
+ BOOLEAN IsRouter;
+ BOOLEAN Override;
+ IP6_DAD_ENTRY *DupAddrDetect;
+ IP6_INTERFACE *IpIf;
+ UINT16 OptionLen;
+ UINT8 *Option;
+ EFI_STATUS Status;
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+ NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
+
+ //
+ // Validate the incoming Neighbor Advertisement
+ //
+ Status = EFI_INVALID_PARAMETER;
+ //
+ // The IP Hop Limit field has a value of 255, i.e., the packet
+ // could not possibly have been forwarded by a router.
+ // ICMP Code is 0.
+ // Target Address is not a multicast address.
+ //
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {
+ goto Exit;
+ }
+
+ //
+ // ICMP length is 24 or more octets.
+ //
+ Provided = FALSE;
+ OptionLen = 0;
+ if (Head->PayloadLength < IP6_ND_LENGTH) {
+ goto Exit;
+ } else {
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);
+ Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
+
+ //
+ // All included options should have a length that is greater than zero.
+ //
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {
+ goto Exit;
+ }
+ }
+
+ //
+ // If the IP destination address is a multicast address, Solicited Flag is ZERO.
+ //
+ Solicited = FALSE;
+ if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) {
+ Solicited = TRUE;
+ }
+ if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) {
+ goto Exit;
+ }
+
+ //
+ // DAD - Check whether the Target is one of our tentative address.
+ //
+ DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
+ if (DupAddrDetect != NULL) {
+ //
+ // DAD fails, some other node is using this address.
+ //
+ NetbufFree (Packet);
+ Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
+ return EFI_ICMP_ERROR;
+ }
+
+ //
+ // Search the Neighbor Cache for the target's entry. If no entry exists,
+ // the advertisement should be silently discarded.
+ //
+ Neighbor = Ip6FindNeighborEntry (IpSb, &Target);
+ if (Neighbor == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Get IsRouter Flag and Override Flag
+ //
+ IsRouter = FALSE;
+ Override = FALSE;
+ if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) {
+ IsRouter = TRUE;
+ }
+ if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) {
+ Override = TRUE;
+ }
+
+ //
+ // Check whether link layer option is included.
+ //
+ if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
+ NetbufCopy (
+ Packet,
+ IP6_ND_LENGTH,
+ sizeof (IP6_ETHER_ADDR_OPTION),
+ (UINT8 *) &LinkLayerOption
+ );
+
+ if (LinkLayerOption.Type == Ip6OptionEtherTarget) {
+ Provided = TRUE;
+ }
+ }
+
+ Compare = 0;
+ if (Provided) {
+ Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
+ }
+
+ if (!Neighbor->IsRouter && IsRouter) {
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
+ if (DefaultRouter != NULL) {
+ DefaultRouter->NeighborCache = Neighbor;
+ }
+ }
+
+ if (Neighbor->State == EfiNeighborInComplete) {
+ //
+ // If the target's Neighbor Cache entry is in INCOMPLETE state and no
+ // Target Link-Layer address option is included while link layer has
+ // address, the message should be silently discarded.
+ //
+ if (!Provided) {
+ goto Exit;
+ }
+ //
+ // Update the Neighbor Cache
+ //
+ CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
+ if (Solicited) {
+ Neighbor->State = EfiNeighborReachable;
+ Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
+ } else {
+ Neighbor->State = EfiNeighborStale;
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ //
+ // Send any packets queued for the neighbor awaiting address resolution.
+ //
+ Neighbor->CallBack ((VOID *) Neighbor);
+ }
+
+ Neighbor->IsRouter = IsRouter;
+
+ } else {
+ if (!Override && Compare != 0) {
+ //
+ // When the Override Flag is clear and supplied link-layer address differs from
+ // that in the cache, if the state of the entry is not REACHABLE, ignore the
+ // message. Otherwise set it to STALE but do not update the entry in any
+ // other way.
+ //
+ if (Neighbor->State == EfiNeighborReachable) {
+ Neighbor->State = EfiNeighborStale;
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ }
+ } else {
+ if (Compare != 0) {
+ CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
+ }
+ //
+ // Update the entry's state
+ //
+ if (Solicited) {
+ Neighbor->State = EfiNeighborReachable;
+ Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
+ } else {
+ if (Compare != 0) {
+ Neighbor->State = EfiNeighborStale;
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ }
+ }
+
+ //
+ // When IsRouter is changed from TRUE to FALSE, remove the router from the
+ // Default Router List and remove the Destination Cache entries for all destinations
+ // using the neighbor as a router.
+ //
+ if (Neighbor->IsRouter && !IsRouter) {
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
+ if (DefaultRouter != NULL) {
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
+ }
+ }
+
+ Neighbor->IsRouter = IsRouter;
+ }
+ }
+
+ if (Neighbor->State == EfiNeighborReachable) {
+ Neighbor->CallBack ((VOID *) Neighbor);
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ NetbufFree (Packet);
+ return Status;
+}
+
+/**
+ Process the Router Advertisement message according to RFC4861.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the message.
+ @param[in] Packet The content of the message with the IP head removed.
+
+ @retval EFI_SUCCESS The packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
+ operation.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessRouterAdvertise (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_INFORMATION_HEAD Icmp;
+ UINT32 ReachableTime;
+ UINT32 RetransTimer;
+ UINT16 RouterLifetime;
+ UINT16 Offset;
+ UINT8 Type;
+ UINT8 Length;
+ IP6_ETHER_ADDR_OPTION LinkLayerOption;
+ UINT32 Fourth;
+ UINT8 CurHopLimit;
+ BOOLEAN Mflag;
+ BOOLEAN Oflag;
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+ IP6_NEIGHBOR_ENTRY *NeighborCache;
+ EFI_MAC_ADDRESS LinkLayerAddress;
+ IP6_MTU_OPTION MTUOption;
+ IP6_PREFIX_INFO_OPTION PrefixOption;
+ IP6_PREFIX_LIST_ENTRY *PrefixList;
+ BOOLEAN OnLink;
+ BOOLEAN Autonomous;
+ EFI_IPv6_ADDRESS StatelessAddress;
+ EFI_STATUS Status;
+ UINT16 OptionLen;
+ UINT8 *Option;
+ INTN Result;
+
+ Status = EFI_INVALID_PARAMETER;
+
+ if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) {
+ //
+ // Skip the process below as it's not required under the current policy.
+ //
+ goto Exit;
+ }
+
+ NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
+
+ //
+ // Validate the incoming Router Advertisement
+ //
+
+ //
+ // The IP source address must be a link-local address
+ //
+ if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
+ goto Exit;
+ }
+ //
+ // The IP Hop Limit field has a value of 255, i.e. the packet
+ // could not possibly have been forwarded by a router.
+ // ICMP Code is 0.
+ // ICMP length (derived from the IP length) is 16 or more octets.
+ //
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 ||
+ Head->PayloadLength < IP6_RA_LENGTH) {
+ goto Exit;
+ }
+
+ //
+ // All included options have a length that is greater than zero.
+ //
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH);
+ Option = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL);
+
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {
+ goto Exit;
+ }
+
+ //
+ // Process Fourth field.
+ // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag,
+ // and Router Lifetime (16 bit).
+ //
+
+ Fourth = NTOHL (Icmp.Fourth);
+ CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16));
+
+ //
+ // If the source address already in the default router list, update it.
+ // Otherwise create a new entry.
+ // A Lifetime of zero indicates that the router is not a default router.
+ //
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress);
+ if (DefaultRouter == NULL) {
+ if (RouterLifetime != 0) {
+ DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime);
+ if (DefaultRouter == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ }
+ } else {
+ if (RouterLifetime != 0) {
+ DefaultRouter->Lifetime = RouterLifetime;
+ //
+ // Check the corresponding neighbor cache entry here.
+ //
+ if (DefaultRouter->NeighborCache == NULL) {
+ DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
+ }
+ } else {
+ //
+ // If the address is in the host's default router list and the router lifetime is zero,
+ // immediately time-out the entry.
+ //
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
+ }
+ }
+
+ CurHopLimit = *((UINT8 *) &Fourth + 3);
+ if (CurHopLimit != 0) {
+ IpSb->CurHopLimit = CurHopLimit;
+ }
+
+ Mflag = FALSE;
+ Oflag = FALSE;
+ if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) {
+ Mflag = TRUE;
+ } else {
+ if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) {
+ Oflag = TRUE;
+ }
+ }
+
+ if (Mflag || Oflag) {
+ //
+ // Use Ip6Config to get available addresses or other configuration from DHCP.
+ //
+ Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag);
+ }
+
+ //
+ // Process Reachable Time and Retrans Timer fields.
+ //
+ NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime);
+ NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer);
+ ReachableTime = NTOHL (ReachableTime);
+ RetransTimer = NTOHL (RetransTimer);
+
+ if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) {
+ //
+ // If new value is not unspecified and differs from the previous one, record it
+ // in BaseReachableTime and recompute a ReachableTime.
+ //
+ IpSb->BaseReachableTime = ReachableTime;
+ Ip6UpdateReachableTime (IpSb);
+ }
+
+ if (RetransTimer != 0) {
+ IpSb->RetransTimer = RetransTimer;
+ }
+
+ //
+ // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists.
+ //
+ NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
+ if (NeighborCache != NULL) {
+ NeighborCache->IsRouter = TRUE;
+ }
+
+ //
+ // If an valid router advertisment is received, stops router solicitation.
+ //
+ IpSb->RouterAdvertiseReceived = TRUE;
+
+ //
+ // The only defined options that may appear are the Source
+ // Link-Layer Address, Prefix information and MTU options.
+ // All included options have a length that is greater than zero.
+ //
+ Offset = 16;
+ while (Offset < Head->PayloadLength) {
+ NetbufCopy (Packet, Offset, sizeof (UINT8), &Type);
+ switch (Type) {
+ case Ip6OptionEtherSource:
+ //
+ // Update the neighbor cache
+ //
+ NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption);
+ if (LinkLayerOption.Length <= 0) {
+ goto Exit;
+ }
+
+ ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS));
+ CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6);
+
+ if (NeighborCache == NULL) {
+ NeighborCache = Ip6CreateNeighborEntry (
+ IpSb,
+ Ip6OnArpResolved,
+ &Head->SourceAddress,
+ &LinkLayerAddress
+ );
+ if (NeighborCache == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ NeighborCache->IsRouter = TRUE;
+ NeighborCache->State = EfiNeighborStale;
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ } else {
+ Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6);
+
+ //
+ // If the link-local address is the same as that already in the cache,
+ // the cache entry's state remains unchanged. Otherwise update the
+ // reachability state to STALE.
+ //
+ if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
+ CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6);
+
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+
+ if (NeighborCache->State == EfiNeighborInComplete) {
+ //
+ // Send queued packets if exist.
+ //
+ NeighborCache->State = EfiNeighborStale;
+ NeighborCache->CallBack ((VOID *) NeighborCache);
+ } else {
+ NeighborCache->State = EfiNeighborStale;
+ }
+ }
+ }
+
+ Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8);
+ break;
+ case Ip6OptionPrefixInfo:
+ NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption);
+ if (PrefixOption.Length != 4) {
+ goto Exit;
+ }
+ PrefixOption.ValidLifetime = NTOHL (PrefixOption.ValidLifetime);
+ PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime);
+
+ //
+ // Get L and A flag, recorded in the lower 2 bits of Reserved1
+ //
+ OnLink = FALSE;
+ if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) {
+ OnLink = TRUE;
+ }
+ Autonomous = FALSE;
+ if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) {
+ Autonomous = TRUE;
+ }
+
+ //
+ // If the prefix is the link-local prefix, silently ignore the prefix option.
+ //
+ if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH &&
+ NetIp6IsLinkLocalAddr (&PrefixOption.Prefix)
+ ) {
+ Offset += sizeof (IP6_PREFIX_INFO_OPTION);
+ break;
+ }
+ //
+ // Do following if on-link flag is set according to RFC4861.
+ //
+ if (OnLink) {
+ PrefixList = Ip6FindPrefixListEntry (
+ IpSb,
+ TRUE,
+ PrefixOption.PrefixLength,
+ &PrefixOption.Prefix
+ );
+ //
+ // Create a new entry for the prefix, if the ValidLifetime is zero,
+ // silently ignore the prefix option.
+ //
+ if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) {
+ PrefixList = Ip6CreatePrefixListEntry (
+ IpSb,
+ TRUE,
+ PrefixOption.ValidLifetime,
+ PrefixOption.PreferredLifetime,
+ PrefixOption.PrefixLength,
+ &PrefixOption.Prefix
+ );
+ if (PrefixList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ } else if (PrefixList != NULL) {
+ if (PrefixOption.ValidLifetime != 0) {
+ PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
+ } else {
+ //
+ // If the prefix exists and incoming ValidLifetime is zero, immediately
+ // remove the prefix.
+ Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);
+ }
+ }
+ }
+
+ //
+ // Do following if Autonomous flag is set according to RFC4862.
+ //
+ if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) {
+ PrefixList = Ip6FindPrefixListEntry (
+ IpSb,
+ FALSE,
+ PrefixOption.PrefixLength,
+ &PrefixOption.Prefix
+ );
+ //
+ // Create a new entry for the prefix, and form an address by prefix + interface id
+ // If the sum of the prefix length and interface identifier length
+ // does not equal 128 bits, the Prefix Information option MUST be ignored.
+ //
+ if (PrefixList == NULL &&
+ PrefixOption.ValidLifetime != 0 &&
+ PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128
+ ) {
+ //
+ // Form the address in network order.
+ //
+ CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64));
+ CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64));
+
+ //
+ // If the address is not yet in the assigned address list, adds it into.
+ //
+ if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) {
+ //
+ // And also not in the DAD process, check its uniqeness firstly.
+ //
+ if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) {
+ Status = Ip6SetAddress (
+ IpSb->DefaultInterface,
+ &StatelessAddress,
+ FALSE,
+ PrefixOption.PrefixLength,
+ PrefixOption.ValidLifetime,
+ PrefixOption.PreferredLifetime,
+ NULL,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+ }
+
+ //
+ // Adds the prefix option to stateless prefix option list.
+ //
+ PrefixList = Ip6CreatePrefixListEntry (
+ IpSb,
+ FALSE,
+ PrefixOption.ValidLifetime,
+ PrefixOption.PreferredLifetime,
+ PrefixOption.PrefixLength,
+ &PrefixOption.Prefix
+ );
+ if (PrefixList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ } else if (PrefixList != NULL) {
+
+ //
+ // Reset the preferred lifetime of the address if the advertised prefix exists.
+ // Perform specific action to valid lifetime together.
+ //
+ PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime;
+ if ((PrefixOption.ValidLifetime > 7200) ||
+ (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) {
+ //
+ // If the received Valid Lifetime is greater than 2 hours or
+ // greater than RemainingLifetime, set the valid lifetime of the
+ // corresponding address to the advertised Valid Lifetime.
+ //
+ PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
+
+ } else if (PrefixList->ValidLifetime <= 7200) {
+ //
+ // If RemainingLifetime is less than or equls to 2 hours, ignore the
+ // Prefix Information option with regards to the valid lifetime.
+ // TODO: If this option has been authenticated, set the valid lifetime.
+ //
+ } else {
+ //
+ // Otherwise, reset the valid lifetime of the corresponding
+ // address to 2 hours.
+ //
+ PrefixList->ValidLifetime = 7200;
+ }
+ }
+ }
+
+ Offset += sizeof (IP6_PREFIX_INFO_OPTION);
+ break;
+ case Ip6OptionMtu:
+ NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption);
+ if (MTUOption.Length != 1) {
+ goto Exit;
+ }
+
+ //
+ // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order
+ // to omit implementation of Path MTU Discovery. Thus ignore the MTU option
+ // in Router Advertisement.
+ //
+
+ Offset += sizeof (IP6_MTU_OPTION);
+ break;
+ default:
+ //
+ // Silently ignore unrecognized options
+ //
+ NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length);
+ if (Length <= 0) {
+ goto Exit;
+ }
+
+ Offset = (UINT16) (Offset + (UINT16) Length * 8);
+ break;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ NetbufFree (Packet);
+ return Status;
+}
+
+/**
+ Process the ICMPv6 redirect message. Find the instance, then update
+ its route cache.
+
+ @param[in] IpSb The IP6 service binding instance that received
+ the packet.
+ @param[in] Head The IP head of the received ICMPv6 packet.
+ @param[in] Packet The content of the ICMPv6 redirect packet with
+ the IP head removed.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the
+ operation.
+ @retval EFI_SUCCESS Successfully updated the route caches.
+
+**/
+EFI_STATUS
+Ip6ProcessRedirect (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ )
+{
+ IP6_ICMP_INFORMATION_HEAD *Icmp;
+ EFI_IPv6_ADDRESS *Target;
+ EFI_IPv6_ADDRESS *IcmpDest;
+ UINT8 *Option;
+ UINT16 OptionLen;
+ IP6_ROUTE_ENTRY *RouteEntry;
+ IP6_ROUTE_CACHE_ENTRY *RouteCache;
+ IP6_NEIGHBOR_ENTRY *NeighborCache;
+ INT32 Length;
+ UINT8 OptLen;
+ IP6_ETHER_ADDR_OPTION *LinkLayerOption;
+ EFI_MAC_ADDRESS Mac;
+ UINT32 Index;
+ BOOLEAN IsRouter;
+ EFI_STATUS Status;
+ INTN Result;
+
+ Status = EFI_INVALID_PARAMETER;
+
+ Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL);
+ if (Icmp == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Validate the incoming Redirect message
+ //
+
+ //
+ // The IP Hop Limit field has a value of 255, i.e. the packet
+ // could not possibly have been forwarded by a router.
+ // ICMP Code is 0.
+ // ICMP length (derived from the IP length) is 40 or more octets.
+ //
+ if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 ||
+ Head->PayloadLength < IP6_REDITECT_LENGTH) {
+ goto Exit;
+ }
+
+ //
+ // The IP source address must be a link-local address
+ //
+ if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
+ goto Exit;
+ }
+
+ //
+ // The dest of this ICMP redirect message is not us.
+ //
+ if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
+ goto Exit;
+ }
+
+ //
+ // All included options have a length that is greater than zero.
+ //
+ OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH);
+ Option = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL);
+
+ if (!Ip6IsNDOptionValid (Option, OptionLen)) {
+ goto Exit;
+ }
+
+ Target = (EFI_IPv6_ADDRESS *) (Icmp + 1);
+ IcmpDest = Target + 1;
+
+ //
+ // The ICMP Destination Address field in the redirect message does not contain
+ // a multicast address.
+ //
+ if (IP6_IS_MULTICAST (IcmpDest)) {
+ goto Exit;
+ }
+
+ //
+ // The ICMP Target Address is either a link-local address (when redirected to
+ // a router) or the same as the ICMP Destination Address (when redirected to
+ // the on-link destination).
+ //
+ IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest);
+ if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) {
+ goto Exit;
+ }
+
+ //
+ // Check the options. The only interested option here is the target-link layer
+ // address option.
+ //
+ Length = Packet->TotalSize - 40;
+ Option = (UINT8 *) (IcmpDest + 1);
+ LinkLayerOption = NULL;
+ while (Length > 0) {
+ switch (*Option) {
+ case Ip6OptionEtherTarget:
+
+ LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option;
+ OptLen = LinkLayerOption->Length;
+ if (OptLen != 1) {
+ //
+ // For ethernet, the length must be 1.
+ //
+ goto Exit;
+ }
+ break;
+
+ default:
+
+ OptLen = *(Option + 1);
+ if (OptLen == 0) {
+ //
+ // A length of 0 is invalid.
+ //
+ goto Exit;
+ }
+ break;
+ }
+
+ Length -= 8 * OptLen;
+ Option += 8 * OptLen;
+ }
+
+ if (Length != 0) {
+ goto Exit;
+ }
+
+ //
+ // The IP source address of the Redirect is the same as the current
+ // first-hop router for the specified ICMP Destination Address.
+ //
+ RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress);
+ if (RouteCache != NULL) {
+ if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) {
+ //
+ // The source of this Redirect message must match the NextHop of the
+ // corresponding route cache entry.
+ //
+ goto Exit;
+ }
+
+ //
+ // Update the NextHop.
+ //
+ IP6_COPY_ADDRESS (&RouteCache->NextHop, Target);
+
+ if (!IsRouter) {
+ RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag;
+ RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE;
+ }
+
+ } else {
+ //
+ // Get the Route Entry.
+ //
+ RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL);
+ if (RouteEntry == NULL) {
+ RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL);
+ if (RouteEntry == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ }
+
+ if (!IsRouter) {
+ RouteEntry->Flag = IP6_DIRECT_ROUTE;
+ }
+
+ //
+ // Create a route cache for this.
+ //
+ RouteCache = Ip6CreateRouteCacheEntry (
+ IcmpDest,
+ &Head->DestinationAddress,
+ Target,
+ (UINTN) RouteEntry
+ );
+ if (RouteCache == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Insert the newly created route cache entry.
+ //
+ Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress);
+ InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link);
+ }
+
+ //
+ // Try to locate the neighbor cache for the Target.
+ //
+ NeighborCache = Ip6FindNeighborEntry (IpSb, Target);
+
+ if (LinkLayerOption != NULL) {
+ if (NeighborCache == NULL) {
+ //
+ // Create a neighbor cache for the Target.
+ //
+ ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS));
+ CopyMem (&Mac, LinkLayerOption->EtherAddr, 6);
+ NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac);
+ if (NeighborCache == NULL) {
+ //
+ // Just report a success here. The neighbor cache can be created in
+ // some other place.
+ //
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ NeighborCache->State = EfiNeighborStale;
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ } else {
+ Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6);
+
+ //
+ // If the link-local address is the same as that already in the cache,
+ // the cache entry's state remains unchanged. Otherwise update the
+ // reachability state to STALE.
+ //
+ if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
+ CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6);
+
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+
+ if (NeighborCache->State == EfiNeighborInComplete) {
+ //
+ // Send queued packets if exist.
+ //
+ NeighborCache->State = EfiNeighborStale;
+ NeighborCache->CallBack ((VOID *) NeighborCache);
+ } else {
+ NeighborCache->State = EfiNeighborStale;
+ }
+ }
+ }
+ }
+
+ if (NeighborCache != NULL && IsRouter) {
+ //
+ // The Target is a router, set IsRouter to TRUE.
+ //
+ NeighborCache->IsRouter = TRUE;
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ NetbufFree (Packet);
+ return Status;
+}
+
+/**
+ Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
+ cache. It will be deleted after Timeout. A value of zero means that
+ the entry is permanent. A non-zero value means that the entry is
+ dynamic.
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will
+ be overridden and updated; if FALSE, and if a
+ corresponding cache entry already existed, EFI_ACCESS_DENIED
+ will be returned.
+
+ @retval EFI_SUCCESS The neighbor cache entry has been added.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache
+ due to insufficient resources.
+ @retval EFI_NOT_FOUND TargetLinkAddress is NULL.
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,
+ and that entry is tagged as un-overridden (when DeleteFlag
+ is FALSE).
+
+**/
+EFI_STATUS
+Ip6AddNeighbor (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
+ IN UINT32 Timeout,
+ IN BOOLEAN Override
+ )
+{
+ IP6_NEIGHBOR_ENTRY *Neighbor;
+
+ Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
+ if (Neighbor != NULL) {
+ if (!Override) {
+ return EFI_ACCESS_DENIED;
+ } else {
+ if (TargetLinkAddress != NULL) {
+ IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress);
+ }
+ }
+ } else {
+ if (TargetLinkAddress == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress);
+ if (Neighbor == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ Neighbor->State = EfiNeighborReachable;
+
+ if (Timeout != 0) {
+ Neighbor->Ticks = IP6_GET_TICKS (Timeout / TICKS_PER_MS);
+ Neighbor->Dynamic = TRUE;
+ } else {
+ Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
+ cache. It will be deleted after Timeout. A value of zero means that
+ the entry is permanent. A non-zero value means that the entry is
+ dynamic.
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will
+ be overridden and updated; if FALSE, and if a
+ corresponding cache entry already existed, EFI_ACCESS_DENIED
+ will be returned.
+
+ @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted.
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache.
+
+**/
+EFI_STATUS
+Ip6DelNeighbor (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
+ IN UINT32 Timeout,
+ IN BOOLEAN Override
+ )
+{
+ IP6_NEIGHBOR_ENTRY *Neighbor;
+
+ Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
+ if (Neighbor == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ RemoveEntryList (&Neighbor->Link);
+ FreePool (Neighbor);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.
+ This time routine handles DAD module and neighbor state transition.
+ It is also responsible for sending out router solicitations.
+
+ @param[in] Event The IP6 service instance's heartbeat timer.
+ @param[in] Context The IP6 service instance.
+
+**/
+VOID
+EFIAPI
+Ip6NdFasterTimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ LIST_ENTRY *Entry2;
+ IP6_INTERFACE *IpIf;
+ IP6_DELAY_JOIN_LIST *DelayNode;
+ EFI_IPv6_ADDRESS Source;
+ IP6_DAD_ENTRY *DupAddrDetect;
+ EFI_STATUS Status;
+ IP6_NEIGHBOR_ENTRY *NeighborCache;
+ EFI_IPv6_ADDRESS Destination;
+ IP6_SERVICE *IpSb;
+ BOOLEAN Flag;
+
+ IpSb = (IP6_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router
+ // Solicitation messages, each separated by at least
+ // RTR_SOLICITATION_INTERVAL (4) seconds.
+ //
+ if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) &&
+ !IpSb->RouterAdvertiseReceived &&
+ IpSb->SolicitTimer > 0
+ ) {
+ if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) {
+ Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL);
+ if (!EFI_ERROR (Status)) {
+ IpSb->SolicitTimer--;
+ IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL);
+ }
+ }
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
+
+ //
+ // Process the delay list to join the solicited-node multicast address.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) {
+ DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link);
+ if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) {
+ //
+ // The timer expires, init the duplicate address detection.
+ //
+ Ip6InitDADProcess (
+ DelayNode->Interface,
+ DelayNode->AddressInfo,
+ DelayNode->DadCallback,
+ DelayNode->Context
+ );
+
+ //
+ // Remove the delay node
+ //
+ RemoveEntryList (&DelayNode->Link);
+ FreePool (DelayNode);
+ }
+ }
+
+ //
+ // Process the duplicate address detection list.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {
+ DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link);
+
+ if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) {
+ //
+ // The timer expires, check the remaining transmit counts.
+ //
+ if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) {
+ //
+ // Send the Neighbor Solicitation message with
+ // Source - unspecified address, destination - solicited-node multicast address
+ // Target - the address to be validated
+ //
+ Status = Ip6SendNeighborSolicit (
+ IpSb,
+ NULL,
+ &DupAddrDetect->Destination,
+ &DupAddrDetect->AddressInfo->Address,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ DupAddrDetect->Transmit++;
+ DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer);
+ } else {
+ //
+ // All required solicitation has been sent out, and the RetransTime after the last
+ // Neighbor Solicit is elapsed, finish the DAD process.
+ //
+ Flag = FALSE;
+ if ((DupAddrDetect->Receive == 0) ||
+ (DupAddrDetect->Transmit == DupAddrDetect->Receive)) {
+ Flag = TRUE;
+ }
+
+ Ip6OnDADFinished (Flag, IpIf, DupAddrDetect);
+ }
+ }
+ }
+ }
+
+ //
+ // Polling the state of Neighbor cache
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {
+ NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
+
+ switch (NeighborCache->State) {
+ case EfiNeighborInComplete:
+ if (NeighborCache->Ticks > 0) {
+ --NeighborCache->Ticks;
+ }
+
+ //
+ // Retransmit Neighbor Solicitation messages approximately every
+ // RetransTimer milliseconds while awaiting a response.
+ //
+ if (NeighborCache->Ticks == 0) {
+ if (NeighborCache->Transmit > 1) {
+ //
+ // Send out multicast neighbor solicitation for address resolution.
+ // After last neighbor solicitation message has been sent out, wait
+ // for RetransTimer and then remove entry if no response is received.
+ //
+ Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ Status = Ip6SendNeighborSolicit (
+ IpSb,
+ &Source,
+ &Destination,
+ &NeighborCache->Neighbor,
+ &IpSb->SnpMode.CurrentAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ }
+
+ //
+ // Update the retransmit times.
+ //
+ if (NeighborCache->Transmit > 0) {
+ --NeighborCache->Transmit;
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
+ }
+ }
+
+ if (NeighborCache->Transmit == 0) {
+ //
+ // Timeout, send ICMP destination unreachable packet and then remove entry
+ //
+ Status = Ip6FreeNeighborEntry (
+ IpSb,
+ NeighborCache,
+ TRUE,
+ TRUE,
+ EFI_ICMP_ERROR,
+ NULL,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ }
+
+ break;
+
+ case EfiNeighborReachable:
+ //
+ // This entry is inserted by EfiIp6Neighbors() as static entry
+ // and will not timeout.
+ //
+ if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) {
+ break;
+ }
+
+ if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
+ if (NeighborCache->Dynamic) {
+ //
+ // This entry is inserted by EfiIp6Neighbors() as dynamic entry
+ // and will be deleted after timeout.
+ //
+ Status = Ip6FreeNeighborEntry (
+ IpSb,
+ NeighborCache,
+ FALSE,
+ TRUE,
+ EFI_TIMEOUT,
+ NULL,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ } else {
+ NeighborCache->State = EfiNeighborStale;
+ NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
+ }
+ }
+
+ break;
+
+ case EfiNeighborDelay:
+ if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
+
+ NeighborCache->State = EfiNeighborProbe;
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
+ NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1;
+ //
+ // Send out unicast neighbor solicitation for Neighbor Unreachability Detection
+ //
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ Status = Ip6SendNeighborSolicit (
+ IpSb,
+ &Source,
+ &NeighborCache->Neighbor,
+ &NeighborCache->Neighbor,
+ &IpSb->SnpMode.CurrentAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ NeighborCache->Transmit--;
+ }
+
+ break;
+
+ case EfiNeighborProbe:
+ if (NeighborCache->Ticks > 0) {
+ --NeighborCache->Ticks;
+ }
+
+ //
+ // Retransmit Neighbor Solicitation messages approximately every
+ // RetransTimer milliseconds while awaiting a response.
+ //
+ if (NeighborCache->Ticks == 0) {
+ if (NeighborCache->Transmit > 1) {
+ //
+ // Send out unicast neighbor solicitation for Neighbor Unreachability
+ // Detection. After last neighbor solicitation message has been sent out,
+ // wait for RetransTimer and then remove entry if no response is received.
+ //
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ Status = Ip6SendNeighborSolicit (
+ IpSb,
+ &Source,
+ &NeighborCache->Neighbor,
+ &NeighborCache->Neighbor,
+ &IpSb->SnpMode.CurrentAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ }
+
+ //
+ // Update the retransmit times.
+ //
+ if (NeighborCache->Transmit > 0) {
+ --NeighborCache->Transmit;
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
+ }
+ }
+
+ if (NeighborCache->Transmit == 0) {
+ //
+ // Delete the neighbor entry.
+ //
+ Status = Ip6FreeNeighborEntry (
+ IpSb,
+ NeighborCache,
+ FALSE,
+ TRUE,
+ EFI_TIMEOUT,
+ NULL,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/**
+ The heartbeat timer of ND module in 1 second. This time routine handles following
+ things: 1) maitain default router list; 2) maintain prefix options;
+ 3) maintain route caches.
+
+ @param[in] IpSb The IP6 service binding instance.
+
+**/
+VOID
+Ip6NdTimerTicking (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_DEFAULT_ROUTER *DefaultRouter;
+ IP6_PREFIX_LIST_ENTRY *PrefixOption;
+ UINT8 Index;
+ IP6_ROUTE_CACHE_ENTRY *RouteCache;
+
+ //
+ // Decrease the lifetime of default router, if expires remove it from default router list.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) {
+ DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);
+ if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) {
+ if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) {
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
+ }
+ }
+ }
+
+ //
+ // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) {
+ PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
+ if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {
+ if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) {
+ if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) &&
+ (PrefixOption->PreferredLifetime > 0)
+ ) {
+ --PrefixOption->PreferredLifetime;
+ }
+ } else {
+ Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE);
+ }
+ }
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) {
+ PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
+ if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {
+ if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) {
+ Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE);
+ }
+ }
+ }
+
+ //
+ // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries.
+ // Remove the entries at the tail of the bucket. These entries
+ // are likely to be used least.
+ // Reclaim frequency is set to 1 second.
+ //
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
+ while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) {
+ Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]);
+ if (Entry == NULL) {
+ break;
+ }
+
+ RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
+ Ip6FreeRouteCacheEntry (RouteCache);
+ ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0);
+ IpSb->RouteTable->Cache.CacheNum[Index]--;
+ }
+ }
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Nd.h b/NetworkPkg/Ip6Dxe/Ip6Nd.h
new file mode 100644
index 0000000000..57fb8feca3
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Nd.h
@@ -0,0 +1,750 @@
+/** @file
+ Definition of Neighbor Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_ND_H__
+#define __EFI_IP6_ND_H__
+
+#define IP6_GET_TICKS(Ms) (((Ms) + IP6_TIMER_INTERVAL_IN_MS - 1) / IP6_TIMER_INTERVAL_IN_MS)
+
+enum {
+ IP6_INF_ROUTER_LIFETIME = 0xFFFF,
+
+ IP6_MAX_RTR_SOLICITATION_DELAY = 1000, ///< 1000 milliseconds
+ IP6_MAX_RTR_SOLICITATIONS = 3,
+ IP6_RTR_SOLICITATION_INTERVAL = 4000,
+
+ IP6_MIN_RANDOM_FACTOR_SCALED = 1,
+ IP6_MAX_RANDOM_FACTOR_SCALED = 3,
+ IP6_RANDOM_FACTOR_SCALE = 2,
+
+ IP6_MAX_MULTICAST_SOLICIT = 3,
+ IP6_MAX_UNICAST_SOLICIT = 3,
+ IP6_MAX_ANYCAST_DELAY_TIME = 1,
+ IP6_MAX_NEIGHBOR_ADV = 3,
+ IP6_REACHABLE_TIME = 30000,
+ IP6_RETRANS_TIMER = 1000,
+ IP6_DELAY_FIRST_PROBE_TIME = 5000,
+
+ IP6_MIN_LINK_MTU = 1280,
+ IP6_MAX_LINK_MTU = 1500,
+
+ IP6_IS_ROUTER_FLAG = 0x80,
+ IP6_SOLICITED_FLAG = 0x40,
+ IP6_OVERRIDE_FLAG = 0x20,
+
+ IP6_M_ADDR_CONFIG_FLAG = 0x80,
+ IP6_O_CONFIG_FLAG = 0x40,
+
+ IP6_ON_LINK_FLAG = 0x80,
+ IP6_AUTO_CONFIG_FLAG = 0x40,
+
+ IP6_ND_LENGTH = 24,
+ IP6_RA_LENGTH = 16,
+ IP6_REDITECT_LENGTH = 40,
+ IP6_DAD_ENTRY_SIGNATURE = SIGNATURE_32 ('I', 'P', 'D', 'E')
+};
+
+typedef
+VOID
+(*IP6_ARP_CALLBACK) (
+ VOID *Context
+ );
+
+typedef struct _IP6_ETHE_ADDR_OPTION {
+ UINT8 Type;
+ UINT8 Length;
+ UINT8 EtherAddr[6];
+} IP6_ETHER_ADDR_OPTION;
+
+typedef struct _IP6_MTU_OPTION {
+ UINT8 Type;
+ UINT8 Length;
+ UINT16 Reserved;
+ UINT32 Mtu;
+} IP6_MTU_OPTION;
+
+typedef struct _IP6_PREFIX_INFO_OPTION {
+ UINT8 Type;
+ UINT8 Length;
+ UINT8 PrefixLength;
+ UINT8 Reserved1;
+ UINT32 ValidLifetime;
+ UINT32 PreferredLifetime;
+ UINT32 Reserved2;
+ EFI_IPv6_ADDRESS Prefix;
+} IP6_PREFIX_INFO_OPTION;
+
+typedef
+VOID
+(*IP6_DAD_CALLBACK) (
+ IN BOOLEAN IsDadPassed,
+ IN EFI_IPv6_ADDRESS *TargetAddress,
+ IN VOID *Context
+ );
+
+typedef struct _IP6_DAD_ENTRY {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ UINT32 MaxTransmit;
+ UINT32 Transmit;
+ UINT32 Receive;
+ UINT32 RetransTick;
+ IP6_ADDRESS_INFO *AddressInfo;
+ EFI_IPv6_ADDRESS Destination;
+ IP6_DAD_CALLBACK Callback;
+ VOID *Context;
+} IP6_DAD_ENTRY;
+
+typedef struct _IP6_DELAY_JOIN_LIST {
+ LIST_ENTRY Link;
+ UINT32 DelayTime; ///< in tick per 50 milliseconds
+ IP6_INTERFACE *Interface;
+ IP6_ADDRESS_INFO *AddressInfo;
+ IP6_DAD_CALLBACK DadCallback;
+ VOID *Context;
+} IP6_DELAY_JOIN_LIST;
+
+typedef struct _IP6_NEIGHBOR_ENTRY {
+ LIST_ENTRY Link;
+ LIST_ENTRY ArpList;
+ INTN RefCnt;
+ BOOLEAN IsRouter;
+ BOOLEAN ArpFree;
+ BOOLEAN Dynamic;
+ EFI_IPv6_ADDRESS Neighbor;
+ EFI_MAC_ADDRESS LinkAddress;
+ EFI_IP6_NEIGHBOR_STATE State;
+ UINT32 Transmit;
+ UINT32 Ticks;
+
+ LIST_ENTRY Frames;
+ IP6_INTERFACE *Interface;
+ IP6_ARP_CALLBACK CallBack;
+} IP6_NEIGHBOR_ENTRY;
+
+typedef struct _IP6_DEFAULT_ROUTER {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ UINT16 Lifetime;
+ EFI_IPv6_ADDRESS Router;
+ IP6_NEIGHBOR_ENTRY *NeighborCache;
+} IP6_DEFAULT_ROUTER;
+
+typedef struct _IP6_PREFIX_LIST_ENTRY {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ UINT32 ValidLifetime;
+ UINT32 PreferredLifetime;
+ UINT8 PrefixLength;
+ EFI_IPv6_ADDRESS Prefix;
+} IP6_PREFIX_LIST_ENTRY;
+
+/**
+ Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number
+ of EFI_IP6_NEIGHBOR_CACHE is also returned.
+
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.
+ @param[out] NeighborCount The number of returned neighbor cache entries.
+ @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.
+
+ @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.
+
+**/
+EFI_STATUS
+Ip6BuildEfiNeighborCache (
+ IN IP6_PROTOCOL *IpInstance,
+ OUT UINT32 *NeighborCount,
+ OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache
+ );
+
+/**
+ Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number
+ of prefix entries is also returned.
+
+ @param[in] IpInstance The pointer to IP6_PROTOCOL instance.
+ @param[out] PrefixCount The number of returned prefix entries.
+ @param[out] PrefixTable The pointer to the array of PrefixTable.
+
+ @retval EFI_SUCCESS The prefix table successfully built.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table.
+
+**/
+EFI_STATUS
+Ip6BuildPrefixTable (
+ IN IP6_PROTOCOL *IpInstance,
+ OUT UINT32 *PrefixCount,
+ OUT EFI_IP6_ADDRESS_INFO **PrefixTable
+ );
+
+/**
+ Allocate and initialize an IP6 default router entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Ip6Address The IPv6 address of the default router.
+ @param[in] RouterLifetime The lifetime associated with the default
+ router, in units of seconds.
+
+ @return NULL if it failed to allocate memory for the default router node.
+ Otherwise, point to the created default router node.
+
+**/
+IP6_DEFAULT_ROUTER *
+Ip6CreateDefaultRouter (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Ip6Address,
+ IN UINT16 RouterLifetime
+ );
+
+/**
+ Destroy an IP6 default router entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.
+
+**/
+VOID
+Ip6DestroyDefaultRouter (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_DEFAULT_ROUTER *DefaultRouter
+ );
+
+/**
+ Clean an IP6 default router list.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.
+
+**/
+VOID
+Ip6CleanDefaultRouterList (
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ Search a default router node from an IP6 default router list.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Ip6Address The IPv6 address of the to be searched default router node.
+
+ @return NULL if it failed to find the matching default router node.
+ Otherwise, point to the found default router node.
+
+**/
+IP6_DEFAULT_ROUTER *
+Ip6FindDefaultRouter (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Ip6Address
+ );
+
+/**
+ The function to be called after DAD (Duplicate Address Detection) is performed.
+
+ @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.
+ @param[in] IpIf Points to the IP6_INTERFACE.
+ @param[in] DadEntry The DAD entry which already performed DAD.
+
+**/
+VOID
+Ip6OnDADFinished (
+ IN BOOLEAN IsDadPassed,
+ IN IP6_INTERFACE *IpIf,
+ IN IP6_DAD_ENTRY *DadEntry
+ );
+
+/**
+ Create a DAD (Duplicate Address Detection) entry and queue it to be performed.
+
+ @param[in] IpIf Points to the IP6_INTERFACE.
+ @param[in] AddressInfo The address information which needs DAD performed.
+ @param[in] Callback The callback routine that will be called after DAD
+ is performed. This is an optional parameter that
+ may be NULL.
+ @param[in] Context The opaque parameter for a DAD callback routine.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The DAD entry was created and queued.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the
+ operation.
+
+
+**/
+EFI_STATUS
+Ip6InitDADProcess (
+ IN IP6_INTERFACE *IpIf,
+ IN IP6_ADDRESS_INFO *AddressInfo,
+ IN IP6_DAD_CALLBACK Callback OPTIONAL,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ Search IP6_DAD_ENTRY from the Duplicate Address Detection List.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Target The address information which needs DAD performed .
+ @param[out] Interface If not NULL, output the IP6 interface that configures
+ the tentative address.
+
+ @return NULL if failed to find the matching DAD entry.
+ Otherwise, point to the found DAD entry.
+
+**/
+IP6_DAD_ENTRY *
+Ip6FindDADEntry (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Target,
+ OUT IP6_INTERFACE **Interface OPTIONAL
+ );
+
+/**
+ Allocate and initialize a IP6 prefix list entry.
+
+ @param[in] IpSb The pointer to IP6_SERVICE instance.
+ @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list.
+ Otherwise, it is created for the autoconfiguration prefix list.
+ @param[in] ValidLifetime The length of time in seconds that the prefix
+ is valid for the purpose of on-link determination.
+ @param[in] PreferredLifetime The length of time in seconds that addresses
+ generated from the prefix via stateless address
+ autoconfiguration remain preferred.
+ @param[in] PrefixLength The prefix length of the Prefix.
+ @param[in] Prefix The prefix address.
+
+ @return NULL if it failed to allocate memory for the prefix node. Otherwise, point
+ to the created or existing prefix list entry.
+
+**/
+IP6_PREFIX_LIST_ENTRY *
+Ip6CreatePrefixListEntry (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN OnLinkOrAuto,
+ IN UINT32 ValidLifetime,
+ IN UINT32 PreferredLifetime,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *Prefix
+ );
+
+/**
+ Destory a IP6 prefix list entry.
+
+ @param[in] IpSb The pointer to IP6_SERVICE instance.
+ @param[in] PrefixEntry The to be destroyed prefix list entry.
+ @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list.
+ Otherwise remove from autoconfiguration prefix list.
+ @param[in] ImmediateDelete If TRUE, remove the entry directly.
+ Otherwise, check the reference count to see whether
+ it should be removed.
+
+**/
+VOID
+Ip6DestroyPrefixListEntry (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_PREFIX_LIST_ENTRY *PrefixEntry,
+ IN BOOLEAN OnLinkOrAuto,
+ IN BOOLEAN ImmediateDelete
+ );
+
+/**
+ Search the list array to find an IP6 prefix list entry.
+
+ @param[in] IpSb The pointer to IP6_SERVICE instance.
+ @param[in] OnLinkOrAuto If TRUE, the search the link prefix list,
+ Otherwise search the autoconfiguration prefix list.
+ @param[in] PrefixLength The prefix length of the Prefix
+ @param[in] Prefix The prefix address.
+
+ @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the
+ pointer to the IP6 prefix list entry.
+
+**/
+IP6_PREFIX_LIST_ENTRY *
+Ip6FindPrefixListEntry (
+ IN IP6_SERVICE *IpSb,
+ IN BOOLEAN OnLinkOrAuto,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *Prefix
+ );
+
+/**
+ Release the resource in prefix list table, and destroy the list entry and
+ corresponding addresses or route entries.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] ListHead The list entry head of the prefix list table.
+
+**/
+VOID
+Ip6CleanPrefixListTable (
+ IN IP6_SERVICE *IpSb,
+ IN LIST_ENTRY *ListHead
+ );
+
+/**
+ Allocate and initialize an IP6 neighbor cache entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] CallBack The callback function to be called when
+ address resolution is finished.
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.
+ @param[in] LinkAddress Points to the MAC address of the neighbor.
+ Ignored if NULL.
+
+ @return NULL if failed to allocate memory for the neighbor cache entry.
+ Otherwise, point to the created neighbor cache entry.
+
+**/
+IP6_NEIGHBOR_ENTRY *
+Ip6CreateNeighborEntry (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_ARP_CALLBACK CallBack,
+ IN EFI_IPv6_ADDRESS *Ip6Address,
+ IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL
+ );
+
+/**
+ Search a IP6 neighbor cache entry.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] Ip6Address Points to the IPv6 address of the neighbor.
+
+ @return NULL if it failed to find the matching neighbor cache entry.
+ Otherwise, point to the found neighbor cache entry.
+
+**/
+IP6_NEIGHBOR_ENTRY *
+Ip6FindNeighborEntry (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Ip6Address
+ );
+
+/**
+ Free a IP6 neighbor cache entry and remove all the frames on the address
+ resolution queue that pass the FrameToCancel. That is, either FrameToCancel
+ is NULL, or it returns true for the frame.
+
+ @param[in] IpSb The pointer to the IP6_SERVICE instance.
+ @param[in] NeighborCache The to be free neighbor cache entry.
+ @param[in] SendIcmpError If TRUE, send out ICMP error.
+ @param[in] FullFree If TRUE, remove the neighbor cache entry.
+ Otherwise remove the pending frames.
+ @param[in] IoStatus The status returned to the cancelled frames'
+ callback function.
+ @param[in] FrameToCancel Function to select which frame to cancel.
+ This is an optional parameter that may be NULL.
+ @param[in] Context Opaque parameter to the FrameToCancel.
+ Ignored if FrameToCancel is NULL.
+
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval EFI_SUCCESS The operation finished successfully.
+
+**/
+EFI_STATUS
+Ip6FreeNeighborEntry (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_NEIGHBOR_ENTRY *NeighborCache,
+ IN BOOLEAN SendIcmpError,
+ IN BOOLEAN FullFree,
+ IN EFI_STATUS IoStatus,
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
+ cache. It will be deleted after Timeout. A value of zero means that
+ the entry is permanent. A non-zero value means that the entry is
+ dynamic.
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will
+ be overridden and updated; if FALSE, and if a
+ corresponding cache entry already existed, EFI_ACCESS_DENIED
+ will be returned.
+
+ @retval EFI_SUCCESS The neighbor cache entry has been added.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache
+ due to insufficient resources.
+ @retval EFI_NOT_FOUND TargetLinkAddress is NULL.
+ @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,
+ and that entry is tagged as un-overridden (when DeleteFlag
+ is FALSE).
+
+**/
+EFI_STATUS
+Ip6AddNeighbor (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
+ IN UINT32 Timeout,
+ IN BOOLEAN Override
+ );
+
+/**
+ Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().
+
+ @param[in] IpSb The IP6 service binding instance.
+ @param[in] TargetIp6Address Pointer to Target IPv6 address.
+ @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.
+ @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
+ cache. It will be deleted after Timeout. A value of zero means that
+ the entry is permanent. A non-zero value means that the entry is
+ dynamic.
+ @param[in] Override If TRUE, the cached link-layer address of the matching entry will
+ be overridden and updated; if FALSE, and if a
+ corresponding cache entry already existed, EFI_ACCESS_DENIED
+ will be returned.
+
+ @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted.
+ @retval EFI_NOT_FOUND This entry is not in the neighbor cache.
+
+**/
+EFI_STATUS
+Ip6DelNeighbor (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
+ IN UINT32 Timeout,
+ IN BOOLEAN Override
+ );
+
+/**
+ Process the Neighbor Solicitation message. The message may be sent for Duplicate
+ Address Detection or Address Resolution.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the message.
+ @param[in] Packet The content of the message with IP head removed.
+
+ @retval EFI_SUCCESS The packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessNeighborSolicit (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Process the Neighbor Advertisement message.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the message.
+ @param[in] Packet The content of the message with IP head removed.
+
+ @retval EFI_SUCCESS The packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval EFI_ICMP_ERROR The packet indicates that DAD is failed.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessNeighborAdvertise (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Process the Router Advertisement message according to RFC4861.
+
+ @param[in] IpSb The IP service that received the packet.
+ @param[in] Head The IP head of the message.
+ @param[in] Packet The content of the message with the IP head removed.
+
+ @retval EFI_SUCCESS The packet processed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is invalid.
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation.
+ @retval Others Failed to process the packet.
+
+**/
+EFI_STATUS
+Ip6ProcessRouterAdvertise (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Process the ICMPv6 redirect message. Find the instance, then update
+ its route cache.
+
+ @param[in] IpSb The IP6 service binding instance that received
+ the packet.
+ @param[in] Head The IP head of the received ICMPv6 packet.
+ @param[in] Packet The content of the ICMPv6 redirect packet with
+ the IP head removed.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the
+ operation.
+ @retval EFI_SUCCESS Successfully updated the route caches.
+
+**/
+EFI_STATUS
+Ip6ProcessRedirect (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Generate router solicit message and send it out to Destination Address or
+ All Router Link Local scope multicast address.
+
+ @param[in] IpSb The IP service to send the packet.
+ @param[in] Interface If not NULL, points to the IP6 interface to send
+ the packet.
+ @param[in] SourceAddress If not NULL, the source address of the message.
+ @param[in] DestinationAddress If not NULL, the destination address of the message.
+ @param[in] SourceLinkAddress If not NULL, the MAC address of the source.
+ A source link-layer address option will be appended
+ to the message.
+
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation.
+ @retval EFI_SUCCESS The router solicit message was successfully sent.
+
+**/
+EFI_STATUS
+Ip6SendRouterSolicit (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *Interface OPTIONAL,
+ IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,
+ IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL,
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL
+ );
+
+/**
+ Generate the Neighbor Solicitation message and send it to the Destination Address.
+
+ @param[in] IpSb The IP service to send the packet
+ @param[in] SourceAddress The source address of the message.
+ @param[in] DestinationAddress The destination address of the message.
+ @param[in] TargetIp6Address The IP address of the target of the solicitation.
+ It must not be a multicast address.
+ @param[in] SourceLinkAddress The MAC address for the sender. If not NULL,
+ a source link-layer address option will be appended
+ to the message.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
+ operation.
+ @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.
+
+**/
+EFI_STATUS
+Ip6SendNeighborSolicit (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *SourceAddress,
+ IN EFI_IPv6_ADDRESS *DestinationAddress,
+ IN EFI_IPv6_ADDRESS *TargetIp6Address,
+ IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL
+ );
+
+/**
+ Set the interface's address. This will trigger the DAD process for the
+ address to set. To set an already set address, the lifetimes wil be
+ updated to the new value passed in.
+
+ @param[in] Interface The interface to set the address.
+ @param[in] Ip6Addr The interface's to be assigned IPv6 address.
+ @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast.
+ Otherwise, it is not anycast.
+ @param[in] PrefixLength The prefix length of the Ip6Addr.
+ @param[in] ValidLifetime The valid lifetime for this address.
+ @param[in] PreferredLifetime The preferred lifetime for this address.
+ @param[in] DadCallback The caller's callback to trigger when DAD finishes.
+ This is an optional parameter that may be NULL.
+ @param[in] Context The context that will be passed to DadCallback.
+ This is an optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The interface is scheduled to be configured with
+ the specified address.
+ @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to
+ lack of resources.
+
+**/
+EFI_STATUS
+Ip6SetAddress (
+ IN IP6_INTERFACE *Interface,
+ IN EFI_IPv6_ADDRESS *Ip6Addr,
+ IN BOOLEAN IsAnycast,
+ IN UINT8 PrefixLength,
+ IN UINT32 ValidLifetime,
+ IN UINT32 PreferredLifetime,
+ IN IP6_DAD_CALLBACK DadCallback OPTIONAL,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.
+ This time routine handles DAD module and neighbor state transition.
+ It is also responsible for sending out router solicitations.
+
+ @param[in] Event The IP6 service instance's heartbeat timer.
+ @param[in] Context The IP6 service instance.
+
+**/
+VOID
+EFIAPI
+Ip6NdFasterTimerTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ The heartbeat timer of ND module in 1 second. This time routine handles following
+ things: 1) maitain default router list; 2) maintain prefix options;
+ 3) maintain route caches.
+
+ @param[in] IpSb The IP6 service binding instance.
+
+**/
+VOID
+Ip6NdTimerTicking (
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ Callback function when address resolution is finished. It will cancel
+ all the queued frames if the address resolution failed, or transmit them
+ if the request succeeded.
+
+ @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.
+
+**/
+VOID
+Ip6OnArpResolved (
+ IN VOID *Context
+ );
+
+/**
+ Update the ReachableTime in IP6 service binding instance data, in milliseconds.
+
+ @param[in, out] IpSb Points to the IP6_SERVICE.
+
+**/
+VOID
+Ip6UpdateReachableTime (
+ IN OUT IP6_SERVICE *IpSb
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6NvData.h b/NetworkPkg/Ip6Dxe/Ip6NvData.h
new file mode 100644
index 0000000000..715473fde3
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6NvData.h
@@ -0,0 +1,70 @@
+/** @file
+ NVData structure used by the IP6 configuration component.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IP6_NV_DATA_H_
+#define _IP6_NV_DATA_H_
+
+#define IP6_CONFIG_NVDATA_GUID \
+ { \
+ 0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99 } \
+ }
+
+#define FORMID_MAIN_FORM 1
+#define FORMID_MANUAL_CONFIG_FORM 2
+
+#define IP6_POLICY_AUTO 0
+#define IP6_POLICY_MANUAL 1
+#define DAD_MAX_TRANSMIT_COUNT 10
+
+#define KEY_INTERFACE_ID 0x101
+#define KEY_MANUAL_ADDRESS 0x102
+#define KEY_GATEWAY_ADDRESS 0x103
+#define KEY_DNS_ADDRESS 0x104
+#define KEY_SAVE_CHANGES 0x105
+#define KEY_SAVE_CONFIG_CHANGES 0x106
+#define KEY_IGNORE_CONFIG_CHANGES 0x107
+
+#define HOST_ADDRESS_LABEL 0x9000
+#define ROUTE_TABLE_LABEL 0xa000
+#define GATEWAY_ADDRESS_LABEL 0xb000
+#define DNS_ADDRESS_LABEL 0xc000
+#define LABEL_END 0xffff
+
+#define INTERFACE_ID_STR_MIN_SIZE 1
+#define INTERFACE_ID_STR_MAX_SIZE 23
+#define INTERFACE_ID_STR_STORAGE 24
+#define IP6_STR_MAX_SIZE 40
+#define ADDRESS_STR_MIN_SIZE 2
+#define ADDRESS_STR_MAX_SIZE 255
+
+///
+/// IP6_CONFIG_IFR_NVDATA contains the IP6 configure
+/// parameters for that NIC.
+///
+#pragma pack(1)
+typedef struct {
+ UINT8 IfType; ///< interface type
+ UINT8 Padding[3];
+ UINT32 Policy; ///< manual or automatic
+ UINT32 DadTransmitCount; ///< dad transmits count
+ CHAR16 InterfaceId[INTERFACE_ID_STR_STORAGE]; ///< alternative interface id
+ CHAR16 ManualAddress[ADDRESS_STR_MAX_SIZE]; ///< IP addresses
+ CHAR16 GatewayAddress[ADDRESS_STR_MAX_SIZE]; ///< Gateway address
+ CHAR16 DnsAddress[ADDRESS_STR_MAX_SIZE]; ///< DNS server address
+} IP6_CONFIG_IFR_NVDATA;
+#pragma pack()
+
+#endif
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.c b/NetworkPkg/Ip6Dxe/Ip6Option.c
new file mode 100644
index 0000000000..9a91fd7cd1
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Option.c
@@ -0,0 +1,758 @@
+/** @file
+ IP6 option support functions and routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+/**
+ Validate the IP6 option format for both the packets we received
+ and that we will transmit. It will compute the ICMPv6 error message fields
+ if the option is malformated.
+
+ @param[in] IpSb The IP6 service data.
+ @param[in] Packet The to be validated packet.
+ @param[in] Option The first byte of the option.
+ @param[in] OptionLen The length of the whole option.
+ @param[in] Pointer Identifies the octet offset within
+ the invoking packet where the error was detected.
+
+
+ @retval TRUE The option is properly formatted.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsOptionValid (
+ IN IP6_SERVICE *IpSb,
+ IN NET_BUF *Packet,
+ IN UINT8 *Option,
+ IN UINT8 OptionLen,
+ IN UINT32 Pointer
+ )
+{
+ UINT8 Offset;
+ UINT8 OptionType;
+
+ Offset = 0;
+
+ while (Offset < OptionLen) {
+ OptionType = *(Option + Offset);
+
+ switch (OptionType) {
+ case Ip6OptionPad1:
+ //
+ // It is a Pad1 option
+ //
+ Offset++;
+ break;
+ case Ip6OptionPadN:
+ //
+ // It is a PadN option
+ //
+ Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2);
+ break;
+ case Ip6OptionRouterAlert:
+ //
+ // It is a Router Alert Option
+ //
+ Offset += 4;
+ break;
+ default:
+ //
+ // The highest-order two bits specify the action must be taken if
+ // the processing IPv6 node does not recognize the option type.
+ //
+ switch (OptionType & Ip6OptionMask) {
+ case Ip6OptionSkip:
+ Offset = (UINT8) (Offset + *(Option + Offset + 1));
+ break;
+ case Ip6OptionDiscard:
+ return FALSE;
+ case Ip6OptionParameterProblem:
+ Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 2,
+ &Pointer
+ );
+ return FALSE;
+ case Ip6OptionMask:
+ if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 2,
+ &Pointer
+ );
+ }
+
+ return FALSE;
+ break;
+ }
+
+ break;
+ }
+
+ }
+
+ return TRUE;
+}
+
+/**
+ Validate the IP6 option format for both the packets we received
+ and that we will transmit. It supports the defined options in Neighbor
+ Discovery messages.
+
+ @param[in] Option The first byte of the option.
+ @param[in] OptionLen The length of the whole option.
+
+ @retval TRUE The option is properly formatted.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsNDOptionValid (
+ IN UINT8 *Option,
+ IN UINT16 OptionLen
+ )
+{
+ UINT16 Offset;
+ UINT8 OptionType;
+ UINT16 Length;
+
+ Offset = 0;
+
+ while (Offset < OptionLen) {
+ OptionType = *(Option + Offset);
+ Length = (UINT16) (*(Option + Offset + 1) * 8);
+
+ switch (OptionType) {
+ case Ip6OptionPrefixInfo:
+ if (Length != 32) {
+ return FALSE;
+ }
+
+ break;
+
+ case Ip6OptionMtu:
+ if (Length != 8) {
+ return FALSE;
+ }
+
+ break;
+
+ default:
+ //
+ // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and
+ // Ip6OptionRedirected here. For unrecognized options, silently ignore
+ // and continue processsing the message.
+ //
+ if (Length == 0) {
+ return FALSE;
+ }
+
+ break;
+ }
+
+ Offset = (UINT16) (Offset + Length);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Validate whether the NextHeader is a known valid protocol or one of the user configured
+ protocols from the upper layer.
+
+ @param[in] IpSb The IP6 service instance.
+ @param[in] NextHeader The next header field.
+
+ @retval TRUE The NextHeader is a known valid protocol or user configured.
+ @retval FALSE The NextHeader is not a known valid protocol.
+
+**/
+BOOLEAN
+Ip6IsValidProtocol (
+ IN IP6_SERVICE *IpSb,
+ IN UINT8 NextHeader
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_PROTOCOL *IpInstance;
+
+ if (NextHeader == EFI_IP_PROTO_TCP ||
+ NextHeader == EFI_IP_PROTO_UDP ||
+ NextHeader == IP6_ICMP ||
+ NextHeader == IP6_ESP
+ ) {
+ return TRUE;
+ }
+
+ if (IpSb == NULL) {
+ return FALSE;
+ }
+
+ if (IpSb->Signature != IP6_SERVICE_SIGNATURE) {
+ return FALSE;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
+ if (IpInstance->State == IP6_STATE_CONFIGED) {
+ if (IpInstance->ConfigData.DefaultProtocol == NextHeader) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Validate the IP6 extension header format for both the packets we received
+ and that we will transmit. It will compute the ICMPv6 error message fields
+ if the option is mal-formated.
+
+ @param[in] IpSb The IP6 service instance. This is an optional parameter.
+ @param[in] Packet The data of the packet. Ignored if NULL.
+ @param[in] NextHeader The next header field in IPv6 basic header.
+ @param[in] ExtHdrs The first byte of the option.
+ @param[in] ExtHdrsLen The length of the whole option.
+ @param[in] Rcvd The option is from the packet we received if TRUE,
+ otherwise, the option we want to transmit.
+ @param[out] FormerHeader The offset of NextHeader which points to Fragment
+ Header when we received, of the ExtHdrs.
+ Ignored if we transmit.
+ @param[out] LastHeader The pointer of NextHeader of the last extension
+ header processed by IP6.
+ @param[out] RealExtsLen The length of extension headers processed by IP6 layer.
+ This is an optional parameter that may be NULL.
+ @param[out] UnFragmentLen The length of unfragmented length of extension headers.
+ This is an optional parameter that may be NULL.
+ @param[out] Fragmented Indicate whether the packet is fragmented.
+ This is an optional parameter that may be NULL.
+
+ @retval TRUE The option is properly formated.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsExtsValid (
+ IN IP6_SERVICE *IpSb OPTIONAL,
+ IN NET_BUF *Packet OPTIONAL,
+ IN UINT8 *NextHeader,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN BOOLEAN Rcvd,
+ OUT UINT32 *FormerHeader OPTIONAL,
+ OUT UINT8 **LastHeader,
+ OUT UINT32 *RealExtsLen OPTIONAL,
+ OUT UINT32 *UnFragmentLen OPTIONAL,
+ OUT BOOLEAN *Fragmented OPTIONAL
+ )
+{
+ UINT32 Pointer;
+ UINT32 Offset;
+ UINT8 *Option;
+ UINT8 OptionLen;
+ BOOLEAN Flag;
+ UINT8 CountD;
+ UINT8 CountA;
+ IP6_FRAGMENT_HEADER *FragmentHead;
+ UINT16 FragmentOffset;
+ IP6_ROUTING_HEADER *RoutingHead;
+
+ if (RealExtsLen != NULL) {
+ *RealExtsLen = 0;
+ }
+
+ if (UnFragmentLen != NULL) {
+ *UnFragmentLen = 0;
+ }
+
+ if (Fragmented != NULL) {
+ *Fragmented = FALSE;
+ }
+
+ *LastHeader = NextHeader;
+
+ if (ExtHdrs == NULL && ExtHdrsLen == 0) {
+ return TRUE;
+ }
+
+ if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {
+ return FALSE;
+ }
+
+ Pointer = 0;
+ Offset = 0;
+ Flag = FALSE;
+ CountD = 0;
+ CountA = 0;
+
+ while (Offset <= ExtHdrsLen) {
+
+ switch (*NextHeader) {
+ case IP6_HOP_BY_HOP:
+ if (Offset != 0) {
+ if (!Rcvd) {
+ return FALSE;
+ }
+ //
+ // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only.
+ // If not, generate a ICMP parameter problem message with code value of 1.
+ //
+ if (Pointer == 0) {
+ Pointer = sizeof (EFI_IP6_HEADER);
+ } else {
+ Pointer = Offset + sizeof (EFI_IP6_HEADER);
+ }
+
+ if ((IpSb != NULL) && (Packet != NULL) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 1,
+ &Pointer
+ );
+ }
+ return FALSE;
+ }
+
+ Flag = TRUE;
+
+ //
+ // Fall through
+ //
+ case IP6_DESTINATION:
+ if (*NextHeader == IP6_DESTINATION) {
+ CountD++;
+ }
+
+ if (CountD > 2) {
+ return FALSE;
+ }
+
+ NextHeader = ExtHdrs + Offset;
+ Pointer = Offset;
+
+ Offset++;
+ Option = ExtHdrs + Offset;
+ OptionLen = (UINT8) ((*Option + 1) * 8 - 2);
+ Option++;
+ Offset++;
+
+ if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) {
+ return FALSE;
+ }
+
+ Offset = Offset + OptionLen;
+
+ if (Flag) {
+ if (UnFragmentLen != NULL) {
+ *UnFragmentLen = Offset;
+ }
+
+ Flag = FALSE;
+ }
+
+ break;
+
+ case IP6_ROUTING:
+ NextHeader = ExtHdrs + Offset;
+ RoutingHead = (IP6_ROUTING_HEADER *) NextHeader;
+
+ //
+ // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095.
+ // Thus all routing types are processed as unrecognized.
+ //
+ if (RoutingHead->SegmentsLeft == 0) {
+ //
+ // Ignore the routing header and proceed to process the next header.
+ //
+ Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;
+
+ if (UnFragmentLen != NULL) {
+ *UnFragmentLen = Offset;
+ }
+
+ } else {
+ //
+ // Discard the packet and send an ICMP Parameter Problem, Code 0, message
+ // to the packet's source address, pointing to the unrecognized routing
+ // type.
+ //
+ Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);
+ if ((IpSb != NULL) && (Packet != NULL) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 0,
+ &Pointer
+ );
+ }
+
+ return FALSE;
+ }
+
+ break;
+
+ case IP6_FRAGMENT:
+
+ //
+ // RFC2402, AH header should after fragment header.
+ //
+ if (CountA > 1) {
+ return FALSE;
+ }
+
+ //
+ // RFC2460, ICMP Parameter Problem message with code 0 should be sent
+ // if the length of a fragment is not a multiple of 8 octects and the M
+ // flag of that fragment is 1, pointing to the Payload length field of the
+ // fragment packet.
+ //
+ if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) {
+ //
+ // Check whether it is the last fragment.
+ //
+ FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset);
+ if (FragmentHead == NULL) {
+ return FALSE;
+ }
+
+ FragmentOffset = NTOHS (FragmentHead->FragmentOffset);
+
+ if (((FragmentOffset & 0x1) == 0x1) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Pointer = sizeof (UINT32);
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 0,
+ &Pointer
+ );
+ return FALSE;
+ }
+ }
+
+ if (Fragmented != NULL) {
+ *Fragmented = TRUE;
+ }
+
+ if (Rcvd && FormerHeader != NULL) {
+ *FormerHeader = (UINT32) (NextHeader - ExtHdrs);
+ }
+
+ NextHeader = ExtHdrs + Offset;
+ Offset = Offset + 8;
+ break;
+
+ case IP6_AH:
+ if (++CountA > 1) {
+ return FALSE;
+ }
+
+ Option = ExtHdrs + Offset;
+ NextHeader = Option;
+ Option++;
+ //
+ // RFC2402, Payload length is specified in 32-bit words, minus "2".
+ //
+ OptionLen = (UINT8) ((*Option + 2) * 4);
+ Offset = Offset + OptionLen;
+ break;
+
+ case IP6_NO_NEXT_HEADER:
+ *LastHeader = NextHeader;
+ return FALSE;
+ break;
+
+ default:
+ if (Ip6IsValidProtocol (IpSb, *NextHeader)) {
+
+ *LastHeader = NextHeader;
+
+ if (RealExtsLen != NULL) {
+ *RealExtsLen = Offset;
+ }
+
+ return TRUE;
+ }
+
+ //
+ // The Next Header value is unrecognized by the node, discard the packet and
+ // send an ICMP parameter problem message with code value of 1.
+ //
+ if (Offset == 0) {
+ //
+ // The Next Header directly follows IPv6 basic header.
+ //
+ Pointer = 6;
+ } else {
+ if (Pointer == 0) {
+ Pointer = sizeof (EFI_IP6_HEADER);
+ } else {
+ Pointer = Offset + sizeof (EFI_IP6_HEADER);
+ }
+ }
+
+ if ((IpSb != NULL) && (Packet != NULL) &&
+ !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
+ Ip6SendIcmpError (
+ IpSb,
+ Packet,
+ NULL,
+ &Packet->Ip.Ip6->SourceAddress,
+ ICMP_V6_PARAMETER_PROBLEM,
+ 1,
+ &Pointer
+ );
+ }
+ return FALSE;
+ }
+ }
+
+ *LastHeader = NextHeader;
+
+ if (RealExtsLen != NULL) {
+ *RealExtsLen = Offset;
+ }
+
+ return TRUE;
+}
+
+/**
+ Generate an IPv6 router alert option in network order and output it through Buffer.
+
+ @param[out] Buffer Points to a buffer to record the generated option.
+ @param[in, out] BufferLen The length of Buffer, in bytes.
+ @param[in] NextHeader The 8-bit selector indicates the type of header
+ immediately following the Hop-by-Hop Options header.
+
+ @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated
+ option. BufferLen is updated for the required size.
+
+ @retval EFI_SUCCESS The option is generated and filled in to Buffer.
+
+**/
+EFI_STATUS
+Ip6FillHopByHop (
+ OUT UINT8 *Buffer,
+ IN OUT UINTN *BufferLen,
+ IN UINT8 NextHeader
+ )
+{
+ UINT8 BufferArray[8];
+
+ if (*BufferLen < 8) {
+ *BufferLen = 8;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Form the Hop-By-Hop option in network order.
+ // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN
+ // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets.
+ //
+ ZeroMem (BufferArray, sizeof (BufferArray));
+ BufferArray[0] = NextHeader;
+ BufferArray[2] = 0x5;
+ BufferArray[3] = 0x2;
+ BufferArray[6] = 1;
+
+ CopyMem (Buffer, BufferArray, sizeof (BufferArray));
+ return EFI_SUCCESS;
+}
+
+/**
+ Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.
+
+ @param[in] IpSb The IP6 service instance to transmit the packet.
+ @param[in] NextHeader The extension header type of first extension header.
+ @param[in] LastHeader The extension header type of last extension header.
+ @param[in] ExtHdrs The length of the original extension header.
+ @param[in] ExtHdrsLen The length of the extension headers.
+ @param[in] FragmentOffset The fragment offset of the data following the header.
+ @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted.
+ It's caller's responsiblity to free this buffer.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of
+ resource.
+ @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not
+ supported currently.
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+Ip6FillFragmentHeader (
+ IN IP6_SERVICE *IpSb,
+ IN UINT8 NextHeader,
+ IN UINT8 LastHeader,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN UINT16 FragmentOffset,
+ OUT UINT8 **UpdatedExtHdrs
+ )
+{
+ UINT32 Length;
+ UINT8 *Buffer;
+ UINT32 FormerHeader;
+ UINT32 Offset;
+ UINT32 Part1Len;
+ UINT32 HeaderLen;
+ UINT8 Current;
+ IP6_FRAGMENT_HEADER FragmentHead;
+
+ if (UpdatedExtHdrs == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
+ Buffer = AllocatePool (Length);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Offset = 0;
+ Part1Len = 0;
+ FormerHeader = 0;
+ Current = NextHeader;
+
+ while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) {
+ switch (NextHeader) {
+ case IP6_ROUTING:
+ case IP6_HOP_BY_HOP:
+ case IP6_DESTINATION:
+ Current = NextHeader;
+ NextHeader = *(ExtHdrs + Offset);
+
+ if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) {
+ //
+ // Destination Options header should occur at most twice, once before
+ // a Routing header and once before the upper-layer header. Here we
+ // find the one before the upper-layer header. Insert the Fragment
+ // Header before it.
+ //
+ CopyMem (Buffer, ExtHdrs, Part1Len);
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;
+ //
+ // Exit the loop.
+ //
+ Offset = ExtHdrsLen + 1;
+ break;
+ }
+
+
+ FormerHeader = Offset;
+ HeaderLen = (*(ExtHdrs + Offset + 1) + 1) * 8;
+ Part1Len = Part1Len + HeaderLen;
+ Offset = Offset + HeaderLen;
+ break;
+
+ case IP6_FRAGMENT:
+ Current = NextHeader;
+
+ if (Part1Len != 0) {
+ CopyMem (Buffer, ExtHdrs, Part1Len);
+ }
+
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;
+
+ //
+ // Exit the loop.
+ //
+ Offset = ExtHdrsLen + 1;
+ break;
+
+ case IP6_AH:
+ Current = NextHeader;
+ NextHeader = *(ExtHdrs + Offset);
+ //
+ // RFC2402, Payload length is specified in 32-bit words, minus "2".
+ //
+ HeaderLen = (*(ExtHdrs + Offset + 1) + 2) * 4;
+ Part1Len = Part1Len + HeaderLen;
+ Offset = Offset + HeaderLen;
+ break;
+
+ default:
+ if (Ip6IsValidProtocol (IpSb, NextHeader)) {
+ Current = NextHeader;
+ CopyMem (Buffer, ExtHdrs, Part1Len);
+ *(Buffer + FormerHeader) = IP6_FRAGMENT;
+ //
+ // Exit the loop.
+ //
+ Offset = ExtHdrsLen + 1;
+ break;
+ }
+
+ FreePool (Buffer);
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ //
+ // Append the Fragment header. If the fragment offset indicates the fragment
+ // is the first fragment.
+ //
+ if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {
+ FragmentHead.NextHeader = Current;
+ } else {
+ FragmentHead.NextHeader = LastHeader;
+ }
+
+ FragmentHead.Reserved = 0;
+ FragmentHead.FragmentOffset = HTONS (FragmentOffset);
+ FragmentHead.Identification = mIp6Id;
+
+ CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER));
+
+ if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) {
+ //
+ // Append the part2 (fragmentable part) of Extension headers
+ //
+ CopyMem (
+ Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER),
+ ExtHdrs + Part1Len,
+ ExtHdrsLen - Part1Len
+ );
+ }
+
+ *UpdatedExtHdrs = Buffer;
+
+ return EFI_SUCCESS;
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Option.h b/NetworkPkg/Ip6Dxe/Ip6Option.h
new file mode 100644
index 0000000000..b62a04216e
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Option.h
@@ -0,0 +1,191 @@
+/** @file
+ Definition of IP6 option process routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_OPTION_H__
+#define __EFI_IP6_OPTION_H__
+
+#define IP6_FRAGMENT_OFFSET_MASK (~0x3)
+
+typedef struct _IP6_FRAGMENT_HEADER {
+ UINT8 NextHeader;
+ UINT8 Reserved;
+ UINT16 FragmentOffset;
+ UINT32 Identification;
+} IP6_FRAGMENT_HEADER;
+
+typedef struct _IP6_ROUTING_HEADER {
+ UINT8 NextHeader;
+ UINT8 HeaderLen;
+ UINT8 RoutingType;
+ UINT8 SegmentsLeft;
+} IP6_ROUTING_HEADER;
+
+typedef enum {
+ Ip6OptionPad1 = 0,
+ Ip6OptionPadN = 1,
+ Ip6OptionRouterAlert = 5,
+ Ip6OptionSkip = 0,
+ Ip6OptionDiscard = 0x40,
+ Ip6OptionParameterProblem = 0x80,
+ Ip6OptionMask = 0xc0,
+
+ Ip6OptionEtherSource = 1,
+ Ip6OptionEtherTarget = 2,
+ Ip6OptionPrefixInfo = 3,
+ Ip6OptionRedirected = 4,
+ Ip6OptionMtu = 5
+} IP6_OPTION_TYPE;
+
+/**
+ Validate the IP6 extension header format for both the packets we received
+ and that we will transmit. It will compute the ICMPv6 error message fields
+ if the option is mal-formated.
+
+ @param[in] IpSb The IP6 service instance. This is an optional parameter.
+ @param[in] Packet The data of the packet. Ignored if NULL.
+ @param[in] NextHeader The next header field in IPv6 basic header.
+ @param[in] ExtHdrs The first byte of the option.
+ @param[in] ExtHdrsLen The length of the whole option.
+ @param[in] Rcvd The option is from the packet we received if TRUE,
+ otherwise, the option we want to transmit.
+ @param[out] FormerHeader The offset of NextHeader which points to Fragment
+ Header when we received, of the ExtHdrs.
+ Ignored if we transmit.
+ @param[out] LastHeader The pointer of NextHeader of the last extension
+ header processed by IP6.
+ @param[out] RealExtsLen The length of extension headers processed by IP6 layer.
+ This is an optional parameter that may be NULL.
+ @param[out] UnFragmentLen The length of unfragmented length of extension headers.
+ This is an optional parameter that may be NULL.
+ @param[out] Fragmented Indicate whether the packet is fragmented.
+ This is an optional parameter that may be NULL.
+
+ @retval TRUE The option is properly formated.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsExtsValid (
+ IN IP6_SERVICE *IpSb OPTIONAL,
+ IN NET_BUF *Packet OPTIONAL,
+ IN UINT8 *NextHeader,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN BOOLEAN Rcvd,
+ OUT UINT32 *FormerHeader OPTIONAL,
+ OUT UINT8 **LastHeader,
+ OUT UINT32 *RealExtsLen OPTIONAL,
+ OUT UINT32 *UnFragmentLen OPTIONAL,
+ OUT BOOLEAN *Fragmented OPTIONAL
+ );
+
+/**
+ Generate an IPv6 router alert option in network order and output it through Buffer.
+
+ @param[out] Buffer Points to a buffer to record the generated option.
+ @param[in, out] BufferLen The length of Buffer, in bytes.
+ @param[in] NextHeader The 8-bit selector indicates the type of header
+ immediately following the Hop-by-Hop Options header.
+
+ @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated
+ option. BufferLen is updated for the required size.
+
+ @retval EFI_SUCCESS The option is generated and filled in to Buffer.
+
+**/
+EFI_STATUS
+Ip6FillHopByHop (
+ OUT UINT8 *Buffer,
+ IN OUT UINTN *BufferLen,
+ IN UINT8 NextHeader
+ );
+
+/**
+ Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.
+
+ @param[in] IpSb The IP6 service instance to transmit the packet.
+ @param[in] NextHeader The extension header type of first extension header.
+ @param[in] LastHeader The extension header type of last extension header.
+ @param[in] ExtHdrs The length of the original extension header.
+ @param[in] ExtHdrsLen The length of the extension headers.
+ @param[in] FragmentOffset The fragment offset of the data following the header.
+ @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted.
+ It's caller's responsiblity to free this buffer.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of
+ resource.
+ @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not
+ supported currently.
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+Ip6FillFragmentHeader (
+ IN IP6_SERVICE *IpSb,
+ IN UINT8 NextHeader,
+ IN UINT8 LastHeader,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN UINT16 FragmentOffset,
+ OUT UINT8 **UpdatedExtHdrs
+ );
+
+/**
+ Copy the extension headers from the original to buffer. A Fragment header is
+ appended to the end.
+
+ @param[in] NextHeader The 8-bit selector indicates the type of
+ the fragment header's next header.
+ @param[in] ExtHdrs The length of the original extension header.
+ @param[in] LastHeader The pointer of next header of last extension header.
+ @param[in] FragmentOffset The fragment offset of the data following the header.
+ @param[in] UnFragmentHdrLen The length of unfragmented length of extension headers.
+ @param[in, out] Buf The buffer to copy options to.
+ @param[in, out] BufLen The length of the buffer.
+
+ @retval EFI_SUCCESS The options are copied over.
+ @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small.
+
+**/
+EFI_STATUS
+Ip6CopyExts (
+ IN UINT8 NextHeader,
+ IN UINT8 *ExtHdrs,
+ IN UINT8 *LastHeader,
+ IN UINT16 FragmentOffset,
+ IN UINT32 UnFragmentHdrLen,
+ IN OUT UINT8 *Buf,
+ IN OUT UINT32 *BufLen
+ );
+
+/**
+ Validate the IP6 option format for both the packets we received
+ and that we will transmit. It supports the defined options in Neighbor
+ Discovery messages.
+
+ @param[in] Option The first byte of the option.
+ @param[in] OptionLen The length of the whole option.
+
+ @retval TRUE The option is properly formatted.
+ @retval FALSE The option is malformated.
+
+**/
+BOOLEAN
+Ip6IsNDOptionValid (
+ IN UINT8 *Option,
+ IN UINT16 OptionLen
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.c b/NetworkPkg/Ip6Dxe/Ip6Output.c
new file mode 100644
index 0000000000..baa4904fc9
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Output.c
@@ -0,0 +1,1077 @@
+/** @file
+ The internal functions and routines to transmit the IP6 packet.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+UINT32 mIp6Id;
+
+/**
+ Output all the available source addresses to a list entry head SourceList. The
+ number of source addresses are also returned.
+
+ @param[in] IpSb Points to an IP6 service binding instance.
+ @param[out] SourceList The list entry head of all source addresses.
+ It is the caller's responsiblity to free the
+ resources.
+ @param[out] SourceCount The number of source addresses.
+
+ @retval EFI_SUCCESS The source addresses were copied to a list entry head
+ SourceList.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation.
+
+**/
+EFI_STATUS
+Ip6CandidateSource (
+ IN IP6_SERVICE *IpSb,
+ OUT LIST_ENTRY *SourceList,
+ OUT UINT32 *SourceCount
+ )
+{
+ IP6_INTERFACE *IpIf;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Entry2;
+ IP6_ADDRESS_INFO *AddrInfo;
+ IP6_ADDRESS_INFO *Copy;
+
+ *SourceCount = 0;
+
+ if (IpSb->LinkLocalOk) {
+ Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO));
+ if (Copy == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Copy->Signature = IP6_ADDR_INFO_SIGNATURE;
+ IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr);
+ Copy->IsAnycast = FALSE;
+ Copy->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH;
+ Copy->ValidLifetime = (UINT32) IP6_INFINIT_LIFETIME;
+ Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME;
+
+ InsertTailList (SourceList, &Copy->Link);
+ (*SourceCount)++;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
+
+ NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
+
+ if (AddrInfo->IsAnycast) {
+ //
+ // Never use an anycast address.
+ //
+ continue;
+ }
+
+ Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo);
+ if (Copy == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (SourceList, &Copy->Link);
+ (*SourceCount)++;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Caculate how many bits are the same between two IPv6 addresses.
+
+ @param[in] AddressA Points to an IPv6 address.
+ @param[in] AddressB Points to another IPv6 address.
+
+ @return The common bits of the AddressA and AddressB.
+
+**/
+UINT8
+Ip6CommonPrefixLen (
+ IN EFI_IPv6_ADDRESS *AddressA,
+ IN EFI_IPv6_ADDRESS *AddressB
+ )
+{
+ UINT8 Count;
+ UINT8 Index;
+ UINT8 ByteA;
+ UINT8 ByteB;
+ UINT8 NumBits;
+
+ Count = 0;
+ Index = 0;
+
+ while (Index < 16) {
+ ByteA = AddressA->Addr[Index];
+ ByteB = AddressB->Addr[Index];
+
+ if (ByteA == ByteB) {
+ Count += 8;
+ Index++;
+ continue;
+ }
+
+ //
+ // Check how many bits are common between the two bytes.
+ //
+ NumBits = 8;
+ ByteA = (UINT8) (ByteA ^ ByteB);
+
+ while (ByteA != 0) {
+ NumBits--;
+ ByteA = (UINT8) (ByteA >> 1);
+ }
+
+ return (UINT8) (Count + NumBits);
+ }
+
+ return Count;
+}
+
+/**
+ Output all the available source addresses to a list entry head SourceList. The
+ number of source addresses are also returned.
+
+ @param[in] IpSb Points to a IP6 service binding instance.
+ @param[in] Destination The IPv6 destination address.
+ @param[out] Source The selected IPv6 source address according to
+ the Destination.
+
+ @retval EFI_SUCCESS The source addresses were copied to a list entry
+ head SourceList.
+ @retval EFI_NO_MAPPING The IPv6 stack is not auto configured.
+
+**/
+EFI_STATUS
+Ip6SelectSourceAddress (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Destination,
+ OUT EFI_IPv6_ADDRESS *Source
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY SourceList;
+ UINT32 SourceCount;
+ UINT8 ScopeD;
+ LIST_ENTRY *Entry;
+ IP6_ADDRESS_INFO *AddrInfo;
+ IP6_PREFIX_LIST_ENTRY *Prefix;
+ UINT8 LastCommonLength;
+ UINT8 CurrentCommonLength;
+ EFI_IPv6_ADDRESS *TmpAddress;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ Status = EFI_SUCCESS;
+ InitializeListHead (&SourceList);
+
+ if (!IpSb->LinkLocalOk) {
+ return EFI_NO_MAPPING;
+ }
+
+ //
+ // Rule 1: Prefer same address.
+ //
+ if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) {
+ IP6_COPY_ADDRESS (Source, Destination);
+ goto Exit;
+ }
+
+ //
+ // Rule 2: Prefer appropriate scope.
+ //
+ if (IP6_IS_MULTICAST (Destination)) {
+ ScopeD = (UINT8) (Destination->Addr[1] >> 4);
+ } else if (NetIp6IsLinkLocalAddr (Destination)) {
+ ScopeD = 0x2;
+ } else {
+ ScopeD = 0xE;
+ }
+
+ if (ScopeD <= 0x2) {
+ //
+ // Return the link-local address if it exists
+ // One IP6_SERVICE only has one link-local address.
+ //
+ IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);
+ goto Exit;
+ }
+
+ //
+ // All candidate source addresses are global unicast address.
+ //
+ Ip6CandidateSource (IpSb, &SourceList, &SourceCount);
+
+ if (SourceCount == 0) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);
+
+ if (SourceCount == 1) {
+ goto Exit;
+ }
+
+ //
+ // Rule 3: Avoid deprecated addresses.
+ // TODO: check the "deprecated" state of the stateful configured address
+ //
+ NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {
+ Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
+ if (Prefix->PreferredLifetime == 0) {
+ Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength);
+
+ if (SourceCount == 1) {
+ goto Exit;
+ }
+ }
+ }
+
+ //
+ // TODO: Rule 4: Prefer home addresses.
+ // TODO: Rule 5: Prefer outgoing interface.
+ // TODO: Rule 6: Prefer matching label.
+ // TODO: Rule 7: Prefer public addresses.
+ //
+
+ //
+ // Rule 8: Use longest matching prefix.
+ //
+ LastCommonLength = Ip6CommonPrefixLen (Source, Destination);
+ TmpAddress = NULL;
+
+ for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) {
+ AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
+
+ CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination);
+ if (CurrentCommonLength > LastCommonLength) {
+ LastCommonLength = CurrentCommonLength;
+ TmpAddress = &AddrInfo->Address;
+ }
+ }
+
+ if (TmpAddress != NULL) {
+ IP6_COPY_ADDRESS (Source, TmpAddress);
+ }
+
+Exit:
+
+ Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0);
+
+ return Status;
+}
+
+/**
+ Select an interface to send the packet generated in the IP6 driver
+ itself: that is, not by the requests of the IP6 child's consumer. Such
+ packets include the ICMPv6 echo replies and other ICMPv6 error packets.
+
+ @param[in] IpSb The IP4 service that wants to send the packets.
+ @param[in] Destination The destination of the packet.
+ @param[in, out] Source The source of the packet.
+
+ @return NULL if no proper interface is found, otherwise, the interface that
+ can be used to send the system packet from.
+
+**/
+IP6_INTERFACE *
+Ip6SelectInterface (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Destination,
+ IN OUT EFI_IPv6_ADDRESS *Source
+ )
+{
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS SelectedSource;
+ IP6_INTERFACE *IpIf;
+ BOOLEAN Exist;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+ ASSERT (Destination != NULL && Source != NULL);
+
+ if (NetIp6IsUnspecifiedAddr (Destination)) {
+ return NULL;
+ }
+
+ if (!NetIp6IsUnspecifiedAddr (Source)) {
+ Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL);
+ ASSERT (Exist);
+
+ return IpIf;
+ }
+
+ //
+ // If source is unspecified, select a source according to the destination.
+ //
+ Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource);
+ if (EFI_ERROR (Status)) {
+ return IpSb->DefaultInterface;
+ }
+
+ Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL);
+ IP6_COPY_ADDRESS (Source, &SelectedSource);
+
+ return IpIf;
+}
+
+/**
+ The default callback function for the system generated packet.
+ It will free the packet.
+
+ @param[in] Packet The packet that transmitted.
+ @param[in] IoStatus The result of the transmission, succeeded or failed.
+ @param[in] LinkFlag Not used when transmitted. Check IP6_FRAME_CALLBACK
+ for reference.
+ @param[in] Context The context provided by us.
+
+**/
+VOID
+Ip6SysPacketSent (
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 LinkFlag,
+ VOID *Context
+ )
+{
+ NetbufFree (Packet);
+ Packet = NULL;
+}
+
+/**
+ Prefix an IP6 basic head and unfragmentable extension headers and a fragment header
+ to the Packet. Used for IP6 fragmentation.
+
+ @param[in] IpSb The IP6 service instance to transmit the packet.
+ @param[in] Packet The packet to prefix the IP6 header to.
+ @param[in] Head The caller supplied header.
+ @param[in] FragmentOffset The fragment offset of the data following the header.
+ @param[in] ExtHdrs The length of the original extension header.
+ @param[in] ExtHdrsLen The length of the extension headers.
+ @param[in] LastHeader The pointer of next header of last extension header.
+ @param[in] HeadLen The length of the unfragmented part of the IP6 header.
+
+ @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of
+ Packet.
+ @retval EFI_SUCCESS The operation performed successfully.
+
+**/
+EFI_STATUS
+Ip6PrependHead (
+ IN IP6_SERVICE *IpSb,
+ IN NET_BUF *Packet,
+ IN EFI_IP6_HEADER *Head,
+ IN UINT16 FragmentOffset,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN UINT8 LastHeader,
+ IN UINT32 HeadLen
+ )
+{
+ UINT32 Len;
+ UINT32 UnFragExtHdrsLen;
+ EFI_IP6_HEADER *PacketHead;
+ UINT8 *UpdatedExtHdrs;
+ EFI_STATUS Status;
+ UINT8 NextHeader;
+
+ //
+ // HeadLen is the length of the fixed part of the sequences of fragments, i.e.
+ // the unfragment part.
+ //
+ PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
+ if (PacketHead == NULL) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Set the head up, convert the host byte order to network byte order
+ //
+ CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
+ PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER)));
+ Packet->Ip.Ip6 = PacketHead;
+
+ Len = HeadLen - sizeof (EFI_IP6_HEADER);
+ UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER);
+
+ if (UnFragExtHdrsLen == 0) {
+ PacketHead->NextHeader = IP6_FRAGMENT;
+ }
+
+ //
+ // Append the extension headers: firstly copy the unfragmentable headers, then append
+ // fragmentation header.
+ //
+ if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {
+ NextHeader = Head->NextHeader;
+ } else {
+ NextHeader = PacketHead->NextHeader;
+ }
+
+ Status = Ip6FillFragmentHeader (
+ IpSb,
+ NextHeader,
+ LastHeader,
+ ExtHdrs,
+ ExtHdrsLen,
+ FragmentOffset,
+ &UpdatedExtHdrs
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CopyMem (
+ (UINT8 *) (PacketHead + 1),
+ UpdatedExtHdrs,
+ UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER)
+ );
+
+ FreePool (UpdatedExtHdrs);
+ return EFI_SUCCESS;
+}
+
+/**
+ Transmit an IP6 packet. The packet comes either from the IP6
+ child's consumer (IpInstance != NULL) or the IP6 driver itself
+ (IpInstance == NULL). It will route the packet, fragment it,
+ then transmit all the fragments through an interface.
+
+ @param[in] IpSb The IP6 service instance to transmit the packet.
+ @param[in] Interface The IP6 interface to transmit the packet. Ignored
+ if NULL.
+ @param[in] IpInstance The IP6 child that issues the transmission. It is
+ NULL if the packet is from the system.
+ @param[in] Packet The user data to send, excluding the IP header.
+ @param[in] Head The caller supplied header. The caller should set
+ the following header fields: NextHeader, HopLimit,
+ Src, Dest, FlowLabel, PayloadLength. This function
+ will fill in the Ver, TrafficClass.
+ @param[in] ExtHdrs The extension headers to append to the IPv6 basic
+ header.
+ @param[in] ExtHdrsLen The length of the extension headers.
+ @param[in] Callback The callback function to issue when transmission
+ completed.
+ @param[in] Context The opaque context for the callback.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.
+ @retval EFI_NO_MAPPING There is no interface to the destination.
+ @retval EFI_NOT_FOUND There is no route to the destination.
+ @retval EFI_SUCCESS The packet successfully transmitted.
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of
+ resources.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Ip6Output (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *Interface OPTIONAL,
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN EFI_IP6_HEADER *Head,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN IP6_FRAME_CALLBACK Callback,
+ IN VOID *Context
+ )
+{
+ IP6_INTERFACE *IpIf;
+ EFI_IPv6_ADDRESS NextHop;
+ IP6_NEIGHBOR_ENTRY *NeighborCache;
+ IP6_ROUTE_CACHE_ENTRY *RouteCache;
+ EFI_STATUS Status;
+ UINT32 Mtu;
+ UINT32 HeadLen;
+ UINT16 FragmentOffset;
+ UINT8 *LastHeader;
+ UINT32 UnFragmentLen;
+ UINT32 UnFragmentHdrsLen;
+ UINT32 FragmentHdrsLen;
+ UINT16 *Checksum;
+ UINT16 PacketChecksum;
+ UINT16 PseudoChecksum;
+ UINT32 Index;
+ UINT32 PacketLen;
+ UINT32 RealExtLen;
+ UINT32 Offset;
+ NET_BUF *TmpPacket;
+ NET_BUF *Fragment;
+ UINT32 Num;
+ UINT8 *Buf;
+ EFI_IP6_HEADER *PacketHead;
+ IP6_ICMP_HEAD *IcmpHead;
+ IP6_TXTOKEN_WRAP *Wrap;
+ IP6_ROUTE_ENTRY *RouteEntry;
+ UINT8 *UpdatedExtHdrs;
+ UINT8 NextHeader;
+ UINT8 LastHeaderBackup;
+ BOOLEAN FragmentHeadInserted;
+ UINT8 *ExtHdrsBackup;
+ UINT8 NextHeaderBackup;
+ EFI_IPv6_ADDRESS Source;
+ EFI_IPv6_ADDRESS Destination;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ //
+ // RFC2460: Each extension header is an integer multiple of 8 octets long,
+ // in order to retain 8-octet alignment for subsequent headers.
+ //
+ if ((ExtHdrsLen & 0x7) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ LastHeader = NULL;
+
+ Ip6IsExtsValid (
+ NULL,
+ NULL,
+ &Head->NextHeader,
+ ExtHdrs,
+ ExtHdrsLen,
+ FALSE,
+ NULL,
+ &LastHeader,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ //
+ // Select an interface/source for system packet, application
+ // should select them itself.
+ //
+ IpIf = Interface;
+ if (IpIf == NULL) {
+ //
+ // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress
+ // and destinationaddress is unspecified.
+ //
+ if (IpInstance == NULL || IpInstance->Interface == NULL) {
+ IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress);
+ if (IpInstance != NULL) {
+ IpInstance->Interface = IpIf;
+ }
+ } else {
+ IpIf = IpInstance->Interface;
+ }
+ }
+
+ if (IpIf == NULL) {
+ return EFI_NO_MAPPING;
+ }
+
+ //
+ // Update the common field in Head here.
+ //
+ Head->Version = 6;
+ Head->TrafficClassL = 0;
+ Head->TrafficClassH = 0;
+
+ Checksum = NULL;
+ NextHeader = *LastHeader;
+
+ switch (NextHeader) {
+ case EFI_IP_PROTO_UDP:
+ Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Packet->Udp != NULL);
+ if (Packet->Udp->Checksum == 0) {
+ Checksum = &Packet->Udp->Checksum;
+ }
+ break;
+
+ case EFI_IP_PROTO_TCP:
+ Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Packet->Tcp != NULL);
+ if (Packet->Tcp->Checksum == 0) {
+ Checksum = &Packet->Tcp->Checksum;
+ }
+ break;
+
+ case IP6_ICMP:
+ //
+ // Don't send ICMP packet to an IPv6 anycast address.
+ //
+ if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (IcmpHead != NULL);
+ if (IcmpHead->Checksum == 0) {
+ Checksum = &IcmpHead->Checksum;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (Checksum != NULL) {
+ //
+ // Calculate the checksum for upper layer protocol if it is not calculated due to lack of
+ // IPv6 source address.
+ //
+ PacketChecksum = NetbufChecksum (Packet);
+ PseudoChecksum = NetIp6PseudoHeadChecksum (
+ &Head->SourceAddress,
+ &Head->DestinationAddress,
+ NextHeader,
+ Packet->TotalSize
+ );
+ *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum);
+ }
+
+ Status = Ip6IpSecProcessPacket (
+ IpSb,
+ Head,
+ LastHeader, // no need get the lasthead value for output
+ &Packet,
+ ExtHdrs,
+ ExtHdrsLen,
+ EfiIPsecOutBound,
+ Context
+ );
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ LastHeader = NULL;
+ //
+ // Check incoming parameters.
+ //
+ if (!Ip6IsExtsValid (
+ IpSb,
+ Packet,
+ &Head->NextHeader,
+ ExtHdrs,
+ ExtHdrsLen,
+ FALSE,
+ NULL,
+ &LastHeader,
+ &RealExtLen,
+ &UnFragmentHdrsLen,
+ NULL
+ )) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((RealExtLen & 0x7) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ LastHeaderBackup = *LastHeader;
+
+ //
+ // Perform next hop determination:
+ // For multicast packets, the next-hop is always the destination address and
+ // is considered to be on-link.
+ //
+ if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {
+ IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);
+ } else {
+ //
+ // For unicast packets, use a combination of the Destination Cache, the Prefix List
+ // and the Default Router List to determine the IP address of the appropriate next hop.
+ //
+ RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress);
+ if (RouteCache == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop);
+ Ip6FreeRouteCacheEntry (RouteCache);
+ }
+
+ //
+ // Examines the Neighbor Cache for link-layer information about that neighbor.
+ // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error.
+ //
+ if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) {
+ NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop);
+ if (NeighborCache == NULL) {
+ NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL);
+
+ if (NeighborCache == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Send out multicast neighbor solicitation for address resolution immediatly.
+ //
+ Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);
+ Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Ip6SendNeighborSolicit (
+ IpSb,
+ &Source,
+ &Destination,
+ &NeighborCache->Neighbor,
+ &IpSb->SnpMode.CurrentAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ --NeighborCache->Transmit;
+ NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1;
+ }
+
+ NeighborCache->Interface = IpIf;
+ }
+
+ UpdatedExtHdrs = NULL;
+ ExtHdrsBackup = NULL;
+ NextHeaderBackup = 0;
+ FragmentHeadInserted = FALSE;
+
+ //
+ // Check whether we received Packet Too Big message for the packet sent to the
+ // Destination. If yes include a Fragment Header in the subsequent packets.
+ //
+ RouteEntry = Ip6FindRouteEntry (
+ IpSb->RouteTable,
+ &Head->DestinationAddress,
+ NULL
+ );
+ if (RouteEntry != NULL) {
+ if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) {
+
+ //
+ // FragmentHead is inserted after Hop-by-Hop Options header, Destination
+ // Options header (first occur), Routing header, and before Fragment header,
+ // Authentication header, Encapsulating Security Payload header, and
+ // Destination Options header (last occur), and upper-layer header.
+ //
+ Status = Ip6FillFragmentHeader (
+ IpSb,
+ Head->NextHeader,
+ LastHeaderBackup,
+ ExtHdrs,
+ ExtHdrsLen,
+ 0,
+ &UpdatedExtHdrs
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {
+ NextHeaderBackup = Head->NextHeader;
+ Head->NextHeader = IP6_FRAGMENT;
+ }
+
+ ExtHdrsBackup = ExtHdrs;
+ ExtHdrs = UpdatedExtHdrs;
+ ExtHdrsLen = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
+ RealExtLen = RealExtLen + sizeof (IP6_FRAGMENT_HEADER);
+
+ mIp6Id++;
+
+ FragmentHeadInserted = TRUE;
+ }
+
+ Ip6FreeRouteEntry (RouteEntry);
+ }
+
+ //
+ // OK, selected the source and route, fragment the packet then send
+ // them. Tag each fragment other than the first one as spawn from it.
+ // Each extension header is an integar multiple of 8 octets long, in
+ // order to retain 8-octet alignment for subsequent headers.
+ //
+ Mtu = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER);
+ HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen;
+
+ if (Packet->TotalSize + HeadLen > Mtu) {
+ //
+ // Remove the inserted Fragment Header since we need fragment the packet.
+ //
+ if (FragmentHeadInserted) {
+ ExtHdrs = ExtHdrsBackup;
+ ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER);
+
+ if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {
+ Head->NextHeader = NextHeaderBackup;
+ }
+ }
+
+ FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen;
+
+ //
+ // The packet is beyond the maximum which can be described through the
+ // fragment offset field in Fragment header.
+ //
+ if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) {
+ Status = EFI_BAD_BUFFER_SIZE;
+ goto Error;
+ }
+
+ if (FragmentHdrsLen != 0) {
+ //
+ // Append the fragmentable extension hdrs before the upper layer payload
+ // to form a new NET_BUF. This NET_BUF contains all the buffer which will
+ // be fragmented below.
+ //
+ TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen);
+ ASSERT (TmpPacket != NULL);
+
+ //
+ // Allocate the space to contain the fragmentable hdrs and copy the data.
+ //
+ Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE);
+ ASSERT (Buf != NULL);
+ CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen);
+
+ //
+ // Free the old Packet.
+ //
+ NetbufFree (Packet);
+ Packet = TmpPacket;
+ }
+
+ //
+ // The unfragment part which appears in every fragmented IPv6 packet includes
+ // the IPv6 header, the unfragmentable extension hdrs and the fragment header.
+ //
+ UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
+
+ //
+ // Mtu now is the length of the fragment part in a full-length fragment.
+ //
+ Mtu = (Mtu - UnFragmentLen) & (~0x07);
+ Num = (Packet->TotalSize + Mtu - 1) / Mtu;
+
+ for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) {
+ //
+ // Get fragment from the Packet, append UnFragnmentLen spare buffer
+ // before the fragmented data, the corresponding data is filled in later.
+ //
+ Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen);
+ if (Fragment == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ FragmentOffset = (UINT16) ((UINT16) Offset | 0x1);
+ if (Index == Num - 1){
+ //
+ // The last fragment, clear the M flag.
+ //
+ FragmentOffset &= (~0x1);
+ }
+
+ Status = Ip6PrependHead (
+ IpSb,
+ Fragment,
+ Head,
+ FragmentOffset,
+ ExtHdrs,
+ ExtHdrsLen,
+ LastHeaderBackup,
+ UnFragmentLen
+ );
+ ASSERT (Status == EFI_SUCCESS);
+
+ Status = Ip6SendFrame (
+ IpIf,
+ IpInstance,
+ Fragment,
+ &NextHop,
+ Ip6SysPacketSent,
+ Packet
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // The last fragment of upper layer packet, update the IP6 token status.
+ //
+ if ((Index == Num -1) && (Context != NULL)) {
+ Wrap = (IP6_TXTOKEN_WRAP *) Context;
+ Wrap->Token->Status = Status;
+ }
+
+ Offset += PacketLen;
+ PacketLen = Packet->TotalSize - Offset;
+ if (PacketLen > Mtu) {
+ PacketLen = Mtu;
+ }
+ }
+
+ NetbufFree (Packet);
+ mIp6Id++;
+
+ if (UpdatedExtHdrs != NULL) {
+ FreePool (UpdatedExtHdrs);
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Need not fragment the packet, send it in one frame.
+ //
+ PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
+ if (PacketHead == NULL) {
+ Status = EFI_BAD_BUFFER_SIZE;
+ goto Error;
+ }
+
+ CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
+ Packet->Ip.Ip6 = PacketHead;
+
+ if (ExtHdrs != NULL) {
+ Buf = (UINT8 *) (PacketHead + 1);
+ CopyMem (Buf, ExtHdrs, ExtHdrsLen);
+ }
+
+ if (UpdatedExtHdrs != NULL) {
+ //
+ // A Fragment Header is inserted to the packet, update the payload length.
+ //
+ PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) +
+ sizeof (IP6_FRAGMENT_HEADER));
+ PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength);
+ FreePool (UpdatedExtHdrs);
+ }
+
+ return Ip6SendFrame (
+ IpIf,
+ IpInstance,
+ Packet,
+ &NextHop,
+ Callback,
+ Context
+ );
+
+Error:
+ if (UpdatedExtHdrs != NULL) {
+ FreePool (UpdatedExtHdrs);
+ }
+ Ip6CancelPacket (IpIf, Packet, Status);
+ return Status;
+}
+
+/**
+ The filter function to find a packet and all its fragments.
+ The packet's fragments have their Context set to the packet.
+
+ @param[in] Frame The frames hold by the low level interface.
+ @param[in] Context Context to the function, which is the packet.
+
+ @retval TRUE This is the packet to cancel or its fragments.
+ @retval FALSE This is an unrelated packet.
+
+**/
+BOOLEAN
+Ip6CancelPacketFragments (
+ IN IP6_LINK_TX_TOKEN *Frame,
+ IN VOID *Context
+ )
+{
+ if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Remove all the frames on the interface that pass the FrameToCancel,
+ either queued on ARP queues or that have already been delivered to
+ MNP and not yet recycled.
+
+ @param[in] Interface Interface to remove the frames from.
+ @param[in] IoStatus The transmit status returned to the frames' callback.
+ @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all.
+ @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if
+ FrameToCancel is NULL.
+
+**/
+VOID
+Ip6CancelFrames (
+ IN IP6_INTERFACE *Interface,
+ IN EFI_STATUS IoStatus,
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_LINK_TX_TOKEN *Token;
+ IP6_SERVICE *IpSb;
+ IP6_NEIGHBOR_ENTRY *ArpQue;
+ EFI_STATUS Status;
+
+ IpSb = Interface->Service;
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ //
+ // Cancel all the pending frames on ARP requests
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {
+ ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);
+
+ Status = Ip6FreeNeighborEntry (
+ IpSb,
+ ArpQue,
+ FALSE,
+ FALSE,
+ IoStatus,
+ FrameToCancel,
+ Context
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ //
+ // Cancel all the frames that have been delivered to MNP
+ // but not yet recycled.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {
+ Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
+
+ if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
+ IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken);
+ }
+ }
+}
+
+/**
+ Cancel the Packet and all its fragments.
+
+ @param[in] IpIf The interface from which the Packet is sent.
+ @param[in] Packet The Packet to cancel.
+ @param[in] IoStatus The status returns to the sender.
+
+**/
+VOID
+Ip6CancelPacket (
+ IN IP6_INTERFACE *IpIf,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus
+ )
+{
+ Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet);
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.h b/NetworkPkg/Ip6Dxe/Ip6Output.h
new file mode 100644
index 0000000000..80abe858e6
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Output.h
@@ -0,0 +1,141 @@
+/** @file
+ The internal functions and routines to transmit the IP6 packet.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_OUTPUT_H__
+#define __EFI_IP6_OUTPUT_H__
+
+extern UINT32 mIp6Id;
+
+/**
+ Output all the available source addresses to the list entry head SourceList. The
+ number of source addresses are also returned.
+
+ @param[in] IpSb Points to a IP6 service binding instance.
+ @param[in] Destination The IPv6 destination address.
+ @param[out] Source The selected IPv6 source address according to
+ the Destination.
+
+ @retval EFI_SUCCESS The source addresses were copied to the list entry
+ head SourceList.
+ @retval EFI_NO_MAPPING The IPv6 stack is not auto configured.
+
+**/
+EFI_STATUS
+Ip6SelectSourceAddress (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Destination,
+ OUT EFI_IPv6_ADDRESS *Source
+ );
+
+/**
+ The default callback function for system generated packet.
+ It will free the packet.
+
+ @param[in] Packet The packet that transmitted.
+ @param[in] IoStatus The result of the transmission: succeeded or failed.
+ @param[in] LinkFlag Not used when transmission. Check IP6_FRAME_CALLBACK
+ for reference.
+ @param[in] Context The context provided by us.
+
+**/
+VOID
+Ip6SysPacketSent (
+ NET_BUF *Packet,
+ EFI_STATUS IoStatus,
+ UINT32 LinkFlag,
+ VOID *Context
+ );
+
+/**
+ Transmit an IP6 packet. The packet comes either from the IP6
+ child's consumer (IpInstance != NULL) or the IP6 driver itself
+ (IpInstance == NULL). It will route the packet, fragment it,
+ then transmit all the fragments through an interface.
+
+ @param[in] IpSb The IP6 service instance to transmit the packet.
+ @param[in] Interface The IP6 interface to transmit the packet. Ignored
+ if NULL.
+ @param[in] IpInstance The IP6 child that issues the transmission. It is
+ NULL if the packet is from the system.
+ @param[in] Packet The user data to send, excluding the IP header.
+ @param[in] Head The caller supplied header. The caller should set
+ the following header fields: NextHeader, HopLimit,
+ Src, Dest, FlowLabel, PayloadLength. This function
+ will fill in the Ver, TrafficClass.
+ @param[in] ExtHdrs The extension headers to append to the IPv6 basic
+ header.
+ @param[in] ExtHdrsLen The length of the extension headers.
+ @param[in] Callback The callback function to issue when transmission
+ completed.
+ @param[in] Context The opaque context for the callback.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.
+ @retval EFI_NO_MAPPING There is no interface to the destination.
+ @retval EFI_NOT_FOUND There is no route to the destination.
+ @retval EFI_SUCCESS The packet successfully transmitted.
+ @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of
+ resources.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Ip6Output (
+ IN IP6_SERVICE *IpSb,
+ IN IP6_INTERFACE *Interface OPTIONAL,
+ IN IP6_PROTOCOL *IpInstance OPTIONAL,
+ IN NET_BUF *Packet,
+ IN EFI_IP6_HEADER *Head,
+ IN UINT8 *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN IP6_FRAME_CALLBACK Callback,
+ IN VOID *Context
+ );
+
+/**
+ Remove all the frames on the interface that pass the FrameToCancel,
+ either queued on ARP queues, or that have already been delivered to
+ MNP and not yet recycled.
+
+ @param[in] Interface Interface to remove the frames from.
+ @param[in] IoStatus The transmit status returned to the frames' callback.
+ @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all.
+ @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if
+ FrameToCancel is NULL.
+
+**/
+VOID
+Ip6CancelFrames (
+ IN IP6_INTERFACE *Interface,
+ IN EFI_STATUS IoStatus,
+ IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ Cancel the Packet and all its fragments.
+
+ @param[in] IpIf The interface from which the Packet is sent.
+ @param[in] Packet The Packet to cancel.
+ @param[in] IoStatus The status returns to the sender.
+
+**/
+VOID
+Ip6CancelPacket (
+ IN IP6_INTERFACE *IpIf,
+ IN NET_BUF *Packet,
+ IN EFI_STATUS IoStatus
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.c b/NetworkPkg/Ip6Dxe/Ip6Route.c
new file mode 100644
index 0000000000..bba365c152
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Route.c
@@ -0,0 +1,635 @@
+/** @file
+ The functions and routines to handle the route caches and route table.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+/**
+ This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value
+ as the index of the route cache bucket according to the prefix of two IPv6 addresses.
+
+ @param[in] Ip1 The IPv6 address.
+ @param[in] Ip2 The IPv6 address.
+
+ @return The hash value of the prefix of two IPv6 addresses.
+
+**/
+UINT32
+Ip6RouteCacheHash (
+ IN EFI_IPv6_ADDRESS *Ip1,
+ IN EFI_IPv6_ADDRESS *Ip2
+ )
+{
+ UINT32 Prefix1;
+ UINT32 Prefix2;
+
+ Prefix1 = *((UINT32 *) ((UINTN *) (Ip1)));
+ Prefix2 = *((UINT32 *) ((UINTN *) (Ip2)));
+
+ return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE);
+}
+
+/**
+ Allocate a route entry then initialize it with the Destination/PrefixLength
+ and Gateway.
+
+ @param[in] Destination The IPv6 destination address. This is an optional
+ parameter that may be NULL.
+ @param[in] PrefixLength The destination network's prefix length.
+ @param[in] GatewayAddress The next hop address. This is an optional parameter
+ that may be NULL.
+
+ @return NULL if failed to allocate memeory; otherwise, the newly created route entry.
+
+**/
+IP6_ROUTE_ENTRY *
+Ip6CreateRouteEntry (
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL
+ )
+{
+ IP6_ROUTE_ENTRY *RtEntry;
+
+ RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY));
+
+ if (RtEntry == NULL) {
+ return NULL;
+ }
+
+ RtEntry->RefCnt = 1;
+ RtEntry->Flag = 0;
+ RtEntry->PrefixLength = PrefixLength;
+
+ if (Destination != NULL) {
+ IP6_COPY_ADDRESS (&RtEntry->Destination, Destination);
+ }
+
+ if (GatewayAddress != NULL) {
+ IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress);
+ }
+
+ return RtEntry;
+}
+
+/**
+ Free the route table entry. It is reference counted.
+
+ @param[in, out] RtEntry The route entry to free.
+
+**/
+VOID
+Ip6FreeRouteEntry (
+ IN OUT IP6_ROUTE_ENTRY *RtEntry
+ )
+{
+ ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0));
+
+ if (--RtEntry->RefCnt == 0) {
+ FreePool (RtEntry);
+ }
+}
+
+/**
+ Search the route table for a most specific match to the Dst. It searches
+ from the longest route area (prefix length == 128) to the shortest route area
+ (default routes). In each route area, it will first search the instance's
+ route table, then the default route table. This is required per the following
+ requirements:
+ 1. IP search the route table for a most specific match.
+ 2. The local route entries have precedence over the default route entry.
+
+ @param[in] RtTable The route table to search from.
+ @param[in] Destination The destionation address to search. If NULL, search
+ the route table by NextHop.
+ @param[in] NextHop The next hop address. If NULL, search the route table
+ by Destination.
+
+ @return NULL if no route matches the Dst. Otherwise, the point to the
+ @return most specific route to the Dst.
+
+**/
+IP6_ROUTE_ENTRY *
+Ip6FindRouteEntry (
+ IN IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,
+ IN EFI_IPv6_ADDRESS *NextHop OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_ROUTE_ENTRY *RtEntry;
+ INTN Index;
+
+ ASSERT (Destination != NULL || NextHop != NULL);
+
+ RtEntry = NULL;
+
+ for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {
+ NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
+
+ if (Destination != NULL) {
+ if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) {
+ NET_GET_REF (RtEntry);
+ return RtEntry;
+ }
+ } else if (NextHop != NULL) {
+ if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) {
+ NET_GET_REF (RtEntry);
+ return RtEntry;
+ }
+ }
+
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Allocate and initialize a IP6 route cache entry.
+
+ @param[in] Dst The destination address.
+ @param[in] Src The source address.
+ @param[in] GateWay The next hop address.
+ @param[in] Tag The tag from the caller. This marks all the cache entries
+ spawned from one route table entry.
+
+ @return NULL if failed to allocate memory for the cache. Otherwise, point
+ to the created route cache entry.
+
+**/
+IP6_ROUTE_CACHE_ENTRY *
+Ip6CreateRouteCacheEntry (
+ IN EFI_IPv6_ADDRESS *Dst,
+ IN EFI_IPv6_ADDRESS *Src,
+ IN EFI_IPv6_ADDRESS *GateWay,
+ IN UINTN Tag
+ )
+{
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;
+
+ RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY));
+
+ if (RtCacheEntry == NULL) {
+ return NULL;
+ }
+
+ RtCacheEntry->RefCnt = 1;
+ RtCacheEntry->Tag = Tag;
+
+ IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst);
+ IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src);
+ IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay);
+
+ return RtCacheEntry;
+}
+
+/**
+ Free the route cache entry. It is reference counted.
+
+ @param[in, out] RtCacheEntry The route cache entry to free.
+
+**/
+VOID
+Ip6FreeRouteCacheEntry (
+ IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry
+ )
+{
+ ASSERT (RtCacheEntry->RefCnt > 0);
+
+ if (--RtCacheEntry->RefCnt == 0) {
+ FreePool (RtCacheEntry);
+ }
+}
+
+/**
+ Find a route cache with the destination and source address. This is
+ used by the ICMPv6 redirect messasge process.
+
+ @param[in] RtTable The route table to search the cache for.
+ @param[in] Dest The destination address.
+ @param[in] Src The source address.
+
+ @return NULL if no route entry to the (Dest, Src). Otherwise, the pointer
+ to the correct route cache entry.
+
+**/
+IP6_ROUTE_CACHE_ENTRY *
+Ip6FindRouteCache (
+ IN IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Dest,
+ IN EFI_IPv6_ADDRESS *Src
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ Index = IP6_ROUTE_CACHE_HASH (Dest, Src);
+
+ NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
+
+ if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) {
+ NET_GET_REF (RtCacheEntry);
+ return RtCacheEntry;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number
+ of EFI_IP6_ROUTE_TABLE is also returned.
+
+ @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used.
+ @param[out] EfiRouteCount The number of returned route entries.
+ @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE.
+ If NULL, only the route entry count is returned.
+
+ @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.
+
+**/
+EFI_STATUS
+Ip6BuildEfiRouteTable (
+ IN IP6_ROUTE_TABLE *RouteTable,
+ OUT UINT32 *EfiRouteCount,
+ OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ IP6_ROUTE_ENTRY *RtEntry;
+ EFI_IP6_ROUTE_TABLE *EfiTable;
+ UINT32 Count;
+ INT32 Index;
+
+ ASSERT (EfiRouteCount != NULL);
+
+ Count = RouteTable->TotalNum;
+ *EfiRouteCount = Count;
+
+ if ((EfiRouteTable == NULL) || (Count == 0)) {
+ return EFI_SUCCESS;
+ }
+
+ if (*EfiRouteTable == NULL) {
+ *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count);
+ if (*EfiRouteTable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ EfiTable = *EfiRouteTable;
+
+ //
+ // Copy the route entry to EFI route table.
+ //
+ Count = 0;
+
+ for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) {
+
+ NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
+
+ Ip6CopyAddressByPrefix (
+ &EfiTable[Count].Destination,
+ &RtEntry->Destination,
+ RtEntry->PrefixLength
+ );
+
+ IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop);
+ EfiTable[Count].PrefixLength = RtEntry->PrefixLength;
+
+ Count++;
+ }
+ }
+
+ ASSERT (Count == RouteTable->TotalNum);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create an empty route table. This includes its internal route cache.
+
+ @return NULL if failed to allocate memory for the route table. Otherwise,
+ the point to newly created route table.
+
+**/
+IP6_ROUTE_TABLE *
+Ip6CreateRouteTable (
+ VOID
+ )
+{
+ IP6_ROUTE_TABLE *RtTable;
+ UINT32 Index;
+
+ RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE));
+ if (RtTable == NULL) {
+ return NULL;
+ }
+
+ RtTable->RefCnt = 1;
+ RtTable->TotalNum = 0;
+
+ for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {
+ InitializeListHead (&RtTable->RouteArea[Index]);
+ }
+
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
+ InitializeListHead (&RtTable->Cache.CacheBucket[Index]);
+ RtTable->Cache.CacheNum[Index] = 0;
+ }
+
+ return RtTable;
+}
+
+/**
+ Free the route table and its associated route cache. Route
+ table is reference counted.
+
+ @param[in, out] RtTable The route table to free.
+
+**/
+VOID
+Ip6CleanRouteTable (
+ IN OUT IP6_ROUTE_TABLE *RtTable
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_ROUTE_ENTRY *RtEntry;
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ ASSERT (RtTable->RefCnt > 0);
+
+ if (--RtTable->RefCnt > 0) {
+ return ;
+ }
+
+ //
+ // Free all the route table entry and its route cache.
+ //
+ for (Index = 0; Index < IP6_PREFIX_NUM; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) {
+ RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
+ RemoveEntryList (Entry);
+ Ip6FreeRouteEntry (RtEntry);
+ }
+ }
+
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) {
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
+ RemoveEntryList (Entry);
+ Ip6FreeRouteCacheEntry (RtCacheEntry);
+ }
+ }
+
+ FreePool (RtTable);
+}
+
+/**
+ Remove all the cache entries bearing the Tag. When a route cache
+ entry is created, it is tagged with the address of route entry
+ from which it is spawned. When a route entry is deleted, the cache
+ entries spawned from it are also deleted.
+
+ @param[in] RtCache Route cache to remove the entries from.
+ @param[in] Tag The Tag of the entries to remove.
+
+**/
+VOID
+Ip6PurgeRouteCache (
+ IN IP6_ROUTE_CACHE *RtCache,
+ IN UINTN Tag
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ UINT32 Index;
+
+ for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {
+
+ RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
+
+ if (RtCacheEntry->Tag == Tag) {
+ RemoveEntryList (Entry);
+ Ip6FreeRouteCacheEntry (RtCacheEntry);
+ }
+ }
+ }
+}
+
+/**
+ Add a route entry to the route table. It is the help function for EfiIp6Routes.
+
+ @param[in, out] RtTable Route table to add route to.
+ @param[in] Destination The destination of the network.
+ @param[in] PrefixLength The PrefixLength of the destination.
+ @param[in] GatewayAddress The next hop address.
+
+ @retval EFI_ACCESS_DENIED The same route already exists.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry.
+ @retval EFI_SUCCESS The route was added successfully.
+
+**/
+EFI_STATUS
+Ip6AddRoute (
+ IN OUT IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Destination,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress
+ )
+{
+ LIST_ENTRY *ListHead;
+ LIST_ENTRY *Entry;
+ IP6_ROUTE_ENTRY *Route;
+
+ ListHead = &RtTable->RouteArea[PrefixLength];
+
+ //
+ // First check whether the route exists
+ //
+ NET_LIST_FOR_EACH (Entry, ListHead) {
+ Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
+
+ if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) &&
+ EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {
+ return EFI_ACCESS_DENIED;
+ }
+ }
+
+ //
+ // Create a route entry and insert it to the route area.
+ //
+ Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress);
+
+ if (Route == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (NetIp6IsUnspecifiedAddr (GatewayAddress)) {
+ Route->Flag = IP6_DIRECT_ROUTE;
+ }
+
+ InsertHeadList (ListHead, &Route->Link);
+ RtTable->TotalNum++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Remove a route entry and all the route caches spawn from it.
+ It is the help function for EfiIp6Routes.
+
+ @param[in, out] RtTable The route table to remove the route from.
+ @param[in] Destination The destination network.
+ @param[in] PrefixLength The PrefixLength of the Destination.
+ @param[in] GatewayAddress The next hop address.
+
+ @retval EFI_SUCCESS The route entry was successfully removed.
+ @retval EFI_NOT_FOUND There is no route entry in the table with that
+ property.
+
+**/
+EFI_STATUS
+Ip6DelRoute (
+ IN OUT IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Destination,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress
+ )
+{
+ LIST_ENTRY *ListHead;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IP6_ROUTE_ENTRY *Route;
+ UINT32 TotalNum;
+
+ ListHead = &RtTable->RouteArea[PrefixLength];
+ TotalNum = RtTable->TotalNum;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) {
+ Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
+
+ if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) {
+ continue;
+ }
+ if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {
+ continue;
+ }
+
+ Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route);
+ RemoveEntryList (Entry);
+ Ip6FreeRouteEntry (Route);
+
+ ASSERT (RtTable->TotalNum > 0);
+ RtTable->TotalNum--;
+ }
+
+ return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
+
+/**
+ Search the route table to route the packet. Return/create a route
+ cache if there is a route to the destination.
+
+ @param[in] IpSb The IP6 service data.
+ @param[in] Dest The destination address to search for.
+ @param[in] Src The source address to search for.
+
+ @return NULL if it failed to route the packet. Otherwise, a route cache
+ entry that can be used to route packets.
+
+**/
+IP6_ROUTE_CACHE_ENTRY *
+Ip6Route (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Dest,
+ IN EFI_IPv6_ADDRESS *Src
+ )
+{
+ IP6_ROUTE_TABLE *RtTable;
+ LIST_ENTRY *ListHead;
+ IP6_ROUTE_CACHE_ENTRY *RtCacheEntry;
+ IP6_ROUTE_ENTRY *RtEntry;
+ EFI_IPv6_ADDRESS NextHop;
+ UINT32 Index;
+
+ RtTable = IpSb->RouteTable;
+
+ ASSERT (RtTable != NULL);
+
+ //
+ // Search the destination cache in IP6_ROUTE_TABLE.
+ //
+ Index = IP6_ROUTE_CACHE_HASH (Dest, Src);
+ ListHead = &RtTable->Cache.CacheBucket[Index];
+
+ RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src);
+
+ //
+ // If found, promote the cache entry to the head of the hash bucket.
+ //
+ if (RtCacheEntry != NULL) {
+ RemoveEntryList (&RtCacheEntry->Link);
+ InsertHeadList (ListHead, &RtCacheEntry->Link);
+ return RtCacheEntry;
+ }
+
+ //
+ // Search the route table for the most specific route
+ //
+ RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL);
+ if (RtEntry == NULL) {
+ return NULL;
+ }
+
+ //
+ // Found a route to the Dest, if it is a direct route, the packet
+ // will be send directly to the destination, such as for connected
+ // network. Otherwise, it is an indirect route, the packet will be
+ // send the next hop router.
+ //
+ if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) {
+ IP6_COPY_ADDRESS (&NextHop, Dest);
+ } else {
+ IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop);
+ }
+
+ Ip6FreeRouteEntry (RtEntry);
+
+ //
+ // Create a route cache entry, and tag it as spawned from this route entry
+ //
+ RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry);
+
+ if (RtCacheEntry == NULL) {
+ return NULL;
+ }
+
+ InsertHeadList (ListHead, &RtCacheEntry->Link);
+ NET_GET_REF (RtCacheEntry);
+ RtTable->Cache.CacheNum[Index]++;
+
+ return RtCacheEntry;
+}
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6Route.h b/NetworkPkg/Ip6Dxe/Ip6Route.h
new file mode 100644
index 0000000000..d81e07b19c
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Route.h
@@ -0,0 +1,299 @@
+/** @file
+ EFI IP6 route table and route cache table defintions.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_ROUTE_H__
+#define __EFI_IP6_ROUTE_H__
+
+#define IP6_DIRECT_ROUTE 0x00000001
+#define IP6_PACKET_TOO_BIG 0x00000010
+
+#define IP6_ROUTE_CACHE_HASH_SIZE 31
+///
+/// Max NO. of cache entry per hash bucket
+///
+#define IP6_ROUTE_CACHE_MAX 32
+
+#define IP6_ROUTE_CACHE_HASH(Ip1, Ip2) Ip6RouteCacheHash ((Ip1), (Ip2))
+
+typedef struct {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ UINT32 Flag;
+ UINT8 PrefixLength;
+ EFI_IPv6_ADDRESS Destination;
+ EFI_IPv6_ADDRESS NextHop;
+} IP6_ROUTE_ENTRY;
+
+typedef struct {
+ LIST_ENTRY Link;
+ INTN RefCnt;
+ UINTN Tag;
+ EFI_IPv6_ADDRESS Destination;
+ EFI_IPv6_ADDRESS Source;
+ EFI_IPv6_ADDRESS NextHop;
+} IP6_ROUTE_CACHE_ENTRY;
+
+typedef struct {
+ LIST_ENTRY CacheBucket[IP6_ROUTE_CACHE_HASH_SIZE];
+ UINT8 CacheNum[IP6_ROUTE_CACHE_HASH_SIZE];
+} IP6_ROUTE_CACHE;
+
+//
+// Each IP6 instance has its own route table. Each ServiceBinding
+// instance has a default route table and default address.
+//
+// All the route table entries with the same prefix length are linked
+// together in one route area. For example, RouteArea[0] contains
+// the default routes. A route table also contains a route cache.
+//
+
+typedef struct _IP6_ROUTE_TABLE {
+ INTN RefCnt;
+ UINT32 TotalNum;
+ LIST_ENTRY RouteArea[IP6_PREFIX_NUM];
+ IP6_ROUTE_CACHE Cache;
+} IP6_ROUTE_TABLE;
+
+/**
+ This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value
+ as the index of the route cache bucket according to the prefix of two IPv6 addresses.
+
+ @param[in] Ip1 The IPv6 address.
+ @param[in] Ip2 The IPv6 address.
+
+ @return The hash value of the prefix of two IPv6 addresses.
+
+**/
+UINT32
+Ip6RouteCacheHash (
+ IN EFI_IPv6_ADDRESS *Ip1,
+ IN EFI_IPv6_ADDRESS *Ip2
+ );
+
+/**
+ Allocate and initialize an IP6 route cache entry.
+
+ @param[in] Dst The destination address.
+ @param[in] Src The source address.
+ @param[in] GateWay The next hop address.
+ @param[in] Tag The tag from the caller. This marks all the cache entries
+ spawned from one route table entry.
+
+ @return NULL if it failed to allocate memory for the cache. Otherwise, point
+ to the created route cache entry.
+
+**/
+IP6_ROUTE_CACHE_ENTRY *
+Ip6CreateRouteCacheEntry (
+ IN EFI_IPv6_ADDRESS *Dst,
+ IN EFI_IPv6_ADDRESS *Src,
+ IN EFI_IPv6_ADDRESS *GateWay,
+ IN UINTN Tag
+ );
+
+/**
+ Free the route cache entry. It is reference counted.
+
+ @param[in, out] RtCacheEntry The route cache entry to free.
+
+**/
+VOID
+Ip6FreeRouteCacheEntry (
+ IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry
+ );
+
+/**
+ Find a route cache with the destination and source address. This is
+ used by the ICMPv6 redirect messasge process.
+
+ @param[in] RtTable The route table to search the cache for.
+ @param[in] Dest The destination address.
+ @param[in] Src The source address.
+
+ @return NULL if no route entry to the (Dest, Src). Otherwise, point
+ to the correct route cache entry.
+
+**/
+IP6_ROUTE_CACHE_ENTRY *
+Ip6FindRouteCache (
+ IN IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Dest,
+ IN EFI_IPv6_ADDRESS *Src
+ );
+
+/**
+ Build a array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number
+ of EFI_IP6_ROUTE_TABLE is also returned.
+
+ @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used.
+ @param[out] EfiRouteCount The number of returned route entries.
+ @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE.
+ If NULL, only the route entry count is returned.
+
+ @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.
+
+**/
+EFI_STATUS
+Ip6BuildEfiRouteTable (
+ IN IP6_ROUTE_TABLE *RouteTable,
+ OUT UINT32 *EfiRouteCount,
+ OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL
+ );
+
+/**
+ Create an empty route table, includes its internal route cache.
+
+ @return NULL if failed to allocate memory for the route table. Otherwise,
+ the point to newly created route table.
+
+**/
+IP6_ROUTE_TABLE *
+Ip6CreateRouteTable (
+ VOID
+ );
+
+/**
+ Free the route table and its associated route cache. Route
+ table is reference counted.
+
+ @param[in, out] RtTable The route table to free.
+
+**/
+VOID
+Ip6CleanRouteTable (
+ IN OUT IP6_ROUTE_TABLE *RtTable
+ );
+
+/**
+ Allocate a route entry then initialize it with the Destination/PrefixLength
+ and Gateway.
+
+ @param[in] Destination The IPv6 destination address. This is an optional
+ parameter that may be NULL.
+ @param[in] PrefixLength The destination network's prefix length.
+ @param[in] GatewayAddress The next hop address. This is optional parameter
+ that may be NULL.
+
+ @return NULL if it failed to allocate memeory. Otherwise, the newly created route entry.
+
+**/
+IP6_ROUTE_ENTRY *
+Ip6CreateRouteEntry (
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL
+ );
+
+/**
+ Search the route table for a most specific match to the Dst. It searches
+ from the longest route area (prefix length == 128) to the shortest route area
+ (default routes). In each route area, it will first search the instance's
+ route table, then the default route table. This is required per the following
+ requirements:
+ 1. IP search the route table for a most specific match.
+ 2. The local route entries have precedence over the default route entry.
+
+ @param[in] RtTable The route table to search from.
+ @param[in] Destination The destionation address to search. If NULL, search
+ the route table by NextHop.
+ @param[in] NextHop The next hop address. If NULL, search the route table
+ by Destination.
+
+ @return NULL if no route matches the Dst. Otherwise the point to the
+ most specific route to the Dst.
+
+**/
+IP6_ROUTE_ENTRY *
+Ip6FindRouteEntry (
+ IN IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Destination OPTIONAL,
+ IN EFI_IPv6_ADDRESS *NextHop OPTIONAL
+ );
+
+/**
+ Free the route table entry. It is reference counted.
+
+ @param[in, out] RtEntry The route entry to free.
+
+**/
+VOID
+Ip6FreeRouteEntry (
+ IN OUT IP6_ROUTE_ENTRY *RtEntry
+ );
+
+/**
+ Add a route entry to the route table. It is the help function for EfiIp6Routes.
+
+ @param[in, out] RtTable Route table to add route to.
+ @param[in] Destination The destination of the network.
+ @param[in] PrefixLength The PrefixLength of the destination.
+ @param[in] GatewayAddress The next hop address.
+
+ @retval EFI_ACCESS_DENIED The same route already exists.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry.
+ @retval EFI_SUCCESS The route was added successfully.
+
+**/
+EFI_STATUS
+Ip6AddRoute (
+ IN OUT IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Destination,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress
+ );
+
+/**
+ Remove a route entry and all the route caches spawn from it.
+ It is the help function for EfiIp6Routes.
+
+ @param[in, out] RtTable The route table to remove the route from.
+ @param[in] Destination The destination network.
+ @param[in] PrefixLength The PrefixLength of the Destination.
+ @param[in] GatewayAddress The next hop address.
+
+ @retval EFI_SUCCESS Successfully removed the route entry.
+ @retval EFI_NOT_FOUND There is no route entry in the table with that
+ properity.
+
+**/
+EFI_STATUS
+Ip6DelRoute (
+ IN OUT IP6_ROUTE_TABLE *RtTable,
+ IN EFI_IPv6_ADDRESS *Destination,
+ IN UINT8 PrefixLength,
+ IN EFI_IPv6_ADDRESS *GatewayAddress
+ );
+
+/**
+ Search the route table to route the packet. Return/create a route
+ cache if there is a route to the destination.
+
+ @param[in] IpSb The IP6 service data.
+ @param[in] Dest The destination address to search for.
+ @param[in] Src The source address to search for.
+
+ @return NULL if failed to route packet. Otherwise, a route cache
+ entry that can be used to route packet.
+
+**/
+IP6_ROUTE_CACHE_ENTRY *
+Ip6Route (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IPv6_ADDRESS *Dest,
+ IN EFI_IPv6_ADDRESS *Src
+ );
+
+#endif
diff --git a/NetworkPkg/IpSecDxe/ComponentName.c b/NetworkPkg/IpSecDxe/ComponentName.c
new file mode 100644
index 0000000000..22b3861081
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/ComponentName.c
@@ -0,0 +1,310 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for IPsec driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecImpl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName = {
+ IpSecComponentNameGetDriverName,
+ IpSecComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IpSecComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IpSecComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIpSecDriverNameTable[] = {
+ {
+ "eng;en",
+ L"IpSec Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This, and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mIpSecDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gIpSecComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.c b/NetworkPkg/IpSecDxe/IpSecConfigImpl.c
new file mode 100644
index 0000000000..e671e42e27
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecConfigImpl.c
@@ -0,0 +1,2928 @@
+/** @file
+ The implementation of IPSEC_CONFIG_PROTOCOL.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfigImpl.h"
+#include "IpSecDebug.h"
+
+LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];
+BOOLEAN mSetBySelf = FALSE;
+
+//
+// Common CompareSelector routine entry for spd/sad/pad.
+//
+IPSEC_COMPARE_SELECTOR mCompareSelector[] = {
+ (IPSEC_COMPARE_SELECTOR) CompareSpdSelector,
+ (IPSEC_COMPARE_SELECTOR) CompareSaId,
+ (IPSEC_COMPARE_SELECTOR) ComparePadId
+};
+
+//
+// Common IsZeroSelector routine entry for spd/sad/pad.
+//
+IPSEC_IS_ZERO_SELECTOR mIsZeroSelector[] = {
+ (IPSEC_IS_ZERO_SELECTOR) IsZeroSpdSelector,
+ (IPSEC_IS_ZERO_SELECTOR) IsZeroSaId,
+ (IPSEC_IS_ZERO_SELECTOR) IsZeroPadId
+};
+
+//
+// Common DuplicateSelector routine entry for spd/sad/pad.
+//
+IPSEC_DUPLICATE_SELECTOR mDuplicateSelector[] = {
+ (IPSEC_DUPLICATE_SELECTOR) DuplicateSpdSelector,
+ (IPSEC_DUPLICATE_SELECTOR) DuplicateSaId,
+ (IPSEC_DUPLICATE_SELECTOR) DuplicatePadId
+};
+
+//
+// Common FixPolicyEntry routine entry for spd/sad/pad.
+//
+IPSEC_FIX_POLICY_ENTRY mFixPolicyEntry[] = {
+ (IPSEC_FIX_POLICY_ENTRY) FixSpdEntry,
+ (IPSEC_FIX_POLICY_ENTRY) FixSadEntry,
+ (IPSEC_FIX_POLICY_ENTRY) FixPadEntry
+};
+
+//
+// Common UnfixPolicyEntry routine entry for spd/sad/pad.
+//
+IPSEC_FIX_POLICY_ENTRY mUnfixPolicyEntry[] = {
+ (IPSEC_FIX_POLICY_ENTRY) UnfixSpdEntry,
+ (IPSEC_FIX_POLICY_ENTRY) UnfixSadEntry,
+ (IPSEC_FIX_POLICY_ENTRY) UnfixPadEntry
+};
+
+//
+// Common SetPolicyEntry routine entry for spd/sad/pad.
+//
+IPSEC_SET_POLICY_ENTRY mSetPolicyEntry[] = {
+ (IPSEC_SET_POLICY_ENTRY) SetSpdEntry,
+ (IPSEC_SET_POLICY_ENTRY) SetSadEntry,
+ (IPSEC_SET_POLICY_ENTRY) SetPadEntry
+};
+
+//
+// Common GetPolicyEntry routine entry for spd/sad/pad.
+//
+IPSEC_GET_POLICY_ENTRY mGetPolicyEntry[] = {
+ (IPSEC_GET_POLICY_ENTRY) GetSpdEntry,
+ (IPSEC_GET_POLICY_ENTRY) GetSadEntry,
+ (IPSEC_GET_POLICY_ENTRY) GetPadEntry
+};
+
+//
+// Routine entry for IpSecConfig protocol.
+//
+EFI_IPSEC_CONFIG_PROTOCOL mIpSecConfigInstance = {
+ EfiIpSecConfigSetData,
+ EfiIpSecConfigGetData,
+ EfiIpSecConfigGetNextSelector,
+ EfiIpSecConfigRegisterNotify,
+ EfiIpSecConfigUnregisterNotify
+};
+
+/**
+ Get the all IPSec configuration variables and store those variables
+ to the internal data structure.
+
+ This founction is called by IpSecConfigInitialize() that is to intialize the
+ IPsecConfiguration Protocol.
+
+ @param[in] Private Point to IPSEC_PRIVATE_DATA.
+
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.
+ @retval EFI_SUCCESS Restore the IPsec Configuration successfully.
+ @retval others Other errors is found during the variable getting.
+
+**/
+EFI_STATUS
+IpSecConfigRestore (
+ IN IPSEC_PRIVATE_DATA *Private
+ );
+
+/**
+ Check if the specified EFI_IP_ADDRESS_INFO is in EFI_IP_ADDRESS_INFO list.
+
+ @param[in] AddressInfo Pointer of IP_ADDRESS_INFO to be search in AddressInfo list.
+ @param[in] AddressInfoList A list that contains IP_ADDRESS_INFOs.
+ @param[in] AddressCount Point out how many IP_ADDRESS_INFO in the list.
+
+ @retval TRUE The specified AddressInfo is in the AddressInfoList.
+ @retval FALSE The specified AddressInfo is not in the AddressInfoList.
+
+**/
+BOOLEAN
+IsInAddressInfoList(
+ IN EFI_IP_ADDRESS_INFO *AddressInfo,
+ IN EFI_IP_ADDRESS_INFO *AddressInfoList,
+ IN UINT32 AddressCount
+ )
+{
+ UINT8 Index;
+
+ for (Index = 0; Index < AddressCount ; Index++) {
+ if (CompareMem (
+ AddressInfo,
+ &AddressInfoList[Index].Address,
+ sizeof (EFI_IP_ADDRESS)
+ ) == 0 &&
+ AddressInfo->PrefixLength == AddressInfoList[Index].PrefixLength
+ ) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ Compare two SPD Selectors.
+
+ Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/
+ NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the
+ Local Addresses and remote Addresses.
+
+ @param[in] Selector1 Pointer of first SPD Selector.
+ @param[in] Selector2 Pointer of second SPD Selector.
+
+ @retval TRUE This two Selector have the same value in above fields.
+ @retval FALSE Not all above fields have the same value in these two Selectors.
+
+**/
+BOOLEAN
+CompareSpdSelector (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ )
+{
+ EFI_IPSEC_SPD_SELECTOR *SpdSel1;
+ EFI_IPSEC_SPD_SELECTOR *SpdSel2;
+ BOOLEAN IsMatch;
+ UINTN Index;
+
+ SpdSel1 = &Selector1->SpdSelector;
+ SpdSel2 = &Selector2->SpdSelector;
+ IsMatch = TRUE;
+
+ //
+ // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/
+ // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the
+ // two Spdselectors. Since the SPD supports two directions, it needs to
+ // compare two directions.
+ //
+ if ((SpdSel1->LocalAddressCount != SpdSel2->LocalAddressCount &&
+ SpdSel1->LocalAddressCount != SpdSel2->RemoteAddressCount) ||
+ (SpdSel1->RemoteAddressCount != SpdSel2->RemoteAddressCount &&
+ SpdSel1->RemoteAddressCount != SpdSel2->LocalAddressCount) ||
+ SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol ||
+ SpdSel1->LocalPort != SpdSel2->LocalPort ||
+ SpdSel1->LocalPortRange != SpdSel2->LocalPortRange ||
+ SpdSel1->RemotePort != SpdSel2->RemotePort ||
+ SpdSel1->RemotePortRange != SpdSel2->RemotePortRange
+ ) {
+ IsMatch = FALSE;
+ return IsMatch;
+ }
+
+ //
+ // Compare the all LocalAddress fields in the two Spdselectors.
+ // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare
+ // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return
+ // TRUE.
+ //
+ for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel1->LocalAddress[Index],
+ SpdSel2->LocalAddress,
+ SpdSel2->LocalAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ if (IsMatch) {
+ for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel2->LocalAddress[Index],
+ SpdSel1->LocalAddress,
+ SpdSel1->LocalAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ }
+ if (IsMatch) {
+ for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel1->RemoteAddress[Index],
+ SpdSel2->RemoteAddress,
+ SpdSel2->RemoteAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ }
+ if (IsMatch) {
+ for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel2->RemoteAddress[Index],
+ SpdSel1->RemoteAddress,
+ SpdSel1->RemoteAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ }
+ //
+ // Finish the one direction compare. If it is matched, return; otherwise,
+ // compare the other direction.
+ //
+ if (IsMatch) {
+ return IsMatch;
+ }
+ //
+ // Secondly, the SpdSel1->LocalAddress doesn't equal to SpdSel2->LocalAddress and
+ // SpdSel1->RemoteAddress doesn't equal to SpdSel2->RemoteAddress. Try to compare
+ // the RemoteAddress to LocalAddress.
+ //
+ IsMatch = TRUE;
+ for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel1->RemoteAddress[Index],
+ SpdSel2->LocalAddress,
+ SpdSel2->LocalAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ if (IsMatch) {
+ for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel2->RemoteAddress[Index],
+ SpdSel1->LocalAddress,
+ SpdSel1->LocalAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ }
+ if (IsMatch) {
+ for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel1->LocalAddress[Index],
+ SpdSel2->RemoteAddress,
+ SpdSel2->RemoteAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ }
+ if (IsMatch) {
+ for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) {
+ if (!IsInAddressInfoList (
+ &SpdSel2->LocalAddress[Index],
+ SpdSel1->RemoteAddress,
+ SpdSel1->RemoteAddressCount
+ )) {
+ IsMatch = FALSE;
+ break;
+ }
+ }
+ }
+ return IsMatch;
+}
+
+/**
+ Compare two SA IDs.
+
+ @param[in] Selector1 Pointer of first SA ID.
+ @param[in] Selector2 Pointer of second SA ID.
+
+ @retval TRUE This two Selectors have the same SA ID.
+ @retval FALSE This two Selecotrs don't have the same SA ID.
+
+**/
+BOOLEAN
+CompareSaId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ )
+{
+ EFI_IPSEC_SA_ID *SaId1;
+ EFI_IPSEC_SA_ID *SaId2;
+ BOOLEAN IsMatch;
+
+ SaId1 = &Selector1->SaId;
+ SaId2 = &Selector2->SaId;
+ IsMatch = TRUE;
+
+ if (CompareMem (SaId1, SaId2, sizeof (EFI_IPSEC_SA_ID)) != 0) {
+ IsMatch = FALSE;
+ }
+
+ return IsMatch;
+}
+
+/**
+ Compare two PAD IDs.
+
+ @param[in] Selector1 Pointer of first PAD ID.
+ @param[in] Selector2 Pointer of second PAD ID.
+
+ @retval TRUE This two Selectors have the same PAD ID.
+ @retval FALSE This two Selecotrs don't have the same PAD ID.
+
+**/
+BOOLEAN
+ComparePadId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ )
+{
+ EFI_IPSEC_PAD_ID *PadId1;
+ EFI_IPSEC_PAD_ID *PadId2;
+ BOOLEAN IsMatch;
+
+ PadId1 = &Selector1->PadId;
+ PadId2 = &Selector2->PadId;
+ IsMatch = TRUE;
+
+ //
+ // Compare the PeerIdValid fields in PadId.
+ //
+ if (PadId1->PeerIdValid != PadId2->PeerIdValid) {
+ IsMatch = FALSE;
+ }
+ //
+ // Compare the PeerId fields in PadId if PeerIdValid is true.
+ //
+ if (IsMatch &&
+ PadId1->PeerIdValid &&
+ AsciiStriCmp ((CONST CHAR8 *) PadId1->Id.PeerId, (CONST CHAR8 *) PadId2->Id.PeerId) != 0
+ ) {
+ IsMatch = FALSE;
+ }
+ //
+ // Compare the IpAddress fields in PadId if PeerIdValid is false.
+ //
+ if (IsMatch &&
+ !PadId1->PeerIdValid &&
+ (PadId1->Id.IpAddress.PrefixLength != PadId2->Id.IpAddress.PrefixLength ||
+ CompareMem (&PadId1->Id.IpAddress.Address, &PadId2->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0)
+ ) {
+ IsMatch = FALSE;
+ }
+
+ return IsMatch;
+}
+
+/**
+ Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount
+ fields.
+
+ @param[in] Selector Pointer of the SPD Selector.
+
+ @retval TRUE If the SPD Selector is Zero.
+ @retval FALSE If the SPD Selector is not Zero.
+
+**/
+BOOLEAN
+IsZeroSpdSelector (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector
+ )
+{
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;
+ BOOLEAN IsZero;
+
+ SpdSel = &Selector->SpdSelector;
+ IsZero = FALSE;
+
+ if (SpdSel->LocalAddressCount == 0 && SpdSel->RemoteAddressCount == 0) {
+ IsZero = TRUE;
+ }
+
+ return IsZero;
+}
+
+/**
+ Check if the SA ID is Zero by its DestAddress.
+
+ @param[in] Selector Pointer of the SA ID.
+
+ @retval TRUE If the SA ID is Zero.
+ @retval FALSE If the SA ID is not Zero.
+
+**/
+BOOLEAN
+IsZeroSaId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector
+ )
+{
+ EFI_IP_ADDRESS *DestAddr;
+ EFI_IP_ADDRESS ZeroAddr;
+ BOOLEAN IsZero;
+
+ DestAddr = &Selector->SaId.DestAddress;
+ IsZero = FALSE;
+
+ ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));
+
+ if (CompareMem (DestAddr, &ZeroAddr, sizeof (EFI_IP_ADDRESS)) == 0) {
+ IsZero = TRUE;
+ }
+
+ return IsZero;
+}
+
+/**
+ Check if the PAD ID is Zero.
+
+ @param[in] Selector Pointer of the PAD ID.
+
+ @retval TRUE If the PAD ID is Zero.
+ @retval FALSE If the PAD ID is not Zero.
+
+**/
+BOOLEAN
+IsZeroPadId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector
+ )
+{
+ EFI_IPSEC_PAD_ID *PadId;
+ EFI_IPSEC_PAD_ID ZeroId;
+ BOOLEAN IsZero;
+
+ PadId = &Selector->PadId;
+ IsZero = FALSE;
+
+ ZeroMem (&ZeroId, sizeof (EFI_IPSEC_PAD_ID));
+
+ if (CompareMem (PadId, &ZeroId, sizeof (EFI_IPSEC_PAD_ID)) == 0) {
+ IsZero = TRUE;
+ }
+
+ return IsZero;
+}
+
+/**
+ Copy Source SPD Selector to the Destination SPD Selector.
+
+ @param[in, out] DstSel Pointer of Destination SPD Selector.
+ @param[in] SrcSel Pointer of Source SPD Selector.
+ @param[in, out] Size The size of the Destination SPD Selector. If it
+ not NULL and its value less than the size of
+ Source SPD Selector, the value of Source SPD
+ Selector's size will be passed to caller by this
+ parameter.
+
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL
+ @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of the Source SPD Selector.
+ @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD
+ Selector successfully.
+
+**/
+EFI_STATUS
+DuplicateSpdSelector (
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,
+ IN OUT UINTN *Size
+ )
+{
+ EFI_IPSEC_SPD_SELECTOR *Dst;
+ EFI_IPSEC_SPD_SELECTOR *Src;
+
+ Dst = &DstSel->SpdSelector;
+ Src = &SrcSel->SpdSelector;
+
+ if (Dst == NULL || Src == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Size != NULL && (*Size) < SIZE_OF_SPD_SELECTOR (Src)) {
+ *Size = SIZE_OF_SPD_SELECTOR (Src);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ //
+ // Copy the base structure of spd selector.
+ //
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_SPD_SELECTOR));
+
+ //
+ // Copy the local address array of spd selector.
+ //
+ Dst->LocalAddress = (EFI_IP_ADDRESS_INFO *) (Dst + 1);
+ CopyMem (
+ Dst->LocalAddress,
+ Src->LocalAddress,
+ sizeof (EFI_IP_ADDRESS_INFO) * Dst->LocalAddressCount
+ );
+
+ //
+ // Copy the remote address array of spd selector.
+ //
+ Dst->RemoteAddress = Dst->LocalAddress + Dst->LocalAddressCount;
+ CopyMem (
+ Dst->RemoteAddress,
+ Src->RemoteAddress,
+ sizeof (EFI_IP_ADDRESS_INFO) * Dst->RemoteAddressCount
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Copy Source SA ID to the Destination SA ID.
+
+ @param[in, out] DstSel Pointer of Destination SA ID.
+ @param[in] SrcSel Pointer of Source SA ID.
+ @param[in, out] Size The size of the Destination SA ID. If it
+ not NULL and its value less than the size of
+ Source SA ID, the value of Source SA ID's size
+ will be passed to caller by this parameter.
+
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL.
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID.
+ @retval EFI_SUCCESS Copy Source SA ID to the Destination SA ID successfully.
+
+**/
+EFI_STATUS
+DuplicateSaId (
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,
+ IN OUT UINTN *Size
+ )
+{
+ EFI_IPSEC_SA_ID *Dst;
+ EFI_IPSEC_SA_ID *Src;
+
+ Dst = &DstSel->SaId;
+ Src = &SrcSel->SaId;
+
+ if (Dst == NULL || Src == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Size != NULL && *Size < sizeof (EFI_IPSEC_SA_ID)) {
+ *Size = sizeof (EFI_IPSEC_SA_ID);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_SA_ID));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Copy Source PAD ID to the Destination PAD ID.
+
+ @param[in, out] DstSel Pointer of Destination PAD ID.
+ @param[in] SrcSel Pointer of Source PAD ID.
+ @param[in, out] Size The size of the Destination PAD ID. If it
+ not NULL and its value less than the size of
+ Source PAD ID, the value of Source PAD ID's size
+ will be passed to caller by this parameter.
+
+ @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL.
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID .
+ @retval EFI_SUCCESS Copy Source PAD ID to the Destination PAD ID successfully.
+
+**/
+EFI_STATUS
+DuplicatePadId (
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,
+ IN OUT UINTN *Size
+ )
+{
+ EFI_IPSEC_PAD_ID *Dst;
+ EFI_IPSEC_PAD_ID *Src;
+
+ Dst = &DstSel->PadId;
+ Src = &SrcSel->PadId;
+
+ if (Dst == NULL || Src == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Size != NULL && *Size < sizeof (EFI_IPSEC_PAD_ID)) {
+ *Size = sizeof (EFI_IPSEC_PAD_ID);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_PAD_ID));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fix the value of some members of SPD Selector.
+
+ This function is called by IpSecCopyPolicyEntry()which copy the Policy
+ Entry into the Variable. Since some members in SPD Selector are pointers,
+ a physical address to relative address convertion is required before copying
+ this SPD entry into the variable.
+
+ @param[in] Selector Pointer of SPD Selector.
+ @param[in, out] Data Pointer of SPD Data.
+
+**/
+VOID
+FixSpdEntry (
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,
+ IN OUT EFI_IPSEC_SPD_DATA *Data
+ )
+{
+ //
+ // It assumes that all ref buffers in spd selector and data are
+ // stored in the continous memory and close to the base structure.
+ //
+ FIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);
+ FIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);
+
+ if (Data->ProcessingPolicy != NULL) {
+ if (Data->ProcessingPolicy->TunnelOption != NULL) {
+ FIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);
+ }
+
+ FIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);
+ }
+
+}
+
+/**
+ Fix the value of some members of SA ID.
+
+ This function is called by IpSecCopyPolicyEntry()which copy the Policy
+ Entry into the Variable. Since some members in SA ID are pointers,
+ a physical address to relative address conversion is required before copying
+ this SAD into the variable.
+
+ @param[in] SaId Pointer of SA ID
+ @param[in, out] Data Pointer of SA Data.
+
+**/
+VOID
+FixSadEntry (
+ IN EFI_IPSEC_SA_ID *SaId,
+ IN OUT EFI_IPSEC_SA_DATA *Data
+ )
+{
+ //
+ // It assumes that all ref buffers in sad selector and data are
+ // stored in the continous memory and close to the base structure.
+ //
+ if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
+ FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);
+ }
+
+ if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
+ FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);
+ }
+
+ if (Data->SpdSelector != NULL) {
+ if (Data->SpdSelector->LocalAddress != NULL) {
+ FIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);
+ }
+
+ FIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);
+ FIX_REF_BUF_ADDR (Data->SpdSelector, Data);
+ }
+
+}
+
+/**
+ Fix the value of some members of PAD ID.
+
+ This function is called by IpSecCopyPolicyEntry()which copy the Policy
+ Entry into the Variable. Since some members in PAD ID are pointers,
+ a physical address to relative address conversion is required before copying
+ this PAD into the variable.
+
+ @param[in] PadId Pointer of PAD ID.
+ @param[in, out] Data Pointer of PAD Data.
+
+**/
+VOID
+FixPadEntry (
+ IN EFI_IPSEC_PAD_ID *PadId,
+ IN OUT EFI_IPSEC_PAD_DATA *Data
+ )
+{
+ //
+ // It assumes that all ref buffers in pad selector and data are
+ // stored in the continous memory and close to the base structure.
+ //
+ if (Data->AuthData != NULL) {
+ FIX_REF_BUF_ADDR (Data->AuthData, Data);
+ }
+
+ if (Data->RevocationData != NULL) {
+ FIX_REF_BUF_ADDR (Data->RevocationData, Data);
+ }
+
+}
+
+/**
+ Recover the value of some members of SPD Selector.
+
+ This function is corresponding to FixSpdEntry(). It recovers the value of members
+ of SPD Selector that are fixed by FixSpdEntry().
+
+ @param[in, out] Selector Pointer of SPD Selector.
+ @param[in, out] Data Pointer of SPD Data.
+
+**/
+VOID
+UnfixSpdEntry (
+ IN OUT EFI_IPSEC_SPD_SELECTOR *Selector,
+ IN OUT EFI_IPSEC_SPD_DATA *Data
+ )
+{
+ //
+ // It assumes that all ref buffers in spd selector and data are
+ // stored in the continous memory and close to the base structure.
+ //
+ UNFIX_REF_BUF_ADDR (Selector->LocalAddress, Selector);
+ UNFIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector);
+
+ if (Data->ProcessingPolicy != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data);
+ if (Data->ProcessingPolicy->TunnelOption != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data);
+ }
+ }
+
+}
+
+/**
+ Recover the value of some members of SA ID.
+
+ This function is corresponding to FixSadEntry(). It recovers the value of members
+ of SAD ID that are fixed by FixSadEntry().
+
+ @param[in, out] SaId Pointer of SAD ID.
+ @param[in, out] Data Pointer of SAD Data.
+
+**/
+VOID
+UnfixSadEntry (
+ IN OUT EFI_IPSEC_SA_ID *SaId,
+ IN OUT EFI_IPSEC_SA_DATA *Data
+ )
+{
+ //
+ // It assumes that all ref buffers in sad selector and data are
+ // stored in the continous memory and close to the base structure.
+ //
+ if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data);
+ }
+
+ if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data);
+ }
+
+ if (Data->SpdSelector != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->SpdSelector, Data);
+ if (Data->SpdSelector->LocalAddress != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data);
+ }
+
+ UNFIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data);
+ }
+
+}
+
+/**
+ Recover the value of some members of PAD ID.
+
+ This function is corresponding to FixPadEntry(). It recovers the value of members
+ of PAD ID that are fixed by FixPadEntry().
+
+ @param[in] PadId Pointer of PAD ID.
+ @param[in, out] Data Pointer of PAD Data.
+
+**/
+VOID
+UnfixPadEntry (
+ IN EFI_IPSEC_PAD_ID *PadId,
+ IN OUT EFI_IPSEC_PAD_DATA *Data
+ )
+{
+ //
+ // It assumes that all ref buffers in pad selector and data are
+ // stored in the continous memory and close to the base structure.
+ //
+ if (Data->AuthData != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->AuthData, Data);
+ }
+
+ if (Data->RevocationData != NULL) {
+ UNFIX_REF_BUF_ADDR (Data->RevocationData, Data);
+ }
+
+}
+
+/**
+ Set the security policy information for the EFI IPsec driver.
+
+ The IPsec configuration data has a unique selector/identifier separately to
+ identify a data entry.
+
+ @param[in] Selector Pointer to an entry selector on operated
+ configuration data specified by DataType.
+ A NULL Selector causes the entire specified-type
+ configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure
+ of the data buffer should be EFI_IPSEC_SPD_DATA.
+ @param[in] Context Pointer to one entry selector that describes
+ the expected position the new data entry will
+ be added. If Context is NULL, the new entry will
+ be appended the end of database.
+
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - Selector is not NULL and its LocalAddress
+ is NULL or its RemoteAddress is NULL.
+ - Data is not NULL and its Action is Protected
+ and its plolicy is NULL.
+ - Data is not NULL, its Action is not protected,
+ and its policy is not NULL.
+ - The Action of Data is Protected, its policy
+ mode is Tunnel, and its tunnel option is NULL.
+ - The Action of Data is protected and its policy
+ mode is not Tunnel and it tunnel option is not NULL.
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+EFI_STATUS
+SetSpdEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context OPTIONAL
+ )
+{
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;
+ EFI_IPSEC_SPD_DATA *SpdData;
+ EFI_IPSEC_SPD_SELECTOR *InsertBefore;
+ LIST_ENTRY *SpdList;
+ LIST_ENTRY *SadList;
+ LIST_ENTRY *SpdSas;
+ LIST_ENTRY *EntryInsertBefore;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ LIST_ENTRY *Entry2;
+ IPSEC_SPD_ENTRY *SpdEntry;
+ IPSEC_SAD_ENTRY *SadEntry;
+ UINTN SpdEntrySize;
+ UINTN Index;
+
+ SpdSel = (Selector == NULL) ? NULL : &Selector->SpdSelector;
+ SpdData = (Data == NULL) ? NULL : (EFI_IPSEC_SPD_DATA *) Data;
+ InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SpdSelector;
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];
+
+ if (SpdSel != NULL) {
+ if (SpdSel->LocalAddress == NULL || SpdSel->RemoteAddress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (SpdData != NULL) {
+ if ((SpdData->Action == EfiIPsecActionProtect && SpdData->ProcessingPolicy == NULL) ||
+ (SpdData->Action != EfiIPsecActionProtect && SpdData->ProcessingPolicy != NULL)
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (SpdData->Action == EfiIPsecActionProtect) {
+ if ((SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption == NULL) ||
+ (SpdData->ProcessingPolicy->Mode != EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption != NULL)
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+ //
+ // The default behavior is to insert the node ahead of the header.
+ //
+ EntryInsertBefore = SpdList;
+
+ //
+ // Remove the existed spd entry.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SpdList) {
+
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
+
+ if (SpdSel == NULL ||
+ CompareSpdSelector ((EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel)
+ ) {
+ //
+ // Record the existed entry position to keep the original order.
+ //
+ EntryInsertBefore = SpdEntry->List.ForwardLink;
+ RemoveEntryList (&SpdEntry->List);
+
+ //
+ // Update the reverse ref of sad entry in the spd.sas list.
+ //
+ SpdSas = &SpdEntry->Data->Sas;
+ NET_LIST_FOR_EACH (Entry2, SpdSas) {
+ SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry2);
+ SadEntry->Data->SpdEntry = NULL;
+ }
+ //
+ // Free the existed spd entry
+ //
+ FreePool (SpdEntry);
+ }
+ }
+ //
+ // Return success here if only want to remove the spd entry.
+ //
+ if (SpdData == NULL || SpdSel == NULL) {
+ return EFI_SUCCESS;
+ }
+ //
+ // Search the appointed entry position if InsertBefore is not NULL.
+ //
+ if (InsertBefore != NULL) {
+
+ NET_LIST_FOR_EACH (Entry, SpdList) {
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
+
+ if (CompareSpdSelector (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,
+ (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore
+ )) {
+ EntryInsertBefore = Entry;
+ break;
+ }
+ }
+ }
+
+ //
+ // Do Padding for the different Arch.
+ //
+ SpdEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SPD_ENTRY));
+ SpdEntrySize = ALIGN_VARIABLE (SpdEntrySize + (UINTN)SIZE_OF_SPD_SELECTOR (SpdSel));
+ SpdEntrySize += IpSecGetSizeOfEfiSpdData (SpdData);
+
+ SpdEntry = AllocateZeroPool (SpdEntrySize);
+
+ if (SpdEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Fix the address of Selector and Data buffer and copy them, which is
+ // continous memory and close to the base structure of spd entry.
+ //
+ SpdEntry->Selector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ((SpdEntry + 1), sizeof (UINTN));
+ SpdEntry->Data = (IPSEC_SPD_DATA *) ALIGN_POINTER (
+ ((UINT8 *) SpdEntry->Selector + SIZE_OF_SPD_SELECTOR (SpdSel)),
+ sizeof (UINTN)
+ );
+
+ DuplicateSpdSelector (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,
+ NULL
+ );
+
+ CopyMem (
+ SpdEntry->Data->Name,
+ SpdData->Name,
+ sizeof (SpdData->Name)
+ );
+ SpdEntry->Data->PackageFlag = SpdData->PackageFlag;
+ SpdEntry->Data->Action = SpdData->Action;
+
+ //
+ // Fix the address of ProcessingPolicy and copy it if need, which is continous
+ // memory and close to the base structure of sad data.
+ //
+ if (SpdData->Action != EfiIPsecActionProtect) {
+ SpdEntry->Data->ProcessingPolicy = NULL;
+ } else {
+ SpdEntry->Data->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER (
+ SpdEntry->Data + 1,
+ sizeof (UINTN)
+ );
+ IpSecDuplicateProcessPolicy (SpdEntry->Data->ProcessingPolicy, SpdData->ProcessingPolicy);
+ }
+ //
+ // Update the sas list of the new spd entry.
+ //
+ InitializeListHead (&SpdEntry->Data->Sas);
+
+ SadList = &mConfigData[IPsecConfigDataTypeSad];
+
+ NET_LIST_FOR_EACH (Entry, SadList) {
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);
+
+ for (Index = 0; Index < SpdData->SaIdCount; Index++) {
+
+ if (CompareSaId (
+ (EFI_IPSEC_CONFIG_SELECTOR *) &SpdData->SaId[Index],
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id
+ )) {
+ InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);
+ SadEntry->Data->SpdEntry = SpdEntry;
+ }
+ }
+ }
+ //
+ // Insert the new spd entry.
+ //
+ InsertTailList (EntryInsertBefore, &SpdEntry->List);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set the security association information for the EFI IPsec driver.
+
+ The IPsec configuration data has a unique selector/identifier separately to
+ identify a data entry.
+
+ @param[in] Selector Pointer to an entry selector on operated
+ configuration data specified by DataType.
+ A NULL Selector causes the entire specified-type
+ configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure
+ of the data buffer should be EFI_IPSEC_SA_DATA.
+ @param[in] Context Pointer to one entry selector which describes
+ the expected position the new data entry will
+ be added. If Context is NULL,the new entry will
+ be appended the end of database.
+
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+EFI_STATUS
+SetSadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context OPTIONAL
+ )
+{
+ IPSEC_SAD_ENTRY *SadEntry;
+ IPSEC_SPD_ENTRY *SpdEntry;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ LIST_ENTRY *SadList;
+ LIST_ENTRY *SpdList;
+ EFI_IPSEC_SA_ID *SaId;
+ EFI_IPSEC_SA_DATA *SaData;
+ EFI_IPSEC_SA_ID *InsertBefore;
+ LIST_ENTRY *EntryInsertBefore;
+ UINTN SadEntrySize;
+
+ SaId = (Selector == NULL) ? NULL : &Selector->SaId;
+ SaData = (Data == NULL) ? NULL : (EFI_IPSEC_SA_DATA *) Data;
+ InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SaId;
+ SadList = &mConfigData[IPsecConfigDataTypeSad];
+
+ //
+ // The default behavior is to insert the node ahead of the header.
+ //
+ EntryInsertBefore = SadList;
+
+ //
+ // Remove the existed sad entry.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SadList) {
+
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);
+
+ if (SaId == NULL ||
+ CompareSaId (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaId
+ )) {
+ //
+ // Record the existed entry position to keep the original order.
+ //
+ EntryInsertBefore = SadEntry->List.ForwardLink;
+
+ //
+ // Update the related sad.byspd field.
+ //
+ if (SadEntry->Data->SpdEntry != NULL) {
+ RemoveEntryList (&SadEntry->BySpd);
+ }
+
+ RemoveEntryList (&SadEntry->List);
+ FreePool (SadEntry);
+ }
+ }
+ //
+ // Return success here if only want to remove the sad entry
+ //
+ if (SaData == NULL || SaId == NULL) {
+ return EFI_SUCCESS;
+ }
+ //
+ // Search the appointed entry position if InsertBefore is not NULL.
+ //
+ if (InsertBefore != NULL) {
+
+ NET_LIST_FOR_EACH (Entry, SadList) {
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);
+
+ if (CompareSaId (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id,
+ (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore
+ )) {
+ EntryInsertBefore = Entry;
+ break;
+ }
+ }
+ }
+
+ //
+ // Do Padding for different Arch.
+ //
+ SadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SAD_ENTRY));
+ SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (EFI_IPSEC_SA_DATA));
+ SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (IPSEC_SAD_DATA));
+
+ if (SaId->Proto == EfiIPsecAH) {
+ SadEntrySize += SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength;
+ } else {
+ SadEntrySize = ALIGN_VARIABLE (SadEntrySize + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength);
+ SadEntrySize += SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;
+ }
+
+ SadEntry = AllocateZeroPool (SadEntrySize);
+
+ if (SadEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Fix the address of Id and Data buffer and copy them, which is
+ // continous memory and close to the base structure of sad entry.
+ //
+ SadEntry->Id = (EFI_IPSEC_SA_ID *) ALIGN_POINTER ((SadEntry + 1), sizeof (UINTN));
+ SadEntry->Data = (IPSEC_SAD_DATA *) ALIGN_POINTER ((SadEntry->Id + 1), sizeof (UINTN));
+
+ CopyMem (SadEntry->Id, SaId, sizeof (EFI_IPSEC_SA_ID));
+
+ SadEntry->Data->Mode = SaData->Mode;
+ SadEntry->Data->SequenceNumber = SaData->SNCount;
+ SadEntry->Data->AntiReplayWindowSize = SaData->AntiReplayWindows;
+
+ ZeroMem (
+ &SadEntry->Data->AntiReplayBitmap,
+ sizeof (SadEntry->Data->AntiReplayBitmap)
+ );
+
+ ZeroMem (
+ &SadEntry->Data->AlgoInfo,
+ sizeof (EFI_IPSEC_ALGO_INFO)
+ );
+
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId = SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId;
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength = SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength;
+
+ if (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SadEntry->Data + 1), sizeof (UINTN));
+ CopyMem (
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
+ SaData->AlgoInfo.EspAlgoInfo.AuthKey,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength
+ );
+ }
+
+ if (SaId->Proto == EfiIPsecESP) {
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId = SaData->AlgoInfo.EspAlgoInfo.EncAlgoId;
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength = SaData->AlgoInfo.EspAlgoInfo.EncKeyLength;
+
+ if (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (
+ ((UINT8 *) (SadEntry->Data + 1) +
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength),
+ sizeof (UINTN)
+ );
+ CopyMem (
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
+ SaData->AlgoInfo.EspAlgoInfo.EncKey,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength
+ );
+ }
+ }
+
+ CopyMem (
+ &SadEntry->Data->SaLifetime,
+ &SaData->SaLifetime,
+ sizeof (EFI_IPSEC_SA_LIFETIME)
+ );
+
+ SadEntry->Data->PathMTU = SaData->PathMTU;
+ SadEntry->Data->SpdEntry = NULL;
+ SadEntry->Data->ESNEnabled = FALSE;
+ SadEntry->Data->ManualSet = SaData->ManualSet;
+
+ //
+ // Update the spd.sas list of the spd entry specified by sad.selector
+ //
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];
+
+ for (Entry = SpdList->ForwardLink; Entry != SpdList && SaData->SpdSelector != NULL; Entry = Entry->ForwardLink) {
+
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
+ if (CompareSpdSelector (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector,
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector
+ ) && SpdEntry->Data->Action == EfiIPsecActionProtect) {
+ SadEntry->Data->SpdEntry = SpdEntry;
+ InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);
+ }
+ }
+ //
+ // Insert the new sad entry.
+ //
+ InsertTailList (EntryInsertBefore, &SadEntry->List);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set the peer authorization configuration information for the EFI IPsec driver.
+
+ The IPsec configuration data has a unique selector/identifier separately to
+ identify a data entry.
+
+ @param[in] Selector Pointer to an entry selector on operated
+ configuration data specified by DataType.
+ A NULL Selector causes the entire specified-type
+ configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure
+ of the data buffer should be EFI_IPSEC_PAD_DATA.
+ @param[in] Context Pointer to one entry selector that describes
+ the expected position the new data entry will
+ be added. If Context is NULL, the new entry will
+ be appended the end of database.
+
+ @retval EFI_OUT_OF_RESOURCES The required system resources could not be allocated.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+EFI_STATUS
+SetPadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context OPTIONAL
+ )
+{
+ IPSEC_PAD_ENTRY *PadEntry;
+ EFI_IPSEC_PAD_ID *PadId;
+ EFI_IPSEC_PAD_DATA *PadData;
+ LIST_ENTRY *PadList;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ EFI_IPSEC_PAD_ID *InsertBefore;
+ LIST_ENTRY *EntryInsertBefore;
+ UINTN PadEntrySize;
+
+ PadId = (Selector == NULL) ? NULL : &Selector->PadId;
+ PadData = (Data == NULL) ? NULL : (EFI_IPSEC_PAD_DATA *) Data;
+ InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->PadId;
+ PadList = &mConfigData[IPsecConfigDataTypePad];
+
+ //
+ // The default behavior is to insert the node ahead of the header.
+ //
+ EntryInsertBefore = PadList;
+
+ //
+ // Remove the existed pad entry.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, PadList) {
+
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);
+
+ if (PadId == NULL ||
+ ComparePadId ((EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, (EFI_IPSEC_CONFIG_SELECTOR *) PadId)
+ ) {
+ //
+ // Record the existed entry position to keep the original order.
+ //
+ EntryInsertBefore = PadEntry->List.ForwardLink;
+ RemoveEntryList (&PadEntry->List);
+
+ FreePool (PadEntry);
+ }
+ }
+ //
+ // Return success here if only want to remove the pad entry
+ //
+ if (PadData == NULL || PadId == NULL) {
+ return EFI_SUCCESS;
+ }
+ //
+ // Search the appointed entry position if InsertBefore is not NULL.
+ //
+ if (InsertBefore != NULL) {
+
+ NET_LIST_FOR_EACH (Entry, PadList) {
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);
+
+ if (ComparePadId (
+ (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id,
+ (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore
+ )) {
+ EntryInsertBefore = Entry;
+ break;
+ }
+ }
+ }
+
+ //
+ // Do PADDING for different arch.
+ //
+ PadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_PAD_ENTRY));
+ PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_ID));
+ PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_DATA));
+ PadEntrySize = ALIGN_VARIABLE (PadEntrySize + (PadData->AuthData != NULL ? PadData->AuthDataSize : 0));
+ PadEntrySize += PadData->RevocationData != NULL ? PadData->RevocationDataSize : 0;
+
+ PadEntry = AllocateZeroPool (PadEntrySize);
+
+ if (PadEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Fix the address of Id and Data buffer and copy them, which is
+ // continous memory and close to the base structure of pad entry.
+ //
+ PadEntry->Id = (EFI_IPSEC_PAD_ID *) ALIGN_POINTER ((PadEntry + 1), sizeof (UINTN));
+ PadEntry->Data = (EFI_IPSEC_PAD_DATA *) ALIGN_POINTER ((PadEntry->Id + 1), sizeof (UINTN));
+
+ CopyMem (PadEntry->Id, PadId, sizeof (EFI_IPSEC_PAD_ID));
+
+ PadEntry->Data->AuthProtocol = PadData->AuthProtocol;
+ PadEntry->Data->AuthMethod = PadData->AuthMethod;
+ PadEntry->Data->IkeIdFlag = PadData->IkeIdFlag;
+
+ if (PadData->AuthData != NULL) {
+ PadEntry->Data->AuthDataSize = PadData->AuthDataSize;
+ PadEntry->Data->AuthData = (VOID *) ALIGN_POINTER (PadEntry->Data + 1, sizeof (UINTN));
+ CopyMem (
+ PadEntry->Data->AuthData,
+ PadData->AuthData,
+ PadData->AuthDataSize
+ );
+ } else {
+ PadEntry->Data->AuthDataSize = 0;
+ PadEntry->Data->AuthData = NULL;
+ }
+
+ if (PadData->RevocationData != NULL) {
+ PadEntry->Data->RevocationDataSize = PadData->RevocationDataSize;
+ PadEntry->Data->RevocationData = (VOID *) ALIGN_POINTER (
+ ((UINT8 *) (PadEntry->Data + 1) + PadData->AuthDataSize),
+ sizeof (UINTN)
+ );
+ CopyMem (
+ PadEntry->Data->RevocationData,
+ PadData->RevocationData,
+ PadData->RevocationDataSize
+ );
+ } else {
+ PadEntry->Data->RevocationDataSize = 0;
+ PadEntry->Data->RevocationData = NULL;
+ }
+ //
+ // Insert the new pad entry.
+ //
+ InsertTailList (EntryInsertBefore, &PadEntry->List);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function lookup the data entry from IPsec SPD. Return the configuration
+ value of the specified SPD Entry.
+
+ @param[in] Selector Pointer to an entry selector which is an identifier
+ of the SPD entry.
+ @param[in, out] DataSize On output the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec
+ configuration data. The type of the data buffer
+ is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+GetSpdEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ )
+{
+ IPSEC_SPD_ENTRY *SpdEntry;
+ IPSEC_SAD_ENTRY *SadEntry;
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;
+ EFI_IPSEC_SPD_DATA *SpdData;
+ LIST_ENTRY *SpdList;
+ LIST_ENTRY *SpdSas;
+ LIST_ENTRY *Entry;
+ UINTN RequiredSize;
+
+ SpdSel = &Selector->SpdSelector;
+ SpdData = (EFI_IPSEC_SPD_DATA *) Data;
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];
+
+ NET_LIST_FOR_EACH (Entry, SpdList) {
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
+
+ //
+ // Find the required spd entry
+ //
+ if (CompareSpdSelector (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel,
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector
+ )) {
+
+ RequiredSize = IpSecGetSizeOfSpdData (SpdEntry->Data);
+ if (*DataSize < RequiredSize) {
+ *DataSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ if (SpdData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *DataSize = RequiredSize;
+
+ //
+ // Extract and fill all SaId array from the spd.sas list
+ //
+ SpdSas = &SpdEntry->Data->Sas;
+ SpdData->SaIdCount = 0;
+
+ NET_LIST_FOR_EACH (Entry, SpdSas) {
+ SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);
+ CopyMem (
+ &SpdData->SaId[SpdData->SaIdCount++],
+ SadEntry->Id,
+ sizeof (EFI_IPSEC_SA_ID)
+ );
+ }
+ //
+ // Fill the other fields in spd data.
+ //
+ CopyMem (SpdData->Name, SpdEntry->Data->Name, sizeof (SpdData->Name));
+
+ SpdData->PackageFlag = SpdEntry->Data->PackageFlag;
+ SpdData->Action = SpdEntry->Data->Action;
+
+ if (SpdData->Action != EfiIPsecActionProtect) {
+ SpdData->ProcessingPolicy = NULL;
+ } else {
+ SpdData->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ((UINT8 *) SpdData + sizeof (EFI_IPSEC_SPD_DATA) + (SpdData->SaIdCount - 1) * sizeof (EFI_IPSEC_SA_ID));
+
+ IpSecDuplicateProcessPolicy (
+ SpdData->ProcessingPolicy,
+ SpdEntry->Data->ProcessingPolicy
+ );
+ }
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ This function lookup the data entry from IPsec SAD. Return the configuration
+ value of the specified SAD Entry.
+
+ @param[in] Selector Pointer to an entry selector which is an identifier
+ of the SAD entry.
+ @param[in, out] DataSize On output, the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec
+ configuration data. The type of the data buffer
+ is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+GetSadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ )
+{
+ IPSEC_SAD_ENTRY *SadEntry;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *SadList;
+ EFI_IPSEC_SA_ID *SaId;
+ EFI_IPSEC_SA_DATA *SaData;
+ UINTN RequiredSize;
+
+ SaId = &Selector->SaId;
+ SaData = (EFI_IPSEC_SA_DATA *) Data;
+ SadList = &mConfigData[IPsecConfigDataTypeSad];
+
+ NET_LIST_FOR_EACH (Entry, SadList) {
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);
+
+ //
+ // Find the required sad entry.
+ //
+ if (CompareSaId (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaId,
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id
+ )) {
+ //
+ // Calculate the required size of the sad entry.
+ // Data Layout is follows:
+ // |EFI_IPSEC_SA_DATA
+ // |AuthKey
+ // |EncryptKey (Optional)
+ // |SpdSelector (Optional)
+ //
+ RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA));
+
+ if (SaId->Proto == EfiIPsecAH) {
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength);
+ } else {
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength);
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength);
+ }
+
+ if (SadEntry->Data->SpdEntry != NULL) {
+ RequiredSize += SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector);
+ }
+
+
+
+ if (*DataSize < RequiredSize) {
+ *DataSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ //
+ // Fill the data fields of sad entry.
+ //
+ *DataSize = RequiredSize;
+ SaData->Mode = SadEntry->Data->Mode;
+ SaData->SNCount = SadEntry->Data->SequenceNumber;
+ SaData->AntiReplayWindows = SadEntry->Data->AntiReplayWindowSize;
+
+ CopyMem (
+ &SaData->SaLifetime,
+ &SadEntry->Data->SaLifetime,
+ sizeof (EFI_IPSEC_SA_LIFETIME)
+ );
+
+ ZeroMem (
+ &SaData->AlgoInfo,
+ sizeof (EFI_IPSEC_ALGO_INFO)
+ );
+
+ if (SaId->Proto == EfiIPsecAH) {
+ //
+ // Copy AH alogrithm INFO to SaData
+ //
+ SaData->AlgoInfo.AhAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthAlgoId;
+ SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength;
+ if (SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength != 0) {
+ SaData->AlgoInfo.AhAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));
+ CopyMem (
+ SaData->AlgoInfo.AhAlgoInfo.AuthKey,
+ SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKey,
+ SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength
+ );
+ }
+ } else if (SaId->Proto == EfiIPsecESP) {
+ //
+ // Copy ESP alogrithem INFO to SaData
+ //
+ SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId;
+ SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength;
+ if (SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) {
+ SaData->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN));
+ CopyMem (
+ SaData->AlgoInfo.EspAlgoInfo.AuthKey,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
+ SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength
+ );
+ }
+
+ SaData->AlgoInfo.EspAlgoInfo.EncAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId;
+ SaData->AlgoInfo.EspAlgoInfo.EncKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength;
+
+ if (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) {
+ SaData->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER (
+ ((UINT8 *) (SaData + 1) +
+ SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength),
+ sizeof (UINTN)
+ );
+ CopyMem (
+ SaData->AlgoInfo.EspAlgoInfo.EncKey,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
+ SaData->AlgoInfo.EspAlgoInfo.EncKeyLength
+ );
+ }
+ }
+
+ SaData->PathMTU = SadEntry->Data->PathMTU;
+
+ //
+ // Fill the spd selector field of sad data
+ //
+ if (SadEntry->Data->SpdEntry != NULL) {
+
+ SaData->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) (
+ (UINT8 *)SaData +
+ RequiredSize -
+ SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdEntry->Selector)
+ );
+
+ DuplicateSpdSelector (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector,
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdEntry->Selector,
+ NULL
+ );
+
+ } else {
+
+ SaData->SpdSelector = NULL;
+ }
+
+ SaData->ManualSet = SadEntry->Data->ManualSet;
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ This function lookup the data entry from IPsec PAD. Return the configuration
+ value of the specified PAD Entry.
+
+ @param[in] Selector Pointer to an entry selector which is an identifier
+ of the PAD entry.
+ @param[in, out] DataSize On output the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec
+ configuration data. The type of the data buffer
+ is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+GetPadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ )
+{
+ IPSEC_PAD_ENTRY *PadEntry;
+ LIST_ENTRY *PadList;
+ LIST_ENTRY *Entry;
+ EFI_IPSEC_PAD_ID *PadId;
+ EFI_IPSEC_PAD_DATA *PadData;
+ UINTN RequiredSize;
+
+ PadId = &Selector->PadId;
+ PadData = (EFI_IPSEC_PAD_DATA *) Data;
+ PadList = &mConfigData[IPsecConfigDataTypePad];
+
+ NET_LIST_FOR_EACH (Entry, PadList) {
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);
+
+ //
+ // Find the required pad entry.
+ //
+ if (ComparePadId (
+ (EFI_IPSEC_CONFIG_SELECTOR *) PadId,
+ (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id
+ )) {
+ //
+ // Calculate the required size of the pad entry.
+ //
+ RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA));
+ RequiredSize = ALIGN_VARIABLE (RequiredSize + PadEntry->Data->AuthDataSize);
+ RequiredSize += PadEntry->Data->RevocationDataSize;
+
+ if (*DataSize < RequiredSize) {
+ *DataSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ //
+ // Fill the data fields of pad entry
+ //
+ *DataSize = RequiredSize;
+ PadData->AuthProtocol = PadEntry->Data->AuthProtocol;
+ PadData->AuthMethod = PadEntry->Data->AuthMethod;
+ PadData->IkeIdFlag = PadEntry->Data->IkeIdFlag;
+
+ //
+ // Copy Authentication data.
+ //
+ if (PadEntry->Data->AuthData != NULL) {
+
+ PadData->AuthDataSize = PadEntry->Data->AuthDataSize;
+ PadData->AuthData = (VOID *) ALIGN_POINTER ((PadData + 1), sizeof (UINTN));
+ CopyMem (
+ PadData->AuthData,
+ PadEntry->Data->AuthData,
+ PadData->AuthDataSize
+ );
+ } else {
+
+ PadData->AuthDataSize = 0;
+ PadData->AuthData = NULL;
+ }
+ //
+ // Copy Revocation Data.
+ //
+ if (PadEntry->Data->RevocationData != NULL) {
+
+ PadData->RevocationDataSize = PadEntry->Data->RevocationDataSize;
+ PadData->RevocationData = (VOID *) ALIGN_POINTER (
+ ((UINT8 *) (PadData + 1) + PadData->AuthDataSize),
+ sizeof (UINTN)
+ );
+ CopyMem (
+ PadData->RevocationData,
+ PadEntry->Data->RevocationData,
+ PadData->RevocationDataSize
+ );
+ } else {
+
+ PadData->RevocationDataSize = 0;
+ PadData->RevocationData = NULL;
+ }
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Copy Source Process Policy to the Destination Process Policy.
+
+ @param[in] Dst Pointer to the Source Process Policy.
+ @param[in] Src Pointer to the Destination Process Policy.
+
+**/
+VOID
+IpSecDuplicateProcessPolicy (
+ IN EFI_IPSEC_PROCESS_POLICY *Dst,
+ IN EFI_IPSEC_PROCESS_POLICY *Src
+ )
+{
+ //
+ // Firstly copy the structure content itself.
+ //
+ CopyMem (Dst, Src, sizeof (EFI_IPSEC_PROCESS_POLICY));
+
+ //
+ // Recursively copy the tunnel option if needed.
+ //
+ if (Dst->Mode != EfiIPsecTunnel) {
+ ASSERT (Dst->TunnelOption == NULL);
+ } else {
+ Dst->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ((Dst + 1), sizeof (UINTN));
+ CopyMem (
+ Dst->TunnelOption,
+ Src->TunnelOption,
+ sizeof (EFI_IPSEC_TUNNEL_OPTION)
+ );
+ }
+}
+
+/**
+ Calculate the a whole size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed
+ to by the pointer members.
+
+ @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA.
+
+ @return the whole size the specified EFI_IPSEC_SPD_DATA.
+
+**/
+UINTN
+IpSecGetSizeOfEfiSpdData (
+ IN EFI_IPSEC_SPD_DATA *SpdData
+ )
+{
+ UINTN Size;
+
+ Size = ALIGN_VARIABLE (sizeof (IPSEC_SPD_DATA));
+
+ if (SpdData->Action == EfiIPsecActionProtect) {
+ Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_PROCESS_POLICY));
+
+ if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {
+ Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_TUNNEL_OPTION));
+ }
+ }
+
+ return Size;
+}
+
+/**
+ Calculate the a whole size of IPSEC_SPD_DATA which includes the buffer size pointed
+ to by the pointer members and the buffer size used by the Sa List.
+
+ @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA.
+
+ @return the whole size of IPSEC_SPD_DATA.
+
+**/
+UINTN
+IpSecGetSizeOfSpdData (
+ IN IPSEC_SPD_DATA *SpdData
+ )
+{
+ UINTN Size;
+ LIST_ENTRY *Link;
+
+ Size = sizeof (EFI_IPSEC_SPD_DATA) - sizeof (EFI_IPSEC_SA_ID);
+
+ if (SpdData->Action == EfiIPsecActionProtect) {
+ Size += sizeof (EFI_IPSEC_PROCESS_POLICY);
+
+ if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) {
+ Size += sizeof (EFI_IPSEC_TUNNEL_OPTION);
+ }
+ }
+
+ NET_LIST_FOR_EACH (Link, &SpdData->Sas) {
+ Size += sizeof (EFI_IPSEC_SA_ID);
+ }
+
+ return Size;
+}
+
+/**
+ Get the IPsec Variable.
+
+ Get the all variables which start with the string contained in VaraiableName.
+ Since all IPsec related variable store in continual space, those kinds of
+ variable can be searched by the EfiGetNextVariableName. Those variables also are
+ returned in a continual buffer.
+
+ @param[in] VariableName Pointer to a specified Variable Name.
+ @param[in] VendorGuid Pointer to a specified Vendor Guid.
+ @param[in] Attributes Point to memory location to return the attributes
+ of variable. If the point is NULL, the parameter
+ would be ignored.
+ @param[in, out] DataSize As input, point to the maximum size of return
+ Data-Buffer. As output, point to the actual
+ size of the returned Data-Buffer.
+ @param[in] Data Point to return Data-Buffer.
+
+ @retval EFI_ABORTED If the Variable size which contained in the variable
+ structure doesn't match the variable size obtained
+ from the EFIGetVariable.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has
+ been updated with the size needed to complete the request.
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval others Other errors found during the variable getting.
+**/
+EFI_STATUS
+IpSecGetVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 *Attributes, OPTIONAL
+ IN OUT UINTN *DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_STATUS Status;
+ EFI_GUID VendorGuidI;
+ UINTN VariableNameLength;
+ CHAR16 *VariableNameI;
+ UINTN VariableNameISize;
+ UINTN VariableNameISizeNew;
+ UINTN VariableIndex;
+ UINTN VariableCount;
+ IP_SEC_VARIABLE_INFO IpSecVariableInfo;
+ UINTN DataSizeI;
+
+ //
+ // The variable name constructor is "VariableName + Info/0001/0002/... + NULL".
+ // So the varialbe name is like "VariableNameInfo", "VariableName0001", ...
+ // "VariableNameNULL".
+ //
+ VariableNameLength = StrLen (VariableName);
+ VariableNameISize = (VariableNameLength + 5) * sizeof (CHAR16);
+ VariableNameI = AllocateZeroPool (VariableNameISize);
+ ASSERT (VariableNameI != NULL);
+
+ //
+ // Construct the varible name of ipsecconfig meta data.
+ //
+ UnicodeSPrint (VariableNameI, VariableNameISize, L"%s%s", VariableName, L"Info");
+
+ DataSizeI = sizeof (IpSecVariableInfo);
+
+ Status = gRT->GetVariable (
+ VariableNameI,
+ VendorGuid,
+ Attributes,
+ &DataSizeI,
+ &IpSecVariableInfo
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (*DataSize < IpSecVariableInfo.VariableSize) {
+ *DataSize = IpSecVariableInfo.VariableSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+
+ VariableCount = IpSecVariableInfo.VariableCount;
+ VariableNameI[0] = L'\0';
+
+ while (VariableCount != 0) {
+ //
+ // Get the variable name one by one in the variable database.
+ //
+ VariableNameISizeNew = VariableNameISize;
+ Status = gRT->GetNextVariableName (
+ &VariableNameISizeNew,
+ VariableNameI,
+ &VendorGuidI
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ VariableNameI = ReallocatePool (
+ VariableNameISize,
+ VariableNameISizeNew,
+ VariableNameI
+ );
+ VariableNameISize = VariableNameISizeNew;
+
+ Status = gRT->GetNextVariableName (
+ &VariableNameISizeNew,
+ VariableNameI,
+ &VendorGuidI
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ //
+ // Check whether the current variable is the required "ipsecconfig".
+ //
+ if (StrnCmp (VariableNameI, VariableName, VariableNameLength) == 0 ||
+ CompareGuid (VendorGuid, &VendorGuidI)
+ ) {
+ //
+ // Parse the variable count of the current ipsecconfig data.
+ //
+ VariableIndex = StrDecimalToUintn (VariableNameI + VariableNameLength);
+ if (VariableIndex!= 0 && VariableIndex <= IpSecVariableInfo.VariableCount) {
+ //
+ // Get the variable size of the current ipsecconfig data.
+ //
+ DataSizeI = 0;
+ Status = gRT->GetVariable (
+ VariableNameI,
+ VendorGuid,
+ Attributes,
+ &DataSizeI,
+ NULL
+ );
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+ //
+ // Validate the variable count and variable size.
+ //
+ if (VariableIndex != IpSecVariableInfo.VariableCount) {
+ //
+ // If the varaibe is not the last one, its size should be the max
+ // size of the single variable.
+ //
+ if (DataSizeI != IpSecVariableInfo.SingleVariableSize) {
+ return EFI_ABORTED;
+ }
+ } else {
+ if (DataSizeI != IpSecVariableInfo.VariableSize % IpSecVariableInfo.SingleVariableSize) {
+ return EFI_ABORTED;
+ }
+ }
+ //
+ // Get the variable data of the current ipsecconfig data and
+ // store it into user buffer continously.
+ //
+ Status = gRT->GetVariable (
+ VariableNameI,
+ VendorGuid,
+ Attributes,
+ &DataSizeI,
+ (UINT8 *) Data + (VariableIndex - 1) * IpSecVariableInfo.SingleVariableSize
+ );
+ ASSERT_EFI_ERROR (Status);
+ VariableCount--;
+ }
+ }
+ }
+ //
+ // The VariableCount in "VariableNameInfo" varaible should have the correct
+ // numbers of variables which name starts with VariableName.
+ //
+ if (VariableCount != 0) {
+ Status = EFI_ABORTED;
+ }
+
+ON_EXIT:
+ FreePool (VariableNameI);
+ return Status;
+}
+
+/**
+ Set the IPsec variables.
+
+ Set all IPsec variables which start with the specified variable name. Those variables
+ are set one by one.
+
+ @param[in] VariableName The name of the vendor's variable. It is a
+ Null-Terminated Unicode String.
+ @param[in] VendorGuid Unify identifier for vendor.
+ @param[in] Attributes Point to memory location to return the attributes of
+ variable. If the point is NULL, the parameter would be ignored.
+ @param[in] DataSize The size in bytes of Data-Buffer.
+ @param[in] Data Points to the content of the variable.
+
+ @retval EFI_SUCCESS The firmware successfully stored the variable and its data, as
+ defined by the Attributes.
+ @retval others Storing the variables failed.
+
+**/
+EFI_STATUS
+IpSecSetVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *VariableNameI;
+ UINTN VariableNameSize;
+ UINTN VariableIndex;
+ IP_SEC_VARIABLE_INFO IpSecVariableInfo;
+ UINT64 MaximumVariableStorageSize;
+ UINT64 RemainingVariableStorageSize;
+ UINT64 MaximumVariableSize;
+
+ Status = gRT->QueryVariableInfo (
+ Attributes,
+ &MaximumVariableStorageSize,
+ &RemainingVariableStorageSize,
+ &MaximumVariableSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // "VariableName + Info/0001/0002/... + NULL"
+ //
+ VariableNameSize = (StrLen (VariableName) + 5) * sizeof (CHAR16);
+ VariableNameI = AllocateZeroPool (VariableNameSize);
+
+ if (VariableNameI == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ //
+ // Construct the variable of ipsecconfig general information. Like the total
+ // numbers of the Ipsecconfig variables, the total size of all ipsecconfig variables.
+ //
+ UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%s", VariableName, L"Info");
+ MaximumVariableSize -= VariableNameSize;
+
+ IpSecVariableInfo.VariableCount = (UINT32) ((DataSize + (UINTN) MaximumVariableSize - 1) / (UINTN) MaximumVariableSize);
+ IpSecVariableInfo.VariableSize = (UINT32) DataSize;
+ IpSecVariableInfo.SingleVariableSize = (UINT32) MaximumVariableSize;
+
+ //
+ // Set the variable of ipsecconfig general information.
+ //
+ Status = gRT->SetVariable (
+ VariableNameI,
+ VendorGuid,
+ Attributes,
+ sizeof (IpSecVariableInfo),
+ &IpSecVariableInfo
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Error set ipsecconfig meta data with %r\n", Status));
+ goto ON_EXIT;
+ }
+
+ for (VariableIndex = 0; VariableIndex < IpSecVariableInfo.VariableCount; VariableIndex++) {
+ //
+ // Construct and set the variable of ipsecconfig data one by one.
+ // The index of variable name begin from 0001, and the varaible name
+ // likes "VariableName0001", "VaraiableName0002"....
+ //
+ UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%04d", VariableName, VariableIndex + 1);
+ Status = gRT->SetVariable (
+ VariableNameI,
+ VendorGuid,
+ Attributes,
+ (VariableIndex == IpSecVariableInfo.VariableCount - 1) ?
+ (DataSize % (UINTN) MaximumVariableSize) :
+ (UINTN) MaximumVariableSize,
+ (UINT8 *) Data + VariableIndex * (UINTN) MaximumVariableSize
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Error set ipsecconfig variable data with %r\n", Status));
+ goto ON_EXIT;
+ }
+ }
+
+ON_EXIT:
+ if (VariableNameI != NULL) {
+ FreePool (VariableNameI);
+ }
+
+ return Status;
+}
+
+/**
+ Return the configuration value for the EFI IPsec driver.
+
+ This function lookup the data entry from IPsec database or IKEv2 configuration
+ information. The expected data type and unique identification are described in
+ DataType and Selector parameters.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to retrieve.
+ @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec
+ configuration data entry.
+ @param[in, out] DataSize On output the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec configuration data.
+ The type of the data buffer associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:
+ - This is NULL.
+ - Selector is NULL.
+ - DataSize is NULL.
+ - Data is NULL and *DataSize is not zero
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigGetData (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ )
+{
+ if (This == NULL || Selector == NULL || DataSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*DataSize != 0 && Data == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= IPsecConfigDataTypeMaximum) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return mGetPolicyEntry[DataType](Selector, DataSize, Data);
+}
+
+/**
+ Set the security association, security policy and peer authorization configuration
+ information for the EFI IPsec driver.
+
+ This function is used to set the IPsec configuration information of type DataType for
+ the EFI IPsec driver.
+ The IPsec configuration data has a unique selector/identifier separately to identify
+ a data entry. The selector structure depends on DataType's definition.
+ Using SetData() with a Data of NULL causes the IPsec configuration data entry identified
+ by DataType and Selector to be deleted.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to be set.
+ @param[in] Selector Pointer to an entry selector on operated configuration data
+ specified by DataType. A NULL Selector causes the entire
+ specified-type configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure of the data buffer is
+ associated with the DataType.
+ @param[in] InsertBefore Pointer to one entry selector which describes the expected
+ position the new data entry will be added. If InsertBefore is NULL,
+ the new entry will be appended to the end of the database.
+
+ @retval EFI_SUCCESS The specified configuration entry data was set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigSetData (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= IPsecConfigDataTypeMaximum) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = mSetPolicyEntry[DataType](Selector, Data, InsertBefore);
+
+ if (!EFI_ERROR (Status) && !mSetBySelf) {
+ //
+ // Save the updated config data into variable.
+ //
+ IpSecConfigSave ();
+ }
+
+ return Status;
+}
+
+/**
+ Enumerates the current selector for IPsec configuration data entry.
+
+ This function is called multiple times to retrieve the entry Selector in IPsec
+ configuration database. On each call to GetNextSelector(), the next entry
+ Selector are retrieved into the output interface.
+
+ If the entire IPsec configuration database has been iterated, the error
+ EFI_NOT_FOUND is returned.
+ If the Selector buffer is too small for the next Selector copy, an
+ EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect
+ the size of buffer needed.
+
+ On the initial call to GetNextSelector() to start the IPsec configuration database
+ search, a pointer to the buffer with all zero value is passed in Selector. Calls
+ to SetData() between calls to GetNextSelector may produce unpredictable results.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of IPsec configuration data to retrieve.
+ @param[in, out] SelectorSize The size of the Selector buffer.
+ @param[in, out] Selector On input, supplies the pointer to last Selector that was
+ returned by GetNextSelector().
+ On output, returns one copy of the current entry Selector
+ of a given DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:
+ - This is NULL.
+ - SelectorSize is NULL.
+ - Selector is NULL.
+ @retval EFI_NOT_FOUND The next configuration data entry was not found.
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.
+ @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter
+ has been updated with the size needed to complete the search
+ request.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigGetNextSelector (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN OUT UINTN *SelectorSize,
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector
+ )
+{
+ LIST_ENTRY *Link;
+ IPSEC_COMMON_POLICY_ENTRY *CommonEntry;
+ BOOLEAN IsFound;
+
+ if (This == NULL || Selector == NULL || SelectorSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataType >= IPsecConfigDataTypeMaximum) {
+ return EFI_UNSUPPORTED;
+ }
+
+ IsFound = FALSE;
+
+ NET_LIST_FOR_EACH (Link, &mConfigData[DataType]) {
+ CommonEntry = BASE_CR (Link, IPSEC_COMMON_POLICY_ENTRY, List);
+
+ if (IsFound || mIsZeroSelector[DataType](Selector)) {
+ //
+ // If found the appointed entry, then duplicate the next one and return,
+ // or if the appointed entry is zero, then return the first one directly.
+ //
+ return mDuplicateSelector[DataType](Selector, CommonEntry->Selector, SelectorSize);
+ } else {
+ //
+ // Set the flag if find the appointed entry.
+ //
+ IsFound = mCompareSelector[DataType](Selector, CommonEntry->Selector);
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Register an event that is to be signaled whenever a configuration process on the
+ specified IPsec configuration information is done.
+
+ The register function is not surpport now and always returns EFI_UNSUPPORTED.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to be registered the event for.
+ @param[in] Event The event to be registered.
+
+ @retval EFI_SUCCESS The event is registered successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
+ @retval EFI_ACCESS_DENIED The Event is already registered for the DataType.
+ @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified
+ DataType is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigRegisterNotify (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Remove the specified event that was previously registered on the specified IPsec
+ configuration data.
+
+ This function is not support now and alwasy return EFI_UNSUPPORTED.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The configuration data type to remove the registered event for.
+ @param[in] Event The event to be unregistered.
+
+ @retval EFI_SUCCESS The event was removed successfully.
+ @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the
+ database.
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
+ @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified
+ DataType is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigUnregisterNotify (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Copy whole data in specified EFI_SIPEC_CONFIG_SELECTOR and the Data to a buffer.
+
+ This function is a caller defined function, and it is called by the IpSecVisitConfigData().
+ The orignal caller is IpSecConfigSave(), which calls the IpsecVisitConfigData() to
+ copy all types of IPsec Config datas into one buffer and store this buffer into firmware in
+ the form of several variables.
+
+ @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE.
+ @param[in] Selector Points to a EFI_IPSEC_CONFIG_SELECTOR to be copied
+ to the buffer.
+ @param[in] Data Points to data to be copied to the buffer. The
+ Data type is related to the Type.
+ @param[in] SelectorSize The size of the Selector.
+ @param[in] DataSize The size of the Data.
+ @param[in, out] Buffer The buffer to store the Selector and Data.
+
+ @retval EFI_SUCCESS Copy the Selector and Data to a buffer successfully.
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.
+
+**/
+EFI_STATUS
+IpSecCopyPolicyEntry (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE Type,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN UINTN SelectorSize,
+ IN UINTN DataSize,
+ IN OUT IPSEC_VARIABLE_BUFFER *Buffer
+ )
+{
+ IPSEC_VAR_ITEM_HEADER SelectorHeader;
+ IPSEC_VAR_ITEM_HEADER DataHeader;
+ UINTN EntrySize;
+ UINT8 *TempPoint;
+
+ if (Type == IPsecConfigDataTypeSad) {
+ //
+ // Don't save automatically-generated sa entry into variable.
+ //
+ if (((EFI_IPSEC_SA_DATA *) Data)->ManualSet == FALSE) {
+ return EFI_SUCCESS;
+ }
+ }
+ //
+ // Increase the capacity size of the buffer if needed.
+ //
+ EntrySize = ALIGN_VARIABLE (sizeof (SelectorHeader));
+ EntrySize = ALIGN_VARIABLE (EntrySize + SelectorSize);
+ EntrySize = ALIGN_VARIABLE (EntrySize + sizeof (SelectorHeader));
+ EntrySize = ALIGN_VARIABLE (EntrySize + DataSize);
+
+ //EntrySize = SelectorSize + DataSize + 2 * sizeof (SelectorHeader);
+ if (Buffer->Capacity - Buffer->Size < EntrySize) {
+ //
+ // Calculate the required buffer
+ //
+ Buffer->Capacity += EntrySize;
+ TempPoint = AllocatePool (Buffer->Capacity);
+
+ if (Buffer->Ptr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Copy the old Buffer to new buffer and free the old one.
+ //
+ CopyMem (TempPoint, Buffer->Ptr, Buffer->Size);
+ FreePool (Buffer->Ptr);
+
+ Buffer->Ptr = TempPoint;
+ }
+
+ mFixPolicyEntry[Type](Selector, Data);
+
+ //
+ // Fill the selector header and copy it into buffer.
+ //
+ SelectorHeader.Type = (UINT8) (Type | IPSEC_VAR_ITEM_HEADER_LOGO_BIT);
+ SelectorHeader.Size = (UINT16) SelectorSize;
+
+ CopyMem (
+ Buffer->Ptr + Buffer->Size,
+ &SelectorHeader,
+ sizeof (SelectorHeader)
+ );
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (SelectorHeader));
+
+ //
+ // Copy the selector into buffer.
+ //
+ CopyMem (
+ Buffer->Ptr + Buffer->Size,
+ Selector,
+ SelectorSize
+ );
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + SelectorSize);
+
+ //
+ // Fill the data header and copy it into buffer.
+ //
+ DataHeader.Type = (UINT8) Type;
+ DataHeader.Size = (UINT16) DataSize;
+
+ CopyMem (
+ Buffer->Ptr + Buffer->Size,
+ &DataHeader,
+ sizeof (DataHeader)
+ );
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (DataHeader));
+ //
+ // Copy the data into buffer.
+ //
+ CopyMem (
+ Buffer->Ptr + Buffer->Size,
+ Data,
+ DataSize
+ );
+ Buffer->Size = ALIGN_VARIABLE (Buffer->Size + DataSize);
+
+ mUnfixPolicyEntry[Type](Selector, Data);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Visit all IPsec Configurations of specified Type and call the caller defined
+ interface.
+
+ @param[in] DataType The specified IPsec Config Data Type.
+ @param[in] Routine The function defined by the caller.
+ @param[in] Context The data passed to the Routine.
+
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated
+ @retval EFI_SUCCESS This function completed successfully.
+
+**/
+EFI_STATUS
+IpSecVisitConfigData (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN IPSEC_COPY_POLICY_ENTRY Routine,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS GetNextStatus;
+ EFI_STATUS GetDataStatus;
+ EFI_STATUS RoutineStatus;
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;
+ VOID *Data;
+ UINTN SelectorSize;
+ UINTN DataSize;
+ UINTN SelectorBufferSize;
+ UINTN DataBufferSize;
+ BOOLEAN FirstGetNext;
+
+ FirstGetNext = TRUE;
+ DataBufferSize = 0;
+ Data = NULL;
+ SelectorBufferSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR);
+ Selector = AllocateZeroPool (SelectorBufferSize);
+
+ if (Selector == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ while (TRUE) {
+ //
+ // Get the real size of the selector.
+ //
+ SelectorSize = SelectorBufferSize;
+ GetNextStatus = EfiIpSecConfigGetNextSelector (
+ &mIpSecConfigInstance,
+ DataType,
+ &SelectorSize,
+ Selector
+ );
+ if (GetNextStatus == EFI_BUFFER_TOO_SMALL) {
+ FreePool (Selector);
+ SelectorBufferSize = SelectorSize;
+ //
+ // Allocate zero pool for the first selector, while store the last
+ // selector content for the other selectors.
+ //
+ if (FirstGetNext) {
+ Selector = AllocateZeroPool (SelectorBufferSize);
+ } else {
+ Selector = AllocateCopyPool (SelectorBufferSize, Selector);
+ }
+
+ if (Selector == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Get the content of the selector.
+ //
+ GetNextStatus = EfiIpSecConfigGetNextSelector (
+ &mIpSecConfigInstance,
+ DataType,
+ &SelectorSize,
+ Selector
+ );
+ }
+
+ if (EFI_ERROR (GetNextStatus)) {
+ break;
+ }
+
+ FirstGetNext = FALSE;
+
+ //
+ // Get the real size of the policy entry according to the selector.
+ //
+ DataSize = DataBufferSize;
+ GetDataStatus = EfiIpSecConfigGetData (
+ &mIpSecConfigInstance,
+ DataType,
+ Selector,
+ &DataSize,
+ Data
+ );
+ if (GetDataStatus == EFI_BUFFER_TOO_SMALL) {
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+
+ DataBufferSize = DataSize;
+ Data = AllocateZeroPool (DataBufferSize);
+
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Get the content of the policy entry according to the selector.
+ //
+ GetDataStatus = EfiIpSecConfigGetData (
+ &mIpSecConfigInstance,
+ DataType,
+ Selector,
+ &DataSize,
+ Data
+ );
+ }
+
+ if (EFI_ERROR (GetDataStatus)) {
+ break;
+ }
+ //
+ // Prepare the buffer of updated policy entry, which is stored in
+ // the continous memory, and then save into variable later.
+ //
+ RoutineStatus = Routine (
+ DataType,
+ Selector,
+ Data,
+ SelectorSize,
+ DataSize,
+ Context
+ );
+ if (EFI_ERROR (RoutineStatus)) {
+ break;
+ }
+ }
+
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+
+ if (Selector != NULL) {
+ FreePool (Selector);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is the subfunction of EFIIpSecConfigSetData.
+
+ This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.
+
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.
+ @retval EFI_SUCCESS Saved the configration successfully.
+ @retval Others Other errors were found while obtaining the variable.
+
+**/
+EFI_STATUS
+IpSecConfigSave (
+ VOID
+ )
+{
+ IPSEC_VARIABLE_BUFFER Buffer;
+ EFI_STATUS Status;
+ EFI_IPSEC_CONFIG_DATA_TYPE Type;
+
+ Buffer.Size = 0;
+ Buffer.Capacity = IPSEC_DEFAULT_VARIABLE_SIZE;
+ Buffer.Ptr = AllocateZeroPool (Buffer.Capacity);
+
+ if (Buffer.Ptr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // For each policy database, prepare the contious buffer to save into variable.
+ //
+ for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {
+ IpSecVisitConfigData (
+ Type,
+ (IPSEC_COPY_POLICY_ENTRY) IpSecCopyPolicyEntry,
+ &Buffer
+ );
+ }
+ //
+ // Save the updated policy database into variable.
+ //
+ Status = IpSecSetVariable (
+ IPSECCONFIG_VARIABLE_NAME,
+ &gEfiIpSecConfigProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ Buffer.Size,
+ Buffer.Ptr
+ );
+
+ FreePool (Buffer.Ptr);
+
+ return Status;
+}
+
+/**
+ Get the all IPSec configuration variables and store those variables
+ to the internal data structure.
+
+ This founction is called by IpSecConfigInitialize() which is to intialize the
+ IPsecConfiguration Protocol.
+
+ @param[in] Private Point to IPSEC_PRIVATE_DATA.
+
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated
+ @retval EFI_SUCCESS Restore the IPsec Configuration successfully.
+ @retval others Other errors is found while obtaining the variable.
+
+**/
+EFI_STATUS
+IpSecConfigRestore (
+ IN IPSEC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ UINT8 *Buffer;
+ IPSEC_VAR_ITEM_HEADER *Header;
+ UINT8 *Ptr;
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;
+ EFI_IPSEC_CONFIG_DATA_TYPE Type;
+ VOID *Data;
+ UINT8 Value;
+ UINTN Size;
+
+ Value = 0;
+ Size = sizeof (Value);
+ BufferSize = 0;
+ Buffer = NULL;
+
+ Status = gRT->GetVariable (
+ IPSECCONFIG_STATUS_NAME,
+ &gEfiIpSecConfigProtocolGuid,
+ NULL,
+ &Size,
+ &Value
+ );
+
+ if (!EFI_ERROR (Status) && Value == IPSEC_STATUS_ENABLED) {
+ Private->IpSec.DisabledFlag = FALSE;
+ }
+ //
+ // Get the real size of policy database in variable.
+ //
+ Status = IpSecGetVariable (
+ IPSECCONFIG_VARIABLE_NAME,
+ &gEfiIpSecConfigProtocolGuid,
+ NULL,
+ &BufferSize,
+ Buffer
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+
+ Buffer = AllocateZeroPool (BufferSize);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Get the content of policy database in variable.
+ //
+ Status = IpSecGetVariable (
+ IPSECCONFIG_VARIABLE_NAME,
+ &gEfiIpSecConfigProtocolGuid,
+ NULL,
+ &BufferSize,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ for (Ptr = Buffer; Ptr < Buffer + BufferSize;) {
+
+ Header = (IPSEC_VAR_ITEM_HEADER *) Ptr;
+ Type = (EFI_IPSEC_CONFIG_DATA_TYPE) (Header->Type & IPSEC_VAR_ITEM_HEADER_CONTENT_BIT);
+ ASSERT (((Header->Type & 0x80) == IPSEC_VAR_ITEM_HEADER_LOGO_BIT) && (Type < IPsecConfigDataTypeMaximum));
+
+ Selector = (EFI_IPSEC_CONFIG_SELECTOR *) ALIGN_POINTER (Header + 1, sizeof (UINTN));
+ Header = (IPSEC_VAR_ITEM_HEADER *) ALIGN_POINTER (
+ (UINT8 *) Selector + Header->Size,
+ sizeof (UINTN)
+ );
+ ASSERT (Header->Type == Type);
+
+ Data = ALIGN_POINTER (Header + 1, sizeof (UINTN));
+
+ mUnfixPolicyEntry[Type](Selector, Data);
+
+ //
+ // Update each policy entry according to the content in variable.
+ //
+ mSetBySelf = TRUE;
+ Status = EfiIpSecConfigSetData (
+ &Private->IpSecConfig,
+ Type,
+ Selector,
+ Data,
+ NULL
+ );
+ mSetBySelf = FALSE;
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ Ptr = ALIGN_POINTER ((UINT8 *) Data + Header->Size, sizeof (UINTN));
+ }
+
+ FreePool (Buffer);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Install and Initialize IPsecConfig protocol
+
+ @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish,
+ the pointer of IPsecConfig Protocol implementation will copy
+ into its IPsecConfig member.
+
+ @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully.
+ @retval Others Initializing the IPsecConfig Protocol failed.
+**/
+EFI_STATUS
+IpSecConfigInitialize (
+ IN OUT IPSEC_PRIVATE_DATA *Private
+ )
+{
+ EFI_IPSEC_CONFIG_DATA_TYPE Type;
+
+ CopyMem (
+ &Private->IpSecConfig,
+ &mIpSecConfigInstance,
+ sizeof (EFI_IPSEC_CONFIG_PROTOCOL)
+ );
+
+ //
+ // Initialize the list head of policy database.
+ //
+ for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) {
+ InitializeListHead (&mConfigData[Type]);
+ }
+ //
+ // Restore the content of policy database according to the variable.
+ //
+ IpSecConfigRestore (Private);
+
+ return gBS->InstallMultipleProtocolInterfaces (
+ &Private->Handle,
+ &gEfiIpSecConfigProtocolGuid,
+ &Private->IpSecConfig,
+ NULL
+ );
+}
diff --git a/NetworkPkg/IpSecDxe/IpSecConfigImpl.h b/NetworkPkg/IpSecDxe/IpSecConfigImpl.h
new file mode 100644
index 0000000000..54d43bd01c
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecConfigImpl.h
@@ -0,0 +1,952 @@
+/** @file
+ Definitions related to IPSEC_CONFIG_PROTOCOL implementations.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IPSEC_CONFIG_IMPL_H_
+#define _IPSEC_CONFIG_IMPL_H_
+
+#include <Protocol/IpSec.h>
+#include <Protocol/IpSecConfig.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PrintLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+
+#include "IpSecImpl.h"
+
+#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF
+#define EFI_IPSEC_ANY_PORT 0
+
+#define IPSEC_VAR_ITEM_HEADER_LOGO_BIT 0x80
+#define IPSEC_VAR_ITEM_HEADER_CONTENT_BIT 0x7F
+
+#define IPSECCONFIG_VARIABLE_NAME L"IpSecConfig"
+#define IPSECCONFIG_STATUS_NAME L"IpSecStatus"
+
+#define SIZE_OF_SPD_SELECTOR(x) (UINTN) (sizeof (EFI_IPSEC_SPD_SELECTOR) \
+ + sizeof (EFI_IP_ADDRESS_INFO) * ((x)->LocalAddressCount + (x)->RemoteAddressCount))
+
+#define FIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) - (UINTN) (base))
+#define UNFIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) + (UINTN) (base))
+
+//
+// The data structure used to store the genernall information of IPsec configuration.
+//
+typedef struct {
+ UINT32 VariableCount; // the total number of the IPsecConfig variables.
+ UINT32 VariableSize; // The total size of all IpsecConfig variables.
+ UINT32 SingleVariableSize; // The max size of single variable
+} IP_SEC_VARIABLE_INFO;
+
+typedef struct {
+ EFI_IPSEC_CONFIG_SELECTOR *Selector;
+ VOID *Data;
+ LIST_ENTRY List;
+} IPSEC_COMMON_POLICY_ENTRY;
+
+typedef struct {
+ UINT8 *Ptr;
+ UINTN Size;
+ UINTN Capacity;
+} IPSEC_VARIABLE_BUFFER;
+
+#pragma pack(1)
+typedef struct {
+ UINT8 Type;
+ UINT16 Size;
+} IPSEC_VAR_ITEM_HEADER;
+#pragma pack()
+
+/**
+ The prototype of Copy Source Selector to the Destination Selector.
+
+ @param[in out] DstSel Pointer of Destination Selector. It would be
+ SPD Selector, or SAD Selector or PAD Selector.
+ @param[in] SrcSel Pointer of Source Selector. It would be
+ SPD Selector, or SAD Selector or PAD Selector.
+ @param[in out] Size The size of the Destination Selector. If it
+ is not NULL and its value is less than the size of
+ Source Selector, the value of Source Selector's
+ size will be passed to the caller by this parameter.
+
+ @retval EFI_INVALID_PARAMETER If the Destination or Source Selector is NULL.
+ @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source Selector.
+ @retval EFI_SUCCESS Copy Source Selector to the Destination
+ Selector successfully.
+
+**/
+typedef
+EFI_STATUS
+(*IPSEC_DUPLICATE_SELECTOR) (
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,
+ IN OUT UINTN *Size
+ );
+
+/**
+ It is prototype of compare two Selectors. The Selector would be SPD Selector,
+ or SAD Selector, or PAD selector.
+
+ @param[in] Selector1 Pointer of the first Selector.
+ @param[in] Selector2 Pointer of the second Selector.
+
+ @retval TRUE These two Selectors have the same value in certain fields.
+ @retval FALSE Not all fields have the same value in these two Selectors.
+
+**/
+typedef
+BOOLEAN
+(*IPSEC_COMPARE_SELECTOR) (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ );
+
+/**
+ The prototype of a function to check if the Selector is Zero by its certain fields.
+
+ @param[in] Selector Pointer of the Selector.
+
+ @retval TRUE If the Selector is Zero.
+ @retval FALSE If the Selector is not Zero.
+
+**/
+typedef
+BOOLEAN
+(*IPSEC_IS_ZERO_SELECTOR) (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector
+ );
+
+/**
+ The prototype of a function to fix the value of particular members of the Selector.
+
+ @param[in] Selector Pointer of Selector.
+ @param[in] Data Pointer of Data.
+
+**/
+typedef
+VOID
+(*IPSEC_FIX_POLICY_ENTRY) (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data
+ );
+
+/**
+ It is prototype function to define a routine function by the caller of IpSecVisitConfigData().
+
+ @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE.
+ @param[in] Selector Points to EFI_IPSEC_CONFIG_SELECTOR to be copied
+ to the buffer.
+ @param[in] Data Points to data to be copied to the buffer. The
+ Data type is related to the Type.
+ @param[in] SelectorSize The size of the Selector.
+ @param[in] DataSize The size of the Data.
+ @param[in out] Buffer The buffer to store the Selector and Data.
+
+ @retval EFI_SUCCESS Copied the Selector and Data to a buffer successfully.
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.
+
+**/
+typedef
+EFI_STATUS
+(*IPSEC_COPY_POLICY_ENTRY) (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE Type,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN UINTN SelectorSize,
+ IN UINTN DataSize,
+ IN OUT VOID *Context
+ );
+
+/**
+ Set the security policy information for the EFI IPsec driver.
+
+ The IPsec configuration data has a unique selector/identifier separately to
+ identify a data entry.
+
+ @param[in] Selector Pointer to an entry selector on operated
+ configuration data specified by DataType.
+ A NULL Selector causes the entire specified-type
+ configuration information to be flushed.
+ @param[in] Data The data buffer to be set.
+ @param[in] Context Pointer to one entry selector that describes
+ the expected position the new data entry will
+ be added. If Context is NULL, the new entry will
+ be appended to the end of the database.
+
+ @retval EFI_INVALID_PARAMETER Certain Parameters are not correct. The Parameter
+ requiring a check depends on the Selector type.
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+typedef
+EFI_STATUS
+(*IPSEC_SET_POLICY_ENTRY) (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ A prototype function definition to lookup the data entry from IPsec. Return the configuration
+ value of the specified Entry.
+
+ @param[in] Selector Pointer to an entry selector that is an identifier
+ of the entry.
+ @param[in, out] DataSize On output, the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec
+ configuration data. The type of the data buffer
+ is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+typedef
+EFI_STATUS
+(*IPSEC_GET_POLICY_ENTRY) (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ IN VOID *Data
+ );
+
+/**
+ Compare two SPD Selectors.
+
+ Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/
+ NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the
+ Local Addresses and remote Addresses.
+
+ @param[in] Selector1 Pointer of the first SPD Selector.
+ @param[in] Selector2 Pointer of the second SPD Selector.
+
+ @retval TRUE These two Selectors have the same value in above fields.
+ @retval FALSE Not all of the above fields have the same value in these two Selectors.
+
+**/
+BOOLEAN
+CompareSpdSelector (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ );
+
+
+/**
+ Visit all IPsec Configurations of specified Type and call the caller defined
+ interface.
+
+ @param[in] DataType The specified IPsec Config Data Type.
+ @param[in] Routine The function caller defined.
+ @param[in] Context The data passed to the Routine.
+
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.
+ @retval EFI_SUCCESS This function complete successfully.
+
+**/
+EFI_STATUS
+IpSecVisitConfigData (
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN IPSEC_COPY_POLICY_ENTRY Routine,
+ IN VOID *Context
+ );
+
+
+/**
+ This function is the subfunction of the EFIIpSecConfigSetData.
+
+ This function call IpSecSetVaraible to set the IPsec Configuration into the firmware.
+
+ @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated.
+ @retval EFI_SUCCESS Saved the configration successfully.
+ @retval Others Other errors were found while obtaining the variable.
+
+**/
+EFI_STATUS
+IpSecConfigSave (
+ VOID
+ );
+
+/**
+ Initialize IPsecConfig protocol
+
+ @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish,
+ the pointer of IPsecConfig Protocol implementation will copy
+ into its IPsecConfig member.
+
+ @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully.
+ @retval Others Initializing the IPsecConfig Protocol failed.
+
+**/
+EFI_STATUS
+IpSecConfigInitialize (
+ IN OUT IPSEC_PRIVATE_DATA *Private
+ );
+
+/**
+ Calculate the entire size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed
+ by the pointer members.
+
+ @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA.
+
+ @return The entire size of the specified EFI_IPSEC_SPD_DATA.
+
+**/
+UINTN
+IpSecGetSizeOfEfiSpdData (
+ IN EFI_IPSEC_SPD_DATA *SpdData
+ );
+
+/**
+ Calculate the a entire size of IPSEC_SPD_DATA, which includes the buffer size pointed
+ by the pointer members and the buffer size used by Sa List.
+
+ @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA.
+
+ @return The entire size of IPSEC_SPD_DATA.
+
+**/
+UINTN
+IpSecGetSizeOfSpdData (
+ IN IPSEC_SPD_DATA *SpdData
+ );
+
+/**
+ Copy Source Process Policy to the Destination Process Policy.
+
+ @param[in] Dst Pointer to the Source Process Policy.
+ @param[in] Src Pointer to the Destination Process Policy.
+
+**/
+VOID
+IpSecDuplicateProcessPolicy (
+ IN EFI_IPSEC_PROCESS_POLICY *Dst,
+ IN EFI_IPSEC_PROCESS_POLICY *Src
+ );
+
+/**
+ Compare two SPD Selectors.
+
+ Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/
+ NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the
+ Local Addresses and remote Addresses.
+
+ @param[in] Selector1 Pointer of the first SPD Selector.
+ @param[in] Selector2 Pointer of the second SPD Selector.
+
+ @retval TRUE This two Selector have the same value in above fields.
+ @retval FALSE Not all of the above fields have the same value in these two Selectors.
+
+**/
+BOOLEAN
+CompareSpdSelector (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ );
+
+/**
+ Compare two SA IDs.
+
+ @param[in] Selector1 Pointer of the first SA ID.
+ @param[in] Selector2 Pointer of the second SA ID.
+
+ @retval TRUE This two Selectors have the same SA ID.
+ @retval FALSE This two Selecotrs don't have the same SA ID.
+
+**/
+BOOLEAN
+CompareSaId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ );
+
+/**
+ Compare two PAD IDs.
+
+ @param[in] Selector1 Pointer of the first PAD ID.
+ @param[in] Selector2 Pointer of the second PAD ID.
+
+ @retval TRUE This two Selectors have the same PAD ID.
+ @retval FALSE This two Selecotrs don't have the same PAD ID.
+
+**/
+BOOLEAN
+ComparePadId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector1,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector2
+ );
+
+/**
+ Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount
+ fields.
+
+ @param[in] Selector Pointer of the SPD Selector.
+
+ @retval TRUE If the SPD Selector is Zero.
+ @retval FALSE If the SPD Selector is not Zero.
+
+**/
+BOOLEAN
+IsZeroSpdSelector (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector
+ );
+
+/**
+ Check if the SA ID is Zero by its DestAddress.
+
+ @param[in] Selector Pointer of the SA ID.
+
+ @retval TRUE If the SA ID is Zero.
+ @retval FALSE If the SA ID is not Zero.
+
+**/
+BOOLEAN
+IsZeroSaId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector
+ );
+
+/**
+ Check if the PAD ID is Zero.
+
+ @param[in] Selector Pointer of the PAD ID.
+
+ @retval TRUE If the PAD ID is Zero.
+ @retval FALSE If the PAD ID is not Zero.
+
+**/
+BOOLEAN
+IsZeroPadId (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector
+ );
+
+/**
+ Copy Source SPD Selector to the Destination SPD Selector.
+
+ @param[in, out] DstSel Pointer of Destination SPD Selector.
+ @param[in] SrcSel Pointer of Source SPD Selector.
+ @param[in, out] Size The size of the Destination SPD Selector. If
+ it is not NULL and its value is less than the
+ size of Source SPD Selector, the value of
+ Source SPD Selector's size will be passed to
+ the caller by this parameter.
+
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL.
+ @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source SPD Selector.
+ @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD
+ Selector successfully.
+
+**/
+EFI_STATUS
+DuplicateSpdSelector (
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,
+ IN OUT UINTN *Size
+ );
+
+/**
+ Copy Source SA ID to the Destination SA ID.
+
+ @param[in, out] DstSel Pointer of the Destination SA ID.
+ @param[in] SrcSel Pointer of the Source SA ID.
+ @param[in, out] Size The size of the Destination SA ID. If it
+ not NULL, and its value is less than the size of
+ Source SA ID, the value of Source SA ID's size
+ will be passed to the caller by this parameter.
+
+ @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL.
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID.
+ @retval EFI_SUCCESS Copied Source SA ID to the Destination SA ID successfully.
+
+**/
+EFI_STATUS
+DuplicateSaId (
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,
+ IN OUT UINTN *Size
+ );
+
+/**
+ Copy Source PAD ID to the Destination PAD ID.
+
+ @param[in, out] DstSel Pointer of Destination PAD ID.
+ @param[in] SrcSel Pointer of Source PAD ID.
+ @param[in, out] Size The size of the Destination PAD ID. If it
+ not NULL, and its value less than the size of
+ Source PAD ID, the value of Source PAD ID's size
+ will be passed to the caller by this parameter.
+
+ @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL.
+ @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID.
+ @retval EFI_SUCCESS Copied Source PAD ID to the Destination PAD ID successfully.
+
+**/
+EFI_STATUS
+DuplicatePadId (
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel,
+ IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel,
+ IN OUT UINTN *Size
+ );
+
+/**
+ Fix the value of some members of the SPD Selector.
+
+ This function is called by IpSecCopyPolicyEntry(), which copies the Policy
+ Entry into the Variable. Since some members in SPD Selector are pointers,
+ a physical address to relative address conversion is required before copying
+ this SPD entry into the variable.
+
+ @param[in] Selector Pointer of SPD Selector.
+ @param[in, out] Data Pointer of SPD Data.
+
+**/
+VOID
+FixSpdEntry (
+ IN EFI_IPSEC_SPD_SELECTOR *Selector,
+ IN OUT EFI_IPSEC_SPD_DATA *Data
+ );
+
+/**
+ Fix the value of some members of SA ID.
+
+ This function is called by IpSecCopyPolicyEntry(), which copies the Policy
+ Entry into the Variable. Since some members in SA ID are pointers,
+ a physical address to relative address conversion is required before copying
+ this SAD into the variable.
+
+ @param[in] SaId Pointer of SA ID.
+ @param[in, out] Data Pointer of SA Data.
+
+**/
+VOID
+FixSadEntry (
+ IN EFI_IPSEC_SA_ID *SaId,
+ IN OUT EFI_IPSEC_SA_DATA *Data
+ );
+
+/**
+ Fix the value of some members of PAD ID.
+
+ This function is called by IpSecCopyPolicyEntry(), which copy the Policy
+ Entry into the Variable. Since some members in PAD ID are pointers,
+ a physical address to relative address conversion is required before copying
+ this PAD into the variable.
+
+ @param[in] PadId Pointer of PAD ID.
+ @param[in, out] Data Pointer of PAD Data.
+
+**/
+VOID
+FixPadEntry (
+ IN EFI_IPSEC_PAD_ID *PadId,
+ IN OUT EFI_IPSEC_PAD_DATA *Data
+ );
+
+/**
+ Recover the value of some members of SPD Selector.
+
+ This function is corresponding to FixSpdEntry(). It recovers the value of members
+ of SPD Selector which fix by the FixSpdEntry().
+
+ @param[in, out] Selector Pointer of SPD Selector.
+ @param[in, out] Data Pointer of SPD Data.
+
+**/
+VOID
+UnfixSpdEntry (
+ IN OUT EFI_IPSEC_SPD_SELECTOR *Selector,
+ IN OUT EFI_IPSEC_SPD_DATA *Data
+ );
+
+
+/**
+ Recover the value of some members of SA ID.
+
+ This function is corresponding to FixSadEntry(). It recovers the value of members
+ of SAD ID which fix by the FixSadEntry().
+
+ @param[in, out] SaId Pointer of SAD ID
+ @param[in, out] Data Pointer of SAD Data.
+
+**/
+VOID
+UnfixSadEntry (
+ IN OUT EFI_IPSEC_SA_ID *SaId,
+ IN OUT EFI_IPSEC_SA_DATA *Data
+ );
+
+/**
+ Recover the value of some members of PAD ID.
+
+ This function is corresponding to FixPadEntry(). It recovers the value of members
+ of PAD ID which fix by the FixPadEntry().
+
+ @param[in] PadId Pointer of PAD ID
+ @param[in, out] Data Pointer of PAD Data.
+
+**/
+VOID
+UnfixPadEntry (
+ IN EFI_IPSEC_PAD_ID *PadId,
+ IN OUT EFI_IPSEC_PAD_DATA *Data
+ );
+
+/**
+ Set the security policy information for the EFI IPsec driver.
+
+ The IPsec configuration data has a unique selector/identifier separately to
+ identify a data entry.
+
+ @param[in] Selector Pointer to an entry selector on operated
+ configuration data specified by DataType.
+ A NULL Selector causes the entire specified-type
+ configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure
+ of the data buffer should be EFI_IPSEC_SPD_DATA.
+ @param[in] Context Pointer to one entry selector that describes
+ the expected position the new data entry will
+ be added. If Context is NULL,the new entry will
+ be appended the end of database.
+
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - Selector is not NULL and its LocalAddress
+ is NULL or its RemoteAddress is NULL.
+ - Data is not NULL, its Action is Protected,
+ and its policy is NULL.
+ - Data is not NULL and its Action is not protected
+ and its policy is not NULL.
+ - The Action of Data is Protected, its policy
+ mode is Tunnel, and its tunnel option is NULL.
+ - The Action of Data is protected, its policy
+ mode is not Tunnel, and it tunnel option is not NULL.
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+EFI_STATUS
+SetSpdEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ Set the security association information for the EFI IPsec driver.
+
+ The IPsec configuration data has a unique selector/identifier separately to
+ identify a data entry.
+
+ @param[in] Selector Pointer to an entry selector on operated
+ configuration data specified by DataType.
+ A NULL Selector causes the entire specified-type
+ configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure
+ of the data buffer should be EFI_IPSEC_SA_DATA.
+ @param[in] Context Pointer to one entry selector which describes
+ the expected position the new data entry will
+ be added. If Context is NULL,the new entry will
+ be appended to the end of database.
+
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+EFI_STATUS
+SetSadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ Set the peer authorization configuration information for the EFI IPsec driver.
+
+ The IPsec configuration data has a unique selector/identifier separately to
+ identify a data entry.
+
+ @param[in] Selector Pointer to an entry selector on operated
+ configuration data specified by DataType.
+ A NULL Selector causes the entire specified-type
+ configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure
+ of the data buffer should be EFI_IPSEC_PAD_DATA.
+ @param[in] Context Pointer to one entry selector that describes
+ the expected position where the new data entry will
+ be added. If Context is NULL, the new entry will
+ be appended the end of database.
+
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+
+**/
+EFI_STATUS
+SetPadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ This function looks up the data entry from IPsec SPD, and returns the configuration
+ value of the specified SPD Entry.
+
+ @param[in] Selector Pointer to an entry selector which is an identifier
+ of the SPD entry.
+ @param[in, out] DataSize On output the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec
+ configuration data. The type of the data buffer
+ is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero.
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+GetSpdEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ );
+
+/**
+ This function looks up the data entry from IPsec SAD and returns the configuration
+ value of the specified SAD Entry.
+
+ @param[in] Selector Pointer to an entry selector that is an identifier
+ of the SAD entry.
+ @param[in, out] DataSize On output, the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec
+ configuration data. This type of the data buffer
+ is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+GetSadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ );
+
+/**
+ This function looks up the data entry from IPsec PADand returns the configuration
+ value of the specified PAD Entry.
+
+ @param[in] Selector Pointer to an entry selector that is an identifier
+ of the PAD entry.
+ @param[in, out] DataSize On output the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec
+ configuration data. This type of the data buffer
+ is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+GetPadEntry (
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ );
+
+/**
+ Return the configuration value for the EFI IPsec driver.
+
+ This function lookup the data entry from IPsec database or IKEv2 configuration
+ information. The expected data type and unique identification are described in
+ DataType and Selector parameters.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to retrieve.
+ @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec
+ configuration data entry.
+ @param[in, out] DataSize On output the size of data returned in Data.
+ @param[out] Data The buffer to return the contents of the IPsec configuration data.
+ The type of the data buffer is associated with the DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:
+ - This is NULL.
+ - Selector is NULL.
+ - DataSize is NULL.
+ - Data is NULL and *DataSize is not zero
+ @retval EFI_NOT_FOUND The configuration data specified by Selector is not found.
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been
+ updated with the size needed to complete the request.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigGetData (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ );
+
+/**
+ Set the security association, security policy and peer authorization configuration
+ information for the EFI IPsec driver.
+
+ This function is used to set the IPsec configuration information of type DataType for
+ the EFI IPsec driver.
+ The IPsec configuration data has a unique selector/identifier separately to identify
+ a data entry. The selector structure depends on DataType's definition.
+ Using SetData() with a Data of NULL causes the IPsec configuration data entry identified
+ by DataType and Selector to be deleted.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to be set.
+ @param[in] Selector Pointer to an entry selector on operated configuration data
+ specified by DataType. A NULL Selector causes the entire
+ specified-type configuration information to be flushed.
+ @param[in] Data The data buffer to be set. The structure of the data buffer is
+ associated with the DataType.
+ @param[in] InsertBefore Pointer to one entry selector which describes the expected
+ position the new data entry will be added. If InsertBefore is NULL,
+ the new entry will be appended the end of database.
+
+ @retval EFI_SUCCESS The specified configuration entry data was set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.
+ @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigSetData (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_IPSEC_CONFIG_SELECTOR *Selector,
+ IN VOID *Data,
+ IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL
+ );
+
+/**
+ Enumerates the current selector for IPsec configuration data entry.
+
+ This function is called multiple times to retrieve the entry Selector in IPsec
+ configuration database. On each call to GetNextSelector(), the next entry
+ Selector are retrieved into the output interface.
+
+ If the entire IPsec configuration database has been iterated, the error
+ EFI_NOT_FOUND is returned.
+ If the Selector buffer is too small for the next Selector copy, an
+ EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect
+ the size of buffer needed.
+
+ On the initial call to GetNextSelector() to start the IPsec configuration database
+ search, a pointer to the buffer with all zero value is passed in Selector. Calls
+ to SetData() between calls to GetNextSelector may produce unpredictable results.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of IPsec configuration data to retrieve.
+ @param[in, out] SelectorSize The size of the Selector buffer.
+ @param[in, out] Selector On input, supplies the pointer to last Selector that was
+ returned by GetNextSelector().
+ On output, returns one copy of the current entry Selector
+ of a given DataType.
+
+ @retval EFI_SUCCESS The specified configuration data was obtained successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE:
+ - This is NULL.
+ - SelectorSize is NULL.
+ - Selector is NULL.
+ @retval EFI_NOT_FOUND The next configuration data entry was not found.
+ @retval EFI_UNSUPPORTED The specified DataType is not supported.
+ @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter
+ has been updated with the size needed to complete the search
+ request.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigGetNextSelector (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN OUT UINTN *SelectorSize,
+ IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector
+ );
+
+/**
+ Register an event that is to be signaled whenever a configuration process on the
+ specified IPsec configuration information is done.
+
+ The register function is not surpport now and always returns EFI_UNSUPPORTED.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The type of data to be registered the event for.
+ @param[in] Event The event to be registered.
+
+ @retval EFI_SUCCESS The event is registered successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL, or Event is NULL.
+ @retval EFI_ACCESS_DENIED The Event is already registered for the DataType.
+ @retval EFI_UNSUPPORTED The notify registration unsupported, or the specified
+ DataType is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigRegisterNotify (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ );
+
+
+/**
+ Remove the specified event that was previously registered on the specified IPsec
+ configuration data.
+
+ This function is not supported now and always returns EFI_UNSUPPORTED.
+
+ @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance.
+ @param[in] DataType The configuration data type to remove the registered event for.
+ @param[in] Event The event to be unregistered.
+
+ @retval EFI_SUCCESS The event was removed successfully.
+ @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the
+ database.
+ @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL.
+ @retval EFI_UNSUPPORTED The notify registration unsupported or the specified
+ DataType is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiIpSecConfigUnregisterNotify (
+ IN EFI_IPSEC_CONFIG_PROTOCOL *This,
+ IN EFI_IPSEC_CONFIG_DATA_TYPE DataType,
+ IN EFI_EVENT Event
+ );
+
+#endif
diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.c b/NetworkPkg/IpSecDxe/IpSecCryptIo.c
new file mode 100644
index 0000000000..7011f98b06
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecCryptIo.c
@@ -0,0 +1,133 @@
+/** @file
+ Common operation for Security.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecCryptIo.h"
+//
+// Alogrithm's informations for the Encrypt/Decrpt Alogrithm.
+//
+ENCRYPT_ALGORITHM mIpsecEncryptAlgorithmList[IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE] = {
+ {EFI_IPSEC_EALG_NULL, 0, 0, 1, NULL, NULL, NULL, NULL},
+ {(UINT8)-1, 0, 0, 0, NULL, NULL, NULL, NULL}
+};
+//
+// Alogrithm's informations for the Authentication algorithm
+//
+AUTH_ALGORITHM mIpsecAuthAlgorithmList[IPSEC_AUTH_ALGORITHM_LIST_SIZE] = {
+ {EFI_IPSEC_AALG_NONE, 0, 0, 0, NULL, NULL, NULL, NULL},
+ {EFI_IPSEC_AALG_NULL, 0, 0, 0, NULL, NULL, NULL, NULL},
+ {(UINT8)-1, 0, 0, 0, NULL, NULL, NULL, NULL}
+};
+
+
+/**
+ Get the block size of encrypt alogrithm. The block size is based on the algorithm used.
+
+ @param[in] AlgorithmId The encrypt algorithm ID.
+
+ @return The value of block size.
+
+**/
+UINTN
+IpSecGetEncryptBlockSize (
+ IN UINT8 AlgorithmId
+ )
+{
+ UINT8 Index;
+
+ for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {
+ if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {
+ //
+ // The BlockSize is same with IvSize.
+ //
+ return mIpsecEncryptAlgorithmList[Index].BlockSize;
+ }
+ }
+
+ return (UINTN) -1;
+}
+
+/**
+ Get the IV size of encrypt alogrithm. The IV size is based on the algorithm used.
+
+ @param[in] AlgorithmId The encrypt algorithm ID.
+
+ @return The value of IV size.
+
+**/
+UINTN
+IpSecGetEncryptIvLength (
+ IN UINT8 AlgorithmId
+ )
+{
+ UINT8 Index;
+
+ for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) {
+ if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) {
+ //
+ // The BlockSize is same with IvSize.
+ //
+ return mIpsecEncryptAlgorithmList[Index].IvLength;
+ }
+ }
+
+ return (UINTN) -1;
+}
+
+/**
+ Get the ICV size of Authenticaion alogrithm. The ICV size is based on the algorithm used.
+
+ @param[in] AuthAlgorithmId The Authentication algorithm ID.
+
+ @return The value of ICV size.
+
+**/
+UINTN
+IpSecGetIcvLength (
+ IN UINT8 AuthAlgorithmId
+ )
+{
+ UINT8 Index;
+ for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) {
+ if (AuthAlgorithmId == mIpsecAuthAlgorithmList[Index].AlgorithmId) {
+ return mIpsecAuthAlgorithmList[Index].IcvLength;
+ }
+ }
+ return (UINTN) -1;
+}
+
+/**
+ Generate a random data for IV. If the IvSize is zero, not needed to create
+ IV and return EFI_SUCCESS.
+
+ @param[in] IvBuffer The pointer of the IV buffer.
+ @param[in] IvSize The IV size.
+
+ @retval EFI_SUCCESS Create a random data for IV.
+
+**/
+EFI_STATUS
+IpSecGenerateIv (
+ IN UINT8 *IvBuffer,
+ IN UINTN IvSize
+ )
+{
+ if (IvSize != 0) {
+ //
+ //TODO: return CryptGenerateRandom (IvBuffer, IvSize);
+ //
+ return EFI_SUCCESS;
+ }
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/IpSecDxe/IpSecCryptIo.h b/NetworkPkg/IpSecDxe/IpSecCryptIo.h
new file mode 100644
index 0000000000..d883a2ef72
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecCryptIo.h
@@ -0,0 +1,322 @@
+/** @file
+ Definition related to the Security operation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _EFI_IPSEC_CRYPTIO_H_
+#define _EFI_IPSEC_CRYPTIO_H_
+
+#include <Protocol/IpSecConfig.h>
+#include <Library/DebugLib.h>
+
+#define IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE 2
+#define IPSEC_AUTH_ALGORITHM_LIST_SIZE 3
+
+/**
+ Prototype of Hash GetContextSize.
+
+ Retrieves the size, in bytes, of the context buffer required.
+
+ @return The size, in bytes, of the context buffer required.
+
+**/
+typedef
+UINTN
+(EFIAPI *CPL_HASH_GETCONTEXTSIZE) (
+ VOID
+ );
+
+/**
+ Prototype of Hash Operation Initiating.
+
+ Initialization with a new context.
+
+
+ @param[in,out] Context Input Context.
+
+ @retval TRUE Initialization Successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *CPL_HASH_INIT) (
+ IN OUT VOID *Context
+ );
+
+/**
+ Prototype of HASH update.
+ Hash update operation. Continue an Hash message digest operation, processing
+ another message block, and updating the Hash context.
+
+ If Context is NULL, then ASSERT().
+ If Data is NULL, then ASSERT().
+
+ @param[in,out] Context The Specified Context.
+ @param[in,out] Data The Input Data to hash.
+ @param[in] DataLength The length, in bytes, of Data.
+
+ @retval TRUE Update data successfully.
+ @retval FALSE The Context has been finalized.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI *CPL_HASH_UPDATE) (
+ IN OUT VOID *Context,
+ IN CONST VOID *Data,
+ IN UINTN DataLength
+ );
+
+/**
+ Prototype of Hash finallization.
+ Terminate a Hash message digest operation and output the message digest.
+
+ If Context is NULL, then ASSERT().
+ If HashValue is NULL, then ASSERT().
+
+ @param[in,out] Context The specified Context.
+ @param[out] HashValue Pointer to a 16-byte message digest output buffer.
+
+ @retval TRUE Finalized successfully.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI *CPL_HASH_FINAL) (
+ IN OUT VOID *Context,
+ OUT UINT8 *HashValue
+ );
+
+/**
+ Prototype of Cipher GetContextSize.
+
+ Retrieves the size, in bytes, of the context buffer required.
+
+ @return The size, in bytes, of the context buffer required.
+
+**/
+typedef
+UINTN
+(EFIAPI *CPL_CIPHER_GETCONTEXTSIZE) (
+ VOID
+ );
+
+/**
+ Prototype of Cipher initiation.
+ Intializes the user-supplied key as the specifed context (key materials) for both
+ encryption and decryption operations.
+
+ If Context is NULL, then ASSERT().
+ If Key is NULL, then generate random key for usage.
+
+ @param[in,out] Context The specified Context.
+ @param[in] Key User-supplied TDES key (64/128/192 bits).
+ @param[in] KeyBits Key length in bits.
+
+ @retval TRUE TDES Initialization was successful.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI *CPL_CIPHER_INIT) (
+ IN OUT VOID *Context,
+ IN CONST UINT8 *Key,
+ IN CONST UINTN KeyBits
+ );
+
+
+/**
+ Prototype of Cipher encryption.
+ Encrypts plaintext message with the specified cipher.
+
+ If Context is NULL, then ASSERT().
+ if InData is NULL, then ASSERT().
+ If Size of input data is not multiple of Cipher algorithm related block size,
+ then ASSERT().
+
+ @param[in] Context The specified Context.
+ @param[in] InData The input plaintext data to be encrypted.
+ @param[out] OutData The resultant encrypted ciphertext.
+ @param[in] DataLength Length of input data in bytes.
+
+ @retval TRUE Encryption successful.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI *CPL_CIPHER_ENCRYPT) (
+ IN VOID *Context,
+ IN CONST UINT8 *InData,
+ OUT UINT8 *OutData,
+ IN CONST UINTN DataLength
+ );
+
+
+/**
+ Prototype of Cipher decryption.
+ Decrypts cipher message with specified cipher.
+
+ If Context is NULL, then ASSERT().
+ if InData is NULL, then ASSERT().
+ If Size of input data is not a multiple of a certaion block size , then ASSERT().
+
+ @param[in] Context The specified Context.
+ @param[in] InData The input ciphertext data to be decrypted.
+ @param[out] OutData The resultant decrypted plaintext.
+ @param[in] DataLength Length of input data in bytes.
+
+ @retval TRUE Decryption successful.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI *CPL_CIPHER_DECRYPT) (
+ IN CONST VOID *Context,
+ IN CONST UINT8 *InData,
+ OUT UINT8 *OutData,
+ IN CONST UINTN DataLength
+ );
+
+//
+// The struct used to store the informatino and operation of Cipher algorithm.
+//
+typedef struct _ENCRYPT_ALGORITHM {
+//
+// The ID of the Algorithm
+//
+UINT8 AlgorithmId;
+//
+// The Key length of the Algorithm
+//
+UINTN KeyLength;
+//
+// Iv Size of the Algorithm
+//
+UINTN IvLength;
+//
+// The Block Size of the Algorithm
+//
+UINTN BlockSize;
+//
+// The Function pointer of GetContextSize.
+//
+CPL_CIPHER_GETCONTEXTSIZE CipherGetContextSize;
+//
+// The Function pointer of Cipher intitiaion.
+//
+CPL_CIPHER_INIT CipherInitiate;
+//
+// The Function pointer of Cipher Encryption.
+//
+CPL_CIPHER_ENCRYPT CipherEncrypt;
+//
+// The Function pointer of Cipher Decrption.
+//
+CPL_CIPHER_DECRYPT CipherDecrypt;
+} ENCRYPT_ALGORITHM;
+
+//
+// The struct used to store the informatino and operation of Autahentication algorithm.
+//
+typedef struct _AUTH_ALGORITHM {
+ //
+ // ID of the Algorithm
+ //
+ UINT8 AlgorithmId;
+ //
+ // The Key length of the Algorithm
+ //
+ UINTN KeyLength;
+ //
+ // The ICV length of the Algorithm
+ //
+ UINTN IcvLength;
+ //
+ // The block size of the Algorithm
+ //
+ UINTN BlockSize;
+ //
+ // The function pointer of GetContextSize.
+ //
+ CPL_HASH_GETCONTEXTSIZE HashGetContextSize;
+ //
+ // The function pointer of Initiatoion
+ //
+ CPL_HASH_INIT HashInitiate;
+ //
+ // The function pointer of Hash Update.
+ //
+ CPL_HASH_UPDATE HashUpdate;
+ //
+ // The fucntion pointer of Hash Final
+ //
+ CPL_HASH_FINAL HashFinal;
+} AUTH_ALGORITHM;
+
+/**
+ Get the IV size of encrypt alogrithm. IV size is different from different algorithm.
+
+ @param[in] AlgorithmId The encrypt algorithm ID.
+
+ @return The value of IV size.
+
+**/
+UINTN
+IpSecGetEncryptIvLength (
+ IN UINT8 AlgorithmId
+ );
+
+/**
+ Get the block size of encrypt alogrithm. Block size is different from different algorithm.
+
+ @param[in] AlgorithmId The encrypt algorithm ID.
+
+ @return The value of block size.
+
+**/
+UINTN
+IpSecGetEncryptBlockSize (
+ IN UINT8 AlgorithmId
+ );
+
+/**
+ Get the ICV size of Authenticaion alogrithm. ICV size is different from different algorithm.
+
+ @param[in] AuthAlgorithmId The Authentication algorithm ID.
+
+ @return The value of ICV size.
+
+**/
+UINTN
+IpSecGetIcvLength (
+ IN UINT8 AuthAlgorithmId
+ );
+
+/**
+ Generate a random data for IV. If the IvSize is zero, not needed to create
+ IV and return EFI_SUCCESS.
+
+ @param[in] IvBuffer The pointer of the IV buffer.
+ @param[in] IvSize The IV size.
+
+ @retval EFI_SUCCESS Create random data for IV.
+
+**/
+EFI_STATUS
+IpSecGenerateIv (
+ IN UINT8 *IvBuffer,
+ IN UINTN IvSize
+ );
+
+#endif
+
diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.c b/NetworkPkg/IpSecDxe/IpSecDebug.c
new file mode 100644
index 0000000000..8a5811b960
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDebug.c
@@ -0,0 +1,172 @@
+/** @file
+ Interface of IPsec printing debug information.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecImpl.h"
+#include "IpSecDebug.h"
+
+//
+// The print title for IKEv1 variety phase.
+//
+CHAR8 *mStateStr[] = {
+ "IKEv1_MAIN_1",
+ "IKEv1_MAIN_2",
+ "IKEv1_MAIN_3",
+ "IKEv1_MAIN_ESTABLISHED",
+ "IKEv1_QUICK_1",
+ "IKEv1_QUICK_2",
+ "IKEv1_QUICK_ESTABLISHED"
+};
+//
+// The print title for IKEv1 variety Exchagne.
+//
+CHAR8 *mExchangeStr[] = {
+ "IKEv1 Main Exchange",
+ "IKEv1 Info Exchange",
+ "IKEv1 Quick Exchange",
+ "IKEv1 Unknown Exchange"
+};
+
+//
+// The print title for IKEv1 variety Payload.
+//
+CHAR8 *mPayloadStr[] = {
+ "IKEv1 None Payload",
+ "IKEv1 SA Payload",
+ "IKEv1 Proposal Payload",
+ "IKEv1 Transform Payload",
+ "IKEv1 KE Payload",
+ "IKEv1 ID Payload",
+ "IKEv1 Certificate Payload",
+ "IKEv1 Certificate Request Payload",
+ "IKEv1 Hash Payload",
+ "IKEv1 Signature Payload",
+ "IKEv1 Nonce Payload",
+ "IKEv1 Notify Payload",
+ "IKEv1 Delete Payload",
+ "IKEv1 Vendor Payload"
+};
+
+/**
+ Print the IP address.
+
+ @param[in] Level Debug print error level. Pass to DEBUG().
+ @param[in] Ip Point to a specified IP address.
+ @param[in] IpVersion The IP Version.
+
+**/
+VOID
+IpSecDumpAddress (
+ IN UINTN Level,
+ IN EFI_IP_ADDRESS *Ip,
+ IN UINT8 IpVersion
+ )
+{
+ if (IpVersion == IP_VERSION_6) {
+ DEBUG (
+ (Level,
+ "%x%x:%x%x:%x%x:%x%x",
+ Ip->v6.Addr[0],
+ Ip->v6.Addr[1],
+ Ip->v6.Addr[2],
+ Ip->v6.Addr[3],
+ Ip->v6.Addr[4],
+ Ip->v6.Addr[5],
+ Ip->v6.Addr[6],
+ Ip->v6.Addr[7])
+ );
+ DEBUG (
+ (Level,
+ ":%x%x:%x%x:%x%x:%x%x\n",
+ Ip->v6.Addr[8],
+ Ip->v6.Addr[9],
+ Ip->v6.Addr[10],
+ Ip->v6.Addr[11],
+ Ip->v6.Addr[12],
+ Ip->v6.Addr[13],
+ Ip->v6.Addr[14],
+ Ip->v6.Addr[15])
+ );
+ } else {
+ DEBUG (
+ (Level,
+ "%d.%d.%d.%d\n",
+ Ip->v4.Addr[0],
+ Ip->v4.Addr[1],
+ Ip->v4.Addr[2],
+ Ip->v4.Addr[3])
+ );
+ }
+
+}
+
+/**
+ Print IKEv1 Current states.
+
+ @param[in] Previous The Previous state of IKEv1.
+ @param[in] Current The current state of IKEv1.
+
+**/
+VOID
+IpSecDumpState (
+ IN UINT32 Previous,
+ IN UINT32 Current
+ )
+{
+ if (Previous == Current) {
+ DEBUG ((DEBUG_INFO, "\n****Current state is %a\n", mStateStr[Previous]));
+ } else {
+ DEBUG ((DEBUG_INFO, "\n****Change state from %a to %a\n", mStateStr[Previous], mStateStr[Current]));
+ }
+
+}
+
+/**
+ Print the buffer in form of Hex.
+
+ @param[in] Title The strings to be printed before the data of the buffer.
+ @param[in] Data Points to buffer to be printed.
+ @param[in] DataSize The size of the buffer to be printed.
+
+**/
+VOID
+IpSecDumpBuf (
+ IN CHAR8 *Title,
+ IN UINT8 *Data,
+ IN UINTN DataSize
+ )
+{
+ UINTN Index;
+ UINTN DataIndex;
+ UINTN BytesRemaining;
+ UINTN BytesToPrint;
+
+ DataIndex = 0;
+ BytesRemaining = DataSize;
+
+ DEBUG ((DEBUG_INFO, "==%a %d bytes==\n", Title, DataSize));
+
+ while (BytesRemaining > 0) {
+
+ BytesToPrint = (BytesRemaining > IPSEC_DEBUG_BYTE_PER_LINE) ? IPSEC_DEBUG_BYTE_PER_LINE : BytesRemaining;
+
+ for (Index = 0; Index < BytesToPrint; Index++) {
+ DEBUG ((DEBUG_INFO, " 0x%02x,", Data[DataIndex++]));
+ }
+
+ DEBUG ((DEBUG_INFO, "\n"));
+ BytesRemaining -= BytesToPrint;
+ }
+
+}
diff --git a/NetworkPkg/IpSecDxe/IpSecDebug.h b/NetworkPkg/IpSecDxe/IpSecDebug.h
new file mode 100644
index 0000000000..0e6e6811c5
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDebug.h
@@ -0,0 +1,102 @@
+/** @file
+ The definition of functions and MACROs used for IPsec debug information print.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _EFI_IPSEC_DEBUG_H_
+#define _EFI_IPSEC_DEBUG_H_
+
+#include <Library/DebugLib.h>
+
+#define IPSEC_DUMP_ADDRESS(Level, Ip, Version) IpSecDumpAddress (Level, Ip, Version)
+#define IPSEC_DUMP_STATE(Previous, Current) IpSecDumpState (Previous, Current)
+#define IPSEC_DUMP_PACKET(Packet, Direction, IpVersion) IpSecDumpPacket (Packet, Direction, IpVersion)
+#define IPSEC_DUMP_PAYLOAD(IkePayload) IpSecDumpPayload (IkePayload)
+#define IPSEC_DUMP_BUF(Title, Data, DataSize) IpSecDumpBuf (Title, Data, DataSize)
+
+#define IPSEC_DEBUG_BYTE_PER_LINE 8
+
+
+/**
+ Print the IP address.
+
+ @param[in] Level Debug print error level. Pass to DEBUG().
+ @param[in] Ip Point to specified IP address.
+ @param[in] IpVersion The IP Version.
+
+**/
+VOID
+IpSecDumpAddress (
+ IN UINTN Level,
+ IN EFI_IP_ADDRESS *Ip,
+ IN UINT8 IpVersion
+ );
+
+/**
+ Print IKEv1 Current states.
+
+ @param[in] Previous The Previous state of IKEv1.
+ @param[in] Current The current state of IKEv1.
+
+**/
+VOID
+IpSecDumpState (
+ IN UINT32 Previous,
+ IN UINT32 Current
+ );
+
+/**
+ Print the Ike Packet.
+
+ @param[in] Packet Point to IKE packet to be printed.
+ @param[in] Direction Point to the IKE packet is inbound or outbound.
+ @param[in] IpVersion Specified IP Version.
+
+**/
+/*
+VOID
+IpSecDumpPacket (
+ IN IKE_PACKET *Packet,
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,
+ IN UINT8 IpVersion
+ );
+*/
+
+/**
+ Print the IKE Paylolad.
+
+ @param[in] IkePayload Points to the payload to be printed.
+
+**/
+/*
+VOID
+IpSecDumpPayload (
+ IN IKE_PAYLOAD *IkePayload
+ );
+*/
+/**
+ Print the buffer in form of Hex.
+
+ @param[in] Title The strings to be printed before the data of the buffer.
+ @param[in] Data Points to the buffer to be printed.
+ @param[in] DataSize The size of the buffer to be printed.
+
+**/
+VOID
+IpSecDumpBuf (
+ IN CHAR8 *Title,
+ IN UINT8 *Data,
+ IN UINTN DataSize
+ );
+
+#endif
diff --git a/NetworkPkg/IpSecDxe/IpSecDriver.c b/NetworkPkg/IpSecDxe/IpSecDriver.c
new file mode 100644
index 0000000000..b38f2a9452
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDriver.c
@@ -0,0 +1,282 @@
+/** @file
+ Driver Binding Protocol for IPsec Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Library/UdpIoLib.h>
+#include "IpSecConfigImpl.h"
+#include "IpSecDebug.h"
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ //
+ //TODO: Add Udp4Protocol and Udp6Protocol testing.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.
+ Currently not implemented.
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ //
+ //TODO: Add Udp4Io and Udp6Io creation for the IKE.
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of a device to stop the driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of
+ children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCES This driver removed ControllerHandle.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ //
+ //TODO: Add UdpIo4 and UdpIo6 destruction when the Udp driver unload or stop.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL gIpSecDriverBinding = {
+ IpSecDriverBindingSupported,
+ IpSecDriverBindingStart,
+ IpSecDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ This is a callback function when the mIpSecInstance.DisabledEvent is signaled.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context Pointer to the notification function's context.
+
+**/
+VOID
+EFIAPI
+IpSecCleanupAllSa (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IPSEC_PRIVATE_DATA *Private;
+ UINT8 Value;
+ EFI_STATUS Status;
+
+ Private = (IPSEC_PRIVATE_DATA *) Context;
+
+ //
+ // Set the Status Variable
+ //
+ Value = IPSEC_STATUS_DISABLED;
+ Status = gRT->SetVariable (
+ IPSECCONFIG_STATUS_NAME,
+ &gEfiIpSecConfigProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ sizeof (Value),
+ &Value
+ );
+ if (!EFI_ERROR (Status)) {
+ Private->IpSec.DisabledFlag = TRUE;
+ }
+
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including
+ both device drivers and bus drivers.
+
+ The entry point for IPsec driver which installs the driver binding,
+ component name protocol, IPsec Config protcolon, and IPsec protocol in
+ its ImageHandle.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_ALREADY_STARTED The IPsec driver has been already loaded.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The operation is failed.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ IPSEC_PRIVATE_DATA *Private;
+ EFI_IPSEC_PROTOCOL *IpSec;
+
+ //
+ // Check whether ipsec protocol has already been installed.
+ //
+ Status = gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &IpSec);
+
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "_ModuleEntryPoint: IpSec has been already loaded\n"));
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, (VOID **) &mDpc);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to locate EfiDpcProtocol\n"));
+ goto ON_EXIT;
+ }
+
+ Private = AllocateZeroPool (sizeof (IPSEC_PRIVATE_DATA));
+
+ if (Private == NULL) {
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to allocate private data\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ //
+ // Create disable event to cleanup all sa when ipsec disabled by user.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ IpSecCleanupAllSa,
+ Private,
+ &mIpSecInstance.DisabledEvent
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to create disable event\n"));
+ goto ON_FREE_PRIVATE;
+ }
+
+ Private->Signature = IPSEC_PRIVATE_DATA_SIGNATURE;
+ Private->ImageHandle = ImageHandle;
+ CopyMem (&Private->IpSec, &mIpSecInstance, sizeof (EFI_IPSEC_PROTOCOL));
+
+ //
+ // Initilize Private's members. Thess members is used for IKE.
+ //
+ InitializeListHead (&Private->Udp4List);
+ InitializeListHead (&Private->Udp6List);
+ InitializeListHead (&Private->Ikev1SessionList);
+ InitializeListHead (&Private->Ikev1EstablishedList);
+ InitializeListHead (&Private->Ikev2SessionList);
+ InitializeListHead (&Private->Ikev2EstablishedList);
+
+ //
+ // Initialize the ipsec config data and restore it from variable.
+ //
+ Status = IpSecConfigInitialize (Private);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to initialize IpSecConfig\n"));
+ goto ON_CLOSE_EVENT;
+ }
+ //
+ // Install ipsec protocol which is used by ip driver to process ipsec header.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Private->Handle,
+ &gEfiIpSecProtocolGuid,
+ &Private->IpSec,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_UNINSTALL_CONFIG;
+ }
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gIpSecDriverBinding,
+ ImageHandle,
+ &gIpSecComponentName,
+ &gIpSecComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_UNINSTALL_CONFIG;
+ }
+
+ return Status;
+
+ON_UNINSTALL_CONFIG:
+ gBS->UninstallProtocolInterface (
+ Private->Handle,
+ &gEfiIpSecConfigProtocolGuid,
+ &Private->IpSecConfig
+ );
+ON_CLOSE_EVENT:
+ gBS->CloseEvent (mIpSecInstance.DisabledEvent);
+ mIpSecInstance.DisabledEvent = NULL;
+ON_FREE_PRIVATE:
+ FreePool (Private);
+ON_EXIT:
+ return Status;
+}
+
diff --git a/NetworkPkg/IpSecDxe/IpSecDxe.inf b/NetworkPkg/IpSecDxe/IpSecDxe.inf
new file mode 100644
index 0000000000..250ef1cdca
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecDxe.inf
@@ -0,0 +1,63 @@
+## @file
+# Component description file for IpSec module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = IpSecDxe
+ FILE_GUID = EE8367C0-A1D6-4565-8F89-EF628547B722
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = IpSecDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ IpSecConfigImpl.c
+ IpSecConfigImpl.h
+ IpSecCryptIo.h
+ IpSecCryptIo.c
+ IpSecDebug.h
+ ComponentName.c
+ IpSecImpl.c
+ IpSecDebug.c
+ IpSecSaEngine.c
+ IpSecDriver.c
+ IpSecImpl.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ MemoryAllocationLib
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+ PrintLib
+ DpcLib
+ NetLib
+
+[Protocols]
+ gEfiIp4ConfigProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIpSecConfigProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEfiIpSecProtocolGuid # PROTOCOL ALWAYS_PRODUCED
diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.c b/NetworkPkg/IpSecDxe/IpSecImpl.c
new file mode 100644
index 0000000000..15884ae403
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecImpl.c
@@ -0,0 +1,856 @@
+/** @file
+ The implementation of IPsec Protocol
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecConfigImpl.h"
+
+EFI_IPSEC_PROTOCOL mIpSecInstance = { IpSecProcess, NULL, TRUE };
+
+extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];
+
+/**
+ Check if the specified Address is the Valid Address Range.
+
+ This function checks if the bytes after prefixed length are all Zero in this
+ Address. This Address is supposed to point to a range address, meaning it only
+ gives the correct prefixed address.
+
+ @param[in] IpVersion The IP version.
+ @param[in] Address Points to EFI_IP_ADDRESS to be checked.
+ @param[in] PrefixLength The PrefixeLength of this address.
+
+ @retval TRUE The address is a vaild address range.
+ @retval FALSE The address is not a vaild address range.
+
+**/
+BOOLEAN
+IpSecValidAddressRange (
+ IN UINT8 IpVersion,
+ IN EFI_IP_ADDRESS *Address,
+ IN UINT8 PrefixLength
+ )
+{
+ UINT8 Div;
+ UINT8 Mod;
+ UINT8 Mask;
+ UINT8 AddrLen;
+ UINT8 *Addr;
+ EFI_IP_ADDRESS ZeroAddr;
+
+ if (PrefixLength == 0) {
+ return TRUE;
+ }
+
+ AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);
+
+ if (AddrLen <= PrefixLength) {
+ return FALSE;
+ }
+
+ Div = (UINT8) (PrefixLength / 8);
+ Mod = (UINT8) (PrefixLength % 8);
+ Addr = (UINT8 *) Address;
+ ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));
+
+ //
+ // Check whether the mod part of host scope is zero or not.
+ //
+ if (Mod > 0) {
+ Mask = (UINT8) (0xFF << (8 - Mod));
+
+ if ((Addr[Div] | Mask) != Mask) {
+ return FALSE;
+ }
+
+ Div++;
+ }
+ //
+ // Check whether the div part of host scope is zero or not.
+ //
+ if (CompareMem (
+ &Addr[Div],
+ &ZeroAddr,
+ sizeof (EFI_IP_ADDRESS) - Div
+ ) != 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Extrct the Address Range from a Address.
+
+ This function keep the prefix address and zero other part address.
+
+ @param[in] Address Point to a specified address.
+ @param[in] PrefixLength The prefix length.
+ @param[out] Range Contain the return Address Range.
+
+**/
+VOID
+IpSecExtractAddressRange (
+ IN EFI_IP_ADDRESS *Address,
+ IN UINT8 PrefixLength,
+ OUT EFI_IP_ADDRESS *Range
+ )
+{
+ UINT8 Div;
+ UINT8 Mod;
+ UINT8 Mask;
+ UINT8 *Addr;
+
+ if (PrefixLength == 0) {
+ return ;
+ }
+
+ Div = (UINT8) (PrefixLength / 8);
+ Mod = (UINT8) (PrefixLength % 8);
+ Addr = (UINT8 *) Range;
+
+ CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));
+
+ //
+ // Zero the mod part of host scope.
+ //
+ if (Mod > 0) {
+ Mask = (UINT8) (0xFF << (8 - Mod));
+ Addr[Div] = (UINT8) (Addr[Div] & Mask);
+ Div++;
+ }
+ //
+ // Zero the div part of host scope.
+ //
+ ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);
+
+}
+
+/**
+ Checks if the IP Address in the address range of AddressInfos specified.
+
+ @param[in] IpVersion The IP version.
+ @param[in] IpAddr Point to EFI_IP_ADDRESS to be check.
+ @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check
+ the IP Address is matched.
+ @param[in] AddressCount The total numbers of the AddressInfo.
+
+ @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.
+ @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.
+
+**/
+BOOLEAN
+IpSecMatchIpAddress (
+ IN UINT8 IpVersion,
+ IN EFI_IP_ADDRESS *IpAddr,
+ IN EFI_IP_ADDRESS_INFO *AddressInfo,
+ IN UINT32 AddressCount
+ )
+{
+ EFI_IP_ADDRESS Range;
+ UINT32 Index;
+ BOOLEAN IsMatch;
+
+ IsMatch = FALSE;
+
+ for (Index = 0; Index < AddressCount; Index++) {
+ //
+ // Check whether the target address is in the address range
+ // if it's a valid range of address.
+ //
+ if (IpSecValidAddressRange (
+ IpVersion,
+ &AddressInfo[Index].Address,
+ AddressInfo[Index].PrefixLength
+ )) {
+ //
+ // Get the range of the target address belongs to.
+ //
+ ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));
+ IpSecExtractAddressRange (
+ IpAddr,
+ AddressInfo[Index].PrefixLength,
+ &Range
+ );
+
+ if (CompareMem (
+ &Range,
+ &AddressInfo[Index].Address,
+ sizeof (EFI_IP_ADDRESS)
+ ) == 0) {
+ //
+ // The target address is in the address range.
+ //
+ IsMatch = TRUE;
+ break;
+ }
+ }
+
+ if (CompareMem (
+ IpAddr,
+ &AddressInfo[Index].Address,
+ sizeof (EFI_IP_ADDRESS)
+ ) == 0) {
+ //
+ // The target address is exact same as the address.
+ //
+ IsMatch = TRUE;
+ break;
+ }
+ }
+
+ return IsMatch;
+}
+
+/**
+ Check if the specified Protocol and Prot is supported by the specified SPD Entry.
+
+ This function is the subfunction of IPsecLookUpSpdEntry() that is used to
+ check if the sent/received IKE packet has the related SPD entry support.
+
+ @param[in] Protocol The Protocol to be checked.
+ @param[in] IpPayload Point to IP Payload to be check.
+ @param[in] SpdProtocol The Protocol supported by SPD.
+ @param[in] SpdLocalPort The Local Port in SPD.
+ @param[in] SpdRemotePort The Remote Port in SPD.
+ @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving.
+
+ @retval TRUE The Protocol and Port are supported by the SPD Entry.
+ @retval FALSE The Protocol and Port are not supported by the SPD Entry.
+
+**/
+BOOLEAN
+IpSecMatchNextLayerProtocol (
+ IN UINT8 Protocol,
+ IN UINT8 *IpPayload,
+ IN UINT16 SpdProtocol,
+ IN UINT16 SpdLocalPort,
+ IN UINT16 SpdRemotePort,
+ IN BOOLEAN IsOutbound
+ )
+{
+ BOOLEAN IsMatch;
+
+ if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {
+ return TRUE;
+ }
+
+ IsMatch = FALSE;
+
+ if (SpdProtocol == Protocol) {
+ switch (Protocol) {
+ case EFI_IP_PROTO_UDP:
+ case EFI_IP_PROTO_TCP:
+ //
+ // For udp and tcp, (0, 0) means no need to check local and remote
+ // port. The payload is passed from upper level, which means it should
+ // be in network order.
+ //
+ IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
+ IsMatch = (BOOLEAN) (IsMatch ||
+ (IsOutbound &&
+ (BOOLEAN)(
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort
+ )
+ ));
+
+ IsMatch = (BOOLEAN) (IsMatch ||
+ (!IsOutbound &&
+ (BOOLEAN)(
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&
+ NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort
+ )
+ ));
+ break;
+
+ case EFI_IP_PROTO_ICMP:
+ //
+ // For icmpv4, type code is replaced with local port and remote port,
+ // and (0, 0) means no need to check.
+ //
+ IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
+ IsMatch = (BOOLEAN) (IsMatch ||
+ (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&
+ ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort
+ )
+ );
+ break;
+
+ case IP6_ICMP:
+ //
+ // For icmpv6, type code is replaced with local port and remote port,
+ // and (0, 0) means no need to check.
+ //
+ IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
+
+ IsMatch = (BOOLEAN) (IsMatch ||
+ (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&
+ ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort
+ )
+ );
+ break;
+
+ default:
+ IsMatch = TRUE;
+ break;
+ }
+ }
+
+ return IsMatch;
+}
+
+/**
+ Find the SAD through a specified SPD's SAD list.
+
+ @param[in] SadList SAD list related to a specified SPD entry.
+ @param[in] DestAddress The destination address used to find the SAD entry.
+
+ @return The pointer to a certain SAD entry.
+
+**/
+IPSEC_SAD_ENTRY *
+IpSecLookupSadBySpd (
+ IN LIST_ENTRY *SadList,
+ IN EFI_IP_ADDRESS *DestAddress
+ )
+{
+ LIST_ENTRY *Entry;
+ IPSEC_SAD_ENTRY *SadEntry;
+
+ for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {
+
+ SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);
+ //
+ // Find the right sad entry which contains the appointed dest address.
+ //
+ if (CompareMem (
+ &SadEntry->Id->DestAddress,
+ DestAddress,
+ sizeof (EFI_IP_ADDRESS)
+ ) == 0) {
+ return SadEntry;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Find the SAD through whole SAD list.
+
+ @param[in] Spi The SPI used to search the SAD entry.
+ @param[in] DestAddress The destination used to search the SAD entry.
+
+ @return the pointer to a certain SAD entry.
+
+**/
+IPSEC_SAD_ENTRY *
+IpSecLookupSadBySpi (
+ IN UINT32 Spi,
+ IN EFI_IP_ADDRESS *DestAddress
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *SadList;
+ IPSEC_SAD_ENTRY *SadEntry;
+
+ SadList = &mConfigData[IPsecConfigDataTypeSad];
+
+ for (Entry = SadList->ForwardLink; Entry != SadList; Entry = Entry->ForwardLink) {
+
+ SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);
+ //
+ // Find the right sad entry which contain the appointed spi and dest addr.
+ //
+ if (SadEntry->Id->Spi == Spi && CompareMem (
+ &SadEntry->Id->DestAddress,
+ DestAddress,
+ sizeof (EFI_IP_ADDRESS)
+ ) == 0) {
+
+ return SadEntry;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Look up if there is existing SAD entry for specified IP packet sending.
+
+ This function is called by the IPsecProcess when there is some IP packet needed to
+ send out. This function checks if there is an existing SAD entry that can be serviced
+ to this IP packet sending. If no existing SAD entry could be used, this
+ function will invoke an IPsec Key Exchange Negotiation.
+
+ @param[in] Private Points to private data.
+ @param[in] NicHandle Points to a NIC handle.
+ @param[in] IpVersion The version of IP.
+ @param[in] IpHead The IP Header of packet to be sent out.
+ @param[in] IpPayload The IP Payload to be sent out.
+ @param[in] OldLastHead The Last protocol of the IP packet.
+ @param[in] SpdEntry Points to a related SPD entry.
+ @param[out] SadEntry Contains the Point of a related SAD entry.
+
+ @retval EFI_DEVICE_ERROR One of following conditions is TRUE:
+ - If don't find related UDP service.
+ - Sequence Number is used up.
+ - Extension Sequence Number is used up.
+ @retval EFI_DEVICE_ERROR GC_TODO: Add description for return value.
+ @retval EFI_NOT_READY No existing SAD entry could be used.
+ @retval EFI_SUCCESS Find the related SAD entry.
+
+**/
+EFI_STATUS
+IpSecLookupSadEntry (
+ IN IPSEC_PRIVATE_DATA *Private,
+ IN EFI_HANDLE NicHandle,
+ IN UINT8 IpVersion,
+ IN VOID *IpHead,
+ IN UINT8 *IpPayload,
+ IN UINT8 OldLastHead,
+ IN IPSEC_SPD_ENTRY *SpdEntry,
+ OUT IPSEC_SAD_ENTRY **SadEntry
+ )
+{
+ IPSEC_SAD_ENTRY *Entry;
+ IPSEC_SAD_DATA *Data;
+ EFI_IP_ADDRESS DestIp;
+ UINT32 SeqNum32;
+
+ *SadEntry = NULL;
+ //
+ // Parse the destination address from ip header.
+ //
+ ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));
+ if (IpVersion == IP_VERSION_4) {
+ CopyMem (
+ &DestIp,
+ &((IP4_HEAD *) IpHead)->Dst,
+ sizeof (IP4_ADDR)
+ );
+ } else {
+ CopyMem (
+ &DestIp,
+ &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
+ sizeof (EFI_IP_ADDRESS)
+ );
+ }
+ //
+ // Find the sad entry in the spd.sas list according to the dest address.
+ //
+ Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp);
+
+ if (Entry == NULL) {
+
+ if (OldLastHead != IP6_ICMP ||
+ (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)
+ ) {
+ //
+ // TODO: Start ike negotiation process except the request packet of ping.
+ //
+ //IkeNegotiate (UdpService, SpdEntry, &DestIp);
+ }
+
+ return EFI_NOT_READY;
+ }
+
+ Data = Entry->Data;
+
+ if (!Data->ManualSet) {
+ if (Data->ESNEnabled) {
+ //
+ // Validate the 64bit sn number if 64bit sn enabled.
+ //
+ if (Data->SequenceNumber + 1 < Data->SequenceNumber) {
+ //
+ // TODO: Re-negotiate SA
+ //
+ return EFI_DEVICE_ERROR;
+ }
+ } else {
+ //
+ // Validate the 32bit sn number if 64bit sn disabled.
+ //
+ SeqNum32 = (UINT32) Data->SequenceNumber;
+ if (SeqNum32 + 1 < SeqNum32) {
+ //
+ // TODO: Re-negotiate SA
+ //
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ }
+
+ *SadEntry = Entry;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find a PAD entry according to a remote IP address.
+
+ @param[in] IpVersion The version of IP.
+ @param[in] IpAddr Points to remote IP address.
+
+ @return the pointer of related PAD entry.
+
+**/
+IPSEC_PAD_ENTRY *
+IpSecLookupPadEntry (
+ IN UINT8 IpVersion,
+ IN EFI_IP_ADDRESS *IpAddr
+ )
+{
+ LIST_ENTRY *PadList;
+ LIST_ENTRY *Entry;
+ EFI_IP_ADDRESS_INFO *IpAddrInfo;
+ IPSEC_PAD_ENTRY *PadEntry;
+
+ PadList = &mConfigData[IPsecConfigDataTypePad];
+
+ for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {
+
+ PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);
+ IpAddrInfo = &PadEntry->Id->Id.IpAddress;
+ //
+ // Find the right pad entry which contain the appointed dest addr.
+ //
+ if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {
+ return PadEntry;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Check if the specified IP packet can be serviced by this SPD entry.
+
+ @param[in] SpdEntry Point to SPD entry.
+ @param[in] IpVersion Version of IP.
+ @param[in] IpHead Point to IP header.
+ @param[in] IpPayload Point to IP payload.
+ @param[in] Protocol The Last protocol of IP packet.
+ @param[in] IsOutbound Traffic direction.
+
+ @retval EFI_IPSEC_ACTION The support action of SPD entry.
+ @retval -1 If the input packet header doesn't match the SpdEntry.
+
+**/
+EFI_IPSEC_ACTION
+IpSecLookupSpdEntry (
+ IN IPSEC_SPD_ENTRY *SpdEntry,
+ IN UINT8 IpVersion,
+ IN VOID *IpHead,
+ IN UINT8 *IpPayload,
+ IN UINT8 Protocol,
+ IN BOOLEAN IsOutbound
+ )
+{
+ EFI_IPSEC_SPD_SELECTOR *SpdSel;
+ IP4_HEAD *Ip4;
+ EFI_IP6_HEADER *Ip6;
+ EFI_IP_ADDRESS SrcAddr;
+ EFI_IP_ADDRESS DstAddr;
+ BOOLEAN SpdMatch;
+
+ ASSERT (SpdEntry != NULL);
+ SpdSel = SpdEntry->Selector;
+ Ip4 = (IP4_HEAD *) IpHead;
+ Ip6 = (EFI_IP6_HEADER *) IpHead;
+
+ ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));
+ ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));
+
+ //
+ // Parse the source and destination address from ip header.
+ //
+ if (IpVersion == IP_VERSION_4) {
+ CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));
+ CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));
+ } else {
+ CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));
+ }
+ //
+ // Check the local and remote addresses for outbound traffic
+ //
+ SpdMatch = (BOOLEAN)(IsOutbound &&
+ IpSecMatchIpAddress (
+ IpVersion,
+ &SrcAddr,
+ SpdSel->LocalAddress,
+ SpdSel->LocalAddressCount
+ ) &&
+ IpSecMatchIpAddress (
+ IpVersion,
+ &DstAddr,
+ SpdSel->RemoteAddress,
+ SpdSel->RemoteAddressCount
+ )
+ );
+
+ //
+ // Check the local and remote addresses for inbound traffic
+ //
+ SpdMatch = (BOOLEAN) (SpdMatch ||
+ (!IsOutbound &&
+ IpSecMatchIpAddress (
+ IpVersion,
+ &DstAddr,
+ SpdSel->LocalAddress,
+ SpdSel->LocalAddressCount
+ ) &&
+ IpSecMatchIpAddress (
+ IpVersion,
+ &SrcAddr,
+ SpdSel->RemoteAddress,
+ SpdSel->RemoteAddressCount
+ )
+ ));
+
+ //
+ // Check the next layer protocol and local and remote ports.
+ //
+ SpdMatch = (BOOLEAN) (SpdMatch &&
+ IpSecMatchNextLayerProtocol (
+ Protocol,
+ IpPayload,
+ SpdSel->NextLayerProtocol,
+ SpdSel->LocalPort,
+ SpdSel->RemotePort,
+ IsOutbound
+ )
+ );
+
+ if (SpdMatch) {
+ //
+ // Find the right spd entry if match the 5 key elements.
+ //
+ return SpdEntry->Data->Action;
+ }
+
+ return (EFI_IPSEC_ACTION) - 1;
+}
+
+/**
+ Handles IPsec packet processing for inbound and outbound IP packets.
+
+ The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.
+ The behavior is that it can perform one of the following actions:
+ bypass the packet, discard the packet, or protect the packet.
+
+ @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance.
+ @param[in] NicHandle Instance of the network interface.
+ @param[in] IpVersion IPV4 or IPV6.
+ @param[in, out] IpHead Pointer to the IP Header.
+ @param[in] LastHead The protocol of the next layer to be processed by IPsec.
+ @param[in] OptionsBuffer Pointer to the options buffer.
+ @param[in] OptionsLength Length of the options buffer.
+ @param[in, out] FragmentTable Pointer to a list of fragments.
+ @param[in] FragmentCount Number of fragments.
+ @param[in] TrafficDirection Traffic direction.
+ @param[out] RecycleSignal Event for recycling of resources.
+
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.
+ @retval EFI_SUCCESS The packet was protected.
+ @retval EFI_ACCESS_DENIED The packet was discarded.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecProcess (
+ IN EFI_IPSEC_PROTOCOL *This,
+ IN EFI_HANDLE NicHandle,
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ IN UINT8 *LastHead,
+ IN VOID *OptionsBuffer,
+ IN UINT32 OptionsLength,
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection,
+ OUT EFI_EVENT *RecycleSignal
+ )
+{
+ IPSEC_PRIVATE_DATA *Private;
+ IPSEC_SPD_ENTRY *SpdEntry;
+ IPSEC_SAD_ENTRY *SadEntry;
+ LIST_ENTRY *SpdList;
+ LIST_ENTRY *Entry;
+ EFI_IPSEC_ACTION Action;
+ EFI_STATUS Status;
+ UINT8 *IpPayload;
+ UINT8 OldLastHead;
+ BOOLEAN IsOutbound;
+
+ Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (This);
+ IpPayload = (*FragmentTable)[0].FragmentBuffer;
+ IsOutbound = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE);
+ OldLastHead = *LastHead;
+ *RecycleSignal = NULL;
+
+ if (!IsOutbound) {
+ //
+ // For inbound traffic, process the ipsec header of the packet.
+ //
+ Status = IpSecProtectInboundPacket (
+ IpVersion,
+ IpHead,
+ LastHead,
+ OptionsBuffer,
+ OptionsLength,
+ FragmentTable,
+ FragmentCount,
+ &SpdEntry,
+ RecycleSignal
+ );
+
+ if (Status == EFI_ACCESS_DENIED) {
+ //
+ // The packet is denied to access.
+ //
+ goto ON_EXIT;
+ }
+
+ if (Status == EFI_SUCCESS) {
+ //
+ // Check the spd entry if the packet is accessible.
+ //
+ if (SpdEntry == NULL) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+ Action = IpSecLookupSpdEntry (
+ SpdEntry,
+ IpVersion,
+ IpHead,
+ IpPayload,
+ *LastHead,
+ IsOutbound
+ );
+
+ if (Action != EfiIPsecActionProtect) {
+ //
+ // Discard the packet if the spd entry is not protect.
+ //
+ gBS->SignalEvent (*RecycleSignal);
+ *RecycleSignal = NULL;
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ goto ON_EXIT;
+ }
+ }
+
+ Status = EFI_ACCESS_DENIED;
+ SpdList = &mConfigData[IPsecConfigDataTypeSpd];
+
+ for (Entry = SpdList->ForwardLink; Entry != SpdList; Entry = Entry->ForwardLink) {
+ //
+ // For outbound and non-ipsec Inbound traffic: check the spd entry.
+ //
+ SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
+ Action = IpSecLookupSpdEntry (
+ SpdEntry,
+ IpVersion,
+ IpHead,
+ IpPayload,
+ OldLastHead,
+ IsOutbound
+ );
+
+ switch (Action) {
+
+ case EfiIPsecActionProtect:
+
+ if (IsOutbound) {
+ //
+ // For outbound traffic, lookup the sad entry.
+ //
+ Status = IpSecLookupSadEntry (
+ Private,
+ NicHandle,
+ IpVersion,
+ IpHead,
+ IpPayload,
+ OldLastHead,
+ SpdEntry,
+ &SadEntry
+ );
+
+ if (SadEntry != NULL) {
+ //
+ // Process the packet by the found sad entry.
+ //
+ Status = IpSecProtectOutboundPacket (
+ IpVersion,
+ IpHead,
+ LastHead,
+ OptionsBuffer,
+ OptionsLength,
+ FragmentTable,
+ FragmentCount,
+ SadEntry,
+ RecycleSignal
+ );
+
+ } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {
+ //
+ // TODO: if no need return not ready to upper layer, change here.
+ //
+ Status = EFI_SUCCESS;
+ }
+ } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {
+ //
+ // For inbound icmpv6 traffic except ping request, accept the packet
+ // although no sad entry associated with protect spd entry.
+ //
+ IpSecLookupSadEntry (
+ Private,
+ NicHandle,
+ IpVersion,
+ IpHead,
+ IpPayload,
+ OldLastHead,
+ SpdEntry,
+ &SadEntry
+ );
+ if (SadEntry == NULL) {
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ goto ON_EXIT;
+
+ case EfiIPsecActionBypass:
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+
+ case EfiIPsecActionDiscard:
+ goto ON_EXIT;
+
+ default:
+ //
+ // Discard the packet if no spd entry match.
+ //
+ break;
+ }
+ }
+
+ON_EXIT:
+ return Status;
+}
+
diff --git a/NetworkPkg/IpSecDxe/IpSecImpl.h b/NetworkPkg/IpSecDxe/IpSecImpl.h
new file mode 100644
index 0000000000..644c658082
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecImpl.h
@@ -0,0 +1,313 @@
+/** @file
+ The definitions related to IPsec protocol implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IP_SEC_IMPL_H_
+#define _IP_SEC_IMPL_H_
+
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Protocol/IpSec.h>
+#include <Protocol/IpSecConfig.h>
+#include <Protocol/Dpc.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+
+typedef struct _IPSEC_PRIVATE_DATA IPSEC_PRIVATE_DATA;
+typedef struct _IPSEC_SPD_ENTRY IPSEC_SPD_ENTRY;
+typedef struct _IPSEC_PAD_ENTRY IPSEC_PAD_ENTRY;
+typedef struct _IPSEC_SPD_DATA IPSEC_SPD_DATA;
+
+#define IPSEC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('I', 'P', 'S', 'E')
+
+#define IPSEC_PRIVATE_DATA_FROM_IPSEC(a) CR (a, IPSEC_PRIVATE_DATA, IpSec, IPSEC_PRIVATE_DATA_SIGNATURE)
+#define IPSEC_PRIVATE_DATA_FROM_UDP4LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp4List, IPSEC_PRIVATE_DATA_SIGNATURE)
+#define IPSEC_PRIVATE_DATA_FROM_UDP6LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp6List, IPSEC_PRIVATE_DATA_SIGNATURE)
+#define IPSEC_UDP_SERVICE_FROM_LIST(a) BASE_CR (a, IKE_UDP_SERVICE, List)
+#define IPSEC_SPD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SPD_ENTRY, List)
+#define IPSEC_SAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SAD_ENTRY, List)
+#define IPSEC_PAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_PAD_ENTRY, List)
+#define IPSEC_SAD_ENTRY_FROM_SPD(a) BASE_CR (a, IPSEC_SAD_ENTRY, BySpd)
+
+#define IPSEC_STATUS_DISABLED 0
+#define IPSEC_STATUS_ENABLED 1
+#define IPSEC_ESP_PROTOCOL 50
+#define IPSEC_AH_PROTOCOL 51
+#define IPSEC_DEFAULT_VARIABLE_SIZE 0x100
+
+//
+// Internal Structure Definition
+//
+#pragma pack(1)
+typedef struct _EFI_AH_HEADER {
+ UINT8 NextHeader;
+ UINT8 PayloadLen;
+ UINT16 Reserved;
+ UINT32 Spi;
+ UINT32 SequenceNumber;
+} EFI_AH_HEADER;
+
+typedef struct _EFI_ESP_HEADER {
+ UINT32 Spi;
+ UINT32 SequenceNumber;
+} EFI_ESP_HEADER;
+
+typedef struct _EFI_ESP_TAIL {
+ UINT8 PaddingLength;
+ UINT8 NextHeader;
+} EFI_ESP_TAIL;
+#pragma pack()
+
+struct _IPSEC_SPD_DATA {
+ CHAR16 Name[100];
+ UINT32 PackageFlag;
+ EFI_IPSEC_ACTION Action;
+ EFI_IPSEC_PROCESS_POLICY *ProcessingPolicy;
+ LIST_ENTRY Sas;
+};
+
+struct _IPSEC_SPD_ENTRY {
+ EFI_IPSEC_SPD_SELECTOR *Selector;
+ IPSEC_SPD_DATA *Data;
+ LIST_ENTRY List;
+};
+
+typedef struct _IPSEC_SAD_DATA {
+ EFI_IPSEC_MODE Mode;
+ UINT64 SequenceNumber;
+ UINT8 AntiReplayWindowSize;
+ UINT64 AntiReplayBitmap[4]; // bitmap for received packet
+ EFI_IPSEC_ALGO_INFO AlgoInfo;
+ EFI_IPSEC_SA_LIFETIME SaLifetime;
+ UINT32 PathMTU;
+ IPSEC_SPD_ENTRY *SpdEntry;
+ BOOLEAN ESNEnabled; // Extended (64-bit) SN enabled
+ BOOLEAN ManualSet;
+} IPSEC_SAD_DATA;
+
+typedef struct _IPSEC_SAD_ENTRY {
+ EFI_IPSEC_SA_ID *Id;
+ IPSEC_SAD_DATA *Data;
+ LIST_ENTRY List;
+ LIST_ENTRY BySpd; // Linked on IPSEC_SPD_DATA.Sas
+} IPSEC_SAD_ENTRY;
+
+struct _IPSEC_PAD_ENTRY {
+ EFI_IPSEC_PAD_ID *Id;
+ EFI_IPSEC_PAD_DATA *Data;
+ LIST_ENTRY List;
+};
+
+typedef struct _IPSEC_RECYCLE_CONTEXT {
+ EFI_IPSEC_FRAGMENT_DATA *FragmentTable;
+ UINT8 *PayloadBuffer;
+} IPSEC_RECYCLE_CONTEXT;
+
+struct _IPSEC_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Handle; // Virtual handle to install private prtocol
+ EFI_HANDLE ImageHandle;
+ EFI_IPSEC_PROTOCOL IpSec;
+ EFI_IPSEC_CONFIG_PROTOCOL IpSecConfig;
+ BOOLEAN SetBySelf;
+ LIST_ENTRY Udp4List;
+ UINTN Udp4Num;
+ LIST_ENTRY Udp6List;
+ UINTN Udp6Num;
+ LIST_ENTRY Ikev1SessionList;
+ LIST_ENTRY Ikev1EstablishedList;
+ LIST_ENTRY Ikev2SessionList;
+ LIST_ENTRY Ikev2EstablishedList;
+ BOOLEAN IsIPsecDisabling;
+};
+
+/**
+ This function processes the inbound traffic with IPsec.
+
+ It checks the received packet security property, trims the ESP/AH header, and then
+ returns without an IPsec protected IP Header and FragmentTable.
+
+ @param[in] IpVersion The version of IP.
+ @param[in, out] IpHead Points to IP header containing the ESP/AH header
+ to be trimed on input, and without ESP/AH header
+ on return.
+ @param[in] LastHead The Last Header in IP header on return.
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.
+ @param[in] OptionsLength Length of the options buffer. It is optional.
+ @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec
+ protected on input, and without IPsec protected
+ on return.
+ @param[in] FragmentCount Number of fragments.
+ @param[out] SpdEntry Pointer to contain the address of SPD entry on return.
+ @param[out] RecycleEvent Event for recycling of resources.
+
+ @retval EFI_SUCCESS The operation is successful.
+ @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.
+
+**/
+EFI_STATUS
+IpSecProtectInboundPacket (
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ IN UINT8 *LastHead,
+ IN VOID *OptionsBuffer, OPTIONAL
+ IN UINT32 OptionsLength, OPTIONAL
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ OUT IPSEC_SPD_ENTRY **SpdEntry,
+ OUT EFI_EVENT *RecycleEvent
+ );
+
+
+/**
+ This fucntion processes the output traffic with IPsec.
+
+ It protected the sending packet by encrypting it payload and inserting ESP/AH header
+ in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.
+
+ @param[in] IpVersion The version of IP.
+ @param[in, out] IpHead Point to IP header containing the orginal IP header
+ to be processed on input, and inserted ESP/AH header
+ on return.
+ @param[in] LastHead The Last Header in IP header.
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.
+ @param[in] OptionsLength Length of the options buffer. It is optional.
+ @param[in, out] FragmentTable Pointer to a list of fragments to be protected by
+ IPsec on input, and with IPsec protected
+ on return.
+ @param[in] FragmentCount Number of fragments.
+ @param[in] SadEntry Related SAD entry.
+ @param[out] RecycleEvent Event for recycling of resources.
+
+ @retval EFI_SUCCESS The operation is successful.
+ @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.
+
+**/
+EFI_STATUS
+IpSecProtectOutboundPacket (
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ IN UINT8 *LastHead,
+ IN VOID *OptionsBuffer, OPTIONAL
+ IN UINT32 OptionsLength, OPTIONAL
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ IN IPSEC_SAD_ENTRY *SadEntry,
+ OUT EFI_EVENT *RecycleEvent
+ );
+
+/**
+ Check if the IP Address in the address range of AddressInfos specified.
+
+ @param[in] IpVersion The IP version.
+ @param[in] IpAddr Points to EFI_IP_ADDRESS to be check.
+ @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check
+ the IP Address is matched.
+ @param[in] AddressCount The total numbers of the AddressInfo.
+
+ @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.
+ @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.
+
+**/
+BOOLEAN
+IpSecMatchIpAddress (
+ IN UINT8 IpVersion,
+ IN EFI_IP_ADDRESS *IpAddr,
+ IN EFI_IP_ADDRESS_INFO *AddressInfo,
+ IN UINT32 AddressCount
+ );
+
+/**
+ Find a PAD entry according to remote IP address.
+
+ @param[in] IpVersion The version of IP.
+ @param[in] IpAddr Point to remote IP address.
+
+ @return The pointer of related PAD entry.
+
+**/
+IPSEC_PAD_ENTRY *
+IpSecLookupPadEntry (
+ IN UINT8 IpVersion,
+ IN EFI_IP_ADDRESS *IpAddr
+ );
+
+/**
+ Find the SAD through whole SAD list.
+
+ @param[in] Spi The SPI used to search the SAD entry.
+ @param[in] DestAddress The destination used to search the SAD entry.
+
+ @return The pointer to a certain SAD entry.
+
+**/
+IPSEC_SAD_ENTRY *
+IpSecLookupSadBySpi (
+ IN UINT32 Spi,
+ IN EFI_IP_ADDRESS *DestAddress
+ )
+;
+
+/**
+ Handles IPsec packet processing for inbound and outbound IP packets.
+
+ The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.
+ The behavior is that it can perform one of the following actions:
+ bypass the packet, discard the packet, or protect the packet.
+
+ @param[in] This Pointer to the EFI_IPSEC_PROTOCOL instance.
+ @param[in] NicHandle Instance of the network interface.
+ @param[in] IpVersion IPV4 or IPV6.
+ @param[in, out] IpHead Pointer to the IP Header.
+ @param[in] LastHead The protocol of the next layer to be processed by IPsec.
+ @param[in] OptionsBuffer Pointer to the options buffer.
+ @param[in] OptionsLength Length of the options buffer.
+ @param[in, out] FragmentTable Pointer to a list of fragments.
+ @param[in] FragmentCount Number of fragments.
+ @param[in] TrafficDirection Traffic direction.
+ @param[out] RecycleSignal Event for recycling of resources.
+
+ @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same.
+ @retval EFI_SUCCESS The packet was protected.
+ @retval EFI_ACCESS_DENIED The packet was discarded.
+
+**/
+EFI_STATUS
+EFIAPI
+IpSecProcess (
+ IN EFI_IPSEC_PROTOCOL *This,
+ IN EFI_HANDLE NicHandle,
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ IN UINT8 *LastHead,
+ IN VOID *OptionsBuffer,
+ IN UINT32 OptionsLength,
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection,
+ OUT EFI_EVENT *RecycleSignal
+ );
+
+extern EFI_DPC_PROTOCOL *mDpc;
+extern EFI_IPSEC_PROTOCOL mIpSecInstance;
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName;
+
+
+#endif
diff --git a/NetworkPkg/IpSecDxe/IpSecSaEngine.c b/NetworkPkg/IpSecDxe/IpSecSaEngine.c
new file mode 100644
index 0000000000..8abf4d6bf4
--- /dev/null
+++ b/NetworkPkg/IpSecDxe/IpSecSaEngine.c
@@ -0,0 +1,934 @@
+/** @file
+ IPsec inbound and outbound traffic processing.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "IpSecImpl.h"
+#include "IpSecDebug.h"
+#include "IpSecCryptIo.h"
+
+extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];
+
+/**
+ The call back function of NetbufFromExt.
+
+ @param[in] Arg The argument passed from the caller.
+
+**/
+VOID
+EFIAPI
+IpSecOnRecyclePacket (
+ IN VOID *Arg
+ )
+{
+}
+
+/**
+ This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP
+ is released.
+
+ @param[in] Event The related event.
+ @param[in] Context The data passed by the caller.
+
+**/
+VOID
+EFIAPI
+IpSecRecycleCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ IPSEC_RECYCLE_CONTEXT *RecycleContext;
+
+ RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;
+
+ if (RecycleContext->FragmentTable != NULL) {
+ FreePool (RecycleContext->FragmentTable);
+ }
+
+ if (RecycleContext->PayloadBuffer != NULL) {
+ FreePool (RecycleContext->PayloadBuffer);
+ }
+
+ FreePool (RecycleContext);
+ gBS->CloseEvent (Event);
+
+}
+
+/**
+ Calculate the extension header of IP. The return length only doesn't contain
+ the fixed IP header length.
+
+ @param[in] IpHead Points to an IP head to be calculated.
+ @param[in] LastHead Points to the last header of the IP header.
+
+ @return The length of the extension header.
+
+**/
+UINT16
+IpSecGetPlainExtHeadSize (
+ IN VOID *IpHead,
+ IN UINT8 *LastHead
+ )
+{
+ UINT16 Size;
+
+ Size = (UINT16) (LastHead - (UINT8 *) IpHead);
+
+ if (Size > sizeof (EFI_IP6_HEADER)) {
+ //
+ // * (LastHead+1) point the last header's length but not include the first
+ // 8 octers, so this formluation add 8 at the end.
+ //
+ Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);
+ } else {
+ Size = 0;
+ }
+
+ return Size;
+}
+
+/**
+ Authenticate the IpSec Payload and store the result in the IcvBuffer.
+
+ @param[in] BufferToAuth The buffer to be Authenticated.
+ @param[in] AuthSize The size of the buffer to be Authenticated.
+ @param[in, out] IcvBuffer The buffer to store the ICV.
+ @param[in] IcvSize The size of ICV.
+ @param[in] Key The Key passed to the CryptLib to generate a
+ CRYPT_HANDLE.
+ @param[in] AuthAlgId The Authentication Algorithm ID.
+
+ @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list.
+ @retval EFI_SUCCESS Authenticated the payload successfully.
+ @retval otherwise Authentication of the payload failed.
+**/
+EFI_STATUS
+IpSecAuthPayload (
+ IN UINT8 *BufferToAuth,
+ IN UINTN AuthSize,
+ IN OUT UINT8 *IcvBuffer,
+ IN UINTN IcvSize,
+ IN VOID *Key,
+ IN UINT8 AuthAlgId
+ )
+{
+ switch (AuthAlgId) {
+ case EFI_IPSEC_AALG_NONE :
+ case EFI_IPSEC_AALG_NULL :
+ return EFI_SUCCESS;
+
+ default:
+ return EFI_UNSUPPORTED;
+ }
+}
+
+/**
+ Verify if the Authentication payload is correct.
+
+ @param[in] EspBuffer Points to the ESP wrapped buffer.
+ @param[in] EspSize The size of the ESP wrapped buffer.
+ @param[in] SadEntry The related SAD entry to store the authentication
+ algorithm key.
+ @param[in] IcvSize The length of ICV.
+
+ @retval EFI_SUCCESS The authentication data is correct.
+ @retval EFI_ACCESS_DENIED The authentication data is not correct.
+
+**/
+EFI_STATUS
+IpSecEspAuthVerifyPayload (
+ IN UINT8 *EspBuffer,
+ IN UINTN EspSize,
+ IN IPSEC_SAD_ENTRY *SadEntry,
+ IN UINTN *IcvSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN AuthSize;
+ UINT8 IcvBuffer[12];
+
+ //
+ // Calculate the size of authentication payload.
+ //
+ *IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
+ AuthSize = EspSize - *IcvSize;
+
+ //
+ // Calculate the icv buffer and size of the payload.
+ //
+ Status = IpSecAuthPayload (
+ EspBuffer,
+ AuthSize,
+ IcvBuffer,
+ *IcvSize,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Compare the calculated icv and the appended original icv.
+ //
+ if (CompareMem (EspBuffer + AuthSize, IcvBuffer, *IcvSize) == 0) {
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));
+ return EFI_ACCESS_DENIED;
+}
+
+/**
+ ESP Decrypt the payload.
+
+ @param[in, out] PayloadBuffer Pointer to the buffer containing the ESP wrapped;
+ to be decrypted on input, and plaintext on return. The
+ number of bytes of data to be decrypted is
+ specified by EncryptSize.
+ @param[in] EncryptSize The size of the PayloadBuffer as input.
+ @param[in] SadEntry The related SAD entry.
+ @param[in] IvSize The size of IV.
+ @param[out] PlainPayloadSize Contains the return value of decrypted size.
+ @param[out] PaddingSize Contains the return value of Padding size.
+ @param[out] NextHeader Contains the return value of the last protocol header
+ of the IP packet.
+
+ @retval EFI_UNSUPPORTED The Algorithm pointed to by the SAD entry is not supported.
+ @retval EFI_SUCCESS The operation completed successfully.
+
+**/
+EFI_STATUS
+IpSecEspDecryptPayload (
+ IN OUT UINT8 *PayloadBuffer,
+ IN UINTN EncryptSize,
+ IN IPSEC_SAD_ENTRY *SadEntry,
+ IN UINTN *IvSize,
+ OUT UINTN *PlainPayloadSize,
+ OUT UINTN *PaddingSize,
+ OUT UINT8 *NextHeader
+ )
+{
+ EFI_ESP_TAIL *EspTail;
+
+ switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {
+ case EFI_IPSEC_EALG_NULL:
+ EspTail = (EFI_ESP_TAIL *) (PayloadBuffer + EncryptSize - sizeof (EFI_ESP_TAIL));
+ *PaddingSize = EspTail->PaddingLength;
+ *NextHeader = EspTail->NextHeader;
+ *PlainPayloadSize = EncryptSize - EspTail->PaddingLength - sizeof (EFI_ESP_TAIL);
+ break;
+
+ case EFI_IPSEC_EALG_3DESCBC:
+ case EFI_IPSEC_EALG_AESCBC:
+ //
+ // TODO: support these algorithm
+ //
+ return EFI_UNSUPPORTED;
+ default :
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ ESP Encrypt the payload.
+
+ @param[in, out] BufferToEncrypt Pointer to the buffer containing plaintext to be
+ encrypted on input, and ciphertext on return. The
+ number of bytes of data to be encrypted is
+ specified by EncryptSize.
+ @param[in, out] EncryptSize The size of the plaintext on input, and the size of the
+ ciphertext on return.
+ @param[in] IvBuffer Points to IV data.
+ @param[in] IvSize Size of IV.
+ @param[in] SadEntry Related SAD entry.
+
+ @retval EFI_UNSUPPORTED The Algorithm pointed by SAD entry is not supported.
+ @retval EFI_SUCCESS The operation completed successfully.
+
+**/
+EFI_STATUS
+IpSecEspEncryptPayload (
+ IN OUT UINT8 *BufferToEncrypt,
+ IN OUT UINTN EncryptSize,
+ IN UINT8 *IvBuffer,
+ IN UINTN IvSize,
+ IN IPSEC_SAD_ENTRY *SadEntry
+ )
+{
+ switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {
+ case EFI_IPSEC_EALG_NULL:
+ return EFI_SUCCESS;
+
+ case EFI_IPSEC_EALG_3DESCBC:
+ case EFI_IPSEC_EALG_AESCBC:
+ //
+ // TODO: support these algorithms
+ //
+ return EFI_UNSUPPORTED;
+ default :
+ return EFI_UNSUPPORTED;
+
+ }
+}
+
+/**
+ The actual entry to relative function processes the inbound traffic of ESP header.
+
+ This function is the subfunction of IpSecProtectInboundPacket(). It checks the
+ received packet security property and trim the ESP header and then returns without
+ an IPsec protected IP Header and FramgmentTable.
+
+ @param[in] IpVersion The version of IP.
+ @param[in, out] IpHead Points to the IP header containing the ESP header
+ to be trimed on input, and without ESP header
+ on return.
+ @param[out] LastHead The Last Header in IP header on return.
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.
+ @param[in] OptionsLength Length of the options buffer. It is optional.
+ @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec
+ protected on input, and without IPsec protected
+ on return.
+ @param[in] FragmentCount The number of fragments.
+ @param[out] SpdEntry Pointer to contain the address of SPD entry on return.
+ @param[out] RecycleEvent The event for recycling of resources.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @retval EFI_ACCESS_DENIED One or more following conditions is TRUE:
+ - ESP header was not found.
+ - The related SAD entry was not found.
+ - The related SAD entry does not support the ESP protocol.
+ @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.
+
+**/
+EFI_STATUS
+IpSecEspInboundPacket (
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ OUT UINT8 *LastHead,
+ IN VOID *OptionsBuffer, OPTIONAL
+ IN UINT32 OptionsLength, OPTIONAL
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ OUT IPSEC_SPD_ENTRY **SpdEntry,
+ OUT EFI_EVENT *RecycleEvent
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF *Payload;
+ UINTN EspSize;
+ UINTN IvSize;
+ UINTN PlainPayloadSize;
+ UINTN PaddingSize;
+ UINTN IcvSize;
+ UINT8 *ProcessBuffer;
+ EFI_IP_ADDRESS DestIp;
+ EFI_ESP_HEADER *EspHeader;
+ EFI_ESP_TAIL *EspTail;
+ EFI_IPSEC_SA_ID *SaId;
+ IPSEC_SAD_DATA *SadData;
+ IPSEC_SAD_ENTRY *SadEntry;
+ IPSEC_RECYCLE_CONTEXT *RecycleContext;
+ UINT32 Spi;
+ UINT8 NextHeader;
+ UINT16 IpSecHeadSize;
+
+ Status = EFI_SUCCESS;
+ Payload = NULL;
+ ProcessBuffer = NULL;
+ RecycleContext = NULL;
+ *RecycleEvent = NULL;
+ PlainPayloadSize = 0;
+ NextHeader = 0;
+ //
+ // Build netbuf from fragment table first.
+ //
+ Payload = NetbufFromExt (
+ (NET_FRAGMENT *) *FragmentTable,
+ *FragmentCount,
+ 0,
+ sizeof (EFI_ESP_HEADER),
+ IpSecOnRecyclePacket,
+ NULL
+ );
+ if (Payload == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ //
+ // Get the esp size and eso header from netbuf.
+ //
+ EspSize = Payload->TotalSize;
+ EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);
+ if (EspHeader == NULL) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+ //
+ // Parse destination address from ip header.
+ //
+ ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));
+ if (IpVersion == IP_VERSION_4) {
+ CopyMem (
+ &DestIp,
+ &((IP4_HEAD *) IpHead)->Dst,
+ sizeof (IP4_ADDR)
+ );
+ } else {
+ CopyMem (
+ &DestIp,
+ &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ }
+ //
+ // Lookup sad entry according to the spi and dest address.
+ //
+ Spi = NTOHL (EspHeader->Spi);
+ SadEntry = IpSecLookupSadBySpi (Spi, &DestIp);
+ if (SadEntry == NULL) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ SaId = SadEntry->Id;
+ SadData = SadEntry->Data;
+
+ //
+ // Only support esp protocol currently.
+ //
+ if (SaId->Proto != EfiIPsecESP) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ if (!SadData->ManualSet) {
+ //
+ // TODO: Check sa lifetime and sequence number
+ //
+ }
+ //
+ // Allocate buffer for decryption and authentication by esp.
+ //
+ ProcessBuffer = AllocateZeroPool (EspSize);
+ if (ProcessBuffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);
+
+ //
+ // Authenticate the esp wrapped buffer by the sad entry if has auth key.
+ //
+ IcvSize = 0;
+ if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
+ Status = IpSecEspAuthVerifyPayload (
+ ProcessBuffer,
+ EspSize,
+ SadEntry,
+ &IcvSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Decrypt the payload by the sad entry if has decrypt key.
+ //
+ IvSize = 0;
+ if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
+ Status = IpSecEspDecryptPayload (
+ ProcessBuffer + sizeof (EFI_ESP_HEADER),
+ EspSize - sizeof (EFI_ESP_HEADER) - IcvSize,
+ SadEntry,
+ &IvSize,
+ &PlainPayloadSize,
+ &PaddingSize,
+ &NextHeader
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else {
+ EspTail = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));
+ PaddingSize = EspTail->PaddingLength;
+ NextHeader = EspTail->NextHeader;
+ PlainPayloadSize = EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize - sizeof (EFI_ESP_TAIL) - PaddingSize;
+ }
+ //
+ // TODO: handle anti-replay window
+ //
+ //
+ // Decryption and authentication with esp has been done, so it's time to
+ // reload the new packet, create recycle event and fixup ip header.
+ //
+ RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
+ if (RecycleContext == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IpSecRecycleCallback,
+ RecycleContext,
+ RecycleEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // TODO: Who take responsible to handle the original fragment table?
+ //
+ *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
+ if (*FragmentTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ RecycleContext->PayloadBuffer = ProcessBuffer;
+ RecycleContext->FragmentTable = *FragmentTable;
+ (*FragmentTable)[0].FragmentBuffer = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;
+ (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize;
+ *FragmentCount = 1;
+
+ //
+ // Update the total length field in ip header since processed by esp.
+ //
+ if (IpVersion == IP_VERSION_4) {
+ ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + PlainPayloadSize));
+ } else {
+ IpSecHeadSize = IpSecGetPlainExtHeadSize (IpHead, LastHead);
+ ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));
+ }
+ //
+ // Update the next layer field in ip header since esp header inserted.
+ //
+ *LastHead = NextHeader;
+
+ //
+ // Update the spd association of the sad entry.
+ //
+ *SpdEntry = SadData->SpdEntry;
+
+ON_EXIT:
+ if (Payload != NULL) {
+ NetbufFree (Payload);
+ }
+
+ if (EFI_ERROR (Status)) {
+ if (ProcessBuffer != NULL) {
+ FreePool (ProcessBuffer);
+ }
+
+ if (RecycleContext != NULL) {
+ FreePool (RecycleContext);
+ }
+
+ if (*RecycleEvent != NULL) {
+ gBS->CloseEvent (*RecycleEvent);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ The actual entry to the relative function processes the output traffic using the ESP protocol.
+
+ This function is the subfunction of IpSecProtectOutboundPacket(). It protected
+ the sending packet by encrypting its payload and inserting ESP header in the orginal
+ IP header, then return the IpHeader and IPsec protected Fragmentable.
+
+ @param[in] IpVersion The version of IP.
+ @param[in, out] IpHead Points to IP header containing the orginal IP header
+ to be processed on input, and inserted ESP header
+ on return.
+ @param[in] LastHead The Last Header in IP header.
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.
+ @param[in] OptionsLength Length of the options buffer. It is optional.
+ @param[in, out] FragmentTable Pointer to a list of fragments to be protected by
+ IPsec on input, and with IPsec protected
+ on return.
+ @param[in] FragmentCount The number of fragments.
+ @param[in] SadEntry The related SAD entry.
+ @param[out] RecycleEvent The event for recycling of resources.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated.
+
+**/
+EFI_STATUS
+IpSecEspOutboundPacket (
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ IN UINT8 *LastHead,
+ IN VOID *OptionsBuffer, OPTIONAL
+ IN UINT32 OptionsLength, OPTIONAL
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ IN IPSEC_SAD_ENTRY *SadEntry,
+ OUT EFI_EVENT *RecycleEvent
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_IPSEC_SA_ID *SaId;
+ IPSEC_SAD_DATA *SadData;
+ IPSEC_RECYCLE_CONTEXT *RecycleContext;
+ UINT8 *ProcessBuffer;
+ UINTN BytesCopied;
+ INTN EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4
+ UINTN EspSize; // Total size of esp wrapped ip payload
+ UINTN IvSize; // Size of IV, optional, might be 0
+ UINTN PlainPayloadSize;// Original IP payload size
+ UINTN PaddingSize; // Size of padding
+ UINTN EncryptSize; // Size of data to be encrypted, start after IV and
+ // stop before ICV
+ UINTN IcvSize; // Size of ICV, optional, might be 0
+ UINT8 *RestOfPayload; // Start of Payload after IV
+ UINT8 *Padding; // Start address of padding
+ EFI_ESP_HEADER *EspHeader; // Start address of ESP frame
+ EFI_ESP_TAIL *EspTail; // Address behind padding
+
+ Status = EFI_ACCESS_DENIED;
+ SaId = SadEntry->Id;
+ SadData = SadEntry->Data;
+ ProcessBuffer = NULL;
+ RecycleContext = NULL;
+ *RecycleEvent = NULL;
+
+ if (!SadData->ManualSet &&
+ SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&
+ SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL
+ ) {
+ //
+ // Invalid manual sad entry configuration.
+ //
+ goto ON_EXIT;
+ }
+ //
+ // Calculate enctrypt block size, need iv by default and 4 bytes alignment.
+ //
+ EncryptBlockSize = 4;
+
+ if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
+ EncryptBlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
+
+ if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Calculate the plain payload size accroding to the fragment table.
+ //
+ PlainPayloadSize = 0;
+ for (Index = 0; Index < *FragmentCount; Index++) {
+ PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;
+ }
+ //
+ // Calculate icv size, optional by default and 4 bytes alignment.
+ //
+ IcvSize = 0;
+ if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
+ IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
+ if (IcvSize % 4 != 0) {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Calcuate the total size of esp wrapped ip payload.
+ //
+ IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
+ EncryptSize = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;
+ PaddingSize = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);
+ EspSize = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;
+
+ ProcessBuffer = AllocateZeroPool (EspSize);
+ if (ProcessBuffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ //
+ // Calculate esp header and esp tail including header, payload and padding.
+ //
+ EspHeader = (EFI_ESP_HEADER *) ProcessBuffer;
+ RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;
+ Padding = RestOfPayload + PlainPayloadSize;
+ EspTail = (EFI_ESP_TAIL *) (Padding + PaddingSize);
+
+ //
+ // Fill the sn and spi fields in esp header.
+ //
+ EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);
+ EspHeader->Spi = HTONL (SaId->Spi);
+
+ //
+ // Copy the rest of payload (after iv) from the original fragment buffer.
+ //
+ BytesCopied = 0;
+ for (Index = 0; Index < *FragmentCount; Index++) {
+ CopyMem (
+ (RestOfPayload + BytesCopied),
+ (*FragmentTable)[Index].FragmentBuffer,
+ (*FragmentTable)[Index].FragmentLength
+ );
+ BytesCopied += (*FragmentTable)[Index].FragmentLength;
+ }
+ //
+ // Fill the padding buffer by natural number sequence.
+ //
+ for (Index = 0; Index < PaddingSize; Index++) {
+ Padding[Index] = (UINT8) (Index + 1);
+ }
+ //
+ // Fill the padding length and next header fields in esp tail.
+ //
+ EspTail->PaddingLength = (UINT8) PaddingSize;
+ EspTail->NextHeader = *LastHead;
+
+ //
+ // Generate iv at random by crypt library.
+ //
+ Status = IpSecGenerateIv (
+ (UINT8 *) (EspHeader + 1),
+ IvSize
+ );
+
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Encrypt the payload (after iv) by the sad entry if has encrypt key.
+ //
+ if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
+ Status = IpSecEspEncryptPayload (
+ RestOfPayload,
+ EncryptSize,
+ (UINT8 *) (EspHeader + 1),
+ IvSize,
+ SadEntry
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Authenticate the esp wrapped buffer by the sad entry if has auth key.
+ //
+ if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
+ Status = IpSecAuthPayload (
+ ProcessBuffer,
+ EspSize - IcvSize,
+ ProcessBuffer + EspSize - IcvSize,
+ IcvSize,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
+ SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ //
+ // Encryption and authentication with esp has been done, so it's time to
+ // reload the new packet, create recycle event and fixup ip header.
+ //
+ RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
+ if (RecycleContext == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IpSecRecycleCallback,
+ RecycleContext,
+ RecycleEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // TODO: Who take responsible to handle the original fragment table?
+ //
+ *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
+ if (*FragmentTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ RecycleContext->FragmentTable = *FragmentTable;
+ RecycleContext->PayloadBuffer = ProcessBuffer;
+ (*FragmentTable)[0].FragmentBuffer = ProcessBuffer;
+ (*FragmentTable)[0].FragmentLength = (UINT32) EspSize;
+ *FragmentCount = 1;
+
+ //
+ // Update the total length field in ip header since processed by esp.
+ //
+ if (IpVersion == IP_VERSION_4) {
+ ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + EspSize));
+ } else {
+ ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);
+ }
+ //
+ // Update the next layer field in ip header since esp header inserted.
+ //
+ *LastHead = IPSEC_ESP_PROTOCOL;
+
+ //
+ // Increase the sn number in sad entry according to rfc4303.
+ //
+ SadData->SequenceNumber++;
+
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ if (ProcessBuffer != NULL) {
+ FreePool (ProcessBuffer);
+ }
+
+ if (RecycleContext != NULL) {
+ FreePool (RecycleContext);
+ }
+
+ if (*RecycleEvent != NULL) {
+ gBS->CloseEvent (*RecycleEvent);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ This function processes the inbound traffic with IPsec.
+
+ It checks the received packet security property, trims the ESP/AH header, and then
+ returns without an IPsec protected IP Header and FragmentTable.
+
+ @param[in] IpVersion The version of IP.
+ @param[in, out] IpHead Points to IP header containing the ESP/AH header
+ to be trimed on input, and without ESP/AH header
+ on return.
+ @param[in] LastHead The Last Header in IP header on return.
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.
+ @param[in] OptionsLength Length of the options buffer. It is optional.
+ @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec
+ protected on input, and without IPsec protected
+ on return.
+ @param[in] FragmentCount The number of fragments.
+ @param[out] SpdEntry Pointer to contain the address of SPD entry on return.
+ @param[out] RecycleEvent The event for recycling of resources.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @retval EFI_UNSUPPORTED The IPSEC protocol is not supported.
+
+**/
+EFI_STATUS
+IpSecProtectInboundPacket (
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ IN UINT8 *LastHead,
+ IN VOID *OptionsBuffer, OPTIONAL
+ IN UINT32 OptionsLength, OPTIONAL
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ OUT IPSEC_SPD_ENTRY **SpdEntry,
+ OUT EFI_EVENT *RecycleEvent
+ )
+{
+ if (*LastHead == IPSEC_ESP_PROTOCOL) {
+ //
+ // Process the esp ipsec header of the inbound traffic.
+ //
+ return IpSecEspInboundPacket (
+ IpVersion,
+ IpHead,
+ LastHead,
+ OptionsBuffer,
+ OptionsLength,
+ FragmentTable,
+ FragmentCount,
+ SpdEntry,
+ RecycleEvent
+ );
+ }
+ //
+ // The other protocols are not supported.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ This function processes the output traffic with IPsec.
+
+ It protected the sending packet by encrypting it payload and inserting ESP/AH header
+ in the orginal IP header, then returns the IpHeader and IPsec protected Fragmentable.
+
+ @param[in] IpVersion The version of IP.
+ @param[in, out] IpHead Points to IP header containing the orginal IP header
+ to be processed on input, and inserted ESP/AH header
+ on return.
+ @param[in] LastHead The Last Header in the IP header.
+ @param[in] OptionsBuffer Pointer to the options buffer. It is optional.
+ @param[in] OptionsLength Length of the options buffer. It is optional.
+ @param[in, out] FragmentTable Pointer to a list of fragments to be protected by
+ IPsec on input, and with IPsec protected
+ on return.
+ @param[in] FragmentCount The number of fragments.
+ @param[in] SadEntry The related SAD entry.
+ @param[out] RecycleEvent The event for recycling of resources.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.
+
+**/
+EFI_STATUS
+IpSecProtectOutboundPacket (
+ IN UINT8 IpVersion,
+ IN OUT VOID *IpHead,
+ IN UINT8 *LastHead,
+ IN VOID *OptionsBuffer, OPTIONAL
+ IN UINT32 OptionsLength, OPTIONAL
+ IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ IN IPSEC_SAD_ENTRY *SadEntry,
+ OUT EFI_EVENT *RecycleEvent
+ )
+{
+ if (SadEntry->Id->Proto == EfiIPsecESP) {
+ //
+ // Process the esp ipsec header of the outbound traffic.
+ //
+ return IpSecEspOutboundPacket (
+ IpVersion,
+ IpHead,
+ LastHead,
+ OptionsBuffer,
+ OptionsLength,
+ FragmentTable,
+ FragmentCount,
+ SadEntry,
+ RecycleEvent
+ );
+ }
+ //
+ // The other protocols are not supported.
+ //
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/Mtftp6Dxe/ComponentName.c b/NetworkPkg/Mtftp6Dxe/ComponentName.c
new file mode 100644
index 0000000000..0f91b64c44
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/ComponentName.c
@@ -0,0 +1,308 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Mtftp6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for bus drivers
+ attempting to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that attempts to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName = {
+ Mtftp6ComponentNameGetDriverName,
+ Mtftp6ComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp6ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp6ComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp6DriverNameTable[] = {
+ {
+ "eng;en",
+ L"MTFTP6 Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mMtftp6DriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gMtftp6ComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ attempting to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that attempts to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c
new file mode 100644
index 0000000000..d448c78a34
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c
@@ -0,0 +1,703 @@
+/** @file
+ Driver Binding functions and Service Binding functions
+ implementation for Mtftp6 Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+
+EFI_DRIVER_BINDING_PROTOCOL gMtftp6DriverBinding = {
+ Mtftp6DriverBindingSupported,
+ Mtftp6DriverBindingStart,
+ Mtftp6DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL gMtftp6ServiceBindingTemplate = {
+ Mtftp6ServiceBindingCreateChild,
+ Mtftp6ServiceBindingDestroyChild
+};
+
+
+/**
+ Destory the MTFTP6 service. The MTFTP6 service may be partly initialized,
+ or partly destroyed. If a resource is destroyed, it is marked as such in
+ case the destroy failed and is called again later.
+
+ @param[in] Service The MTFTP6 service to be destroyed.
+
+**/
+VOID
+Mtftp6DestroyService (
+ IN MTFTP6_SERVICE *Service
+ )
+{
+ //
+ // Make sure all children instances have been already destoryed.
+ //
+ ASSERT (Service->ChildrenNum == 0);
+
+ if (Service->DummyUdpIo != NULL) {
+ UdpIoFreeIo (Service->DummyUdpIo);
+ }
+
+ if (Service->Timer != NULL) {
+ gBS->CloseEvent (Service->Timer);
+ }
+
+ FreePool (Service);
+}
+
+
+/**
+ Create then initialize a MTFTP6 service binding instance.
+
+ @param[in] Controller The controller to install the MTFTP6 service
+ binding on.
+ @param[in] Image The driver binding image of the MTFTP6 driver.
+ @param[out] Service The variable to receive the created service
+ binding instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to create the instance
+ @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep connection with UDP.
+ @retval EFI_SUCCESS The service instance is created for the controller.
+
+**/
+EFI_STATUS
+Mtftp6CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ OUT MTFTP6_SERVICE **Service
+ )
+{
+ MTFTP6_SERVICE *Mtftp6Srv;
+ EFI_STATUS Status;
+
+ ASSERT (Service != NULL);
+
+ *Service = NULL;
+ Mtftp6Srv = AllocateZeroPool (sizeof (MTFTP6_SERVICE));
+
+ if (Mtftp6Srv == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Mtftp6Srv->Signature = MTFTP6_SERVICE_SIGNATURE;
+ Mtftp6Srv->Controller = Controller;
+ Mtftp6Srv->Image = Image;
+ Mtftp6Srv->InDestory = FALSE;
+ Mtftp6Srv->ChildrenNum = 0;
+
+ CopyMem (
+ &Mtftp6Srv->ServiceBinding,
+ &gMtftp6ServiceBindingTemplate,
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)
+ );
+
+ InitializeListHead (&Mtftp6Srv->Children);
+
+ //
+ // Create a internal timer for all instances.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Mtftp6OnTimerTick,
+ Mtftp6Srv,
+ &Mtftp6Srv->Timer
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Mtftp6Srv);
+ return Status;
+ }
+
+ //
+ // Create a dummy Udp6Io to build parent-child relationship between Udp6 driver
+ // and Mtftp6 driver.
+ //
+ Mtftp6Srv->DummyUdpIo = UdpIoCreateIo (
+ Controller,
+ Image,
+ Mtftp6ConfigDummyUdpIo,
+ UDP_IO_UDP6_VERSION,
+ NULL
+ );
+
+ if (Mtftp6Srv->DummyUdpIo == NULL) {
+ gBS->CloseEvent (Mtftp6Srv->Timer);
+ FreePool (Mtftp6Srv);
+ return EFI_DEVICE_ERROR;
+ }
+
+ *Service = Mtftp6Srv;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Destroy the MTFTP6 instance and recycle the resources.
+
+ @param[in] Instance The pointer to the MTFTP6 instance.
+
+**/
+VOID
+Mtftp6DestroyInstance (
+ IN MTFTP6_INSTANCE *Instance
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP6_BLOCK_RANGE *Block;
+
+ if (Instance->Config != NULL) {
+ FreePool (Instance->Config);
+ }
+
+ if (Instance->Token != NULL && Instance->Token->Event != NULL) {
+ gBS->SignalEvent (Instance->Token->Event);
+ }
+
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ if (Instance->UdpIo!= NULL) {
+ UdpIoFreeIo (Instance->UdpIo);
+ }
+
+ if (Instance->McastUdpIo != NULL) {
+ UdpIoFreeIo (Instance->McastUdpIo);
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
+ RemoveEntryList (Entry);
+ FreePool (Block);
+ }
+
+ FreePool (Instance);
+}
+
+
+/**
+ Create the MTFTP6 instance and initialize it.
+
+ @param[in] Service The pointer to the MTFTP6 service.
+ @param[out] Instance The pointer to the MTFTP6 instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_SUCCESS The MTFTP6 instance is created.
+
+**/
+EFI_STATUS
+Mtftp6CreateInstance (
+ IN MTFTP6_SERVICE *Service,
+ OUT MTFTP6_INSTANCE **Instance
+ )
+{
+ MTFTP6_INSTANCE *Mtftp6Ins;
+
+ *Instance = NULL;
+ Mtftp6Ins = AllocateZeroPool (sizeof (MTFTP6_INSTANCE));
+
+ if (Mtftp6Ins == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Mtftp6Ins->Signature = MTFTP6_INSTANCE_SIGNATURE;
+ Mtftp6Ins->InDestory = FALSE;
+ Mtftp6Ins->Service = Service;
+
+ CopyMem (
+ &Mtftp6Ins->Mtftp6,
+ &gMtftp6ProtocolTemplate,
+ sizeof (EFI_MTFTP6_PROTOCOL)
+ );
+
+ InitializeListHead (&Mtftp6Ins->Link);
+ InitializeListHead (&Mtftp6Ins->BlkList);
+
+ *Instance = Mtftp6Ins;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including
+ both device drivers and bus drivers.
+
+ Entry point of the MTFTP6 driver to install various protocols.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable The pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gMtftp6DriverBinding,
+ ImageHandle,
+ &gMtftp6ComponentName,
+ &gMtftp6ComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported(), it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child.
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ return gBS->OpenProtocol (
+ Controller,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ MTFTP6_SERVICE *Service;
+ EFI_STATUS Status;
+
+ //
+ // Directly return if driver is already running on this Nic handle.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiMtftp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Create Mtftp6 service for this Nic handle
+ //
+ Status = Mtftp6CreateService (
+ Controller,
+ This->DriverBindingHandle,
+ &Service
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (Service != NULL);
+
+ //
+ // Start the internal timer to track the packet retransmission.
+ //
+ Status = gBS->SetTimer (
+ Service->Timer,
+ TimerPeriodic,
+ TICKS_PER_SECOND
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Mtftp6 service on the Nic handle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiMtftp6ServiceBindingProtocolGuid,
+ &Service->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ Mtftp6DestroyService (Service);
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ MTFTP6_SERVICE *Service;
+ MTFTP6_INSTANCE *Instance;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // Locate the Nic handle to retrieve the Mtftp6 private data.
+ //
+ NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp6ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiMtftp6ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Service = MTFTP6_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (Service->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (NumberOfChildren == 0) {
+ //
+ // Destory the Mtftp6 service if there is no Mtftp6 child instance left.
+ //
+ Service->InDestory = TRUE;
+
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiMtftp6ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ Mtftp6DestroyService (Service);
+
+ } else {
+ //
+ // Destory the Mtftp6 child instance one by one.
+ //
+ while (!IsListEmpty (&Service->Children)) {
+ Instance = NET_LIST_HEAD (&Service->Children, MTFTP6_INSTANCE, Link);
+ Mtftp6ServiceBindingDestroyChild (ServiceBinding, Instance->Handle);
+ }
+
+ if (Service->ChildrenNum != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing
+ UEFI handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval Others The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ MTFTP6_SERVICE *Service;
+ MTFTP6_INSTANCE *Instance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ VOID *Udp6;
+
+ if (This == NULL || ChildHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Service = MTFTP6_SERVICE_FROM_THIS (This);
+
+ Status = Mtftp6CreateInstance (Service, &Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (Instance != NULL);
+
+ //
+ // Install the Mtftp6 protocol on the new child handle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiMtftp6ProtocolGuid,
+ &Instance->Mtftp6,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Udp6 protocol by child.
+ //
+ Status = gBS->OpenProtocol (
+ Service->DummyUdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ (VOID **) &Udp6,
+ gMtftp6DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiMtftp6ProtocolGuid,
+ &Instance->Mtftp6,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ //
+ // Add the new Mtftp6 instance into the children list of Mtftp6 service.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&Service->Children, &Instance->Link);
+ Service->ChildrenNum++;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ Mtftp6DestroyInstance (Instance);
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval Others The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ MTFTP6_SERVICE *Service;
+ MTFTP6_INSTANCE *Instance;
+ EFI_MTFTP6_PROTOCOL *Mtftp6;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL || ChildHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Locate the Nic handle to retrieve the Mtftp6 private data.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiMtftp6ProtocolGuid,
+ (VOID **) &Mtftp6,
+ gMtftp6DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = MTFTP6_INSTANCE_FROM_THIS (Mtftp6);
+ Service = MTFTP6_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != Service) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether the instance already in destory state.
+ //
+ if (Instance->InDestory) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance->InDestory = TRUE;
+
+ gBS->CloseProtocol (
+ Service->DummyUdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ gMtftp6DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the MTFTP6 protocol first to enable a top down destruction.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiMtftp6ProtocolGuid,
+ Mtftp6
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestory = FALSE;
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Remove the Mtftp6 instance from the children list of Mtftp6 service.
+ //
+ RemoveEntryList (&Instance->Link);
+ Service->ChildrenNum --;
+
+ Mtftp6DestroyInstance (Instance);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h
new file mode 100644
index 0000000000..94f73a80a9
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h
@@ -0,0 +1,151 @@
+/** @file
+ Driver Binding functions and Service Binding functions
+ declaration for Mtftp6 Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_MTFTP6_DRIVER_H__
+#define __EFI_MTFTP6_DRIVER_H__
+
+#include <Protocol/ServiceBinding.h>
+
+extern EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2;
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported(), it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval Others This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop(), it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle.
+ @retval EFI_DEVICE_ERROR An unexpected error.
+ @retval Others This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing
+ UEFI handle, then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval Others The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a protocol installed on it.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval Others The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf b/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
new file mode 100644
index 0000000000..ecf1f7cf0d
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
@@ -0,0 +1,69 @@
+## @file
+# Component description file for Mtftp6 module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Mtftp6Dxe
+ FILE_GUID = 99F03B99-98D8-49dd-A8D3-3219D0FFE41E
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Mtftp6DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+# DRIVER_BINDING = gMtftp6DriverBinding
+# COMPONENT_NAME = gMtftp6ComponentName
+# COMPONENT_NAME2 = gMtftp6ComponentName2
+#
+
+[Sources]
+ Mtftp6Driver.c
+ Mtftp6Driver.h
+ Mtftp6Impl.c
+ Mtftp6Impl.h
+ Mtftp6Option.c
+ Mtftp6Option.h
+ Mtftp6Support.h
+ Mtftp6Support.c
+ Mtftp6Rrq.c
+ Mtftp6Wrq.c
+ ComponentName.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ UdpIoLib
+
+
+[Protocols]
+ gEfiUdp6ServiceBindingProtocolGuid
+ gEfiUdp6ProtocolGuid
+ gEfiMtftp6ServiceBindingProtocolGuid
+ gEfiMtftp6ProtocolGuid
+
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c
new file mode 100644
index 0000000000..fcbbf11134
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c
@@ -0,0 +1,634 @@
+/** @file
+ This EFI_MTFTP6_PROTOCOL interface implementation.
+
+ It supports the following RFCs:
+ RFC1350 - THE TFTP PROTOCOL (REVISION 2)
+ RFC2090 - TFTP Multicast Option
+ RFC2347 - TFTP Option Extension
+ RFC2348 - TFTP Blocksize Option
+ RFC2349 - TFTP Timeout Interval and Transfer Size Options
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate = {
+ EfiMtftp6GetModeData,
+ EfiMtftp6Configure,
+ EfiMtftp6GetInfo,
+ EfiMtftp6ParseOptions,
+ EfiMtftp6ReadFile,
+ EfiMtftp6WriteFile,
+ EfiMtftp6ReadDirectory,
+ EfiMtftp6Poll
+ };
+
+/**
+ Returns the current operating mode data for the MTFTP6 instance.
+
+ The GetModeData() function returns the current operating mode and
+ cached data packet for the MTFTP6 instance.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode
+ data is returned.
+
+ @retval EFI_SUCCESS The configuration data was returned successfully.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+ @retval EFI_INVALID_PARAMETER This is NULL or ModeData is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6GetModeData (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ OUT EFI_MTFTP6_MODE_DATA *ModeData
+ )
+{
+ MTFTP6_INSTANCE *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL || ModeData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);
+
+ //
+ // Copy back the configure data if the instance already configured.
+ //
+ if (Instance->Config != NULL) {
+ CopyMem (
+ &ModeData->ConfigData,
+ Instance->Config,
+ sizeof (EFI_MTFTP6_CONFIG_DATA)
+ );
+ } else {
+ ZeroMem (
+ &ModeData->ConfigData,
+ sizeof (EFI_MTFTP6_CONFIG_DATA)
+ );
+ }
+
+ //
+ // Set the current support options in mode data.
+ //
+ ModeData->SupportedOptionCount = MTFTP6_SUPPORTED_OPTIONS_NUM;
+ ModeData->SupportedOptions = (UINT8 **) mMtftp6SupportedOptions;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Initializes, changes, or resets the default operational setting for
+ this EFI MTFTPv6 Protocol driver instance.
+
+ The Configure() function is used to set and change the configuration
+ data for this EFI MTFTPv6 Protocol driver instance. The configuration
+ data can be reset to startup defaults by calling Configure() with
+ MtftpConfigData set to NULL. Whenever the instance is reset, any
+ pending operation is aborted. By changing the EFI MTFTPv6 Protocol
+ driver instance configuration data, the client can connect to
+ different MTFTPv6 servers. The configuration parameters in
+ MtftpConfigData are used as the default parameters in later MTFTPv6
+ operations and can be overridden in later operations.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] MtftpConfigData Pointer to the configuration data structure.
+
+ @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ - This is NULL.
+ - MtftpConfigData.StationIp is neither zero nor one
+ of the configured IP addresses in the underlying IPv6 driver.
+ - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there
+ is some MTFTP background operation in progress.
+ - MtftpCofigData.LocalPort is already in use.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be
+ allocated.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI
+ MTFTPv6 Protocol driver instance is not configured.
+ Note: It is not defined in the UEFI 2.3 Specification.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6Configure (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL
+ )
+{
+ MTFTP6_SERVICE *Service;
+ MTFTP6_INSTANCE *Instance;
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA Udp6Cfg;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (MtftpConfigData != NULL && !NetIp6IsValidUnicast (&MtftpConfigData->ServerIp)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);
+ Service = Instance->Service;
+ Status = EFI_SUCCESS;
+
+ if (MtftpConfigData == NULL) {
+ //
+ // Configure the instance as NULL to abort the current session.
+ //
+ Mtftp6OperationClean (Instance, EFI_ABORTED);
+ FreePool (Instance->Config);
+ Instance->Config = NULL;
+ } else {
+ //
+ // It's not allowed to configure one instance twice without configure null.
+ //
+ if (Instance->Config != NULL) {
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+ //
+ // Allocate the configure buffer of the instance and store the user's data.
+ //
+ Instance->Config = AllocateZeroPool (sizeof (EFI_MTFTP6_CONFIG_DATA));
+
+ if (Instance->Config == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ CopyMem (Instance->Config, MtftpConfigData, sizeof (EFI_MTFTP6_CONFIG_DATA));
+
+ //
+ // Don't configure the udpio here because each operation might override
+ // the configuration, so delay udpio configuration in each operation.
+ //
+ Instance->UdpIo = UdpIoCreateIo (
+ Service->Controller,
+ Service->Image,
+ Mtftp6ConfigDummyUdpIo,
+ UDP_IO_UDP6_VERSION,
+ NULL
+ );
+
+ if (Instance->UdpIo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Continue to configure the downside Udp6 instance by user's data.
+ //
+ ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ Udp6Cfg.AcceptPromiscuous = FALSE;
+ Udp6Cfg.AcceptAnyPort = FALSE;
+ Udp6Cfg.AllowDuplicatePort = FALSE;
+ Udp6Cfg.TrafficClass = 0;
+ Udp6Cfg.HopLimit = 128;
+ Udp6Cfg.ReceiveTimeout = 0;
+ Udp6Cfg.TransmitTimeout = 0;
+ Udp6Cfg.StationPort = Instance->Config->LocalPort;
+ Udp6Cfg.RemotePort = Instance->Config->InitialServerPort;
+
+ CopyMem (
+ &Udp6Cfg.StationAddress,
+ &Instance->Config->StationIp,
+ sizeof(EFI_IPv6_ADDRESS)
+ );
+
+ CopyMem (
+ &Udp6Cfg.RemoteAddress,
+ &Instance->Config->ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ Udp6 = Instance->UdpIo->Protocol.Udp6;
+ Status = Udp6->Configure (Udp6, &Udp6Cfg);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ if (Instance->Config != NULL) {
+ FreePool (Instance->Config);
+ Instance->Config = NULL;
+ }
+ if (Instance->UdpIo != NULL) {
+ UdpIoFreeIo (Instance->UdpIo);
+ Instance->UdpIo = NULL;
+ }
+ }
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Get the information of the download from the server.
+
+ The GetInfo() function assembles an MTFTPv6 request packet
+ with options, sends it to the MTFTPv6 server, and may return
+ an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries
+ occur only if no response packets are received from the MTFTPv6
+ server before the timeout expires.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the
+ default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()
+ function are used.
+ @param[in] Filename Pointer to ASCIIZ file name string.
+ @param[in] ModeStr Pointer to ASCIIZ mode string. If NULL, octet will be used.
+ @param[in] OptionCount Number of option/value string pairs in OptionList.
+ @param[in] OptionList Pointer to array of option/value string pairs. Ignored if
+ OptionCount is zero.
+ @param[out] PacketLength The number of bytes in the returned packet.
+ @param[out] Packet The pointer to the received packet. This buffer must be freed by
+ the caller.
+
+ @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Filename is NULL.
+ - OptionCount is not zero and OptionList is NULL.
+ - One or more options in OptionList have wrong format.
+ - PacketLength is NULL.
+ - OverrideData.ServerIp is not valid unicast IPv6 addresses.
+ @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by
+ this implementation.
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet.
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received and the Packet is set to NULL.
+ Note: It is not defined in UEFI 2.3 Specification.
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received and the Packet is set to NULL.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received and the Packet is set to NULL.
+ @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received and the Packet is set to NULL.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6GetInfo (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL,
+ IN UINT8 *Filename,
+ IN UINT8 *ModeStr OPTIONAL,
+ IN UINT8 OptionCount,
+ IN EFI_MTFTP6_OPTION *OptionList OPTIONAL,
+ OUT UINT32 *PacketLength,
+ OUT EFI_MTFTP6_PACKET **Packet OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_MTFTP6_TOKEN Token;
+ MTFTP6_GETINFO_CONTEXT Context;
+
+ if (This == NULL ||
+ Filename == NULL ||
+ PacketLength == NULL ||
+ (OptionCount != 0 && OptionList == NULL) ||
+ (OverrideData != NULL && !NetIp6IsValidUnicast (&OverrideData->ServerIp))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet != NULL) {
+ *Packet = NULL;
+ }
+
+ *PacketLength = 0;
+
+ Context.Packet = Packet;
+ Context.PacketLen = PacketLength;
+ Context.Status = EFI_SUCCESS;
+
+ //
+ // Fill fields of the Token for GetInfo operation.
+ //
+ Token.Status = EFI_SUCCESS;
+ Token.Event = NULL;
+ Token.OverrideData = OverrideData;
+ Token.Filename = Filename;
+ Token.ModeStr = ModeStr;
+ Token.OptionCount = OptionCount;
+ Token.OptionList = OptionList;
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ Token.Context = &Context;
+ Token.CheckPacket = Mtftp6CheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ //
+ // Start the GetInfo operation by issue the Token.
+ //
+ Status = Mtftp6OperationStart (This, &Token, EFI_MTFTP6_OPCODE_RRQ);
+
+ if (Status == EFI_ABORTED) {
+ //
+ // Return the status if failed to issue.
+ //
+ return Context.Status;
+ }
+
+ return Status;
+}
+
+
+/**
+ Parse the options in an MTFTPv6 OACK packet.
+
+ The ParseOptions() function parses the option fields in an MTFTPv6 OACK
+ packet and returns the number of options that were found, and optionally,
+ a list of pointers to the options in the packet. If one or more of the
+ option fields are not valid, then EFI_PROTOCOL_ERROR is returned and
+ *OptionCount and *OptionList stop at the last valid option.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] PacketLen Length of the OACK packet to be parsed.
+ @param[in] Packet Pointer to the OACK packet to be parsed.
+ @param[out] OptionCount Pointer to the number of options in the following OptionList.
+ @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the
+ OptionList points to the corresponding MTFTP option buffer
+ in the Packet. Call the EFI Boot Service FreePool() to
+ release the OptionList if the options in this OptionList
+ are not needed anymore.
+
+ @retval EFI_SUCCESS The OACK packet was valid and the OptionCount and
+ OptionList parameters have been updated.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - PacketLen is 0.
+ - Packet is NULL or Packet is not a valid MTFTPv6 packet.
+ - OptionCount is NULL.
+ @retval EFI_NOT_FOUND No options were found in the OACK packet.
+ @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated.
+ @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6ParseOptions (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN UINT32 PacketLen,
+ IN EFI_MTFTP6_PACKET *Packet,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL
+ )
+{
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return Mtftp6ParseStart (Packet, PacketLen, OptionCount, OptionList);
+}
+
+
+/**
+ Download a file from an MTFTPv6 server.
+
+ The ReadFile() function is used to initialize and start an MTFTPv6 download
+ process, and optionally, wait for completion. When the download operation
+ completes, whether successfully or not, the Token.Status field is updated
+ by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it
+ is not NULL.
+ Data can be downloaded from the MTFTPv6 server into either of the following
+ locations:
+ - A fixed buffer that is pointed to by Token.Buffer
+ - A download service function that is pointed to by Token.CheckPacket.
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket
+ will be called first. If the call is successful, the packet will be stored
+ in Token.Buffer.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] Token Pointer to the token structure to provide the parameters that are
+ used in this operation.
+
+ @retval EFI_SUCCESS The data file has been transferred successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the
+ downloaded data in downloading process.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_ABORTED Current operation is aborted by user.
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6ReadFile (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token
+ )
+{
+ return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_RRQ);
+}
+
+
+/**
+ Send a file to an MTFTPv6 server.
+
+ The WriteFile() function is used to initialize an uploading operation
+ with the given option list and optionally wait for completion. If one
+ or more of the options is not supported by the server, the unsupported
+ options are ignored and a standard TFTP process starts instead. When
+ the upload process completes, whether successfully or not, Token.Event
+ is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.
+ The caller can supply the data to be uploaded in the following two modes:
+ - Through the user-provided buffer
+ - Through a callback function
+ With the user-provided buffer, the Token.BufferSize field indicates
+ the length of the buffer, and the driver will upload the data in the
+ buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver
+ will call this callback function to get more data from the user to upload.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] Token Pointer to the token structure to provide the parameters that are
+ used in this operation.
+
+ @retval EFI_SUCCESS The upload session has started.
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Filename is NULL.
+ - Token.OptionCount is not zero and Token.OptionList is NULL.
+ - One or more options in Token.OptionList have wrong format.
+ - Token.Buffer and Token.PacketNeeded are both NULL.
+ - Token.OverrideData.ServerIp is not a valid unicast IPv6 address.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not
+ supported by this implementation.
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6WriteFile (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token
+ )
+{
+ return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_WRQ);
+}
+
+
+/**
+ Download a data file directory from an MTFTPv6 server.
+
+ The ReadDirectory() function is used to return a list of files on the
+ MTFTPv6 server that are logically (or operationally) related to
+ Token.Filename. The directory request packet that is sent to the server
+ is built with the option list that was provided by the caller, if present.
+ The file information that the server returns is put into either of
+ the following locations:
+ - A fixed buffer that is pointed to by Token.Buffer.
+ - A download service function that is pointed to by Token.CheckPacket.
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket
+ will be called first. If the call is successful, the packet will be stored
+ in Token.Buffer.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] Token Pointer to the token structure to provide the parameters that are
+ used in this operation.
+
+ @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded.
+ @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Filename is NULL.
+ - Token.OptionCount is not zero and Token.OptionList is NULL.
+ - One or more options in Token.OptionList have wrong format.
+ - Token.Buffer and Token.CheckPacket are both NULL.
+ - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not
+ supported by this implementation.
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6ReadDirectory (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token
+ )
+{
+ return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_DIR);
+}
+
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications
+ to increase the rate that data packets are moved between the
+ communications device and the transmit and receive queues. In some
+ systems, the periodic timer event in the managed network driver may
+ not poll the underlying communications device fast enough to transmit
+ and/or receive all data packets without missing incoming packets or
+ dropping outgoing packets. Drivers and applications that are
+ experiencing packet loss should try calling the Poll() function
+ more often.
+
+ @param[in] This The MTFTP6 protocol instance.
+
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6Poll (
+ IN EFI_MTFTP6_PROTOCOL *This
+ )
+{
+ MTFTP6_INSTANCE *Instance;
+ EFI_UDP6_PROTOCOL *Udp6;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);
+
+ //
+ // Check the instance whether configured or in destory.
+ //
+ if (Instance->Config == NULL) {
+ return EFI_NOT_STARTED;
+ } else if (Instance->InDestory) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Udp6 = Instance->UdpIo->Protocol.Udp6;
+
+ return Udp6->Poll (Udp6);
+}
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h
new file mode 100644
index 0000000000..bf924a980c
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h
@@ -0,0 +1,469 @@
+/** @file
+ Mtftp6 internal data structure and definition declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved. <BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_MTFTP6_IMPL_H__
+#define __EFI_MTFTP6_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/Udp6.h>
+#include <Protocol/Mtftp6.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+
+typedef struct _MTFTP6_SERVICE MTFTP6_SERVICE;
+typedef struct _MTFTP6_INSTANCE MTFTP6_INSTANCE;
+
+#include "Mtftp6Driver.h"
+#include "Mtftp6Option.h"
+#include "Mtftp6Support.h"
+
+#define MTFTP6_SERVICE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'S')
+#define MTFTP6_INSTANCE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'I')
+
+#define MTFTP6_DEFAULT_SERVER_CMD_PORT 69
+#define MTFTP6_DEFAULT_TIMEOUT 3
+#define MTFTP6_GET_MAPPING_TIMEOUT 3
+#define MTFTP6_DEFAULT_MAX_RETRY 5
+#define MTFTP6_DEFAULT_BLK_SIZE 512
+#define MTFTP6_TICK_PER_SECOND 10000000U
+
+#define MTFTP6_SERVICE_FROM_THIS(a) CR (a, MTFTP6_SERVICE, ServiceBinding, MTFTP6_SERVICE_SIGNATURE)
+#define MTFTP6_INSTANCE_FROM_THIS(a) CR (a, MTFTP6_INSTANCE, Mtftp6, MTFTP6_INSTANCE_SIGNATURE)
+
+extern EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate;
+
+typedef struct _MTFTP6_GETINFO_CONTEXT{
+ EFI_MTFTP6_PACKET **Packet;
+ UINT32 *PacketLen;
+ EFI_STATUS Status;
+} MTFTP6_GETINFO_CONTEXT;
+
+//
+// Control block for MTFTP6 instance, it's per configuration data.
+//
+struct _MTFTP6_INSTANCE {
+ UINT32 Signature;
+ EFI_HANDLE Handle;
+ LIST_ENTRY Link;
+ EFI_MTFTP6_PROTOCOL Mtftp6;
+ MTFTP6_SERVICE *Service;
+ EFI_MTFTP6_CONFIG_DATA *Config;
+
+ EFI_MTFTP6_TOKEN *Token;
+ MTFTP6_EXT_OPTION_INFO ExtInfo;
+
+ UINT16 BlkSize;
+ UINT16 LastBlk;
+ LIST_ENTRY BlkList;
+
+ EFI_IPv6_ADDRESS ServerIp;
+ UINT16 ServerCmdPort;
+ UINT16 ServerDataPort;
+ UDP_IO *UdpIo;
+
+ EFI_IPv6_ADDRESS McastIp;
+ UINT16 McastPort;
+ UDP_IO *McastUdpIo;
+
+ NET_BUF *LastPacket;
+ UINT32 CurRetry;
+ UINT32 MaxRetry;
+ UINT32 PacketToLive;
+ UINT32 Timeout;
+
+ EFI_TPL OldTpl;
+ BOOLEAN IsTransmitted;
+ BOOLEAN IsMaster;
+ BOOLEAN InDestory;
+};
+
+//
+// Control block for MTFTP6 service, it's per Nic handle.
+//
+struct _MTFTP6_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ UINT16 ChildrenNum;
+ LIST_ENTRY Children;
+ //
+ // It is used to be as internal calculagraph for all instances.
+ //
+ EFI_EVENT Timer;
+ //
+ // It is used to maintain the parent-child relationship between
+ // mtftp driver and udp driver.
+ //
+ UDP_IO *DummyUdpIo;
+ BOOLEAN InDestory;
+};
+
+/**
+ Returns the current operating mode data for the MTFTP6 instance.
+
+ The GetModeData() function returns the current operating mode and
+ cached data packet for the MTFTP6 instance.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode
+ data is returned.
+
+ @retval EFI_SUCCESS The configuration data was returned successfully.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+ @retval EFI_INVALID_PARAMETER This is NULL, or ModeData is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6GetModeData (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ OUT EFI_MTFTP6_MODE_DATA *ModeData
+ );
+
+/**
+ Initializes, changes, or resets the default operational setting for
+ this EFI MTFTPv6 Protocol driver instance.
+
+ The Configure() function is used to set and change the configuration
+ data for this EFI MTFTPv6 Protocol driver instance. The configuration
+ data can be reset to startup defaults by calling Configure() with
+ MtftpConfigData set to NULL. Whenever the instance is reset, any
+ pending operation is aborted. By changing the EFI MTFTPv6 Protocol
+ driver instance configuration data, the client can connect to
+ different MTFTPv6 servers. The configuration parameters in
+ MtftpConfigData are used as the default parameters in later MTFTPv6
+ operations and can be overridden in later operations.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] MtftpConfigData Pointer to the configuration data structure.
+
+ @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ - This is NULL.
+ - MtftpConfigData.StationIp is neither zero nor one
+ of the configured IP addresses in the underlying IPv6 driver.
+ - MtftpCofigData.ServerIp is not a valid IPv6 unicast address.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there
+ is some MTFTP background operation in progress.
+ - MtftpCofigData.LocalPort is already in use.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be
+ allocated.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI
+ MTFTPv6 Protocol driver instance is not configured.
+ Note: It is not defined in the UEFI 2.3 Specification.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6Configure (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL
+ );
+
+/**
+ Get the information of the download from the server.
+
+ The GetInfo() function assembles an MTFTPv6 request packet
+ with options, sends it to the MTFTPv6 server, and may return
+ an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries
+ occur only if no response packets are received from the MTFTPv6
+ server before the timeout expires.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the
+ default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure()
+ function are used.
+ @param[in] Filename Pointer to ASCIIZ file name string.
+ @param[in] ModeStr Pointer to ASCIIZ mode string. If NULL, octet will be used
+ @param[in] OptionCount Number of option/value string pairs in OptionList.
+ @param[in] OptionList Pointer to array of option/value string pairs. Ignored if
+ OptionCount is zero.
+ @param[out] PacketLength The number of bytes in the returned packet.
+ @param[out] Packet The pointer to the received packet. This buffer must be freed by
+ the caller.
+
+ @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Filename is NULL.
+ - OptionCount is not zero and OptionList is NULL.
+ - One or more options in OptionList have wrong format.
+ - PacketLength is NULL.
+ - OverrideData.ServerIp is not a valid unicast IPv6 address.
+ @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by
+ this implementation.
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet.
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received, and the Packet is set to NULL.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received, and the Packet is set to NULL.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received, and the Packet is set to NULL.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received, and the Packet is set to NULL.
+ @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received, and the Packet is set to NULL.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6GetInfo (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL,
+ IN UINT8 *Filename,
+ IN UINT8 *ModeStr OPTIONAL,
+ IN UINT8 OptionCount,
+ IN EFI_MTFTP6_OPTION *OptionList OPTIONAL,
+ OUT UINT32 *PacketLength,
+ OUT EFI_MTFTP6_PACKET **Packet OPTIONAL
+ );
+
+/**
+ Parse the options in an MTFTPv6 OACK packet.
+
+ The ParseOptions() function parses the option fields in an MTFTPv6 OACK
+ packet and returns the number of options that were found, and optionally,
+ a list of pointers to the options in the packet. If one or more of the
+ option fields are not valid, then EFI_PROTOCOL_ERROR is returned and
+ *OptionCount and *OptionList stop at the last valid option.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] PacketLen Length of the OACK packet to be parsed.
+ @param[in] Packet Pointer to the OACK packet to be parsed.
+ @param[out] OptionCount Pointer to the number of options in the following OptionList.
+ @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the
+ OptionList points to the corresponding MTFTP option buffer
+ in the Packet. Call the EFI Boot Service FreePool() to
+ release the OptionList if the options in this OptionList
+ are not needed any more.
+
+ @retval EFI_SUCCESS The OACK packet was valid and the OptionCount, and
+ OptionList parameters have been updated.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - PacketLen is 0.
+ - Packet is NULL or Packet is not a valid MTFTPv6 packet.
+ - OptionCount is NULL.
+ @retval EFI_NOT_FOUND No options were found in the OACK packet.
+ @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated.
+ @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6ParseOptions (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN UINT32 PacketLen,
+ IN EFI_MTFTP6_PACKET *Packet,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL
+ );
+
+/**
+ Download a file from an MTFTPv6 server.
+
+ The ReadFile() function is used to initialize and start an MTFTPv6 download
+ process and optionally wait for completion. When the download operation
+ completes, whether successfully or not, the Token.Status field is updated
+ by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it
+ is not NULL.
+ Data can be downloaded from the MTFTPv6 server into either of the following
+ locations:
+ - A fixed buffer that is pointed to by Token.Buffer.
+ - A download service function that is pointed to by Token.CheckPacket.
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket
+ will be called first. If the call is successful, the packet will be stored
+ in Token.Buffer.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] Token Pointer to the token structure to provide the parameters that are
+ used in this operation.
+
+ @retval EFI_SUCCESS The data file has been transferred successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the
+ downloaded data in downloading process.
+ Note: It does not match the UEFI 2.3 Specification.
+ @retval EFI_ABORTED Current operation is aborted by user.
+ @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received.
+ Note: It is not defined in the UEFI 2.3 Specification.
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server.
+ @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6ReadFile (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token
+ );
+
+/**
+ Send a file to an MTFTPv6 server.
+
+ The WriteFile() function is used to initialize an uploading operation
+ with the given option list, and optionally, wait for completion. If one
+ or more of the options is not supported by the server, the unsupported
+ options are ignored and a standard TFTP process starts instead. When
+ the upload process completes, whether successfully or not, Token.Event
+ is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status.
+ The caller can supply the data to be uploaded in the following two modes:
+ - Through the user-provided buffer.
+ - Through a callback function.
+ With the user-provided buffer, the Token.BufferSize field indicates
+ the length of the buffer, and the driver will upload the data in the
+ buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver
+ will call this callback function to get more data from the user to upload.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] Token Pointer to the token structure to provide the parameters that are
+ used in this operation.
+
+ @retval EFI_SUCCESS The upload session has started.
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Filename is NULL.
+ - Token.OptionCount is not zero and Token.OptionList is NULL.
+ - One or more options in Token.OptionList have wrong format.
+ - Token.Buffer and Token.PacketNeeded are both NULL.
+ - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not
+ supported by this implementation.
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6WriteFile (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token
+ );
+
+/**
+ Download a data file directory from an MTFTPv6 server.
+
+ The ReadDirectory() function is used to return a list of files on the
+ MTFTPv6 server that are logically (or operationally) related to
+ Token.Filename. The directory request packet that is sent to the server
+ is built with the option list that was provided by caller, if present.
+ The file information that the server returns is put into either of
+ the following locations:
+ - A fixed buffer that is pointed to by Token.Buffer.
+ - A download service function that is pointed to by Token.CheckPacket.
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket
+ will be called first. If the call is successful, the packet will be stored
+ in Token.Buffer.
+
+ @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance.
+ @param[in] Token Pointer to the token structure to provide the parameters that are
+ used in this operation.
+
+ @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded.
+ @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token.Filename is NULL.
+ - Token.OptionCount is not zero and Token.OptionList is NULL.
+ - One or more options in Token.OptionList have wrong format.
+ - Token.Buffer and Token.CheckPacket are both NULL.
+ - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not
+ supported by this implementation.
+ @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6ReadDirectory (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token
+ );
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications
+ to increase the rate that data packets are moved between the
+ communications device and the transmit and receive queues.In some
+ systems, the periodic timer event in the managed network driver may
+ not poll the underlying communications device fast enough to transmit
+ and/or receive all data packets without missing incoming packets or
+ dropping outgoing packets. Drivers and applications that are
+ experiencing packet loss should try calling the Poll() function
+ more often.
+
+ @param[in] This The MTFTP6 protocol instance.
+
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp6Poll (
+ IN EFI_MTFTP6_PROTOCOL *This
+ );
+
+#endif
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c
new file mode 100644
index 0000000000..0dcf546fa8
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c
@@ -0,0 +1,416 @@
+/** @file
+ Mtftp6 option parse functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM] = {
+ "blksize",
+ "timeout",
+ "tsize",
+ "multicast"
+};
+
+
+/**
+ Parse the NULL terminated ASCII string of multicast option.
+
+ @param[in] Str The pointer to the Ascii string of multicast option.
+ @param[in] ExtInfo The pointer to the option information to be filled.
+
+ @retval EFI_SUCCESS Parse the multicast option successfully.
+ @retval EFI_INVALID_PARAMETER The string is malformatted.
+ @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of
+ resources.
+
+**/
+EFI_STATUS
+Mtftp6ParseMcastOption (
+ IN UINT8 *Str,
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Num;
+ CHAR8 *Ip6Str;
+ CHAR8 *TempStr;
+
+ //
+ // The multicast option is formated like "addr,port,mc"
+ // The server can also omit the ip and port, use ",,1"
+ //
+ if (*Str == ',') {
+
+ ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS));
+ } else {
+
+ Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str);
+ if (Ip6Str == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // The IPv6 address locates before comma in the input Str.
+ //
+ TempStr = Ip6Str;
+ while ((*TempStr != '\0') && (*TempStr != ',')) {
+ TempStr++;
+ }
+
+ *TempStr = '\0';
+
+ Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp);
+ FreePool (Ip6Str);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while ((*Str != '\0') && (*Str != ',')) {
+ Str++;
+ }
+ }
+
+ if (*Str != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Str++;
+
+ //
+ // Convert the port setting. the server can send us a port number or
+ // empty string. such as the port in ",,1"
+ //
+ if (*Str == ',') {
+
+ ExtInfo->McastPort = 0;
+ } else {
+
+ Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);
+
+ if (Num > 65535) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ExtInfo->McastPort = (UINT16) Num;
+
+ while (NET_IS_DIGIT (*Str)) {
+ Str++;
+ }
+ }
+
+ if (*Str != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Str++;
+
+ //
+ // Check the master/slave setting, 1 for master, 0 for slave.
+ //
+ Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str);
+
+ if (Num != 0 && Num != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ExtInfo->IsMaster = (BOOLEAN) (Num == 1);
+
+ while (NET_IS_DIGIT (*Str)) {
+ Str++;
+ }
+
+ if (*Str != '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the MTFTP6 extesion options.
+
+ @param[in] Options The pointer to the extension options list.
+ @param[in] Count The num of the extension options.
+ @param[in] IsRequest If FALSE, the extension options is included
+ by a request packet.
+ @param[in] ExtInfo The pointer to the option information to be filled.
+
+ @retval EFI_SUCCESS Parse the multicast option successfully.
+ @retval EFI_INVALID_PARAMETER There is one option is malformatted at least.
+ @retval EFI_UNSUPPORTED There is one option is not supported at least.
+
+**/
+EFI_STATUS
+Mtftp6ParseExtensionOption (
+ IN EFI_MTFTP6_OPTION *Options,
+ IN UINT32 Count,
+ IN BOOLEAN IsRequest,
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo
+ )
+{
+ EFI_STATUS Status;
+ EFI_MTFTP6_OPTION *Opt;
+ UINT32 Index;
+ UINT32 Value;
+
+ ExtInfo->BitMap = 0;
+
+ for (Index = 0; Index < Count; Index++) {
+
+ Opt = Options + Index;
+
+ if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) {
+ //
+ // block size option, valid value is between [8, 65464]
+ //
+ Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);
+
+ if ((Value < 8) || (Value > 65464)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ExtInfo->BlkSize = (UINT16) Value;
+ ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT;
+
+ } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) {
+ //
+ // timeout option, valid value is between [1, 255]
+ //
+ Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);
+
+ if (Value < 1 || Value > 255) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ExtInfo->Timeout = (UINT8) Value;
+ ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT;
+
+ } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) {
+ //
+ // tsize option, the biggest transfer supported is 4GB with block size option
+ //
+ ExtInfo->Tsize = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr);
+ ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT;
+
+ } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) {
+ //
+ // Multicast option, if it is a request, the value must be a zero string,
+ // otherwise, it must be like "addr,port,mc" string, mc indicates master.
+ //
+ if (!IsRequest) {
+
+ Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else if (*(Opt->ValueStr) != '\0') {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT;
+
+ } else if (IsRequest) {
+ //
+ // If it's a request, unsupported; else if it's a reply, ignore.
+ //
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Go through the packet to fill the options array with the start
+ addresses of each MTFTP option name/value pair.
+
+ @param[in] Packet The packet to be checked.
+ @param[in] PacketLen The length of the packet.
+ @param[in, out] Count The num of the Options on input.
+ The actual one on output.
+ @param[in] Options The option array to be filled.
+ It is optional.
+
+ @retval EFI_SUCCESS The packet has been parsed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is malformatted.
+ @retval EFI_BUFFER_TOO_SMALL The Options array is too small.
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received.
+
+**/
+EFI_STATUS
+Mtftp6ParsePacketOption (
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *Count,
+ IN EFI_MTFTP6_OPTION *Options OPTIONAL
+ )
+{
+ UINT8 *Cur;
+ UINT8 *Last;
+ UINT8 Num;
+ UINT8 *Name;
+ UINT8 *Value;
+
+ Num = 0;
+ Cur = (UINT8 *) Packet + MTFTP6_OPCODE_LEN;
+ Last = (UINT8 *) Packet + PacketLen - 1;
+
+ //
+ // process option name and value pairs.
+ // The last byte is always zero.
+ //
+ while (Cur < Last) {
+ Name = Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ if (Cur == Last) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Value = ++Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ Num++;
+
+ if (Options != NULL && Num <= *Count) {
+ Options[Num - 1].OptionStr = Name;
+ Options[Num - 1].ValueStr = Value;
+ }
+
+ Cur++;
+ }
+
+ //
+ // Return buffer too small if the buffer passed-in isn't enough.
+ //
+ if (*Count < Num || Options == NULL) {
+ *Count = Num;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *Count = Num;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Go through the packet, generate option list array and fill it
+ by the result of parse options.
+
+ @param[in] Packet The packet to be checked.
+ @param[in] PacketLen The length of the packet.
+ @param[in, out] OptionCount The num of the Options on input.
+ The actual one on output.
+ @param[out] OptionList The option list array to be generated
+ and filled. It is optional.
+
+ @retval EFI_SUCCESS The packet has been parsed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is malformatted.
+ @retval EFI_PROTOCOL_ERROR There is one option is malformatted at least.
+ @retval EFI_NOT_FOUND The packet has no options.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array.
+ @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small.
+
+**/
+EFI_STATUS
+Mtftp6ParseStart (
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ if (PacketLen == 0 || Packet == NULL || OptionCount == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *OptionCount = 0;
+
+ if (OptionList != NULL) {
+ *OptionList = NULL;
+ }
+
+ if (NTOHS (Packet->OpCode) != EFI_MTFTP6_OPCODE_OACK) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The last byte must be zero to terminate the options.
+ //
+ if (*((UINT8 *) Packet + PacketLen - 1) != 0) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Parse packet with NULL buffer for the first time to get the number
+ // of options in the packet.
+ //
+ Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, NULL);
+
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+
+ //
+ // Return not found if there is no option parsed.
+ //
+ if (*OptionCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Only need parse out the number of options.
+ //
+ if (OptionList == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Allocate the buffer according to the option number parsed before.
+ //
+ *OptionList = AllocateZeroPool (*OptionCount * sizeof (EFI_MTFTP6_OPTION));
+
+ if (*OptionList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Parse packet with allocated buffer for the second time to fill the pointer array
+ // of the options in the packet.
+ //
+ Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, *OptionList);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h
new file mode 100644
index 0000000000..8e2671fa21
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h
@@ -0,0 +1,148 @@
+/** @file
+ Mtftp6 option parse functions declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_MTFTP6_OPTION_H__
+#define __EFI_MTFTP6_OPTION_H__
+
+#include <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#define MTFTP6_SUPPORTED_OPTIONS_NUM 4
+#define MTFTP6_OPCODE_LEN 2
+#define MTFTP6_ERRCODE_LEN 2
+#define MTFTP6_BLKNO_LEN 2
+#define MTFTP6_DATA_HEAD_LEN 4
+
+//
+// The bit map definition for Mtftp6 extension options.
+//
+#define MTFTP6_OPT_BLKSIZE_BIT 0x01
+#define MTFTP6_OPT_TIMEOUT_BIT 0x02
+#define MTFTP6_OPT_TSIZE_BIT 0x04
+#define MTFTP6_OPT_MCAST_BIT 0x08
+
+extern CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM];
+
+typedef struct {
+ UINT16 BlkSize;
+ UINT8 Timeout;
+ UINT32 Tsize;
+ EFI_IPv6_ADDRESS McastIp;
+ UINT16 McastPort;
+ BOOLEAN IsMaster;
+ UINT32 BitMap;
+} MTFTP6_EXT_OPTION_INFO;
+
+/**
+ Parse the Ascii string of multi-cast option.
+
+ @param[in] Str The pointer to the Ascii string of multi-cast option.
+ @param[in] ExtInfo The pointer to the option information to be filled.
+
+ @retval EFI_SUCCESS Parse the multicast option successfully.
+ @retval EFI_INVALID_PARAMETER The string is malformatted.
+
+**/
+EFI_STATUS
+Mtftp6ParseMcastOption (
+ IN UINT8 *Str,
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo
+ );
+
+
+/**
+ Parse the MTFTP6 extesion options.
+
+ @param[in] Options The pointer to the extension options list.
+ @param[in] Count The num of the extension options.
+ @param[in] IsRequest If FALSE, the extension options is included
+ by a request packet.
+ @param[in] ExtInfo The pointer to the option information to be filled.
+
+ @retval EFI_SUCCESS Parse the multi-cast option successfully.
+ @retval EFI_INVALID_PARAMETER An option is malformatted.
+ @retval EFI_UNSUPPORTED An option is not supported.
+
+**/
+EFI_STATUS
+Mtftp6ParseExtensionOption (
+ IN EFI_MTFTP6_OPTION *Options,
+ IN UINT32 Count,
+ IN BOOLEAN IsRequest,
+ IN MTFTP6_EXT_OPTION_INFO *ExtInfo
+ );
+
+
+/**
+ Go through the packet to fill the options array with the start
+ addresses of each MTFTP option name/value pair.
+
+ @param[in] Packet The packet to be checked.
+ @param[in] PacketLen The length of the packet.
+ @param[in, out] Count The num of the Options on input.
+ The actual one on output.
+ @param[in] Options The option array to be filled
+ it's optional.
+
+ @retval EFI_SUCCESS The packet has been parsed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is malformatted
+ @retval EFI_BUFFER_TOO_SMALL The Options array is too small
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received.
+
+**/
+EFI_STATUS
+Mtftp6ParsePacketOption (
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *Count,
+ IN EFI_MTFTP6_OPTION *Options OPTIONAL
+ );
+
+
+/**
+ Go through the packet, generate option list array and fill it
+ by the result of parse options.
+
+ @param[in] Packet The packet to be checked.
+ @param[in] PacketLen The length of the packet.
+ @param[in, out] OptionCount The num of the Options on input.
+ The actual one on output.
+ @param[out] OptionList The option list array to be generated
+ and filled. It is optional.
+
+ @retval EFI_SUCCESS The packet has been parsed successfully.
+ @retval EFI_INVALID_PARAMETER The packet is malformatted.
+ @retval EFI_PROTOCOL_ERROR An option is malformatted.
+ @retval EFI_NOT_FOUND The packet has no options.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array.
+ @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small.
+
+**/
+EFI_STATUS
+Mtftp6ParseStart (
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL
+ );
+
+#endif
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c
new file mode 100644
index 0000000000..da364329fc
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c
@@ -0,0 +1,900 @@
+/** @file
+ Mtftp6 Rrq process functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+
+/**
+ Build and send a ACK packet for download.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] BlockNum The block number to be acked.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.
+ @retval EFI_SUCCESS The ACK has been sent.
+ @retval Others Failed to send the ACK.
+
+**/
+EFI_STATUS
+Mtftp6RrqSendAck (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 BlockNum
+ )
+{
+ EFI_MTFTP6_PACKET *Ack;
+ NET_BUF *Packet;
+
+ //
+ // Allocate net buffer to create ack packet.
+ //
+ Packet = NetbufAlloc (sizeof (EFI_MTFTP6_ACK_HEADER));
+
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ack = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (
+ Packet,
+ sizeof (EFI_MTFTP6_ACK_HEADER),
+ FALSE
+ );
+ ASSERT (Ack != NULL);
+
+ Ack->Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK);
+ Ack->Ack.Block[0] = HTONS (BlockNum);
+
+ //
+ // Reset current retry count of the instance.
+ //
+ Instance->CurRetry = 0;
+
+ return Mtftp6TransmitPacket (Instance, Packet);
+}
+
+
+/**
+ Deliver the received data block to the user, which can be saved
+ in the user provide buffer or through the CheckPacket callback.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the received packet.
+ @param[in] Len The packet length.
+ @param[out] UdpPacket The net buf of the received packet.
+
+ @retval EFI_SUCCESS The data was saved successfully.
+ @retval EFI_ABORTED The user tells to abort by return an error through
+ CheckPacket.
+ @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small, and buffer length is
+ updated to the actual buffer size needed.
+
+**/
+EFI_STATUS
+Mtftp6RrqSaveBlock (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 Len,
+ OUT NET_BUF **UdpPacket
+ )
+{
+ EFI_MTFTP6_TOKEN *Token;
+ EFI_STATUS Status;
+ UINT16 Block;
+ UINT64 Start;
+ UINT32 DataLen;
+ UINT64 TotalBlock;
+ BOOLEAN Completed;
+
+ Completed = FALSE;
+ Token = Instance->Token;
+ Block = NTOHS (Packet->Data.Block);
+ DataLen = Len - MTFTP6_DATA_HEAD_LEN;
+
+ //
+ // This is the last block, save the block num
+ //
+ if (DataLen < Instance->BlkSize) {
+ Completed = TRUE;
+ Instance->LastBlk = Block;
+ Mtftp6SetLastBlockNum (&Instance->BlkList, Block);
+ }
+
+ //
+ // Remove this block number from the file hole. If Mtftp6RemoveBlockNum
+ // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
+ // Note that : For bigger files, allowing the block counter to roll over
+ // to accept transfers of unlimited size. So TotalBlock is memorised as
+ // continuous block counter.
+ //
+ Status = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &TotalBlock);
+
+ if (Status == EFI_NOT_FOUND) {
+ return EFI_SUCCESS;
+ } else if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Token->CheckPacket != NULL) {
+ //
+ // Callback to the check packet routine with the received packet.
+ //
+ Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet);
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the Udp6Io might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if user aborted the current session.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "User aborted download"
+ );
+
+ return EFI_ABORTED;
+ }
+ }
+
+ if (Token->Buffer != NULL) {
+
+ Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize);
+ if (Start + DataLen <= Token->BufferSize) {
+ CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);
+ //
+ // Update the file size when received the last block
+ //
+ if ((Instance->LastBlk == Block) && Completed) {
+ Token->BufferSize = Start + DataLen;
+ }
+ } else if (Instance->LastBlk != 0) {
+ //
+ // Don't save the data if the buffer is too small, return
+ // EFI_BUFFER_TOO_SMALL if received the last packet. This
+ // will give a accurate file length.
+ //
+ Token->BufferSize = Start + DataLen;
+
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if no enough buffer.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_DISK_FULL,
+ (UINT8 *) "User provided memory block is too small"
+ );
+
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Process the received data packets. It will save the block
+ then send back an ACK if it is active.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the received packet.
+ @param[in] Len The length of the packet.
+ @param[out] UdpPacket The net buf of received packet.
+ @param[out] IsCompleted If TRUE, the download has been completed.
+ Otherwise, the download has not been completed.
+
+ @retval EFI_SUCCESS The data packet was successfully processed.
+ @retval EFI_ABORTED The download was aborted by the user.
+ @retval EFI_BUFFER_TOO_SMALL The user-provided buffer is too small.
+
+**/
+EFI_STATUS
+Mtftp6RrqHandleData (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 Len,
+ OUT NET_BUF **UdpPacket,
+ OUT BOOLEAN *IsCompleted
+ )
+{
+ EFI_STATUS Status;
+ UINT16 BlockNum;
+ INTN Expected;
+
+ *IsCompleted = FALSE;
+ BlockNum = NTOHS (Packet->Data.Block);
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // If we are active and received an unexpected packet, retransmit
+ // the last ACK then restart receiving. If we are passive, save
+ // the block.
+ //
+ if (Instance->IsMaster && (Expected != BlockNum)) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+
+ Mtftp6TransmitPacket (Instance, Instance->LastPacket);
+ return EFI_SUCCESS;
+ }
+
+ Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Reset the passive client's timer whenever it received a valid data packet.
+ //
+ if (!Instance->IsMaster) {
+ Instance->PacketToLive = Instance->Timeout * 2;
+ }
+
+ //
+ // Check whether we have received all the blocks. Send the ACK if we
+ // are active (unicast client or master client for multicast download).
+ // If we have received all the blocks, send an ACK even if we are passive
+ // to tell the server that we are done.
+ //
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ if (Instance->IsMaster || Expected < 0) {
+ if (Expected < 0) {
+ //
+ // If we are passive client, then the just received Block maybe
+ // isn't the last block. We need to send an ACK to the last block
+ // to inform the server that we are done. If we are active client,
+ // the Block == Instance->LastBlock.
+ //
+ BlockNum = Instance->LastBlk;
+ *IsCompleted = TRUE;
+
+ } else {
+ BlockNum = (UINT16) (Expected - 1);
+ }
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+
+ Mtftp6RrqSendAck (Instance, BlockNum);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Validate whether the options received in the server's OACK packet is valid.
+ The options are valid only if:
+ 1. The server doesn't include options not requested by us.
+ 2. The server can only use smaller blksize than that is requested.
+ 3. The server can only use the same timeout as requested.
+ 4. The server doesn't change its multicast channel.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] ReplyInfo The pointer to options information in reply packet.
+ @param[in] RequestInfo The pointer to requested options info.
+
+ @retval TRUE If the option in the OACK is valid.
+ @retval FALSE If the option is invalid.
+
+**/
+BOOLEAN
+Mtftp6RrqOackValid (
+ IN MTFTP6_INSTANCE *Instance,
+ IN MTFTP6_EXT_OPTION_INFO *ReplyInfo,
+ IN MTFTP6_EXT_OPTION_INFO *RequestInfo
+ )
+{
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||
+ (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))
+ ) {
+ return FALSE;
+ }
+
+ //
+ // The server can send ",,master" to client to change its master
+ // setting. But if it use the specific multicast channel, it can't
+ // change the setting.
+ //
+ if (((ReplyInfo->BitMap & MTFTP6_OPT_MCAST_BIT) != 0) && !NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {
+
+ if (!NetIp6IsUnspecifiedAddr (&ReplyInfo->McastIp) && CompareMem (
+ &ReplyInfo->McastIp,
+ &Instance->McastIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ ) != 0) {
+ return FALSE;
+ }
+
+ if ((ReplyInfo->McastPort != 0) && (ReplyInfo->McastPort != Instance->McastPort)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Configure Udp6Io to receive a packet from a multicast address.
+
+ @param[in] McastIo The pointer to the mcast Udp6Io.
+ @param[in] Context The pointer to the context.
+
+ @retval EFI_SUCCESS The mcast Udp6Io was successfully configured.
+ @retval Others Failed to configure the Udp6Io.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6RrqConfigMcastUdpIo (
+ IN UDP_IO *McastIo,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA *Udp6Cfg;
+ EFI_IPv6_ADDRESS Group;
+ MTFTP6_INSTANCE *Instance;
+
+ Udp6 = McastIo->Protocol.Udp6;
+ Udp6Cfg = &(McastIo->Config.Udp6);
+ Instance = (MTFTP6_INSTANCE *) Context;
+
+ //
+ // Set the configure data for the mcast Udp6Io.
+ //
+ ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ Udp6Cfg->AcceptPromiscuous = FALSE;
+ Udp6Cfg->AcceptAnyPort = FALSE;
+ Udp6Cfg->AllowDuplicatePort = FALSE;
+ Udp6Cfg->TrafficClass = 0;
+ Udp6Cfg->HopLimit = 128;
+ Udp6Cfg->ReceiveTimeout = 0;
+ Udp6Cfg->TransmitTimeout = 0;
+ Udp6Cfg->StationPort = Instance->McastPort;
+ Udp6Cfg->RemotePort = 0;
+
+ CopyMem (
+ &Udp6Cfg->RemoteAddress,
+ &Instance->ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ //
+ // Configure the mcast Udp6Io.
+ //
+ Status = Udp6->Configure (Udp6, Udp6Cfg);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Join the multicast group
+ //
+ CopyMem (&Group, &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));
+
+ return Udp6->Groups (Udp6, TRUE, &Group);
+}
+
+
+/**
+ Process the OACK packet for Rrq.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the received packet.
+ @param[in] Len The length of the packet.
+ @param[out] UdpPacket The net buf of received packet.
+ @param[out] IsCompleted If TRUE, the download has been completed.
+ Otherwise, the download has not been completed.
+
+ @retval EFI_DEVICE_ERROR Failed to create/start a multicast Udp6 child.
+ @retval EFI_TFTP_ERROR An error happened during the process.
+ @retval EFI_SUCCESS The OACK packet successfully processed.
+
+**/
+EFI_STATUS
+Mtftp6RrqHandleOack (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 Len,
+ OUT NET_BUF **UdpPacket,
+ OUT BOOLEAN *IsCompleted
+ )
+{
+ EFI_MTFTP6_OPTION *Options;
+ UINT32 Count;
+ MTFTP6_EXT_OPTION_INFO ExtInfo;
+ EFI_STATUS Status;
+ INTN Expected;
+
+ *IsCompleted = FALSE;
+
+ //
+ // If already started the master download, don't change the
+ // setting. Master download always succeeds.
+ //
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+ ASSERT (Expected != -1);
+
+ if (Instance->IsMaster && Expected != 1) {
+ return EFI_SUCCESS;
+ }
+
+ ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
+
+ //
+ // Parse the options in the packet.
+ //
+ Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Parse the extensive options in the packet.
+ //
+ Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);
+
+ if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) {
+ //
+ // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if invalid packet.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Mal-formated OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) {
+
+ //
+ // Save the multicast info. Always update the Master, only update the
+ // multicast IP address, block size, timeoute at the first time. If IP
+ // address is updated, create a UDP child to receive the multicast.
+ //
+ Instance->IsMaster = ExtInfo.IsMaster;
+
+ if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) {
+ if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if invalid multi-cast setting.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Illegal multicast setting"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+
+ //
+ // Create a UDP child then start receive the multicast from it.
+ //
+ CopyMem (
+ &Instance->McastIp,
+ &ExtInfo.McastIp,
+ sizeof (EFI_IP_ADDRESS)
+ );
+
+ Instance->McastPort = ExtInfo.McastPort;
+ Instance->McastUdpIo = UdpIoCreateIo (
+ Instance->Service->Controller,
+ Instance->Service->Image,
+ Mtftp6RrqConfigMcastUdpIo,
+ UDP_IO_UDP6_VERSION,
+ Instance
+ );
+
+ if (Instance->McastUdpIo == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = UdpIoRecvDatagram (
+ Instance->McastUdpIo,
+ Mtftp6RrqInput,
+ Instance,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if failed to create Udp6Io to receive.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION,
+ (UINT8 *) "Failed to create socket to receive multicast packet"
+ );
+
+ return Status;
+ }
+
+ //
+ // Update the parameters used.
+ //
+ if (ExtInfo.BlkSize != 0) {
+ Instance->BlkSize = ExtInfo.BlkSize;
+ }
+
+ if (ExtInfo.Timeout != 0) {
+ Instance->Timeout = ExtInfo.Timeout;
+ }
+ }
+
+ } else {
+
+ Instance->IsMaster = TRUE;
+
+ if (ExtInfo.BlkSize != 0) {
+ Instance->BlkSize = ExtInfo.BlkSize;
+ }
+
+ if (ExtInfo.Timeout != 0) {
+ Instance->Timeout = ExtInfo.Timeout;
+ }
+ }
+
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send an ACK to (Expected - 1) which is 0 for unicast download,
+ // or tell the server we want to receive the Expected block.
+ //
+ return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1));
+}
+
+
+/**
+ The packet process callback for Mtftp6 download.
+
+ @param[in] UdpPacket The pointer to the packet received.
+ @param[in] UdpEpt The pointer to the Udp6 access point.
+ @param[in] IoStatus The status from Udp6 instance.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *UdpEpt,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP6_INSTANCE *Instance;
+ EFI_MTFTP6_PACKET *Packet;
+ BOOLEAN IsCompleted;
+ BOOLEAN IsMcast;
+ EFI_STATUS Status;
+ UINT16 Opcode;
+ UINT32 TotalNum;
+ UINT32 Len;
+
+ Instance = (MTFTP6_INSTANCE *) Context;
+
+ NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);
+
+ Status = EFI_SUCCESS;
+ Packet = NULL;
+ IsCompleted = FALSE;
+ IsMcast = FALSE;
+ TotalNum = 0;
+
+ //
+ // Return error status if Udp6 instance failed to receive.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Find the port this packet is from to restart receive correctly.
+ //
+ if (CompareMem (
+ Ip6Swap128 (&UdpEpt->LocalAddr.v6),
+ &Instance->McastIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ ) == 0) {
+ IsMcast = TRUE;
+ } else {
+ IsMcast = FALSE;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client. The server
+ // is required to use the same port as RemotePort to multicast the
+ // data.
+ //
+ if (UdpEpt->RemotePort != Instance->ServerDataPort) {
+ if (Instance->ServerDataPort != 0) {
+ goto ON_EXIT;
+ } else {
+ //
+ // For the subsequent exchange of requests, reconfigure the udpio as
+ // (serverip, serverport, localip, localport).
+ // Ususally, the client set serverport as 0 to receive and reset it
+ // once the first packet arrives to send ack.
+ //
+ Instance->ServerDataPort = UdpEpt->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+ TotalNum = UdpPacket->BlockOpNum;
+
+ if (TotalNum > 1) {
+ Packet = AllocateZeroPool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ ASSERT (Packet != NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Callback to the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if ((Instance->Token->CheckPacket != NULL) &&
+ (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)
+ ) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp6,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (UdpPacket);
+ UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if user aborted the current session.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Switch the process routines by the operation code.
+ //
+ switch (Opcode) {
+ case EFI_MTFTP6_OPCODE_DATA:
+ if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) {
+ goto ON_EXIT;
+ }
+ //
+ // Handle the data packet of Rrq.
+ //
+ Status = Mtftp6RrqHandleData (
+ Instance,
+ Packet,
+ Len,
+ &UdpPacket,
+ &IsCompleted
+ );
+ break;
+
+ case EFI_MTFTP6_OPCODE_OACK:
+ if (IsMcast || Len <= MTFTP6_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+ //
+ // Handle the Oack packet of Rrq.
+ //
+ Status = Mtftp6RrqHandleOack (
+ Instance,
+ Packet,
+ Len,
+ &UdpPacket,
+ &IsCompleted
+ );
+ break;
+
+ default:
+ //
+ // Drop and return eror if received error message.
+ //
+ Status = EFI_TFTP_ERROR;
+ break;
+ }
+
+ON_EXIT:
+ //
+ // Free the resources, then if !EFI_ERROR (Status), restart the
+ // receive, otherwise end the session.
+ //
+ if (Packet != NULL && TotalNum > 1) {
+ FreePool (Packet);
+ }
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+ if (!EFI_ERROR (Status) && !IsCompleted) {
+ if (IsMcast) {
+ Status = UdpIoRecvDatagram (
+ Instance->McastUdpIo,
+ Mtftp6RrqInput,
+ Instance,
+ 0
+ );
+ } else {
+ Status = UdpIoRecvDatagram (
+ Instance->UdpIo,
+ Mtftp6RrqInput,
+ Instance,
+ 0
+ );
+ }
+ }
+ //
+ // Clean up the current session if failed to continue.
+ //
+ if (EFI_ERROR (Status) || IsCompleted) {
+ Mtftp6OperationClean (Instance, Status);
+ }
+}
+
+
+/**
+ Start the Mtftp6 instance to download. It first initializes some
+ of the internal states, then builds and sends an RRQ reqeuest packet.
+ Finally, it starts receive for the downloading.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of current packet.
+
+ @retval EFI_SUCCESS The Mtftp6 is started to download.
+ @retval Others Failed to start to download.
+
+**/
+EFI_STATUS
+Mtftp6RrqStart (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [1, 0xffff]. For example:
+ // the client sends an RRQ request to the server, the server
+ // transfers the DATA1 block. If option negoitation is ongoing,
+ // the server will send back an OACK, then client will send ACK0.
+ //
+ Status = Mtftp6InitBlockRange (&Instance->BlkList, 1, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6SendRequest (Instance, Operation);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (
+ Instance->UdpIo,
+ Mtftp6RrqInput,
+ Instance,
+ 0
+ );
+}
+
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
new file mode 100644
index 0000000000..24ce0e85ba
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
@@ -0,0 +1,1189 @@
+/** @file
+ Mtftp6 support functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+
+/**
+ Allocate a MTFTP block range, then init it to the range of [Start, End].
+
+ @param[in] Start The start block number.
+ @param[in] End The last block number in the range.
+
+ @return Range The range of the allocated block buffer.
+
+**/
+MTFTP6_BLOCK_RANGE *
+Mtftp6AllocateRange (
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));
+
+ if (Range == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Range->Link);
+ Range->Start = Start;
+ Range->End = End;
+ Range->Bound = End;
+
+ return Range;
+}
+
+
+/**
+ Initialize the block range for either RRQ or WRQ. RRQ and WRQ have
+ different requirements for Start and End. For example, during startup,
+ WRQ initializes its whole valid block range to [0, 0xffff]. This
+ is bacause the server will send an ACK0 to inform the user to start the
+ upload. When the client receives an ACK0, it will remove 0 from the range,
+ get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send the BLOCK1
+ in response to the client's RRQ. When received BLOCK1, the client will
+ remove it from the block range and send an ACK. It also works if there
+ is option negotiation.
+
+ @param[in] Head The block range head to initialize.
+ @param[in] Start The Start block number.
+ @param[in] End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range.
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp6InitBlockRange (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ Range = Mtftp6AllocateRange (Start, End);
+
+ if (Range == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (Head, &Range->Link);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the first valid block number on the range list.
+
+ @param[in] Head The block range head.
+
+ @retval ==-1 If the block range is empty.
+ @retval >-1 The first valid block number.
+
+**/
+INTN
+Mtftp6GetNextBlockNum (
+ IN LIST_ENTRY *Head
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ if (IsListEmpty (Head)) {
+ return -1;
+ }
+
+ Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);
+ return Range->Start;
+}
+
+
+/**
+ Set the last block number of the block range list. It
+ removes all the blocks after the Last. MTFTP initialize the
+ block range to the maximum possible range, such as [0, 0xffff]
+ for WRQ. When it gets the last block number, it calls
+ this function to set the last block number.
+
+ @param[in] Head The block range list.
+ @param[in] Last The last block number.
+
+**/
+VOID
+Mtftp6SetLastBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Last
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+
+ //
+ // Iterate from the tail to head to remove the block number
+ // after the last.
+ //
+ while (!IsListEmpty (Head)) {
+ Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);
+
+ if (Range->Start > Last) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ continue;
+ }
+
+ if (Range->End > Last) {
+ Range->End = Last;
+ }
+ return ;
+ }
+}
+
+
+/**
+ Remove the block number from the block range list.
+
+ @param[in] Head The block range list to remove from.
+ @param[in] Num The block number to remove.
+ @param[in] Completed Whether Num is the last block number
+ @param[out] TotalBlock The continuous block number in all
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list.
+ @retval EFI_SUCCESS The block number has been removed from the list.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+
+**/
+EFI_STATUS
+Mtftp6RemoveBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Num,
+ IN BOOLEAN Completed,
+ OUT UINT64 *TotalBlock
+ )
+{
+ MTFTP6_BLOCK_RANGE *Range;
+ MTFTP6_BLOCK_RANGE *NewRange;
+ LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+
+ //
+ // Each block represents a hole [Start, End] in the file,
+ // skip to the first range with End >= Num
+ //
+ Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
+
+ if (Range->End < Num) {
+ continue;
+ }
+
+ //
+ // There are three different cases for Start
+ // 1. (Start > Num) && (End >= Num):
+ // because all the holes before this one has the condition of
+ // End < Num, so this block number has been removed.
+ //
+ // 2. (Start == Num) && (End >= Num):
+ // Need to increase the Start by one, and if End == Num, this
+ // hole has been removed completely, remove it.
+ //
+ // 3. (Start < Num) && (End >= Num):
+ // if End == Num, only need to decrease the End by one because
+ // we have (Start < Num) && (Num == End), so (Start <= End - 1).
+ // if (End > Num), the hold is splited into two holes, with
+ // [Start, Num - 1] and [Num + 1, End].
+ //
+ if (Range->Start > Num) {
+ return EFI_NOT_FOUND;
+
+ } else if (Range->Start == Num) {
+ Range->Start++;
+
+ //
+ // Note that: RFC 1350 does not mention block counter roll-over,
+ // but several TFTP hosts implement the roll-over be able to accept
+ // transfers of unlimited size. There is no consensus, however, whether
+ // the counter should wrap around to zero or to one. Many implementations
+ // wrap to zero, because this is the simplest to implement. Here we choose
+ // this solution.
+ //
+ *TotalBlock = Num;
+
+ if (Range->Round > 0) {
+ *TotalBlock += Range->Bound + MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1;
+ }
+
+ if (Range->Start > Range->Bound) {
+ Range->Start = 0;
+ Range->Round ++;
+ }
+
+ if ((Range->Start > Range->End) || Completed) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ }
+
+ return EFI_SUCCESS;
+
+ } else {
+ if (Range->End == Num) {
+ Range->End--;
+ } else {
+ NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
+
+ if (NewRange == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Range->End = Num - 1;
+ NetListInsertAfter (&Range->Link, &NewRange->Link);
+ }
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Configure the opened Udp6 instance until the corresponding Ip6 instance
+ has been configured.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] UdpCfgData The pointer to the Udp6 configure data.
+
+ @retval EFI_SUCCESS Configure the Udp6 instance successfully.
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not
+ been configured yet.
+
+**/
+EFI_STATUS
+Mtftp6GetMapping (
+ IN UDP_IO *UdpIo,
+ IN EFI_UDP6_CONFIG_DATA *UdpCfgData
+ )
+{
+ EFI_IP6_MODE_DATA Ip6Mode;
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ Event = NULL;
+ Udp6 = UdpIo->Protocol.Udp6;
+
+ //
+ // Create a timer to check whether the Ip6 instance configured or not.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->SetTimer (
+ Event,
+ TimerRelative,
+ MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check the Ip6 mode data till timeout.
+ //
+ while (EFI_ERROR (gBS->CheckEvent (Event))) {
+
+ Udp6->Poll (Udp6);
+
+ Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);
+
+ if (!EFI_ERROR (Status)) {
+
+ if (Ip6Mode.IsConfigured) {
+ //
+ // Continue to configure the Udp6 instance.
+ //
+ Status = Udp6->Configure (Udp6, UdpCfgData);
+ } else {
+ Status = EFI_NO_MAPPING;
+ }
+ }
+ }
+
+ON_EXIT:
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ return Status;
+}
+
+
+/**
+ The dummy configure routine for create a new Udp6 Io.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] Context The pointer to the context.
+
+ @retval EFI_SUCCESS This value is always returned.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ConfigDummyUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The configure routine for Mtftp6 instance to transmit/receive.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] ServerIp The pointer to the server address.
+ @param[in] ServerPort The pointer to the server port.
+ @param[in] LocalIp The pointer to the local address.
+ @param[in] LocalPort The pointer to the local port.
+
+ @retval EFI_SUCCESS Configured the Udp6 Io for Mtftp6 successfully.
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been
+ configured yet.
+
+**/
+EFI_STATUS
+Mtftp6ConfigUdpIo (
+ IN UDP_IO *UdpIo,
+ IN EFI_IPv6_ADDRESS *ServerIp,
+ IN UINT16 ServerPort,
+ IN EFI_IPv6_ADDRESS *LocalIp,
+ IN UINT16 LocalPort
+ )
+{
+ EFI_STATUS Status;
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA *Udp6Cfg;
+
+ Udp6 = UdpIo->Protocol.Udp6;
+ Udp6Cfg = &(UdpIo->Config.Udp6);
+
+ ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ //
+ // Set the Udp6 Io configure data.
+ //
+ Udp6Cfg->AcceptPromiscuous = FALSE;
+ Udp6Cfg->AcceptAnyPort = FALSE;
+ Udp6Cfg->AllowDuplicatePort = FALSE;
+ Udp6Cfg->TrafficClass = 0;
+ Udp6Cfg->HopLimit = 128;
+ Udp6Cfg->ReceiveTimeout = 0;
+ Udp6Cfg->TransmitTimeout = 0;
+ Udp6Cfg->StationPort = LocalPort;
+ Udp6Cfg->RemotePort = ServerPort;
+
+ CopyMem (
+ &Udp6Cfg->StationAddress,
+ LocalIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ CopyMem (
+ &Udp6Cfg->RemoteAddress,
+ ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ //
+ // Configure the Udp6 instance with current configure data.
+ //
+ Status = Udp6->Configure (Udp6, Udp6Cfg);
+
+ if (Status == EFI_NO_MAPPING) {
+
+ return Mtftp6GetMapping (UdpIo, Udp6Cfg);
+ }
+
+ return Status;
+}
+
+
+/**
+ Build and transmit the request packet for the Mtftp6 instance.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of this packet.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request.
+ @retval EFI_SUCCESS The request is built and sent.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6SendRequest (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_MTFTP6_PACKET *Packet;
+ EFI_MTFTP6_OPTION *Options;
+ EFI_MTFTP6_TOKEN *Token;
+ NET_BUF *Nbuf;
+ UINT8 *Mode;
+ UINT8 *Cur;
+ UINT32 Len1;
+ UINT32 Len2;
+ UINT32 Len;
+ UINTN Index;
+
+ Token = Instance->Token;
+ Options = Token->OptionList;
+ Mode = Token->ModeStr;
+
+ if (Mode == NULL) {
+ Mode = (UINT8 *) "octet";
+ }
+
+ //
+ // The header format of RRQ/WRQ packet is:
+ //
+ // 2 bytes string 1 byte string 1 byte
+ // ------------------------------------------------
+ // | Opcode | Filename | 0 | Mode | 0 |
+ // ------------------------------------------------
+ //
+ // The common option format is:
+ //
+ // string 1 byte string 1 byte
+ // ---------------------------------------
+ // | OptionStr | 0 | ValueStr | 0 |
+ // ---------------------------------------
+ //
+
+ //
+ // Compute the size of new Mtftp6 packet.
+ //
+ Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename);
+ Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode);
+ Len = Len1 + Len2 + 4;
+
+ for (Index = 0; Index < Token->OptionCount; Index++) {
+ Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
+ Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
+ Len += Len1 + Len2 + 2;
+ }
+
+ //
+ // Allocate a packet then copy the data.
+ //
+ if ((Nbuf = NetbufAlloc (Len)) == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the opcode, filename and mode into packet.
+ //
+ Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);
+ ASSERT (Packet != NULL);
+
+ Packet->OpCode = HTONS (Operation);
+ Cur = Packet->Rrq.Filename;
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename);
+ Cur += AsciiStrLen ((CHAR8 *) Token->Filename) + 1;
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode);
+ Cur += AsciiStrLen ((CHAR8 *) Mode) + 1;
+
+ //
+ // Copy all the extension options into the packet.
+ //
+ for (Index = 0; Index < Token->OptionCount; ++Index) {
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr);
+ Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1;
+ Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr);
+ Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1;
+ }
+
+ //
+ // Save the packet buf for retransmit
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ Instance->LastPacket = Nbuf;
+ Instance->CurRetry = 0;
+
+ return Mtftp6TransmitPacket (Instance, Nbuf);
+}
+
+
+/**
+ Build and send an error packet.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] ErrCode The error code in the packet.
+ @param[in] ErrInfo The error message in the packet.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet.
+ @retval EFI_SUCCESS The error packet is transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6SendError (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8* ErrInfo
+ )
+{
+ NET_BUF *Nbuf;
+ EFI_MTFTP6_PACKET *TftpError;
+ UINT32 Len;
+
+ //
+ // Allocate a packet then copy the data.
+ //
+ Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));
+ Nbuf = NetbufAlloc (Len);
+
+ if (Nbuf == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);
+
+ if (TftpError == NULL) {
+ NetbufFree (Nbuf);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TftpError->OpCode = HTONS (EFI_MTFTP6_OPCODE_ERROR);
+ TftpError->Error.ErrorCode = HTONS (ErrCode);
+
+ AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo);
+
+ //
+ // Save the packet buf for retransmit
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ Instance->LastPacket = Nbuf;
+ Instance->CurRetry = 0;
+
+ return Mtftp6TransmitPacket (Instance, Nbuf);
+}
+
+
+/**
+ The callback function called when the packet is transmitted.
+
+ @param[in] Packet The pointer to the packet.
+ @param[in] UdpEpt The pointer to the Udp6 access point.
+ @param[in] IoStatus The result of the transmission.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6OnPacketSent (
+ IN NET_BUF *Packet,
+ IN UDP_END_POINT *UdpEpt,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ NetbufFree (Packet);
+ *(BOOLEAN *) Context = TRUE;
+}
+
+
+/**
+ Send the packet for the Mtftp6 instance.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the packet to be sent.
+
+ @retval EFI_SUCCESS The packet was sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6TransmitPacket (
+ IN MTFTP6_INSTANCE *Instance,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_UDP6_PROTOCOL *Udp6;
+ EFI_UDP6_CONFIG_DATA Udp6CfgData;
+ EFI_STATUS Status;
+ UINT16 *Temp;
+ UINT16 Value;
+ UINT16 OpCode;
+
+ ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));
+ Udp6 = Instance->UdpIo->Protocol.Udp6;
+
+ //
+ // Set the live time of the packet.
+ //
+ Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);
+
+ Temp = (UINT16 *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Temp != NULL);
+
+ Value = *Temp;
+ OpCode = NTOHS (Value);
+
+ if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {
+ //
+ // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as
+ // (serverip, 69, localip, localport) to send.
+ // Usually local address and local port are both default as zero.
+ //
+ Status = Udp6->Configure (Udp6, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ConfigUdpIo (
+ Instance->UdpIo,
+ &Instance->ServerIp,
+ Instance->ServerCmdPort,
+ &Instance->Config->StationIp,
+ Instance->Config->LocalPort
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the current local address and port by get Udp6 mode data.
+ //
+ Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NET_GET_REF (Packet);
+
+ Instance->IsTransmitted = FALSE;
+
+ Status = UdpIoSendDatagram (
+ Instance->UdpIo,
+ Packet,
+ NULL,
+ NULL,
+ Mtftp6OnPacketSent,
+ &Instance->IsTransmitted
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Packet);
+ return Status;
+ }
+
+ //
+ // Poll till the packet sent out from the ip6 queue.
+ //
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ while (!Instance->IsTransmitted) {
+ Udp6->Poll (Udp6);
+ }
+
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // For the subsequent exchange of such requests, reconfigure the Udp6Io as
+ // (serverip, 0, localip, localport) to receive.
+ // Currently local address and local port are specified by Udp6 mode data.
+ //
+ Status = Udp6->Configure (Udp6, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ConfigUdpIo (
+ Instance->UdpIo,
+ &Instance->ServerIp,
+ Instance->ServerDataPort,
+ &Udp6CfgData.StationAddress,
+ Udp6CfgData.StationPort
+ );
+ } else {
+ //
+ // For the data exchange, configure the Udp6Io as (serverip, dataport,
+ // localip, localport) to send/receive.
+ // Currently local address and local port are specified by Udp6 mode data.
+ //
+ Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {
+
+ Status = Udp6->Configure (Udp6, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ConfigUdpIo (
+ Instance->UdpIo,
+ &Instance->ServerIp,
+ Instance->ServerDataPort,
+ &Udp6CfgData.StationAddress,
+ Udp6CfgData.StationPort
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ NET_GET_REF (Packet);
+
+ Instance->IsTransmitted = FALSE;
+
+ Status = UdpIoSendDatagram (
+ Instance->UdpIo,
+ Packet,
+ NULL,
+ NULL,
+ Mtftp6OnPacketSent,
+ &Instance->IsTransmitted
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Packet);
+ }
+
+ //
+ // Poll till the packet sent out from the ip6 queue.
+ //
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ while (!Instance->IsTransmitted) {
+ Udp6->Poll (Udp6);
+ }
+
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ }
+
+ return Status;
+}
+
+
+/**
+ Check packet for GetInfo callback routine.
+
+ GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect
+ the first packet from server, then abort the session.
+
+ @param[in] This The pointer to the Mtftp6 protocol.
+ @param[in] Token The pointer to the Mtftp6 token.
+ @param[in] PacketLen The length of the packet.
+ @param[in] Packet The pointer to the received packet.
+
+ @retval EFI_ABORTED Abort the Mtftp6 operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6CheckPacket (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP6_PACKET *Packet
+ )
+{
+ MTFTP6_GETINFO_CONTEXT *Context;
+ UINT16 OpCode;
+
+ Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;
+ OpCode = NTOHS (Packet->OpCode);
+
+ //
+ // Set the GetInfo's return status according to the OpCode.
+ //
+ switch (OpCode) {
+ case EFI_MTFTP6_OPCODE_ERROR:
+ Context->Status = EFI_TFTP_ERROR;
+ break;
+
+ case EFI_MTFTP6_OPCODE_OACK:
+ Context->Status = EFI_SUCCESS;
+ break;
+
+ default:
+ Context->Status = EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Allocate buffer then copy the packet over. Use gBS->AllocatePool
+ // in case NetAllocatePool will implements something tricky.
+ //
+ *(Context->Packet) = AllocateZeroPool (PacketLen);
+
+ if (*(Context->Packet) == NULL) {
+ Context->Status = EFI_OUT_OF_RESOURCES;
+ return EFI_ABORTED;
+ }
+
+ *(Context->PacketLen) = PacketLen;
+ CopyMem (*(Context->Packet), Packet, PacketLen);
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Clean up the current Mtftp6 operation.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Result The result to be returned to the user.
+
+**/
+VOID
+Mtftp6OperationClean (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_STATUS Result
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP6_BLOCK_RANGE *Block;
+
+ //
+ // Clean up the current token and event.
+ //
+ if (Instance->Token != NULL) {
+ Instance->Token->Status = Result;
+ if (Instance->Token->Event != NULL) {
+ gBS->SignalEvent (Instance->Token->Event);
+ }
+ Instance->Token = NULL;
+ }
+
+ //
+ // Clean up the corresponding Udp6Io.
+ //
+ if (Instance->UdpIo != NULL) {
+ UdpIoCleanIo (Instance->UdpIo);
+ }
+
+ if (Instance->McastUdpIo != NULL) {
+ UdpIoFreeIo (Instance->McastUdpIo);
+ Instance->McastUdpIo = NULL;
+ }
+
+ //
+ // Clean up the stored last packet.
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ Instance->LastPacket = NULL;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
+ RemoveEntryList (Entry);
+ FreePool (Block);
+ }
+
+ //
+ // Reinitialize the corresponding fields of the Mtftp6 operation.
+ //
+ ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
+ ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));
+ ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));
+
+ Instance->ServerCmdPort = 0;
+ Instance->ServerDataPort = 0;
+ Instance->McastPort = 0;
+ Instance->BlkSize = 0;
+ Instance->LastBlk = 0;
+ Instance->PacketToLive = 0;
+ Instance->MaxRetry = 0;
+ Instance->CurRetry = 0;
+ Instance->Timeout = 0;
+ Instance->IsMaster = TRUE;
+}
+
+
+/**
+ Start the Mtftp6 instance to perform the operation, such as read file,
+ write file, and read directory.
+
+ @param[in] This The MTFTP session.
+ @param[in] Token The token than encapsues the user's request.
+ @param[in] OpCode The operation to perform.
+
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.
+ @retval EFI_SUCCESS The operation is successfully started.
+
+**/
+EFI_STATUS
+Mtftp6OperationStart (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token,
+ IN UINT16 OpCode
+ )
+{
+ MTFTP6_INSTANCE *Instance;
+ EFI_STATUS Status;
+
+ if (This == NULL ||
+ Token == NULL ||
+ Token->Filename == NULL ||
+ (Token->OptionCount != 0 && Token->OptionList == NULL) ||
+ (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // At least define one method to collect the data for download.
+ //
+ if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&
+ Token->Buffer == NULL &&
+ Token->CheckPacket == NULL
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // At least define one method to provide the data for upload.
+ //
+ if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP6_INSTANCE_FROM_THIS (This);
+
+ if (Instance->Config == NULL) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Instance->Token != NULL) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ Status = EFI_SUCCESS;
+ Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Parse the extension options in the request packet.
+ //
+ if (Token->OptionCount != 0) {
+
+ Status = Mtftp6ParseExtensionOption (
+ Token->OptionList,
+ Token->OptionCount,
+ TRUE,
+ &Instance->ExtInfo
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Initialize runtime data from config data or override data.
+ //
+ Instance->Token = Token;
+ Instance->ServerCmdPort = Instance->Config->InitialServerPort;
+ Instance->ServerDataPort = 0;
+ Instance->MaxRetry = Instance->Config->TryCount;
+ Instance->Timeout = Instance->Config->TimeoutValue;
+ Instance->IsMaster = TRUE;
+
+ CopyMem (
+ &Instance->ServerIp,
+ &Instance->Config->ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ if (Token->OverrideData != NULL) {
+ Instance->ServerCmdPort = Token->OverrideData->ServerPort;
+ Instance->MaxRetry = Token->OverrideData->TryCount;
+ Instance->Timeout = Token->OverrideData->TimeoutValue;
+
+ CopyMem (
+ &Instance->ServerIp,
+ &Token->OverrideData->ServerIp,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ }
+
+ //
+ // Set default value for undefined parameters.
+ //
+ if (Instance->ServerCmdPort == 0) {
+ Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;
+ }
+ if (Instance->BlkSize == 0) {
+ Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;
+ }
+ if (Instance->MaxRetry == 0) {
+ Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;
+ }
+ if (Instance->Timeout == 0) {
+ Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;
+ }
+
+ Token->Status = EFI_NOT_READY;
+
+ //
+ // Switch the routines by the operation code.
+ //
+ switch (OpCode) {
+ case EFI_MTFTP6_OPCODE_RRQ:
+ Status = Mtftp6RrqStart (Instance, OpCode);
+ break;
+
+ case EFI_MTFTP6_OPCODE_DIR:
+ Status = Mtftp6RrqStart (Instance, OpCode);
+ break;
+
+ case EFI_MTFTP6_OPCODE_WRQ:
+ Status = Mtftp6WrqStart (Instance, OpCode);
+ break;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Return immediately for asynchronous or poll the instance for synchronous.
+ //
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ if (Token->Event == NULL) {
+ while (Token->Status == EFI_NOT_READY) {
+ This->Poll (This);
+ }
+ return Token->Status;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ Mtftp6OperationClean (Instance, Status);
+ gBS->RestoreTPL (Instance->OldTpl);
+
+ return Status;
+}
+
+
+/**
+ The timer ticking routine for the Mtftp6 instance.
+
+ @param[in] Event The pointer to the ticking event.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MTFTP6_SERVICE *Service;
+ MTFTP6_INSTANCE *Instance;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ EFI_MTFTP6_TOKEN *Token;
+ EFI_STATUS Status;
+
+ Service = (MTFTP6_SERVICE *) Context;
+
+ //
+ // Iterate through all the children of the Mtftp service instance. Time
+ // out the packet. If maximum retries reached, clean the session up.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);
+
+ if (Instance->Token == NULL) {
+ continue;
+ }
+
+ if (Instance->PacketToLive > 0) {
+ Instance->PacketToLive--;
+ continue;
+ }
+
+ Instance->CurRetry++;
+ Token = Instance->Token;
+
+ if (Token->TimeoutCallback != NULL) {
+ //
+ // Call the timeout callback routine if has.
+ //
+ Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer in time out"
+ );
+ Mtftp6OperationClean (Instance, EFI_ABORTED);
+ continue;
+ }
+ }
+
+ //
+ // Retransmit the packet if haven't reach the maxmium retry count,
+ // otherwise exit the transfer.
+ //
+ if (Instance->CurRetry < Instance->MaxRetry) {
+ Mtftp6TransmitPacket (Instance, Instance->LastPacket);
+ } else {
+ Mtftp6OperationClean (Instance, EFI_TIMEOUT);
+ continue;
+ }
+ }
+}
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h
new file mode 100644
index 0000000000..37f03fe298
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h
@@ -0,0 +1,359 @@
+/** @file
+ Mtftp6 support functions declaration.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_MTFTP6_SUPPORT_H__
+#define __EFI_MTFTP6_SUPPORT_H__
+
+//
+// The structure representing a range of block numbers, [Start, End].
+// It is used to remember the holes in the MTFTP block space. If all
+// the holes are filled in, then the download or upload has completed.
+//
+typedef struct {
+ LIST_ENTRY Link;
+ INTN Start;
+ INTN End;
+ INTN Round;
+ INTN Bound;
+} MTFTP6_BLOCK_RANGE;
+
+
+/**
+ Initialize the block range for either RRQ or WRQ. RRQ and WRQ have
+ different requirements for Start and End. For example, during startup,
+ WRQ initializes its whole valid block range to [0, 0xffff]. This
+ is because the server will send an ACK0 to inform the user to start the
+ upload. When the client receives an ACK0, it will remove 0 from the range,
+ get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send us the BLOCK1
+ in response to the client's RRQ. When BLOCK1 is received, the client will
+ remove it from the block range and send an ACK. It also works if there
+ is option negotiation.
+
+ @param[in] Head The block range head to initialize.
+ @param[in] Start The Start block number.
+ @param[in] End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range.
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp6InitBlockRange (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ );
+
+
+/**
+ Get the first valid block number on the range list.
+
+ @param[in] Head The block range head.
+
+ @retval ==-1 If the block range is empty.
+ @retval >-1 The first valid block number.
+
+**/
+INTN
+Mtftp6GetNextBlockNum (
+ IN LIST_ENTRY *Head
+ );
+
+
+/**
+ Set the last block number of the block range list. It
+ removes all the blocks after the Last. MTFTP initialize the
+ block range to the maximum possible range, such as [0, 0xffff]
+ for WRQ. When it gets the last block number, it calls
+ this function to set the last block number.
+
+ @param[in] Head The block range list.
+ @param[in] Last The last block number.
+
+**/
+VOID
+Mtftp6SetLastBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Last
+ );
+
+
+/**
+ Remove the block number from the block range list.
+
+ @param[in] Head The block range list to remove from.
+ @param[in] Num The block number to remove.
+ @param[in] Completed Whether Num is the last block number
+ @param[out] TotalBlock The continuous block number in all
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list.
+ @retval EFI_SUCCESS The block number has been removed from the list.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+
+**/
+EFI_STATUS
+Mtftp6RemoveBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Num,
+ IN BOOLEAN Completed,
+ OUT UINT64 *TotalBlock
+ );
+
+
+/**
+ Build and transmit the request packet for the Mtftp6 instance.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of this packet.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request.
+ @retval EFI_SUCCESS The request was built and sent.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6SendRequest (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ );
+
+
+/**
+ Build and send an error packet.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] ErrCode The error code in the packet.
+ @param[in] ErrInfo The error message in the packet.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet.
+ @retval EFI_SUCCESS The error packet was transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6SendError (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8* ErrInfo
+ );
+
+
+/**
+ Send the packet for the Mtftp6 instance.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the packet to be sent.
+
+ @retval EFI_SUCCESS The packet was sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp6TransmitPacket (
+ IN MTFTP6_INSTANCE *Instance,
+ IN NET_BUF *Packet
+ );
+
+
+/**
+ Check packet for GetInfo callback routine.
+
+ @param[in] This The pointer to the Mtftp6 protocol.
+ @param[in] Token The pointer to the Mtftp6 token.
+ @param[in] PacketLen The length of the packet
+ @param[in] Packet The pointer to the received packet.
+
+ @retval EFI_SUCCESS The check process passed successfully.
+ @retval EFI_ABORTED Abort the Mtftp6 operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6CheckPacket (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP6_PACKET *Packet
+ );
+
+
+/**
+ The dummy configure routine for create a new Udp6 Io.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] Context The pointer to the context.
+
+ @retval EFI_SUCCESS The value is always returned.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp6ConfigDummyUdpIo (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ );
+
+
+/**
+ The configure routine for the Mtftp6 instance to transmit/receive.
+
+ @param[in] UdpIo The pointer to the Udp6 Io.
+ @param[in] ServerIp The pointer to the server address.
+ @param[in] ServerPort The pointer to the server port.
+ @param[in] LocalIp The pointer to the local address.
+ @param[in] LocalPort The pointer to the local port.
+
+ @retval EFI_SUCCESS Configure the Udp6 Io for Mtftp6 successfully.
+ @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been
+ configured yet.
+
+**/
+EFI_STATUS
+Mtftp6ConfigUdpIo (
+ IN UDP_IO *UdpIo,
+ IN EFI_IPv6_ADDRESS *ServerIp,
+ IN UINT16 ServerPort,
+ IN EFI_IPv6_ADDRESS *LocalIp,
+ IN UINT16 LocalPort
+ );
+
+
+/**
+ Clean up the current Mtftp6 operation.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Result The result to be returned to the user.
+
+**/
+VOID
+Mtftp6OperationClean (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_STATUS Result
+ );
+
+
+/**
+ Start the Mtftp6 instance to perform the operation, such as read file,
+ write file, and read directory.
+
+ @param[in] This The MTFTP session
+ @param[in] Token The token that encapsulates the user's request.
+ @param[in] OpCode The operation to perform.
+
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.
+ @retval EFI_SUCCESS The operation was successfully started.
+
+**/
+EFI_STATUS
+Mtftp6OperationStart (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token,
+ IN UINT16 OpCode
+ );
+
+
+/**
+ The timer ticking routine for the Mtftp6 instance.
+
+ @param[in] Event The pointer to the ticking event.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+
+/**
+ The packet process callback for Mtftp6 upload.
+
+ @param[in] UdpPacket The pointer to the packet received.
+ @param[in] UdpEpt The pointer to the Udp6 access point.
+ @param[in] IoStatus The status from the Udp6 instance.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6WrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *UdpEpt,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+
+/**
+ Start the Mtftp6 instance to upload. It will first init some states,
+ then send the WRQ request packet, and start to receive the packet.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of current packet.
+
+ @retval EFI_SUCCESS The Mtftp6 was started to upload.
+ @retval Others Failed to start to upload.
+
+**/
+EFI_STATUS
+Mtftp6WrqStart (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ );
+
+
+/**
+ The packet process callback for Mtftp6 download.
+
+ @param[in] UdpPacket The pointer to the packet received.
+ @param[in] UdpEpt The pointer to the Udp6 access point.
+ @param[in] IoStatus The status from Udp6 instance.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *UdpEpt,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+
+/**
+ Start the Mtftp6 instance to download. It first initializes some
+ of the internal states then builds and sends an RRQ reqeuest packet.
+ Finally, it starts receive for the downloading.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of current packet.
+
+ @retval EFI_SUCCESS The Mtftp6 was started to download.
+ @retval Others Failed to start to download.
+
+**/
+EFI_STATUS
+Mtftp6RrqStart (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ );
+
+#endif
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
new file mode 100644
index 0000000000..69ef4d2093
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
@@ -0,0 +1,602 @@
+/** @file
+ Mtftp6 Wrq process functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+
+
+/**
+ Build and send a Mtftp6 data packet for upload.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] BlockNum The block num to be sent.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.
+ @retval EFI_SUCCESS The data packet was sent.
+ @retval EFI_ABORTED The user aborted this process.
+
+**/
+EFI_STATUS
+Mtftp6WrqSendBlock (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 BlockNum
+ )
+{
+ EFI_MTFTP6_PACKET *Packet;
+ EFI_MTFTP6_TOKEN *Token;
+ NET_BUF *UdpPacket;
+ EFI_STATUS Status;
+ UINT16 DataLen;
+ UINT8 *DataBuf;
+ UINT64 Start;
+
+ //
+ // Allocate net buffer to create data packet.
+ //
+ UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN);
+
+ if (UdpPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (
+ UdpPacket,
+ MTFTP6_DATA_HEAD_LEN,
+ FALSE
+ );
+ ASSERT (Packet != NULL);
+
+ Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA);
+ Packet->Data.Block = HTONS (BlockNum);
+
+ //
+ // Read the block from either the buffer or PacketNeeded callback
+ //
+ Token = Instance->Token;
+ DataLen = Instance->BlkSize;
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
+
+ if (Token->BufferSize < Start + Instance->BlkSize) {
+ DataLen = (UINT16) (Token->BufferSize - Start);
+ Instance->LastBlk = BlockNum;
+ Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);
+ }
+
+ } else {
+ //
+ // Get data from PacketNeeded
+ //
+ DataBuf = NULL;
+ Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf);
+
+ if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
+ if (DataBuf != NULL) {
+ gBS->FreePool (DataBuf);
+ }
+ //
+ // The received packet has already been freed.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+
+ return EFI_ABORTED;
+ }
+
+ if (DataLen < Instance->BlkSize) {
+ Instance->LastBlk = BlockNum;
+ Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, DataBuf, DataLen);
+ gBS->FreePool (DataBuf);
+ }
+ }
+
+ //
+ // Reset current retry count of the instance.
+ //
+ Instance->CurRetry = 0;
+
+ return Mtftp6TransmitPacket (Instance, UdpPacket);
+}
+
+
+/**
+ Function to handle received ACK packet. If the ACK number matches the
+ expected block number, with more data pending, send the next
+ block. Otherwise, tell the caller that we are done.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the received packet.
+ @param[in] Len The length of the packet.
+ @param[out] UdpPacket The net buf of received packet.
+ @param[out] IsCompleted If TRUE, the upload has been completed.
+ Otherwise, the upload has not been completed.
+
+ @retval EFI_SUCCESS The ACK packet successfully processed.
+ @retval EFI_TFTP_ERROR The block number loops back.
+ @retval Others Failed to transmit the next data packet.
+
+**/
+EFI_STATUS
+Mtftp6WrqHandleAck (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 Len,
+ OUT NET_BUF **UdpPacket,
+ OUT BOOLEAN *IsCompleted
+ )
+{
+ UINT16 AckNum;
+ INTN Expected;
+ UINT64 TotalBlock;
+
+ *IsCompleted = FALSE;
+ AckNum = NTOHS (Packet->Ack.Block[0]);
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput
+ // restart receive.
+ //
+ if (Expected != AckNum) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Remove the acked block number, if this is the last block number,
+ // tell the Mtftp6WrqInput to finish the transfer. This is the last
+ // block number if the block range are empty..
+ //
+ Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock);
+
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ if (Expected < 0) {
+ //
+ // The block range is empty. It may either because the the last
+ // block has been ACKed, or the sequence number just looped back,
+ // that is, there is more than 0xffff blocks.
+ //
+ if (Instance->LastBlk == AckNum) {
+ ASSERT (Instance->LastBlk >= 1);
+ *IsCompleted = TRUE;
+ return EFI_SUCCESS;
+
+ } else {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if block number rolls back.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "Block number rolls back, not supported, try blksize option"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+ }
+
+ //
+ // Free the receive buffer before send new packet since it might need
+ // reconfigure udpio.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+
+ return Mtftp6WrqSendBlock (Instance, (UINT16) Expected);
+}
+
+
+/**
+ Check whether the received OACK is valid. The OACK is valid
+ only if:
+ 1. It only include options requested by us.
+ 2. It can only include a smaller block size.
+ 3. It can't change the proposed time out value.
+ 4. Other requirements of the individal MTFTP6 options as required.
+
+ @param[in] ReplyInfo The pointer to options information in reply packet.
+ @param[in] RequestInfo The pointer to requested options information.
+
+ @retval TRUE If the option in OACK is valid.
+ @retval FALSE If the option is invalid.
+
+**/
+BOOLEAN
+Mtftp6WrqOackValid (
+ IN MTFTP6_EXT_OPTION_INFO *ReplyInfo,
+ IN MTFTP6_EXT_OPTION_INFO *RequestInfo
+ )
+{
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||
+ (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))
+ ) {
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Process the OACK packet for Wrq.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the received packet.
+ @param[in] Len The length of the packet.
+ @param[out] UdpPacket The net buf of received packet.
+ @param[out] IsCompleted If TRUE, the upload has been completed.
+ Otherwise, the upload has not been completed.
+
+ @retval EFI_SUCCESS The OACK packet successfully processed.
+ @retval EFI_TFTP_ERROR An TFTP communication error happened.
+ @retval Others Failed to process the OACK packet.
+
+**/
+EFI_STATUS
+Mtftp6WrqHandleOack (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 Len,
+ OUT NET_BUF **UdpPacket,
+ OUT BOOLEAN *IsCompleted
+ )
+{
+ EFI_MTFTP6_OPTION *Options;
+ UINT32 Count;
+ MTFTP6_EXT_OPTION_INFO ExtInfo;
+ EFI_MTFTP6_PACKET Dummy;
+ EFI_STATUS Status;
+ INTN Expected;
+
+ *IsCompleted = FALSE;
+
+ //
+ // Ignore the OACK if already started the upload
+ //
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ if (Expected != 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
+
+ Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);
+
+ if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) {
+ //
+ // Don't send a MTFTP error packet when out of resource, it can
+ // only make it worse.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if invalid Oack packet received.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Mal-formated OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if (ExtInfo.BlkSize != 0) {
+ Instance->BlkSize = ExtInfo.BlkSize;
+ }
+
+ if (ExtInfo.Timeout != 0) {
+ Instance->Timeout = ExtInfo.Timeout;
+ }
+
+ //
+ // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck,
+ // which will start the transmission of the first data block.
+ //
+ Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK);
+ Dummy.Ack.Block[0] = 0;
+
+ return Mtftp6WrqHandleAck (
+ Instance,
+ &Dummy,
+ sizeof (EFI_MTFTP6_ACK_HEADER),
+ UdpPacket,
+ IsCompleted
+ );
+}
+
+
+/**
+ The packet process callback for Mtftp6 upload.
+
+ @param[in] UdpPacket The pointer to the packet received.
+ @param[in] UdpEpt The pointer to the Udp6 access point.
+ @param[in] IoStatus The status from Udp6 instance.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6WrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *UdpEpt,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP6_INSTANCE *Instance;
+ EFI_MTFTP6_PACKET *Packet;
+ BOOLEAN IsCompleted;
+ EFI_STATUS Status;
+ UINT32 TotalNum;
+ UINT32 Len;
+ UINT16 Opcode;
+
+ Instance = (MTFTP6_INSTANCE *) Context;
+
+ NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);
+
+ IsCompleted = FALSE;
+ Packet = NULL;
+ Status = EFI_SUCCESS;
+ TotalNum = 0;
+
+ //
+ // Return error status if Udp6 instance failed to receive.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client.
+ //
+ if (UdpEpt->RemotePort != Instance->ServerDataPort) {
+ if (Instance->ServerDataPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ServerDataPort = UdpEpt->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+ TotalNum = UdpPacket->BlockOpNum;
+
+ if (TotalNum > 1) {
+ Packet = AllocateZeroPool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ ASSERT (Packet != NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Callback to the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if (Instance->Token->CheckPacket != NULL &&
+ (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)
+ ) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp6,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (UdpPacket);
+ UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if user aborted the current session.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Switch the process routines by the operation code.
+ //
+ switch (Opcode) {
+ case EFI_MTFTP6_OPCODE_ACK:
+ if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) {
+ goto ON_EXIT;
+ }
+ //
+ // Handle the Ack packet of Wrq.
+ //
+ Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted);
+ break;
+
+ case EFI_MTFTP6_OPCODE_OACK:
+ if (Len <= MTFTP6_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+ //
+ // Handle the Oack packet of Wrq.
+ //
+ Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted);
+ break;
+
+ default:
+ //
+ // Drop and return eror if received error message.
+ //
+ Status = EFI_TFTP_ERROR;
+ break;
+ }
+
+ON_EXIT:
+ //
+ // Free the resources, then if !EFI_ERROR (Status) and not completed,
+ // restart the receive, otherwise end the session.
+ //
+ if (Packet != NULL && TotalNum > 1) {
+ FreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !IsCompleted) {
+ Status = UdpIoRecvDatagram (
+ Instance->UdpIo,
+ Mtftp6WrqInput,
+ Instance,
+ 0
+ );
+ }
+ //
+ // Clean up the current session if failed to continue.
+ //
+ if (EFI_ERROR (Status) || IsCompleted) {
+ Mtftp6OperationClean (Instance, Status);
+ }
+}
+
+
+/**
+ Start the Mtftp6 instance to upload. It will first init some states,
+ then send the WRQ request packet, and start to receive the packet.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of the current packet.
+
+ @retval EFI_SUCCESS The Mtftp6 was started to upload.
+ @retval Others Failed to start to upload.
+
+**/
+EFI_STATUS
+Mtftp6WrqStart (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [0, 0xffff]. For example:
+ // the client sends an WRQ request to the server, the server
+ // ACK with an ACK0 to let client start transfer the first
+ // packet.
+ //
+ Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6SendRequest (Instance, Operation);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (
+ Instance->UdpIo,
+ Mtftp6WrqInput,
+ Instance,
+ 0
+ );
+}
+
diff --git a/NetworkPkg/NetworkPkg.dec b/NetworkPkg/NetworkPkg.dec
new file mode 100644
index 0000000000..0c8df4eed8
--- /dev/null
+++ b/NetworkPkg/NetworkPkg.dec
@@ -0,0 +1,21 @@
+## @file
+#
+# This package provides network modules that conform to UEFI 2.2 specification.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials are licensed and made available under
+# the terms and conditions of the BSD License which accompanies this distribution.
+# The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ DEC_SPECIFICATION = 0x00010005
+ PACKAGE_NAME = NetworkPkg
+ PACKAGE_GUID = 947988BE-8D5C-471a-893D-AD181C46BEBB
+ PACKAGE_VERSION = 0.92
diff --git a/NetworkPkg/NetworkPkg.dsc b/NetworkPkg/NetworkPkg.dsc
new file mode 100644
index 0000000000..b12d5fd04a
--- /dev/null
+++ b/NetworkPkg/NetworkPkg.dsc
@@ -0,0 +1,95 @@
+## @file
+# UEFI 2.2 Network Module Package for All Architectures
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ PLATFORM_NAME = NetworkPkg
+ PLATFORM_GUID = 3FD34E9B-E90C-44e1-B510-1F632A509F10
+ PLATFORM_VERSION = 0.92
+ DSC_SPECIFICATION = 0x00010005
+ OUTPUT_DIRECTORY = Build/NetworkPkg
+ SUPPORTED_ARCHITECTURES = IA32|IPF|X64|EBC|ARM
+ BUILD_TARGETS = DEBUG|RELEASE
+ SKUID_IDENTIFIER = DEFAULT
+
+[LibraryClasses]
+ BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+ BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+ DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+ HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+ PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+ PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+ UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+ UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+ UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+ UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+ UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+ UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+
+ DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf
+ NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf
+ IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf
+ UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
+
+[LibraryClasses.common.UEFI_DRIVER]
+ DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
+
+[LibraryClasses.common.UEFI_APPLICATION]
+ DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf
+ FileHandleLib|ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.inf
+ ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
+
+[PcdsFeatureFlag]
+ gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable|TRUE
+ gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable|TRUE
+
+[PcdsFixedAtBuild]
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000
+
+###################################################################################################
+#
+# Components Section - list of the modules and components that will be processed by compilation
+# tools and the EDK II tools to generate PE32/PE32+/Coff image files.
+#
+# Note: The EDK II DSC file is not used to specify how compiled binary images get placed
+# into firmware volume images. This section is just a list of modules to compile from
+# source into UEFI-compliant binaries.
+# It is the FDF file that contains information on combining binary files into firmware
+# volume images, whose concept is beyond UEFI and is described in PI specification.
+# Binary modules do not need to be listed in this section, as they should be
+# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),
+# Logo (Logo.bmp), and etc.
+# There may also be modules listed in this section that are not required in the FDF file,
+# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be
+# generated for it, but the binary will not be put into any firmware volume.
+#
+###################################################################################################
+
+[Components]
+ NetworkPkg/IpSecDxe/IpSecDxe.inf
+ NetworkPkg/Ip6Dxe/Ip6Dxe.inf
+ NetworkPkg/TcpDxe/TcpDxe.inf
+ NetworkPkg/Udp6Dxe/Udp6Dxe.inf
+ NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
+ NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
+
+
+[Components.IA32, Components.X64, Components.IPF]
+ NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
+ NetworkPkg/Application/Ping6/Ping6.inf
+ NetworkPkg/Application/IfConfig6/IfConfig6.inf
+ NetworkPkg/Application/IpsecConfig/IpSecConfig.inf
+ NetworkPkg/Application/VConfig/VConfig.inf
diff --git a/NetworkPkg/TcpDxe/ComponentName.c b/NetworkPkg/TcpDxe/ComponentName.c
new file mode 100644
index 0000000000..956792afec
--- /dev/null
+++ b/NetworkPkg/TcpDxe/ComponentName.c
@@ -0,0 +1,304 @@
+/** @file
+ Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and
+ EFI_COMPONENT_NAME2_PROTOCOL.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+//
+// EFI Component Name Functions
+//
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This, and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language, from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+///
+/// EFI Component Name Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName = {
+ TcpComponentNameGetDriverName,
+ TcpComponentNameGetControllerName,
+ "eng"
+};
+
+///
+/// EFI Component Name 2 Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = {
+ {
+ "eng;en",
+ L"TCP Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This, and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language or DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mTcpDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gTcpComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language, from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/TcpDxe/SockImpl.c b/NetworkPkg/TcpDxe/SockImpl.c
new file mode 100644
index 0000000000..7fad042be6
--- /dev/null
+++ b/NetworkPkg/TcpDxe/SockImpl.c
@@ -0,0 +1,1230 @@
+/** @file
+ Implementation of the Socket.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "SockImpl.h"
+
+/**
+ Get the first buffer block in the specific socket buffer.
+
+ @param[in] Sockbuf Pointer to the socket buffer.
+
+ @return Pointer to the first buffer in the queue. NULL if the queue is empty.
+
+**/
+NET_BUF *
+SockBufFirst (
+ IN SOCK_BUFFER *Sockbuf
+ )
+{
+ LIST_ENTRY *NetbufList;
+
+ NetbufList = &(Sockbuf->DataQueue->BufList);
+
+ if (IsListEmpty (NetbufList)) {
+ return NULL;
+ }
+
+ return NET_LIST_HEAD (NetbufList, NET_BUF, List);
+}
+
+/**
+ Get the next buffer block in the specific socket buffer.
+
+ @param[in] Sockbuf Pointer to the socket buffer.
+ @param[in] SockEntry Pointer to the buffer block prior to the required one.
+
+ @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is
+ the tail or head entry.
+
+**/
+NET_BUF *
+SockBufNext (
+ IN SOCK_BUFFER *Sockbuf,
+ IN NET_BUF *SockEntry
+ )
+{
+ LIST_ENTRY *NetbufList;
+
+ NetbufList = &(Sockbuf->DataQueue->BufList);
+
+ if ((SockEntry->List.ForwardLink == NetbufList) ||
+ (SockEntry->List.BackLink == &SockEntry->List) ||
+ (SockEntry->List.ForwardLink == &SockEntry->List)
+ ) {
+
+ return NULL;
+ }
+
+ return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List);
+}
+
+/**
+ User provided callback function for NetbufFromExt.
+
+ @param[in] Event The Event this notify function registered to, ignored.
+
+**/
+VOID
+EFIAPI
+SockFreeFoo (
+ IN EFI_EVENT Event
+ )
+{
+ return;
+}
+
+/**
+ Get the length of the data that can be retrieved from the socket
+ receive buffer.
+
+ @param[in] SockBuffer Pointer to the socket receive buffer.
+ @param[out] IsUrg Pointer to a BOOLEAN variable.
+ If TRUE the data is OOB.
+ @param[in] BufLen The maximum length of the data buffer to
+ store the received data in the socket layer.
+
+ @return The length of the data can be retreived.
+
+**/
+UINT32
+SockTcpDataToRcv (
+ IN SOCK_BUFFER *SockBuffer,
+ OUT BOOLEAN *IsUrg,
+ IN UINT32 BufLen
+ )
+{
+ NET_BUF *RcvBufEntry;
+ UINT32 DataLen;
+ TCP_RSV_DATA *TcpRsvData;
+ BOOLEAN Urg;
+
+ ASSERT ((SockBuffer != NULL) && (IsUrg != NULL) && (BufLen > 0));
+
+ //
+ // Get the first socket receive buffer
+ //
+ RcvBufEntry = SockBufFirst (SockBuffer);
+ ASSERT (RcvBufEntry != NULL);
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ //
+ // Check whether the receive data is out of bound. If yes, calculate the maximum
+ // allowed length of the urgent data and output it.
+ //
+ *IsUrg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);
+
+ if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) {
+
+ DataLen = MIN (TcpRsvData->UrgLen, BufLen);
+
+ if (DataLen < TcpRsvData->UrgLen) {
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen;
+ } else {
+ TcpRsvData->UrgLen = 0;
+ }
+
+ return DataLen;
+
+ }
+
+ //
+ // Process the next socket receive buffer to get the maximum allowed length
+ // of the received data.
+ //
+ DataLen = RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+
+ while ((BufLen > DataLen) && (RcvBufEntry != NULL)) {
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ Urg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);
+
+ if (*IsUrg != Urg) {
+ break;
+ }
+
+ if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {
+
+ if (TcpRsvData->UrgLen + DataLen < BufLen) {
+ TcpRsvData->UrgLen = 0;
+ } else {
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen);
+ }
+
+ return MIN (TcpRsvData->UrgLen + DataLen, BufLen);
+
+ }
+
+ DataLen += RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+ }
+
+ DataLen = MIN (BufLen, DataLen);
+ return DataLen;
+}
+
+/**
+ Copy data from socket buffer to an application provided receive buffer.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] TcpRxData Pointer to the application provided receive buffer.
+ @param[in] RcvdBytes The maximum length of the data can be copied.
+ @param[in] IsUrg If TRUE the data is Out of Bound, FALSE the data is normal.
+
+**/
+VOID
+SockSetTcpRxData (
+ IN SOCKET *Sock,
+ IN VOID *TcpRxData,
+ IN UINT32 RcvdBytes,
+ IN BOOLEAN IsUrg
+ )
+{
+ UINT32 Index;
+ UINT32 CopyBytes;
+ UINT32 OffSet;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ EFI_TCP4_FRAGMENT_DATA *Fragment;
+
+ RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData;
+
+ OffSet = 0;
+
+ ASSERT (RxData->DataLength >= RcvdBytes);
+
+ RxData->DataLength = RcvdBytes;
+ RxData->UrgentFlag = IsUrg;
+
+ //
+ // Copy the CopyBytes data from socket receive buffer to RxData.
+ //
+ for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) {
+
+ Fragment = &RxData->FragmentTable[Index];
+ CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes);
+
+ NetbufQueCopy (
+ Sock->RcvBuffer.DataQueue,
+ OffSet,
+ CopyBytes,
+ Fragment->FragmentBuffer
+ );
+
+ Fragment->FragmentLength = CopyBytes;
+ RcvdBytes -= CopyBytes;
+ OffSet += CopyBytes;
+ }
+}
+
+/**
+ Process the send token.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockProcessSndToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ UINT32 FreeSpace;
+ SOCK_TOKEN *SockToken;
+ UINT32 DataLen;
+ SOCK_IO_TOKEN *SndToken;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_STATUS Status;
+
+ ASSERT ((Sock != NULL) && (SockStream == Sock->Type));
+
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);
+
+ //
+ // to determine if process a send token using
+ // socket layer flow control policy
+ //
+ while ((FreeSpace >= Sock->SndBuffer.LowWater) && !IsListEmpty (&Sock->SndTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &(Sock->SndTokenList),
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ //
+ // process this token
+ //
+ RemoveEntryList (&(SockToken->TokenList));
+ InsertTailList (
+ &(Sock->ProcessingSndTokenList),
+ &(SockToken->TokenList)
+ );
+
+ //
+ // Proceess it in the light of SockType
+ //
+ SndToken = (SOCK_IO_TOKEN *) SockToken->Token;
+ TxData = SndToken->Packet.TxData;
+
+ DataLen = TxData->DataLength;
+ Status = SockProcessTcpSndData (Sock, TxData);
+
+ if (EFI_ERROR (Status)) {
+ goto OnError;
+ }
+
+ if (DataLen >= FreeSpace) {
+ FreeSpace = 0;
+
+ } else {
+ FreeSpace -= DataLen;
+
+ }
+ }
+
+ return;
+
+OnError:
+
+ RemoveEntryList (&SockToken->TokenList);
+ SIGNAL_TOKEN (SockToken->Token, Status);
+ FreePool (SockToken);
+}
+
+/**
+ Get received data from the socket layer to the receive token.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in, out] RcvToken Pointer to the application provided receive token.
+
+ @return The length of data received in this token.
+
+**/
+UINT32
+SockProcessRcvToken (
+ IN OUT SOCKET *Sock,
+ IN OUT SOCK_IO_TOKEN *RcvToken
+ )
+{
+ UINT32 TokenRcvdBytes;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ BOOLEAN IsUrg;
+
+ ASSERT (Sock != NULL);
+
+ ASSERT (SockStream == Sock->Type);
+
+ RxData = RcvToken->Packet.RxData;
+
+ TokenRcvdBytes = SockTcpDataToRcv (
+ &Sock->RcvBuffer,
+ &IsUrg,
+ RxData->DataLength
+ );
+
+ //
+ // Copy data from RcvBuffer of socket to user
+ // provided RxData and set the fields in TCP RxData
+ //
+ SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg);
+
+ NetbufQueTrim (Sock->RcvBuffer.DataQueue, TokenRcvdBytes);
+ SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS);
+
+ return TokenRcvdBytes;
+}
+
+/**
+ Process the TCP send data, buffer the tcp txdata, and append
+ the buffer to socket send buffer, then try to send it.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] TcpTxData Pointer to the application provided send buffer.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
+
+**/
+EFI_STATUS
+SockProcessTcpSndData (
+ IN SOCKET *Sock,
+ IN VOID *TcpTxData
+ )
+{
+ NET_BUF *SndData;
+ EFI_STATUS Status;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData;
+
+ //
+ // transform this TxData into a NET_BUFFER
+ // and insert it into Sock->SndBuffer
+ //
+ SndData = NetbufFromExt (
+ (NET_FRAGMENT *) TxData->FragmentTable,
+ TxData->FragmentCount,
+ 0,
+ 0,
+ SockFreeFoo,
+ NULL
+ );
+
+ if (NULL == SndData) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockKProcessSndData: Failed to call NetBufferFromExt\n")
+ );
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData);
+
+ //
+ // notify the low layer protocol to handle this send token
+ //
+ if (TxData->Urgent) {
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (TxData->Push) {
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // low layer protocol should really handle the sending
+ // process when catching SOCK_SND request
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Flush the tokens in the specific token list.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in, out] PendingTokenList Pointer to the token list to be flushed.
+
+**/
+VOID
+SockFlushPendingToken (
+ IN SOCKET *Sock,
+ IN OUT LIST_ENTRY *PendingTokenList
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *Token;
+
+ ASSERT ((Sock != NULL) && (PendingTokenList != NULL));
+
+ while (!IsListEmpty (PendingTokenList)) {
+ SockToken = NET_LIST_HEAD (
+ PendingTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ Token = SockToken->Token;
+ SIGNAL_TOKEN (Token, Sock->SockError);
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ }
+}
+
+/**
+ Wake up the connection token while the connection is successfully established,
+ then try to process any pending send token.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeConnToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ ASSERT (Sock->ConnectionToken != NULL);
+
+ SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS);
+ Sock->ConnectionToken = NULL;
+
+ //
+ // check to see if some pending send token existed?
+ //
+ SockProcessSndToken (Sock);
+}
+
+/**
+ Wake up the listen token while the connection is established successfully.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeListenToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ SOCKET *Parent;
+ SOCK_TOKEN *SockToken;
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;
+
+ Parent = Sock->Parent;
+
+ ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock));
+
+ if (!IsListEmpty (&Parent->ListenTokenList)) {
+ SockToken = NET_LIST_HEAD (
+ &Parent->ListenTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token;
+ ListenToken->NewChildHandle = Sock->SockHandle;
+
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);
+
+ RemoveEntryList (&SockToken->TokenList);
+ FreePool (SockToken);
+
+ RemoveEntryList (&Sock->ConnectionList);
+
+ Parent->ConnCnt--;
+ DEBUG (
+ (EFI_D_INFO,
+ "SockWakeListenToken: accept a socket, now conncnt is %d",
+ Parent->ConnCnt)
+ );
+
+ Sock->Parent = NULL;
+ }
+}
+
+/**
+ Wake up the receive token while some data is received.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeRcvToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ UINT32 RcvdBytes;
+ UINT32 TokenRcvdBytes;
+ SOCK_TOKEN *SockToken;
+ SOCK_IO_TOKEN *RcvToken;
+
+ ASSERT (Sock->RcvBuffer.DataQueue != NULL);
+
+ RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize;
+
+ ASSERT (RcvdBytes > 0);
+
+ while (RcvdBytes > 0 && !IsListEmpty (&Sock->RcvTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &Sock->RcvTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ RcvToken = (SOCK_IO_TOKEN *) SockToken->Token;
+ TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken);
+
+ if (0 == TokenRcvdBytes) {
+ return ;
+ }
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ RcvdBytes -= TokenRcvdBytes;
+ }
+}
+
+/**
+ Create a socket with initial data SockInitData.
+
+ @param[in] SockInitData Pointer to the initial data of the socket.
+
+ @return Pointer to the newly created socket, return NULL when an exception occurs.
+
+**/
+SOCKET *
+SockCreate (
+ IN SOCK_INIT_DATA *SockInitData
+ )
+{
+ SOCKET *Sock;
+ SOCKET *Parent;
+ EFI_STATUS Status;
+ EFI_GUID *TcpProtocolGuid;
+ UINTN ProtocolLength;
+
+ ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL));
+ ASSERT (SockInitData->Type == SockStream);
+ ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN));
+
+ if (SockInitData->IpVersion == IP_VERSION_4) {
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ ProtocolLength = sizeof (EFI_TCP4_PROTOCOL);
+ } else {
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ ProtocolLength = sizeof (EFI_TCP6_PROTOCOL);
+ }
+
+
+ Parent = SockInitData->Parent;
+
+ if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n",
+ Parent->ConnCnt,
+ Parent->BackLog)
+ );
+
+ return NULL;
+ }
+
+ Sock = AllocateZeroPool (sizeof (SOCKET));
+ if (NULL == Sock) {
+
+ DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n"));
+ return NULL;
+ }
+
+ InitializeListHead (&Sock->Link);
+ InitializeListHead (&Sock->ConnectionList);
+ InitializeListHead (&Sock->ListenTokenList);
+ InitializeListHead (&Sock->RcvTokenList);
+ InitializeListHead (&Sock->SndTokenList);
+ InitializeListHead (&Sock->ProcessingSndTokenList);
+
+ EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK);
+
+ Sock->SndBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->SndBuffer.DataQueue) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreate: No resource to allocate SndBuffer for new socket\n")
+ );
+
+ goto OnError;
+ }
+
+ Sock->RcvBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->RcvBuffer.DataQueue) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreate: No resource to allocate RcvBuffer for new socket\n")
+ );
+
+ goto OnError;
+ }
+
+ Sock->Signature = SOCK_SIGNATURE;
+
+ Sock->Parent = Parent;
+ Sock->BackLog = SockInitData->BackLog;
+ Sock->ProtoHandler = SockInitData->ProtoHandler;
+ Sock->SndBuffer.HighWater = SockInitData->SndBufferSize;
+ Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize;
+ Sock->Type = SockInitData->Type;
+ Sock->DriverBinding = SockInitData->DriverBinding;
+ Sock->State = SockInitData->State;
+ Sock->CreateCallback = SockInitData->CreateCallback;
+ Sock->DestroyCallback = SockInitData->DestroyCallback;
+ Sock->Context = SockInitData->Context;
+
+ Sock->SockError = EFI_ABORTED;
+ Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+ Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+
+ Sock->IpVersion = SockInitData->IpVersion;
+
+ //
+ // Install protocol on Sock->SockHandle
+ //
+ CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength);
+
+ //
+ // copy the protodata into socket
+ //
+ CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Sock->SockHandle,
+ TcpProtocolGuid,
+ &Sock->NetProtocol,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreate: Install TCP protocol in socket failed with %r\n",
+ Status)
+ );
+
+ goto OnError;
+ }
+
+ if (Parent != NULL) {
+ ASSERT (Parent->BackLog > 0);
+ ASSERT (SOCK_IS_LISTENING (Parent));
+
+ //
+ // need to add it into Parent->ConnectionList
+ // if the Parent->ConnCnt < Parent->BackLog
+ //
+ Parent->ConnCnt++;
+
+ DEBUG (
+ (EFI_D_INFO,
+ "SockCreate: Create a new socket and add to parent, now conncnt is %d\n",
+ Parent->ConnCnt)
+ );
+
+ InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList);
+ }
+
+ if (Sock->CreateCallback != NULL) {
+ Status = Sock->CreateCallback (Sock, Sock->Context);
+ if (EFI_ERROR (Status)) {
+ goto OnError;
+ }
+ }
+
+ return Sock;
+
+OnError:
+
+ if (Sock->SockHandle != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ &Sock->NetProtocol,
+ NULL
+ );
+ }
+
+ if (NULL != Sock->SndBuffer.DataQueue) {
+ NetbufQueFree (Sock->SndBuffer.DataQueue);
+ }
+
+ if (NULL != Sock->RcvBuffer.DataQueue) {
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);
+ }
+
+ FreePool (Sock);
+
+ return NULL;
+}
+
+/**
+ Destroy a socket.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockDestroy (
+ IN OUT SOCKET *Sock
+ )
+{
+ VOID *SockProtocol;
+ EFI_GUID *TcpProtocolGuid;
+ EFI_STATUS Status;
+
+ ASSERT (SockStream == Sock->Type);
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ //
+ // Flush the completion token buffered
+ // by sock and rcv, snd buffer
+ //
+ if (!SOCK_IS_UNCONFIGURED (Sock)) {
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+ Sock->ConfigureState = SO_UNCONFIGURED;
+
+ }
+ //
+ // Destory the RcvBuffer Queue and SendBuffer Queue
+ //
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);
+ NetbufQueFree (Sock->SndBuffer.DataQueue);
+
+ //
+ // Remove it from parent connection list if needed
+ //
+ if (Sock->Parent != NULL) {
+
+ RemoveEntryList (&(Sock->ConnectionList));
+ (Sock->Parent->ConnCnt)--;
+
+ DEBUG (
+ (EFI_D_WARN,
+ "SockDestory: Delete a unaccepted socket from parent now conncnt is %d\n",
+ Sock->Parent->ConnCnt)
+ );
+
+ Sock->Parent = NULL;
+ }
+
+ //
+ // Set the protocol guid and driver binding handle
+ // in the light of Sock->SockType
+ //
+ if (Sock->IpVersion == IP_VERSION_4) {
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ //
+ // Retrieve the protocol installed on this sock
+ //
+ Status = gBS->OpenProtocol (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ &SockProtocol,
+ Sock->DriverBinding,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockDestroy: Open protocol installed on socket failed with %r\n",
+ Status)
+ );
+
+ goto FreeSock;
+ }
+
+ //
+ // Uninstall the protocol installed on this sock
+ // in the light of Sock->SockType
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+
+FreeSock:
+
+ FreePool (Sock);
+}
+
+/**
+ Flush the sndBuffer and rcvBuffer of socket.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockConnFlush (
+ IN OUT SOCKET *Sock
+ )
+{
+ SOCKET *Child;
+
+ ASSERT (Sock != NULL);
+
+ //
+ // Clear the flag in this socket
+ //
+ Sock->Flag = 0;
+
+ //
+ // Flush the SndBuffer and RcvBuffer of Sock
+ //
+ NetbufQueFlush (Sock->SndBuffer.DataQueue);
+ NetbufQueFlush (Sock->RcvBuffer.DataQueue);
+
+ //
+ // Signal the pending token
+ //
+ if (Sock->ConnectionToken != NULL) {
+ SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError);
+ Sock->ConnectionToken = NULL;
+ }
+
+ if (Sock->CloseToken != NULL) {
+ SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError);
+ Sock->CloseToken = NULL;
+ }
+
+ SockFlushPendingToken (Sock, &(Sock->ListenTokenList));
+ SockFlushPendingToken (Sock, &(Sock->RcvTokenList));
+ SockFlushPendingToken (Sock, &(Sock->SndTokenList));
+ SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList));
+
+ //
+ // Destroy the pending connection, if it is a listening socket
+ //
+ if (SOCK_IS_LISTENING (Sock)) {
+ while (!IsListEmpty (&Sock->ConnectionList)) {
+ Child = NET_LIST_HEAD (
+ &Sock->ConnectionList,
+ SOCKET,
+ ConnectionList
+ );
+
+ SockDestroyChild (Child);
+ }
+
+ Sock->ConnCnt = 0;
+ }
+
+}
+
+/**
+ Set the state of the socket.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in] State The new socket state to be set.
+
+**/
+VOID
+SockSetState (
+ IN OUT SOCKET *Sock,
+ IN UINT8 State
+ )
+{
+ Sock->State = State;
+}
+
+/**
+ Clone a new socket, including its associated protocol control block.
+
+ @param[in] Sock Pointer to the socket to be cloned.
+
+ @return Pointer to the newly cloned socket. If NULL, an error condition occurred.
+
+**/
+SOCKET *
+SockClone (
+ IN SOCKET *Sock
+ )
+{
+ SOCKET *ClonedSock;
+ SOCK_INIT_DATA InitData;
+
+ InitData.BackLog = Sock->BackLog;
+ InitData.Parent = Sock;
+ InitData.State = Sock->State;
+ InitData.ProtoHandler = Sock->ProtoHandler;
+ InitData.Type = Sock->Type;
+ InitData.RcvBufferSize = Sock->RcvBuffer.HighWater;
+ InitData.SndBufferSize = Sock->SndBuffer.HighWater;
+ InitData.DriverBinding = Sock->DriverBinding;
+ InitData.IpVersion = Sock->IpVersion;
+ InitData.Protocol = &(Sock->NetProtocol);
+ InitData.CreateCallback = Sock->CreateCallback;
+ InitData.DestroyCallback = Sock->DestroyCallback;
+ InitData.Context = Sock->Context;
+ InitData.ProtoData = Sock->ProtoReserved;
+ InitData.DataSize = sizeof (Sock->ProtoReserved);
+
+ ClonedSock = SockCreate (&InitData);
+
+ if (NULL == ClonedSock) {
+ DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n"));
+ return NULL;
+ }
+
+ SockSetState (ClonedSock, SO_CONNECTING);
+ ClonedSock->ConfigureState = Sock->ConfigureState;
+
+ return ClonedSock;
+}
+
+/**
+ Called by the low layer protocol to indicate the socket a connection is
+ established.
+
+ This function just changes the socket's state to SO_CONNECTED
+ and signals the token used for connection establishment.
+
+ @param[in, out] Sock Pointer to the socket associated with the
+ established connection.
+
+**/
+VOID
+SockConnEstablished (
+ IN OUT SOCKET *Sock
+ )
+{
+
+ ASSERT (SO_CONNECTING == Sock->State);
+
+ SockSetState (Sock, SO_CONNECTED);
+
+ if (NULL == Sock->Parent) {
+ SockWakeConnToken (Sock);
+ } else {
+ SockWakeListenToken (Sock);
+ }
+
+}
+
+/**
+ Called by the low layer protocol to indicate the connection is closed.
+
+ This function flushes the socket, sets the state to SO_CLOSED, and signals
+ the close token.
+
+ @param[in, out] Sock Pointer to the socket associated with the closed
+ connection.
+
+**/
+VOID
+SockConnClosed (
+ IN OUT SOCKET *Sock
+ )
+{
+ if (Sock->CloseToken != NULL) {
+ SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS);
+ Sock->CloseToken = NULL;
+ }
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ if (Sock->Parent != NULL) {
+ SockDestroyChild (Sock);
+ }
+
+}
+
+/**
+ Called by low layer protocol to indicate that some data was sent or processed.
+
+ This function trims the sent data in the socket send buffer, and signals the data
+ token if proper.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in] Count The length of the data processed or sent, in bytes.
+
+**/
+VOID
+SockDataSent (
+ IN OUT SOCKET *Sock,
+ IN UINT32 Count
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *SndToken;
+
+ ASSERT (!IsListEmpty (&Sock->ProcessingSndTokenList));
+ ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize);
+
+ NetbufQueTrim (Sock->SndBuffer.DataQueue, Count);
+
+ //
+ // To check if we can signal some snd token in this socket
+ //
+ while (Count > 0) {
+ SockToken = NET_LIST_HEAD (
+ &(Sock->ProcessingSndTokenList),
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ SndToken = SockToken->Token;
+
+ if (SockToken->RemainDataLen <= Count) {
+
+ RemoveEntryList (&(SockToken->TokenList));
+ SIGNAL_TOKEN (SndToken, EFI_SUCCESS);
+ Count -= SockToken->RemainDataLen;
+ FreePool (SockToken);
+ } else {
+
+ SockToken->RemainDataLen -= Count;
+ Count = 0;
+ }
+ }
+
+ //
+ // to judge if we can process some send token in
+ // Sock->SndTokenList, if so process those send token
+ //
+ SockProcessSndToken (Sock);
+}
+
+/**
+ Called by the low layer protocol to copy some data in the socket send
+ buffer starting from the specific offset to a buffer provided by
+ the caller.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Offset The start point of the data to be copied.
+ @param[in] Len The length of the data to be copied.
+ @param[out] Dest Pointer to the destination to copy the data.
+
+ @return The data size copied.
+
+**/
+UINT32
+SockGetDataToSend (
+ IN SOCKET *Sock,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ OUT UINT8 *Dest
+ )
+{
+ ASSERT ((Sock != NULL) && SockStream == Sock->Type);
+
+ return NetbufQueCopy (
+ Sock->SndBuffer.DataQueue,
+ Offset,
+ Len,
+ Dest
+ );
+}
+
+/**
+ Called by the low layer protocol to deliver received data to socket layer.
+
+ This function will append the data to the socket receive buffer, set the
+ urgent data length, and then check if any receive token can be signaled.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in, out] NetBuffer Pointer to the buffer that contains the received data.
+ @param[in] UrgLen The length of the urgent data in the received data.
+
+**/
+VOID
+SockDataRcvd (
+ IN OUT SOCKET *Sock,
+ IN OUT NET_BUF *NetBuffer,
+ IN UINT32 UrgLen
+ )
+{
+ ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) &&
+ UrgLen <= NetBuffer->TotalSize);
+
+ NET_GET_REF (NetBuffer);
+
+ ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen;
+
+ NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer);
+
+ SockWakeRcvToken (Sock);
+}
+
+/**
+ Get the length of the free space of the specific socket buffer.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Which Flag to indicate which socket buffer to check:
+ either send buffer or receive buffer.
+
+ @return The length of the free space, in bytes.
+
+**/
+UINT32
+SockGetFreeSpace (
+ IN SOCKET *Sock,
+ IN UINT32 Which
+ )
+{
+ UINT32 BufferCC;
+ SOCK_BUFFER *SockBuffer;
+
+ ASSERT (Sock != NULL && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which)));
+
+ if (SOCK_SND_BUF == Which) {
+ SockBuffer = &(Sock->SndBuffer);
+ } else {
+ SockBuffer = &(Sock->RcvBuffer);
+ }
+
+ BufferCC = (SockBuffer->DataQueue)->BufSize;
+
+ if (BufferCC >= SockBuffer->HighWater) {
+
+ return 0;
+ }
+
+ return SockBuffer->HighWater - BufferCC;
+}
+
+/**
+ Called by the low layer protocol to indicate that there will be no more data
+ from the communication peer.
+
+ This function sets the socket's state to SO_NO_MORE_DATA and signals all queued
+ IO tokens with the error status EFI_CONNECTION_FIN.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockNoMoreData (
+ IN OUT SOCKET *Sock
+ )
+{
+ EFI_STATUS Err;
+
+ SOCK_NO_MORE_DATA (Sock);
+
+ if (!IsListEmpty (&Sock->RcvTokenList)) {
+
+ ASSERT (0 == GET_RCV_DATASIZE (Sock));
+
+ Err = Sock->SockError;
+
+ SOCK_ERROR (Sock, EFI_CONNECTION_FIN);
+
+ SockFlushPendingToken (Sock, &Sock->RcvTokenList);
+
+ SOCK_ERROR (Sock, Err);
+
+ }
+}
+
diff --git a/NetworkPkg/TcpDxe/SockImpl.h b/NetworkPkg/TcpDxe/SockImpl.h
new file mode 100644
index 0000000000..bb4f6c2085
--- /dev/null
+++ b/NetworkPkg/TcpDxe/SockImpl.h
@@ -0,0 +1,103 @@
+/** @file
+ The function declaration that provided for Socket Interface.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SOCK_IMPL_H_
+#define _SOCK_IMPL_H_
+
+#include "Socket.h"
+
+/**
+ Signal a event with the given status.
+
+ @param[in] Token The token's event is to be signaled.
+ @param[in] TokenStatus The status to be sent with the event.
+
+**/
+#define SIGNAL_TOKEN(Token, TokenStatus) \
+ do { \
+ (Token)->Status = (TokenStatus); \
+ gBS->SignalEvent ((Token)->Event); \
+ } while (0)
+
+#define SOCK_HEADER_SPACE (60 + 60 + 72)
+
+/**
+ Process the TCP send data, buffer the tcp txdata and append
+ the buffer to socket send buffer, then try to send it.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] TcpTxData Pointer to the application provided send buffer.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
+
+**/
+EFI_STATUS
+SockProcessTcpSndData (
+ IN SOCKET *Sock,
+ IN VOID *TcpTxData
+ );
+
+/**
+ Get received data from the socket layer to the receive token.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in, out] RcvToken Pointer to the application provided receive token.
+
+ @return The length of data received in this token.
+
+**/
+UINT32
+SockProcessRcvToken (
+ IN OUT SOCKET *Sock,
+ IN OUT SOCK_IO_TOKEN *RcvToken
+ );
+
+/**
+ Flush the sndBuffer and rcvBuffer of socket.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockConnFlush (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Create a socket with initial data SockInitData.
+
+ @param[in] SockInitData Pointer to the initial data of the socket.
+
+ @return Pointer to the newly created socket, return NULL when exception occured.
+
+**/
+SOCKET *
+SockCreate (
+ IN SOCK_INIT_DATA *SockInitData
+ );
+
+/**
+ Destroy a socket.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockDestroy (
+ IN OUT SOCKET *Sock
+ );
+
+#endif
diff --git a/NetworkPkg/TcpDxe/SockInterface.c b/NetworkPkg/TcpDxe/SockInterface.c
new file mode 100644
index 0000000000..e36c0e97c8
--- /dev/null
+++ b/NetworkPkg/TcpDxe/SockInterface.c
@@ -0,0 +1,999 @@
+/** @file
+ Interface function of the Socket.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "SockImpl.h"
+
+/**
+ Check whether the Event is in the List.
+
+ @param[in] List Pointer to the token list to be searched.
+ @param[in] Event The event to be checked.
+
+ @retval TRUE The specific Event exists in the List.
+ @retval FALSE The specific Event is not in the List.
+
+**/
+BOOLEAN
+SockTokenExistedInList (
+ IN LIST_ENTRY *List,
+ IN EFI_EVENT Event
+ )
+{
+ LIST_ENTRY *ListEntry;
+ SOCK_TOKEN *SockToken;
+
+ NET_LIST_FOR_EACH (ListEntry, List) {
+ SockToken = NET_LIST_USER_STRUCT (
+ ListEntry,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ if (Event == SockToken->Token->Event) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Call SockTokenExistedInList() to check whether the Event is
+ in the related socket's lists.
+
+ @param[in] Sock Pointer to the instance's socket.
+ @param[in] Event The event to be checked.
+
+ @retval TRUE The Event exists in related socket's lists.
+ @retval FALSE The Event is not in related socket's lists.
+
+**/
+BOOLEAN
+SockTokenExisted (
+ IN SOCKET *Sock,
+ IN EFI_EVENT Event
+ )
+{
+
+ if (SockTokenExistedInList (&Sock->SndTokenList, Event) ||
+ SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) ||
+ SockTokenExistedInList (&Sock->RcvTokenList, Event) ||
+ SockTokenExistedInList (&Sock->ListenTokenList, Event)
+ ) {
+
+ return TRUE;
+ }
+
+ if ((Sock->ConnectionToken != NULL) && (Sock->ConnectionToken->Event == Event)) {
+
+ return TRUE;
+ }
+
+ if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Buffer a token into the specific list of the socket Sock.
+
+ @param[in] Sock Pointer to the instance's socket.
+ @param[in] List Pointer to the list to store the token.
+ @param[in] Token Pointer to the token to be buffered.
+ @param[in] DataLen The data length of the buffer contained in Token.
+
+ @return Pointer to the token that wraps Token. If NULL, an error condition occurred.
+
+**/
+SOCK_TOKEN *
+SockBufferToken (
+ IN SOCKET *Sock,
+ IN LIST_ENTRY *List,
+ IN VOID *Token,
+ IN UINT32 DataLen
+ )
+{
+ SOCK_TOKEN *SockToken;
+
+ SockToken = AllocateZeroPool (sizeof (SOCK_TOKEN));
+ if (NULL == SockToken) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockBufferIOToken: No Memory to allocate SockToken\n")
+ );
+
+ return NULL;
+ }
+
+ SockToken->Sock = Sock;
+ SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token;
+ SockToken->RemainDataLen = DataLen;
+ InsertTailList (List, &SockToken->TokenList);
+
+ return SockToken;
+}
+
+/**
+ Destory the socket Sock and its associated protocol control block.
+
+ @param[in, out] Sock The socket to be destroyed.
+
+ @retval EFI_SUCCESS The socket Sock was destroyed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockDestroyChild (
+ IN OUT SOCKET *Sock
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));
+
+ if (Sock->IsDestroyed) {
+ return EFI_SUCCESS;
+ }
+
+ Sock->IsDestroyed = TRUE;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockDestroyChild: Get the lock to access socket failed with %r\n",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // force protocol layer to detach the PCB
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL);
+
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockDestroyChild: Protocol detach socket failed with %r\n",
+ Status)
+ );
+
+ Sock->IsDestroyed = FALSE;
+ } else if (SOCK_IS_CONFIGURED (Sock)) {
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ Sock->ConfigureState = SO_UNCONFIGURED;
+ }
+
+ EfiReleaseLock (&(Sock->Lock));
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SockDestroy (Sock);
+ return EFI_SUCCESS;
+}
+
+/**
+ Create a socket and its associated protocol control block
+ with the intial data SockInitData and protocol specific
+ data ProtoData.
+
+ @param[in] SockInitData Inital data to setting the socket.
+
+ @return Pointer to the newly created socket. If NULL, an error condition occured.
+
+**/
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ //
+ // create a new socket
+ //
+ Sock = SockCreate (SockInitData);
+ if (NULL == Sock) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreateChild: No resource to create a new socket\n")
+ );
+
+ return NULL;
+ }
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreateChild: Get the lock to access socket failed with %r\n",
+ Status)
+ );
+
+ SockDestroy (Sock);
+ return NULL;
+ }
+ //
+ // inform the protocol layer to attach the socket
+ // with a new protocol control block
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL);
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreateChild: Protocol failed to attach a socket with %r\n",
+ Status)
+ );
+
+ SockDestroy (Sock);
+ Sock = NULL;
+ }
+
+ EfiReleaseLock (&(Sock->Lock));
+ return Sock;
+}
+
+/**
+ Configure the specific socket Sock using configuration data ConfigData.
+
+ @param[in] Sock Pointer to the socket to be configured.
+ @param[in] ConfigData Pointer to the configuration data.
+
+ @retval EFI_SUCCESS The socket configured successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is already configured.
+
+**/
+EFI_STATUS
+SockConfigure (
+ IN SOCKET *Sock,
+ IN VOID *ConfigData
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockConfigure: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_CONFIGURED (Sock)) {
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ ASSERT (Sock->State == SO_CLOSED);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData);
+
+OnExit:
+ EfiReleaseLock (&(Sock->Lock));
+
+ return Status;
+}
+
+/**
+ Initiate a connection establishment process.
+
+ @param[in] Sock Pointer to the socket to initiate the initate the
+ connection.
+ @param[in] Token Pointer to the token used for the connection
+ operation.
+
+ @retval EFI_SUCCESS The connection initialized successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be an active one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockConnect (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockConnect: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto OnExit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto OnExit;
+ }
+
+ if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token;
+ SockSetState (Sock, SO_CONNECTING);
+ Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL);
+
+OnExit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+/**
+ Issue a listen token to get an existed connected network instance
+ or wait for a connection if there is none.
+
+ @param[in] Sock Pointer to the socket to accept connections.
+ @param[in] Token The token to accept a connection.
+
+ @retval EFI_SUCCESS Either a connection is accpeted or the Token is
+ buffered for further acception.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be a passive one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limits.
+
+**/
+EFI_STATUS
+SockAccept (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;
+ LIST_ENTRY *ListEntry;
+ EFI_STATUS Status;
+ SOCKET *Socket;
+ EFI_EVENT Event;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockAccept: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!SOCK_IS_LISTENING (Sock)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token;
+
+ //
+ // Check if a connection has already in this Sock->ConnectionList
+ //
+ NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) {
+
+ Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList);
+
+ if (SOCK_IS_CONNECTED (Socket)) {
+ ListenToken->NewChildHandle = Socket->SockHandle;
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);
+
+ RemoveEntryList (ListEntry);
+
+ ASSERT (Socket->Parent != NULL);
+
+ Socket->Parent->ConnCnt--;
+
+ DEBUG (
+ (EFI_D_INFO,
+ "SockAccept: Accept a socket, now conncount is %d",
+ Socket->Parent->ConnCnt)
+ );
+ Socket->Parent = NULL;
+
+ goto Exit;
+ }
+ }
+
+ //
+ // Buffer this token for latter incoming connection request
+ //
+ if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) {
+
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+
+ return Status;
+}
+
+/**
+ Issue a token with data to the socket to send out.
+
+ @param[in] Sock Pointer to the socket to process the token with
+ data.
+ @param[in] Token The token with data that needs to send out.
+
+ @retval EFI_SUCCESS The token processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limits.
+
+**/
+EFI_STATUS
+SockSend (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ SOCK_IO_TOKEN *SndToken;
+ EFI_EVENT Event;
+ UINT32 FreeSpace;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_STATUS Status;
+ SOCK_TOKEN *SockToken;
+ UINT32 DataLen;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockSend: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ SndToken = (SOCK_IO_TOKEN *) Token;
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData;
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ //
+ // check if a token is already in the token buffer
+ //
+ Event = SndToken->Token.Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ DataLen = TxData->DataLength;
+
+ //
+ // process this sending token now or buffer it only?
+ //
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);
+
+ if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) {
+
+ SockToken = SockBufferToken (
+ Sock,
+ &Sock->SndTokenList,
+ SndToken,
+ DataLen
+ );
+
+ if (NULL == SockToken) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+
+ SockToken = SockBufferToken (
+ Sock,
+ &Sock->ProcessingSndTokenList,
+ SndToken,
+ DataLen
+ );
+
+ if (NULL == SockToken) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockSend: Failed to buffer IO token into socket processing SndToken List\n",
+ Status)
+ );
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ Status = SockProcessTcpSndData (Sock, TxData);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockSend: Failed to process Snd Data\n",
+ Status)
+ );
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ }
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+/**
+ Issue a token to get data from the socket.
+
+ @param[in] Sock Pointer to the socket to get data from.
+ @param[in] Token The token to store the received data from the
+ socket.
+
+ @retval EFI_SUCCESS The token processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_CONNECTION_FIN The connection is closed and there is no more data.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.
+
+**/
+EFI_STATUS
+SockRcv (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ SOCK_IO_TOKEN *RcvToken;
+ UINT32 RcvdBytes;
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockRcv: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ RcvToken = (SOCK_IO_TOKEN *) Token;
+
+ //
+ // check if a token is already in the token buffer of this socket
+ //
+ Event = RcvToken->Token.Event;
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ RcvToken = (SOCK_IO_TOKEN *) Token;
+ RcvdBytes = GET_RCV_DATASIZE (Sock);
+
+ //
+ // check whether an error has happened before
+ //
+ if (EFI_ABORTED != Sock->SockError) {
+
+ SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError);
+ Sock->SockError = EFI_ABORTED;
+ goto Exit;
+ }
+
+ //
+ // check whether can not receive and there is no any
+ // data buffered in Sock->RcvBuffer
+ //
+ if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) {
+
+ Status = EFI_CONNECTION_FIN;
+ goto Exit;
+ }
+
+ if (RcvdBytes != 0) {
+ Status = SockProcessRcvToken (Sock, RcvToken);
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL);
+ } else {
+
+ if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+/**
+ Reset the socket and its associated protocol control block.
+
+ @param[in, out] Sock Pointer to the socket to be flushed.
+
+ @retval EFI_SUCCESS The socket is flushed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockFlush (
+ IN OUT SOCKET *Sock
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockFlush: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (!SOCK_IS_CONFIGURED (Sock)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL);
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockFlush: Protocol failed handling SOCK_FLUSH with %r",
+ Status)
+ );
+
+ goto Exit;
+ }
+
+ SOCK_ERROR (Sock, EFI_ABORTED);
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ Sock->ConfigureState = SO_UNCONFIGURED;
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+/**
+ Close or abort the socket associated connection.
+
+ @param[in, out] Sock Pointer to the socket of the connection to close
+ or abort.
+ @param[in] Token The token for a close operation.
+ @param[in] OnAbort TRUE for aborting the connection; FALSE to close it.
+
+ @retval EFI_SUCCESS The close or abort operation initialized
+ successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockClose (
+ IN OUT SOCKET *Sock,
+ IN VOID *Token,
+ IN BOOLEAN OnAbort
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockClose: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (SOCK_IS_DISCONNECTING (Sock)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Sock->CloseToken = Token;
+ SockSetState (Sock, SO_DISCONNECTING);
+
+ if (OnAbort) {
+ Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL);
+ } else {
+ Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL);
+ }
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+/**
+ Get the mode data of the low layer protocol.
+
+ @param[in] Sock Pointer to the socket to get mode data from.
+ @param[in, out] Mode Pointer to the data to store the low layer mode
+ information.
+
+ @retval EFI_SUCCESS The mode data was obtained successfully.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGetMode (
+ IN SOCKET *Sock,
+ IN OUT VOID *Mode
+ )
+{
+ return Sock->ProtoHandler (Sock, SOCK_MODE, Mode);
+}
+
+/**
+ Configure the low level protocol to join a multicast group for
+ this socket's connection.
+
+ @param[in] Sock Pointer to the socket of the connection to join the
+ specific multicast group.
+ @param[in] GroupInfo Pointer to the multicast group info.
+
+ @retval EFI_SUCCESS The configuration completed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGroup (
+ IN SOCKET *Sock,
+ IN VOID *GroupInfo
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockGroup: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo);
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
+/**
+ Add or remove route information in IP route table associated
+ with this socket.
+
+ @param[in] Sock Pointer to the socket associated with the IP route
+ table to operate on.
+ @param[in] RouteInfo Pointer to the route information to be processed.
+
+ @retval EFI_SUCCESS The route table updated successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockRoute (
+ IN SOCKET *Sock,
+ IN VOID *RouteInfo
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockRoute: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo);
+
+Exit:
+ EfiReleaseLock (&(Sock->Lock));
+ return Status;
+}
+
diff --git a/NetworkPkg/TcpDxe/Socket.h b/NetworkPkg/TcpDxe/Socket.h
new file mode 100644
index 0000000000..a00625244e
--- /dev/null
+++ b/NetworkPkg/TcpDxe/Socket.h
@@ -0,0 +1,924 @@
+/** @file
+ Common head file for TCP socket.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SOCKET_H_
+#define _SOCKET_H_
+
+#include <Uefi.h>
+
+#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
+
+#include <Library/NetLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DpcLib.h>
+
+#define SOCK_SND_BUF 0
+#define SOCK_RCV_BUF 1
+
+#define SOCK_BUFF_LOW_WATER (2 * 1024)
+#define SOCK_RCV_BUFF_SIZE (8 * 1024)
+#define SOCK_SND_BUFF_SIZE (8 * 1024)
+#define SOCK_BACKLOG 5
+
+#define PROTO_RESERVED_LEN 20
+
+#define SO_NO_MORE_DATA 0x0001
+
+//
+//
+//
+// When a socket is created it enters into SO_UNCONFIGURED,
+// no actions can be taken on this socket, only after calling
+// SockConfigure. The state transition diagram of socket is
+// as following:
+//
+// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING
+// ^ | |
+// | ---> SO_LISTENING |
+// | |
+// |------------------SO_DISCONNECTING<-- SO_CONNECTED
+//
+// A passive socket can only go into SO_LISTENING and
+// SO_UNCONFIGURED state. SO_XXXING state is a middle state
+// when a socket is undergoing a protocol procedure such
+// as requesting a TCP connection.
+//
+//
+//
+
+///
+/// Socket state
+///
+#define SO_CLOSED 0
+#define SO_LISTENING 1
+#define SO_CONNECTING 2
+#define SO_CONNECTED 3
+#define SO_DISCONNECTING 4
+
+///
+/// Socket configure state
+///
+#define SO_UNCONFIGURED 0
+#define SO_CONFIGURED_ACTIVE 1
+#define SO_CONFIGURED_PASSIVE 2
+#define SO_NO_MAPPING 3
+
+///
+/// The request issued from socket layer to protocol layer.
+///
+#define SOCK_ATTACH 0 ///< Attach current socket to a new PCB
+#define SOCK_DETACH 1 ///< Detach current socket from the PCB
+#define SOCK_CONFIGURE 2 ///< Configure attached PCB
+#define SOCK_FLUSH 3 ///< Flush attached PCB
+#define SOCK_SND 4 ///< Need protocol to send something
+#define SOCK_SNDPUSH 5 ///< Need protocol to send pushed data
+#define SOCK_SNDURG 6 ///< Need protocol to send urgent data
+#define SOCK_CONSUMED 7 ///< Application has retrieved data from socket
+#define SOCK_CONNECT 8 ///< Need to connect to a peer
+#define SOCK_CLOSE 9 ///< Need to close the protocol process
+#define SOCK_ABORT 10 ///< Need to reset the protocol process
+#define SOCK_POLL 11 ///< Need to poll to the protocol layer
+#define SOCK_ROUTE 12 ///< Need to add a route information
+#define SOCK_MODE 13 ///< Need to get the mode data of the protocol
+#define SOCK_GROUP 14 ///< Need to join a mcast group
+
+/**
+ Set socket SO_NO_MORE_DATA flag.
+
+ @param[in] Sock Pointer to the socket
+
+**/
+#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA)
+
+/**
+ Check whether the socket is unconfigured.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is unconfigued.
+ @retval FALSE The socket is not unconfigued.
+
+**/
+#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED)
+
+/**
+ Check whether the socket is configured.
+
+ @param[in] Sock Pointer to the socket
+
+ @retval TRUE The socket is configued
+ @retval FALSE The socket is not configued
+
+**/
+#define SOCK_IS_CONFIGURED(Sock) \
+ (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \
+ ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE))
+
+/**
+ Check whether the socket is configured to active mode.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is configued to active mode.
+ @retval FALSE The socket is not configued to active mode.
+
+**/
+#define SOCK_IS_CONFIGURED_ACTIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE)
+
+/**
+ Check whether the socket is configured to passive mode.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is configued to passive mode.
+ @retval FALSE The socket is not configued to passive mode.
+
+**/
+#define SOCK_IS_CONNECTED_PASSIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)
+
+/**
+ Check whether the socket is mapped.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is not mapping.
+ @retval FALSE The socket is mapped.
+
+**/
+#define SOCK_IS_NO_MAPPING(Sock) ((Sock)->ConfigureState == SO_NO_MAPPING)
+
+/**
+ Check whether the socket is closed.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is closed.
+ @retval FALSE The socket is not closed.
+
+**/
+#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED)
+
+/**
+ Check whether the socket is listening.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is listening.
+ @retval FALSE The socket is not listening.
+
+**/
+#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING)
+
+/**
+ Check whether the socket is connecting.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is connecting.
+ @retval FALSE The socket is not connecting.
+
+**/
+#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING)
+
+/**
+ Check whether the socket has connected.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket has connected.
+ @retval FALSE The socket has not connected.
+
+**/
+#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED)
+
+/**
+ Check whether the socket is disconnecting.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is disconnecting.
+ @retval FALSE The socket is not disconnecting.
+
+**/
+#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING)
+
+/**
+ Check whether the socket is no more data.
+
+ @param[in] Sock Pointer to the socket.
+
+ @retval TRUE The socket is no more data.
+ @retval FALSE The socket still has data.
+
+**/
+#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA))
+
+/**
+ Set the size of the receive buffer.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Size The size to set.
+
+**/
+#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size))
+
+/**
+ Get the size of the receive buffer.
+
+ @param[in] Sock Pointer to the socket.
+
+ @return The receive buffer size.
+
+**/
+#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater)
+
+/**
+ Get the size of the receive data.
+
+ @param[in] Sock Pointer to the socket.
+
+ @return The received data size.
+
+**/
+#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize)
+
+/**
+ Set the size of the send buffer.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Size The size to set.
+
+**/
+#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size))
+
+/**
+ Get the size of the send buffer.
+
+ @param[in] Sock Pointer to the socket.
+
+ @return The send buffer size.
+
+**/
+#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater)
+
+/**
+ Get the size of the send data.
+
+ @param[in] Sock Pointer to the socket.
+
+ @return The send data size.
+
+**/
+#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize)
+
+/**
+ Set the backlog value of the socket.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Value The value to set.
+
+**/
+#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value))
+
+/**
+ Get the backlog value of the socket.
+
+ @param[in] Sock Pointer to the socket.
+
+ @return The backlog value.
+
+**/
+#define GET_BACKLOG(Sock) ((Sock)->BackLog)
+
+/**
+ Set the socket with error state.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Error The error state.
+
+**/
+#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error))
+
+#define SOCK_SIGNATURE SIGNATURE_32 ('S', 'O', 'C', 'K')
+
+#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE)
+
+#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock)
+
+#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) ((Type *) (((SOCK_TOKEN *) (SockToken))->Token))
+
+typedef struct _TCP_SOCKET SOCKET;
+
+///
+/// Socket completion token
+///
+typedef struct _SOCK_COMPLETION_TOKEN {
+ EFI_EVENT Event; ///< The event to be issued
+ EFI_STATUS Status; ///< The status to be issued
+} SOCK_COMPLETION_TOKEN;
+
+typedef union {
+ VOID *RxData;
+ VOID *TxData;
+} SOCK_IO_DATA;
+
+///
+/// The application token with data packet
+///
+typedef struct _SOCK_IO_TOKEN {
+ SOCK_COMPLETION_TOKEN Token;
+ SOCK_IO_DATA Packet;
+} SOCK_IO_TOKEN;
+
+///
+/// The socket type.
+///
+typedef enum {
+ SockDgram, ///< This socket providing datagram service
+ SockStream ///< This socket providing stream service
+} SOCK_TYPE;
+
+///
+/// The buffer structure of rcvd data and send data used by socket.
+///
+typedef struct _SOCK_BUFFER {
+ UINT32 HighWater; ///< The buffersize upper limit of sock_buffer
+ UINT32 LowWater; ///< The low warter mark of sock_buffer
+ NET_BUF_QUEUE *DataQueue; ///< The queue to buffer data
+} SOCK_BUFFER;
+
+/**
+ The handler of protocol for request from socket.
+
+ @param[in] Socket The socket issuing the request to protocol.
+ @param[in] Request The request issued by socket.
+ @param[in] RequestData The request related data.
+
+ @retval EFI_SUCCESS The socket request is completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+typedef
+EFI_STATUS
+(*SOCK_PROTO_HANDLER) (
+ IN SOCKET *Socket,
+ IN UINT8 Request,
+ IN VOID *RequestData
+ );
+
+/**
+ The Callback funtion called after the TCP socket is created.
+
+ @param[in] This Pointer to the socket just created.
+ @param[in] Context Context of the socket.
+
+ @retval EFI_SUCCESS This protocol installed successfully.
+ @retval other Some error occured.
+
+**/
+typedef
+EFI_STATUS
+(*SOCK_CREATE_CALLBACK) (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+/**
+ The callback function called before the TCP socket is to be destroyed.
+
+ @param[in] This The TCP socket to be destroyed.
+ @param[in] Context The context.
+
+**/
+typedef
+VOID
+(*SOCK_DESTROY_CALLBACK) (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+///
+/// The initialize data for create a new socket.
+///
+typedef struct _SOCK_INIT_DATA {
+ SOCK_TYPE Type;
+ UINT8 State;
+
+ SOCKET *Parent; ///< The parent of this socket
+ UINT32 BackLog; ///< The connection limit for listening socket
+ UINT32 SndBufferSize; ///< The high warter mark of send buffer
+ UINT32 RcvBufferSize; ///< The high warter mark of receive buffer
+ UINT8 IpVersion;
+ VOID *Protocol; ///< The pointer to protocol function template
+ ///< wanted to install on socket
+
+ //
+ // Callbacks after socket is created and before socket is to be destroyed.
+ //
+ SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created
+ SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied
+ VOID *Context; ///< The context of the callback
+
+ //
+ // Opaque protocol data.
+ //
+ VOID *ProtoData;
+ UINT32 DataSize;
+
+ SOCK_PROTO_HANDLER ProtoHandler; ///< The handler of protocol for socket request
+
+ EFI_HANDLE DriverBinding; ///< The driver binding handle
+} SOCK_INIT_DATA;
+
+///
+/// The union type of TCP and UDP protocol.
+///
+typedef union _NET_PROTOCOL {
+ EFI_TCP4_PROTOCOL Tcp4Protocol; ///< Tcp4 protocol
+ EFI_TCP6_PROTOCOL Tcp6Protocol; ///< Tcp6 protocol
+} NET_PROTOCOL;
+///
+/// The socket structure representing a network service access point.
+///
+struct _TCP_SOCKET {
+ //
+ // Socket description information
+ //
+ UINT32 Signature; ///< Signature of the socket
+ EFI_HANDLE SockHandle; ///< The virtual handle of the socket
+ EFI_HANDLE DriverBinding; ///< Socket's driver binding protocol
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ LIST_ENTRY Link;
+ UINT8 ConfigureState;
+ SOCK_TYPE Type;
+ UINT8 State;
+ UINT16 Flag;
+ EFI_LOCK Lock; ///< The lock of socket
+ SOCK_BUFFER SndBuffer; ///< Send buffer of application's data
+ SOCK_BUFFER RcvBuffer; ///< Receive buffer of received data
+ EFI_STATUS SockError; ///< The error returned by low layer protocol
+ BOOLEAN IsDestroyed;
+
+ //
+ // Fields used to manage the connection request
+ //
+ UINT32 BackLog; ///< the limit of connection to this socket
+ UINT32 ConnCnt; ///< the current count of connections to it
+ SOCKET *Parent; ///< listening parent that accept the connection
+ LIST_ENTRY ConnectionList; ///< the connections maintained by this socket
+ //
+ // The queue to buffer application's asynchronous token
+ //
+ LIST_ENTRY ListenTokenList;
+ LIST_ENTRY RcvTokenList;
+ LIST_ENTRY SndTokenList;
+ LIST_ENTRY ProcessingSndTokenList;
+
+ SOCK_COMPLETION_TOKEN *ConnectionToken; ///< app's token to signal if connected
+ SOCK_COMPLETION_TOKEN *CloseToken; ///< app's token to signal if closed
+ //
+ // Interface for low level protocol
+ //
+ SOCK_PROTO_HANDLER ProtoHandler; ///< The request handler of protocol
+ UINT8 ProtoReserved[PROTO_RESERVED_LEN]; ///< Data fields reserved for protocol
+ UINT8 IpVersion;
+ NET_PROTOCOL NetProtocol; ///< TCP or UDP protocol socket used
+ //
+ // Callbacks after socket is created and before socket is to be destroyed.
+ //
+ SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created
+ SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied
+ VOID *Context; ///< The context of the callback
+};
+
+///
+/// The token structure buffered in socket layer.
+///
+typedef struct _SOCK_TOKEN {
+ LIST_ENTRY TokenList; ///< The entry to add in the token list
+ SOCK_COMPLETION_TOKEN *Token; ///< The application's token
+ UINT32 RemainDataLen; ///< Unprocessed data length
+ SOCKET *Sock; ///< The poninter to the socket this token
+ ///< belongs to
+} SOCK_TOKEN;
+
+///
+/// Reserved data to access the NET_BUF delivered by TCP driver.
+///
+typedef struct _TCP_RSV_DATA {
+ UINT32 UrgLen;
+} TCP_RSV_DATA;
+
+//
+// Socket provided oprerations for low layer protocol implemented in SockImpl.c
+//
+
+/**
+ Set the state of the socket.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in] State The new socket state to be set.
+
+**/
+VOID
+SockSetState (
+ IN OUT SOCKET *Sock,
+ IN UINT8 State
+ );
+
+/**
+ Clone a new socket including its associated protocol control block.
+
+ @param[in] Sock Pointer to the socket to be cloned.
+
+ @return Pointer to the newly cloned socket. If NULL, an error condition occurred.
+
+**/
+SOCKET *
+SockClone (
+ IN SOCKET *Sock
+ );
+
+/**
+ Called by the low layer protocol to indicate the socket a connection is
+ established.
+
+ This function just changes the socket's state to SO_CONNECTED
+ and signals the token used for connection establishment.
+
+ @param[in, out] Sock Pointer to the socket associated with the
+ established connection.
+
+**/
+VOID
+SockConnEstablished (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Called by the low layer protocol to indicate that the connection is closed.
+
+ This function flushes the socket, sets the state to SO_CLOSED, and signals
+ the close token.
+
+ @param[in, out] Sock Pointer to the socket associated with the closed
+ connection.
+
+**/
+VOID
+SockConnClosed (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Called by low layer protocol to indicate that some data is sent or processed.
+
+ This function trims the sent data in the socket send buffer and signals the data
+ token, if proper.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in] Count The length of the data processed or sent, in bytes.
+
+**/
+VOID
+SockDataSent (
+ IN OUT SOCKET *Sock,
+ IN UINT32 Count
+ );
+
+/**
+ Called by the low layer protocol to copy some data in socket send
+ buffer starting from the specific offset to a buffer provided by
+ the caller.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Offset The start point of the data to be copied.
+ @param[in] Len The length of the data to be copied.
+ @param[out] Dest Pointer to the destination to copy the data.
+
+ @return The data size copied.
+
+**/
+UINT32
+SockGetDataToSend (
+ IN SOCKET *Sock,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ OUT UINT8 *Dest
+ );
+
+/**
+ Called by the low layer protocol to deliver received data to socket layer.
+
+ This function appends the data to the socket receive buffer, set the
+ urgent data length, then checks if any receive token can be signaled.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in, out] NetBuffer Pointer to the buffer that contains the received data.
+ @param[in] UrgLen The length of the urgent data in the received data.
+
+**/
+VOID
+SockDataRcvd (
+ IN OUT SOCKET *Sock,
+ IN OUT NET_BUF *NetBuffer,
+ IN UINT32 UrgLen
+ );
+
+/**
+ Get the length of the free space of the specific socket buffer.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Which Flag to indicate which socket buffer to check:
+ either send buffer or receive buffer.
+
+ @return The length of the free space, in bytes.
+
+**/
+UINT32
+SockGetFreeSpace (
+ IN SOCKET *Sock,
+ IN UINT32 Which
+ );
+
+/**
+ Called by the low layer protocol to indicate that there will be no more data
+ from the communication peer.
+
+ This function sets the socket's state to SO_NO_MORE_DATA and signals all queued
+ IO tokens with the error status EFI_CONNECTION_FIN.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockNoMoreData (
+ IN OUT SOCKET *Sock
+ );
+
+//
+// Socket provided operations for user interface implemented in SockInterface.c
+//
+
+/**
+ Create a socket and its associated protocol control block
+ with the intial data SockInitData and protocol specific
+ data ProtoData.
+
+ @param[in] SockInitData Inital data to setting the socket.
+
+ @return Pointer to the newly created socket. If NULL, an error condition occured.
+
+**/
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData
+ );
+
+/**
+ Destory the socket Sock and its associated protocol control block.
+
+ @param[in, out] Sock The socket to be destroyed.
+
+ @retval EFI_SUCCESS The socket Sock was destroyed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockDestroyChild (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Configure the specific socket Sock using configuration data ConfigData.
+
+ @param[in] Sock Pointer to the socket to be configured.
+ @param[in] ConfigData Pointer to the configuration data.
+
+ @retval EFI_SUCCESS The socket configured successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is already configured.
+
+**/
+EFI_STATUS
+SockConfigure (
+ IN SOCKET *Sock,
+ IN VOID *ConfigData
+ );
+
+/**
+ Initiate a connection establishment process.
+
+ @param[in] Sock Pointer to the socket to initiate the initate the
+ connection.
+ @param[in] Token Pointer to the token used for the connection
+ operation.
+
+ @retval EFI_SUCCESS The connection initialized successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be an active one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockConnect (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Issue a listen token to get an existed connected network instance,
+ or wait for a connection if there is none.
+
+ @param[in] Sock Pointer to the socket to accept connections.
+ @param[in] Token The token to accept a connection.
+
+ @retval EFI_SUCCESS Either a connection is accepted or the Token is
+ buffered for further acceptance.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be a passive one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit.
+
+**/
+EFI_STATUS
+SockAccept (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Issue a token with data to the socket to send out.
+
+ @param[in] Sock Pointer to the socket to process the token with
+ data.
+ @param[in] Token The token with data that needs to send out.
+
+ @retval EFI_SUCCESS The token processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit.
+
+**/
+EFI_STATUS
+SockSend (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Issue a token to get data from the socket.
+
+ @param[in] Sock Pointer to the socket to get data from.
+ @param[in] Token The token to store the received data from the
+ socket.
+
+ @retval EFI_SUCCESS The token processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_CONNECTION_FIN The connection is closed and there is no more data.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit.
+
+**/
+EFI_STATUS
+SockRcv (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ Reset the socket and its associated protocol control block.
+
+ @param[in, out] Sock Pointer to the socket to be flushed.
+
+ @retval EFI_SUCCESS The socket flushed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockFlush (
+ IN OUT SOCKET *Sock
+ );
+
+/**
+ Close or abort the socket associated connection.
+
+ @param[in, out] Sock Pointer to the socket of the connection to close
+ or abort.
+ @param[in] Token The token for close operation.
+ @param[in] OnAbort TRUE for aborting the connection, FALSE to close it.
+
+ @retval EFI_SUCCESS The close or abort operation initialized
+ successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockClose (
+ IN OUT SOCKET *Sock,
+ IN VOID *Token,
+ IN BOOLEAN OnAbort
+ );
+
+/**
+ Get the mode data of the low layer protocol.
+
+ @param[in] Sock Pointer to the socket to get mode data from.
+ @param[in, out] Mode Pointer to the data to store the low layer mode
+ information.
+
+ @retval EFI_SUCCESS The mode data was obtained successfully.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGetMode (
+ IN SOCKET *Sock,
+ IN OUT VOID *Mode
+ );
+
+/**
+ Configure the low level protocol to join a multicast group for
+ this socket's connection.
+
+ @param[in] Sock Pointer to the socket of the connection to join the
+ specific multicast group.
+ @param[in] GroupInfo Pointer to the multicast group information.
+
+ @retval EFI_SUCCESS The configuration completed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGroup (
+ IN SOCKET *Sock,
+ IN VOID *GroupInfo
+ );
+
+/**
+ Add or remove route information in IP route table associated
+ with this socket.
+
+ @param[in] Sock Pointer to the socket associated with the IP route
+ table to operate on.
+ @param[in] RouteInfo Pointer to the route information to be processed.
+
+ @retval EFI_SUCCESS The route table updated successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockRoute (
+ IN SOCKET *Sock,
+ IN VOID *RouteInfo
+ );
+
+#endif
diff --git a/NetworkPkg/TcpDxe/TcpDispatcher.c b/NetworkPkg/TcpDxe/TcpDispatcher.c
new file mode 100644
index 0000000000..eaa75a4ec4
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDispatcher.c
@@ -0,0 +1,861 @@
+/** @file
+ The implementation of a dispatch routine for processing TCP requests.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+/**
+ Add or remove a route entry in the IP route table associated with this TCP instance.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] RouteInfo Pointer to the route information to be processed.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The driver instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration(DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table
+ (when RouteInfo->DeleteRoute is TRUE).
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table
+ (when RouteInfo->DeleteRoute is FALSE).
+**/
+EFI_STATUS
+Tcp4Route (
+ IN TCP_CB *Tcb,
+ IN TCP4_ROUTE_INFO *RouteInfo
+ )
+{
+ IP_IO_IP_PROTOCOL Ip;
+
+ Ip = Tcb->IpInfo->Ip;
+
+ ASSERT (Ip.Ip4!= NULL);
+
+ return Ip.Ip4->Routes (
+ Ip.Ip4,
+ RouteInfo->DeleteRoute,
+ RouteInfo->SubnetAddress,
+ RouteInfo->SubnetMask,
+ RouteInfo->GatewayAddress
+ );
+
+}
+
+/**
+ Get the operational settings of this TCPv4 instance.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Mode Pointer to the buffer to store the operational
+ settings.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+
+**/
+EFI_STATUS
+Tcp4GetMode (
+ IN TCP_CB *Tcb,
+ IN OUT TCP4_MODE_DATA *Mode
+ )
+{
+ SOCKET *Sock;
+ EFI_TCP4_CONFIG_DATA *ConfigData;
+ EFI_TCP4_ACCESS_POINT *AccessPoint;
+ EFI_TCP4_OPTION *Option;
+ EFI_IP4_PROTOCOL *Ip;
+
+ Sock = Tcb->Sk;
+
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->Tcp4State != NULL) {
+ *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State;
+ }
+
+ if (Mode->Tcp4ConfigData != NULL) {
+
+ ConfigData = Mode->Tcp4ConfigData;
+ AccessPoint = &(ConfigData->AccessPoint);
+ Option = ConfigData->ControlOption;
+
+ ConfigData->TypeOfService = Tcb->Tos;
+ ConfigData->TimeToLive = Tcb->Ttl;
+
+ AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr;
+
+ CopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ AccessPoint->SubnetMask = Tcb->SubnetMask;
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
+
+ CopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
+
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
+
+ if (Option != NULL) {
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
+
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
+ Option->DataRetries = Tcb->MaxRexmit;
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
+
+ Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));
+ Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));
+ Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));
+
+ Option->EnableSelectiveAck = FALSE;
+ Option->EnablePathMtuDiscovery = FALSE;
+ }
+ }
+
+ Ip = Tcb->IpInfo->Ip.Ip4;
+ ASSERT (Ip != NULL);
+
+ return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);
+}
+
+/**
+ Get the operational settings of this TCPv6 instance.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Mode Pointer to the buffer to store the operational
+ settings.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+
+**/
+EFI_STATUS
+Tcp6GetMode (
+ IN TCP_CB *Tcb,
+ IN OUT TCP6_MODE_DATA *Mode
+ )
+{
+ SOCKET *Sock;
+ EFI_TCP6_CONFIG_DATA *ConfigData;
+ EFI_TCP6_ACCESS_POINT *AccessPoint;
+ EFI_TCP6_OPTION *Option;
+ EFI_IP6_PROTOCOL *Ip;
+
+ Sock = Tcb->Sk;
+
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->Tcp6State != NULL) {
+ *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State);
+ }
+
+ if (Mode->Tcp6ConfigData != NULL) {
+
+ ConfigData = Mode->Tcp6ConfigData;
+ AccessPoint = &(ConfigData->AccessPoint);
+ Option = ConfigData->ControlOption;
+
+ ConfigData->TrafficClass = Tcb->Tos;
+ ConfigData->HopLimit = Tcb->Ttl;
+
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
+
+ IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);
+ IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);
+
+ if (Option != NULL) {
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
+
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
+ Option->DataRetries = Tcb->MaxRexmit;
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
+
+ Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));
+ Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));
+ Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));
+
+ Option->EnableSelectiveAck = FALSE;
+ Option->EnablePathMtuDiscovery = FALSE;
+ }
+ }
+
+ Ip = Tcb->IpInfo->Ip.Ip6;
+ ASSERT (Ip != NULL);
+
+ return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData);
+}
+
+/**
+ If TcpAp->StationPort isn't zero, check whether the access point
+ is registered, else generate a random station port for this
+ access point.
+
+ @param[in] TcpAp Pointer to the access point.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6
+
+ @retval EFI_SUCCESS The check passed or the port is assigned.
+ @retval EFI_INVALID_PARAMETER The non-zero station port is already used.
+ @retval EFI_OUT_OF_RESOURCES No port can be allocated.
+
+**/
+EFI_STATUS
+TcpBind (
+ IN TCP_ACCESS_POINT *TcpAp,
+ IN UINT8 IpVersion
+ )
+{
+ BOOLEAN Cycle;
+ EFI_IP_ADDRESS Local;
+ UINT16 *Port;
+ UINT16 *RandomPort;
+
+ if (IpVersion == IP_VERSION_4) {
+ CopyMem (&Local, &TcpAp->Tcp4Ap.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+ Port = &TcpAp->Tcp4Ap.StationPort;
+ RandomPort = &mTcp4RandomPort;
+ } else {
+ IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress);
+ Port = &TcpAp->Tcp6Ap.StationPort;
+ RandomPort = &mTcp6RandomPort;
+ }
+
+ if (0 != *Port) {
+ //
+ // Check if a same endpoing is bound.
+ //
+ if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ //
+ // generate a random port
+ //
+ Cycle = FALSE;
+
+ if (TCP_PORT_USER_RESERVED == *RandomPort) {
+ *RandomPort = TCP_PORT_KNOWN;
+ }
+
+ (*RandomPort)++;
+
+ while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) {
+ (*RandomPort)++;
+
+ if (*RandomPort <= TCP_PORT_KNOWN) {
+ if (Cycle) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpBind: no port can be allocated for this pcb\n")
+ );
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *RandomPort = TCP_PORT_KNOWN + 1;
+
+ Cycle = TRUE;
+ }
+ }
+
+ *Port = *RandomPort;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Flush the Tcb add its associated protocols.
+
+ @param[in, out] Tcb Pointer to the TCP_CB to be flushed.
+
+**/
+VOID
+TcpFlushPcb (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ SOCKET *Sock;
+ TCP_PROTO_DATA *TcpProto;
+
+ IpIoConfigIp (Tcb->IpInfo, NULL);
+
+ Sock = Tcb->Sk;
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+
+ if (SOCK_IS_CONFIGURED (Sock)) {
+ RemoveEntryList (&Tcb->List);
+
+ if (Sock->DevicePath != NULL) {
+ //
+ // Uninstall the device path protocl.
+ //
+ gBS->UninstallProtocolInterface (
+ Sock->SockHandle,
+ &gEfiDevicePathProtocolGuid,
+ Sock->DevicePath
+ );
+
+ FreePool (Sock->DevicePath);
+ Sock->DevicePath = NULL;
+ }
+
+ TcpSetVariableData (TcpProto->TcpService);
+ }
+
+ NetbufFreeList (&Tcb->SndQue);
+ NetbufFreeList (&Tcb->RcvQue);
+ Tcb->State = TCP_CLOSED;
+}
+
+/**
+ Attach a Pcb to the socket.
+
+ @param[in] Sk Pointer to the socket of this TCP instance.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
+
+**/
+EFI_STATUS
+TcpAttachPcb (
+ IN SOCKET *Sk
+ )
+{
+ TCP_CB *Tcb;
+ TCP_PROTO_DATA *ProtoData;
+ IP_IO *IpIo;
+
+ Tcb = AllocateZeroPool (sizeof (TCP_CB));
+
+ if (Tcb == NULL) {
+
+ DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;
+ IpIo = ProtoData->TcpService->IpIo;
+
+ //
+ // Create an IpInfo for this Tcb.
+ //
+ Tcb->IpInfo = IpIoAddIp (IpIo);
+ if (Tcb->IpInfo == NULL) {
+
+ FreePool (Tcb);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&Tcb->List);
+ InitializeListHead (&Tcb->SndQue);
+ InitializeListHead (&Tcb->RcvQue);
+
+ Tcb->State = TCP_CLOSED;
+ Tcb->Sk = Sk;
+ ProtoData->TcpPcb = Tcb;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detach the Pcb of the socket.
+
+ @param[in, out] Sk Pointer to the socket of this TCP instance.
+
+**/
+VOID
+TcpDetachPcb (
+ IN OUT SOCKET *Sk
+ )
+{
+ TCP_PROTO_DATA *ProtoData;
+ TCP_CB *Tcb;
+
+ ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ TcpFlushPcb (Tcb);
+
+ IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);
+
+ FreePool (Tcb);
+
+ ProtoData->TcpPcb = NULL;
+}
+
+/**
+ Configure the Pcb using CfgData.
+
+ @param[in] Sk Pointer to the socket of this TCP instance.
+ @param[in] CfgData Pointer to the TCP configuration data.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER A same access point has been configured in
+ another TCP instance.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
+
+**/
+EFI_STATUS
+TcpConfigurePcb (
+ IN SOCKET *Sk,
+ IN TCP_CONFIG_DATA *CfgData
+ )
+{
+ IP_IO_IP_CONFIG_DATA IpCfgData;
+ EFI_STATUS Status;
+ EFI_TCP4_OPTION *Option;
+ TCP_PROTO_DATA *TcpProto;
+ TCP_CB *Tcb;
+ TCP_ACCESS_POINT *TcpAp;
+
+ ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL));
+
+ TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = TcpProto->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ if (Sk->IpVersion == IP_VERSION_4) {
+ //
+ // Add Ip for send pkt to the peer
+ //
+ CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA));
+ IpCfgData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ IpCfgData.Ip4CfgData.TypeOfService = CfgData->Tcp4CfgData.TypeOfService;
+ IpCfgData.Ip4CfgData.TimeToLive = CfgData->Tcp4CfgData.TimeToLive;
+ IpCfgData.Ip4CfgData.UseDefaultAddress = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;
+ IpCfgData.Ip4CfgData.SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;
+ IpCfgData.Ip4CfgData.ReceiveTimeout = (UINT32) (-1);
+ CopyMem (
+ &IpCfgData.Ip4CfgData.StationAddress,
+ &CfgData->Tcp4CfgData.AccessPoint.StationAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ } else {
+ ASSERT (Sk->IpVersion == IP_VERSION_6);
+
+ CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA));
+ IpCfgData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ IpCfgData.Ip6CfgData.TrafficClass = CfgData->Tcp6CfgData.TrafficClass;
+ IpCfgData.Ip6CfgData.HopLimit = CfgData->Tcp6CfgData.HopLimit;
+ IpCfgData.Ip6CfgData.ReceiveTimeout = (UINT32) (-1);
+ IP6_COPY_ADDRESS (
+ &IpCfgData.Ip6CfgData.StationAddress,
+ &CfgData->Tcp6CfgData.AccessPoint.StationAddress
+ );
+ IP6_COPY_ADDRESS (
+ &IpCfgData.Ip6CfgData.DestinationAddress,
+ &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress
+ );
+ }
+
+ //
+ // Configure the IP instance this Tcb consumes.
+ //
+ Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);
+ if (EFI_ERROR (Status)) {
+ goto OnExit;
+ }
+
+ if (Sk->IpVersion == IP_VERSION_4) {
+ //
+ // Get the default address information if the instance is configured to use default address.
+ //
+ CfgData->Tcp4CfgData.AccessPoint.StationAddress = IpCfgData.Ip4CfgData.StationAddress;
+ CfgData->Tcp4CfgData.AccessPoint.SubnetMask = IpCfgData.Ip4CfgData.SubnetMask;
+
+ TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint;
+ } else {
+ IP6_COPY_ADDRESS (
+ &CfgData->Tcp6CfgData.AccessPoint.StationAddress,
+ &IpCfgData.Ip6CfgData.StationAddress
+ );
+
+ TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint;
+ }
+
+ //
+ // check if we can bind this endpoint in CfgData
+ //
+ Status = TcpBind (TcpAp, Sk->IpVersion);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpConfigurePcb: Bind endpoint failed with %r\n",
+ Status)
+ );
+
+ goto OnExit;
+ }
+
+ //
+ // Initalize the operating information in this Tcb
+ //
+ ASSERT (Tcb->State == TCP_CLOSED &&
+ IsListEmpty (&Tcb->SndQue) &&
+ IsListEmpty (&Tcb->RcvQue));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+ Tcb->State = TCP_CLOSED;
+
+ Tcb->SndMss = 536;
+ Tcb->RcvMss = TcpGetRcvMss (Sk);
+
+ Tcb->SRtt = 0;
+ Tcb->Rto = 3 * TCP_TICK_HZ;
+
+ Tcb->CWnd = Tcb->SndMss;
+ Tcb->Ssthresh = 0xffffffff;
+
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+
+ Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN;
+ Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD;
+ Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE;
+ Tcb->MaxRexmit = TCP_MAX_LOSS;
+ Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME;
+ Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME;
+ Tcb->ConnectTimeout = TCP_CONNECT_TIME;
+
+ if (Sk->IpVersion == IP_VERSION_4) {
+ //
+ // initialize Tcb in the light of CfgData
+ //
+ Tcb->Ttl = CfgData->Tcp4CfgData.TimeToLive;
+ Tcb->Tos = CfgData->Tcp4CfgData.TypeOfService;
+
+ Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;
+
+ CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR));
+ Tcb->LocalEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort);
+ Tcb->SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask;
+
+ CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
+ Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort);
+
+ Option = CfgData->Tcp4CfgData.ControlOption;
+ } else {
+ Tcb->Ttl = CfgData->Tcp6CfgData.HopLimit;
+ Tcb->Tos = CfgData->Tcp6CfgData.TrafficClass;
+
+ IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress);
+ Tcb->LocalEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort);
+
+ IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress);
+ Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort);
+
+ //
+ // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same.
+ //
+ Option = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption;
+ }
+
+ if (Option != NULL) {
+ SET_RCV_BUFFSIZE (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_RCV_BUF_SIZE_MIN,
+ TCP_RCV_BUF_SIZE,
+ TCP_RCV_BUF_SIZE,
+ Option->ReceiveBufferSize
+ )
+ )
+ );
+ SET_SND_BUFFSIZE (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_SND_BUF_SIZE_MIN,
+ TCP_SND_BUF_SIZE,
+ TCP_SND_BUF_SIZE,
+ Option->SendBufferSize
+ )
+ )
+ );
+
+ SET_BACKLOG (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_BACKLOG_MIN,
+ TCP_BACKLOG,
+ TCP_BACKLOG,
+ Option->MaxSynBackLog
+ )
+ )
+ );
+
+ Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL (
+ TCP_MAX_LOSS_MIN,
+ TCP_MAX_LOSS,
+ TCP_MAX_LOSS,
+ Option->DataRetries
+ );
+ Tcb->FinWait2Timeout = TCP_COMP_VAL (
+ TCP_FIN_WAIT2_TIME,
+ TCP_FIN_WAIT2_TIME_MAX,
+ TCP_FIN_WAIT2_TIME,
+ (UINT32) (Option->FinTimeout * TCP_TICK_HZ)
+ );
+
+ if (Option->TimeWaitTimeout != 0) {
+ Tcb->TimeWaitTimeout = TCP_COMP_VAL (
+ TCP_TIME_WAIT_TIME,
+ TCP_TIME_WAIT_TIME_MAX,
+ TCP_TIME_WAIT_TIME,
+ (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ)
+ );
+ } else {
+ Tcb->TimeWaitTimeout = 0;
+ }
+
+ if (Option->KeepAliveProbes != 0) {
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+
+ Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL (
+ TCP_MAX_KEEPALIVE_MIN,
+ TCP_MAX_KEEPALIVE,
+ TCP_MAX_KEEPALIVE,
+ Option->KeepAliveProbes
+ );
+ Tcb->KeepAliveIdle = TCP_COMP_VAL (
+ TCP_KEEPALIVE_IDLE_MIN,
+ TCP_KEEPALIVE_IDLE_MAX,
+ TCP_KEEPALIVE_IDLE_MIN,
+ (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ)
+ );
+ Tcb->KeepAlivePeriod = TCP_COMP_VAL (
+ TCP_KEEPALIVE_PERIOD_MIN,
+ TCP_KEEPALIVE_PERIOD,
+ TCP_KEEPALIVE_PERIOD,
+ (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ)
+ );
+ }
+
+ Tcb->ConnectTimeout = TCP_COMP_VAL (
+ TCP_CONNECT_TIME_MIN,
+ TCP_CONNECT_TIME,
+ TCP_CONNECT_TIME,
+ (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ)
+ );
+
+ if (!Option->EnableNagle) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);
+ }
+
+ if (!Option->EnableTimeStamp) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS);
+ }
+
+ if (!Option->EnableWindowScaling) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS);
+ }
+ }
+
+ //
+ // The socket is bound, the <SrcIp, SrcPort, DstIp, DstPort> is
+ // determined, construct the IP device path and install it.
+ //
+ Status = TcpInstallDevicePath (Sk);
+ if (EFI_ERROR (Status)) {
+ goto OnExit;
+ }
+
+ //
+ // update state of Tcb and socket
+ //
+ if (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) ||
+ ((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag)
+ ) {
+
+ TcpSetState (Tcb, TCP_LISTEN);
+ SockSetState (Sk, SO_LISTENING);
+
+ Sk->ConfigureState = SO_CONFIGURED_PASSIVE;
+ } else {
+
+ Sk->ConfigureState = SO_CONFIGURED_ACTIVE;
+ }
+
+ if (Sk->IpVersion == IP_VERSION_6) {
+ Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK;
+ }
+
+ TcpInsertTcb (Tcb);
+
+OnExit:
+
+ return Status;
+}
+
+/**
+ The procotol handler provided to the socket layer, which is used to
+ dispatch the socket level requests by calling the corresponding
+ TCP layer functions.
+
+ @param[in] Sock Pointer to the socket of this TCP instance.
+ @param[in] Request The code of this operation request.
+ @param[in] Data Pointer to the operation specific data passed in
+ together with the operation request. This is an
+ optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The socket request completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+EFI_STATUS
+TcpDispatcher (
+ IN SOCKET *Sock,
+ IN UINT8 Request,
+ IN VOID *Data OPTIONAL
+ )
+{
+ TCP_CB *Tcb;
+ TCP_PROTO_DATA *ProtoData;
+
+ ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ switch (Request) {
+ case SOCK_POLL:
+ if (Tcb->Sk->IpVersion == IP_VERSION_4) {
+ ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4);
+ } else {
+ ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6);
+ }
+
+ break;
+
+ case SOCK_CONSUMED:
+ //
+ // After user received data from socket buffer, socket will
+ // notify TCP using this message to give it a chance to send out
+ // window update information
+ //
+ ASSERT (Tcb != NULL);
+ TcpOnAppConsume (Tcb);
+ break;
+
+ case SOCK_SND:
+
+ ASSERT (Tcb != NULL);
+ TcpOnAppSend (Tcb);
+ break;
+
+ case SOCK_CLOSE:
+
+ TcpOnAppClose (Tcb);
+
+ break;
+
+ case SOCK_ABORT:
+
+ TcpOnAppAbort (Tcb);
+
+ break;
+
+ case SOCK_SNDPUSH:
+ Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
+
+ break;
+
+ case SOCK_SNDURG:
+ Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1;
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);
+
+ break;
+
+ case SOCK_CONNECT:
+
+ TcpOnAppConnect (Tcb);
+
+ break;
+
+ case SOCK_ATTACH:
+
+ return TcpAttachPcb (Sock);
+
+ break;
+
+ case SOCK_FLUSH:
+
+ TcpFlushPcb (Tcb);
+
+ break;
+
+ case SOCK_DETACH:
+
+ TcpDetachPcb (Sock);
+
+ break;
+
+ case SOCK_CONFIGURE:
+
+ return TcpConfigurePcb (
+ Sock,
+ (TCP_CONFIG_DATA *) Data
+ );
+
+ break;
+
+ case SOCK_MODE:
+
+ ASSERT ((Data != NULL) && (Tcb != NULL));
+
+ if (Tcb->Sk->IpVersion == IP_VERSION_4) {
+
+ return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data);
+ } else {
+
+ return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data);
+ }
+
+ break;
+
+ case SOCK_ROUTE:
+
+ ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4));
+
+ return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data);
+
+ default:
+
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/TcpDxe/TcpDriver.c b/NetworkPkg/TcpDxe/TcpDriver.c
new file mode 100644
index 0000000000..37e53af4b8
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDriver.c
@@ -0,0 +1,891 @@
+/** @file
+ The driver binding and service binding protocol for the TCP driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+UINT16 mTcp4RandomPort;
+UINT16 mTcp6RandomPort;
+
+TCP_HEARTBEAT_TIMER mTcpTimer = {
+ NULL,
+ 0
+};
+
+EFI_TCP4_PROTOCOL gTcp4ProtocolTemplate = {
+ Tcp4GetModeData,
+ Tcp4Configure,
+ Tcp4Routes,
+ Tcp4Connect,
+ Tcp4Accept,
+ Tcp4Transmit,
+ Tcp4Receive,
+ Tcp4Close,
+ Tcp4Cancel,
+ Tcp4Poll
+};
+
+EFI_TCP6_PROTOCOL gTcp6ProtocolTemplate = {
+ Tcp6GetModeData,
+ Tcp6Configure,
+ Tcp6Connect,
+ Tcp6Accept,
+ Tcp6Transmit,
+ Tcp6Receive,
+ Tcp6Close,
+ Tcp6Cancel,
+ Tcp6Poll
+};
+
+SOCK_INIT_DATA mTcpDefaultSockData = {
+ SockStream,
+ SO_CLOSED,
+ NULL,
+ TCP_BACKLOG,
+ TCP_SND_BUF_SIZE,
+ TCP_RCV_BUF_SIZE,
+ IP_VERSION_4,
+ NULL,
+ TcpCreateSocketCallback,
+ TcpDestroySocketCallback,
+ NULL,
+ NULL,
+ 0,
+ TcpDispatcher,
+ NULL,
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gTcpDriverBinding = {
+ TcpDriverBindingSupported,
+ TcpDriverBindingStart,
+ TcpDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL gTcpServiceBinding = {
+ TcpServiceBindingCreateChild,
+ TcpServiceBindingDestroyChild
+};
+
+
+/**
+ Create and start the heartbeat timer for the TCP driver.
+
+ @retval EFI_SUCCESS The timer was successfully created and started.
+ @retval other The timer was not created.
+
+**/
+EFI_STATUS
+TcpCreateTimer (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (mTcpTimer.RefCnt == 0) {
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ TcpTicking,
+ NULL,
+ &mTcpTimer.TimerEvent
+ );
+ if (!EFI_ERROR (Status)) {
+
+ Status = gBS->SetTimer (
+ mTcpTimer.TimerEvent,
+ TimerPeriodic,
+ (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ)
+ );
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+
+ mTcpTimer.RefCnt++;
+ }
+
+ return Status;
+}
+
+/**
+ Stop and destroy the heartbeat timer for TCP driver.
+
+**/
+VOID
+TcpDestroyTimer (
+ VOID
+ )
+{
+ ASSERT (mTcpTimer.RefCnt > 0);
+
+ mTcpTimer.RefCnt--;
+
+ if (mTcpTimer.RefCnt > 0) {
+ return;
+ }
+
+ gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0);
+ gBS->CloseEvent (mTcpTimer.TimerEvent);
+ mTcpTimer.TimerEvent = NULL;
+}
+
+/**
+ The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle.
+
+ @param[in] ImageHandle The firmware allocated handle for this driver image.
+ @param[in] SystemTable Pointer to the EFI system table.
+
+ @retval EFI_SUCCESS The driver loaded.
+ @retval other The driver did not load.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Seed;
+
+ //
+ // Install the TCP Driver Binding Protocol
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gTcpDriverBinding,
+ ImageHandle,
+ &gTcpComponentName,
+ &gTcpComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Initialize ISS and random port.
+ //
+ Seed = NetRandomInitSeed ();
+ mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss;
+ mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN));
+ mTcp6RandomPort = mTcp4RandomPort;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create a new TCP4 or TCP6 driver service binding protocol
+
+ @param[in] Controller Controller handle of device to bind driver to.
+ @param[in] ImageHandle The TCP driver's image handle.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_SUCCESS A new IP6 service binding private was created.
+
+**/
+EFI_STATUS
+TcpCreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_STATUS Status;
+ EFI_GUID *IpServiceBindingGuid;
+ EFI_GUID *TcpServiceBindingGuid;
+ TCP_SERVICE_DATA *TcpServiceData;
+ IP_IO_OPEN_DATA OpenData;
+
+ if (IpVersion == IP_VERSION_4) {
+ IpServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid;
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ } else {
+ IpServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid;
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ TcpServiceBindingGuid,
+ NULL,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ IpServiceBindingGuid,
+ NULL,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Create the TCP service data.
+ //
+ TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA));
+ if (TcpServiceData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TcpServiceData->Signature = TCP_DRIVER_SIGNATURE;
+ TcpServiceData->ControllerHandle = Controller;
+ TcpServiceData->DriverBindingHandle = Image;
+ TcpServiceData->IpVersion = IpVersion;
+ CopyMem (
+ &TcpServiceData->ServiceBinding,
+ &gTcpServiceBinding,
+ sizeof (EFI_SERVICE_BINDING_PROTOCOL)
+ );
+
+ TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);
+ if (TcpServiceData->IpIo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+
+ InitializeListHead (&TcpServiceData->SocketList);
+ ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));
+
+ if (IpVersion == IP_VERSION_4) {
+ CopyMem (
+ &OpenData.IpConfigData.Ip4CfgData,
+ &mIp4IoDefaultIpConfigData,
+ sizeof (EFI_IP4_CONFIG_DATA)
+ );
+ OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ } else {
+ CopyMem (
+ &OpenData.IpConfigData.Ip6CfgData,
+ &mIp6IoDefaultIpConfigData,
+ sizeof (EFI_IP6_CONFIG_DATA)
+ );
+ OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ }
+
+ OpenData.PktRcvdNotify = TcpRxCallback;
+ Status = IpIoOpen (TcpServiceData->IpIo, &OpenData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = TcpCreateTimer ();
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ TcpServiceBindingGuid,
+ &TcpServiceData->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ TcpDestroyTimer ();
+
+ goto ON_ERROR;
+ }
+
+ TcpSetVariableData (TcpServiceData);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (TcpServiceData->IpIo != NULL) {
+ IpIoDestroy (TcpServiceData->IpIo);
+ }
+
+ FreePool (TcpServiceData);
+
+ return Status;
+}
+
+/**
+ Destroy a TCP6 or TCP4 service binding instance. It will release all
+ the resources allocated by the instance.
+
+ @param[in] Controller Controller handle of device to bind driver to.
+ @param[in] ImageHandle The TCP driver's image handle.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero stop the entire bus driver.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6
+
+ @retval EFI_SUCCESS The resources used by the instance were cleaned up.
+ @retval Others Failed to clean up some of the resources.
+
+**/
+EFI_STATUS
+TcpDestroyService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ IN UINTN NumberOfChildren,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_HANDLE NicHandle;
+ EFI_GUID *IpProtocolGuid;
+ EFI_GUID *ServiceBindingGuid;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ TCP_SERVICE_DATA *TcpServiceData;
+ EFI_STATUS Status;
+ SOCKET *Sock;
+
+ ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));
+
+ if (IpVersion == IP_VERSION_4) {
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ } else {
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ }
+
+ NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ ServiceBindingGuid,
+ (VOID **) &ServiceBinding,
+ ImageHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (NumberOfChildren == 0) {
+ //
+ // Uninstall TCP servicebinding protocol
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ ServiceBindingGuid,
+ ServiceBinding,
+ NULL
+ );
+
+ //
+ // Destroy the IpIO consumed by TCP driver
+ //
+ IpIoDestroy (TcpServiceData->IpIo);
+
+ //
+ // Destroy the heartbeat timer.
+ //
+ TcpDestroyTimer ();
+
+ //
+ // Clear the variable.
+ //
+ TcpClearVariableData (TcpServiceData);
+
+ //
+ // Release the TCP service data
+ //
+ FreePool (TcpServiceData);
+ } else {
+
+ while (!IsListEmpty (&TcpServiceData->SocketList)) {
+ Sock = NET_LIST_HEAD (&TcpServiceData->SocketList, SOCKET, Link);
+
+ ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific
+ child device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN IsTcp4Started;
+
+ //
+ // Test for the Tcp4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Test for the Ip4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+
+ IsTcp4Started = FALSE;
+ } else {
+ IsTcp4Started = TRUE;
+ }
+
+ //
+ // Check the Tcp6ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Test for the Ip6ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+ } else if (IsTcp4Started) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS The driver is added to ControllerHandle.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the
+ driver.
+ @retval other The driver cannot be added to ControllerHandle.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Tcp4Status;
+ EFI_STATUS Tcp6Status;
+
+ Tcp4Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4);
+ if ((Tcp4Status == EFI_ALREADY_STARTED) || (Tcp4Status == EFI_UNSUPPORTED)) {
+ Tcp4Status = EFI_SUCCESS;
+ }
+
+ Tcp6Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6);
+ if ((Tcp6Status == EFI_ALREADY_STARTED) || (Tcp6Status == EFI_UNSUPPORTED)) {
+ Tcp6Status = EFI_SUCCESS;
+ }
+
+ if (!EFI_ERROR (Tcp4Status) || !EFI_ERROR (Tcp6Status)) {
+ return EFI_SUCCESS;
+ } else if (EFI_ERROR (Tcp4Status)) {
+ return Tcp4Status;
+ } else {
+ return Tcp6Status;
+ }
+}
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Tcp4Status;
+ EFI_STATUS Tcp6Status;
+
+ Tcp4Status = TcpDestroyService (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ NumberOfChildren,
+ IP_VERSION_4
+ );
+
+ Tcp6Status = TcpDestroyService (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ NumberOfChildren,
+ IP_VERSION_6
+ );
+
+ if (EFI_ERROR (Tcp4Status) && EFI_ERROR (Tcp6Status)) {
+ return EFI_DEVICE_ERROR;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ The Callback funtion called after the TCP socket was created.
+
+ @param[in] This Pointer to the socket just created
+ @param[in] Context Context of the socket
+
+ @retval EFI_SUCCESS This protocol installed successfully.
+ @retval other An error occured.
+
+**/
+EFI_STATUS
+TcpCreateSocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ TCP_SERVICE_DATA *TcpServiceData;
+ EFI_GUID *IpProtocolGuid;
+ VOID *Ip;
+
+ if (This->IpVersion == IP_VERSION_4) {
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ } else {
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ }
+
+ TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;
+
+ //
+ // Open the default IP protocol of IP_IO BY_DRIVER.
+ //
+ Status = gBS->OpenProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ IpProtocolGuid,
+ &Ip,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open the device path on the handle where service binding resides on.
+ //
+ Status = gBS->OpenProtocol (
+ TcpServiceData->ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &This->ParentDevicePath,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ IpProtocolGuid,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle
+ );
+ } else {
+ //
+ // Insert this socket into the SocketList.
+ //
+ InsertTailList (&TcpServiceData->SocketList, &This->Link);
+ }
+
+ return Status;
+}
+
+/**
+ The callback function called before the TCP socket was to be destroyed.
+
+ @param[in] This The TCP socket to be destroyed.
+ @param[in] Context The context of the socket.
+
+**/
+VOID
+TcpDestroySocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ )
+{
+ TCP_SERVICE_DATA *TcpServiceData;
+ EFI_GUID *IpProtocolGuid;
+
+ if (This->IpVersion == IP_VERSION_4) {
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ } else {
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ }
+
+ TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService;
+
+ //
+ // Remove this node from the list.
+ //
+ RemoveEntryList (&This->Link);
+
+ //
+ // Close the device path protocol
+ //
+ gBS->CloseProtocol (
+ TcpServiceData->ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle
+ );
+
+ //
+ // Close the IP protocol.
+ //
+ gBS->CloseProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ IpProtocolGuid,
+ TcpServiceData->DriverBindingHandle,
+ This->SockHandle
+ );
+}
+
+/**
+ Creates a child handle with a set of TCP services.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create.
+ If it is NULL, then a new handle is created.
+ If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ SOCKET *Sock;
+ TCP_SERVICE_DATA *TcpServiceData;
+ TCP_PROTO_DATA TcpProto;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (NULL == This || NULL == ChildHandle) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Status = EFI_SUCCESS;
+ TcpServiceData = TCP_SERVICE_FROM_THIS (This);
+ TcpProto.TcpService = TcpServiceData;
+ TcpProto.TcpPcb = NULL;
+
+ //
+ // Create a tcp instance with defualt Tcp default
+ // sock init data and TcpProto
+ //
+ mTcpDefaultSockData.ProtoData = &TcpProto;
+ mTcpDefaultSockData.DataSize = sizeof (TCP_PROTO_DATA);
+ mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;
+ mTcpDefaultSockData.IpVersion = TcpServiceData->IpVersion;
+
+ if (TcpServiceData->IpVersion == IP_VERSION_4) {
+ mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate;
+ } else {
+ mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate;
+ }
+
+ Sock = SockCreateChild (&mTcpDefaultSockData);
+ if (NULL == Sock) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpDriverBindingCreateChild: No resource to create a Tcp Child\n")
+ );
+
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ *ChildHandle = Sock->SockHandle;
+ }
+
+ mTcpDefaultSockData.ProtoData = NULL;
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Destroys a child handle with a set of TCP services.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to be destroyed.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ VOID *Tcp;
+ SOCKET *Sock;
+ EFI_TPL OldTpl;
+
+ if (NULL == This || NULL == ChildHandle) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // retrieve the Tcp4 protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ &Tcp,
+ gTcpDriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // No Tcp4, try the Tcp6 protocol
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ &Tcp,
+ gTcpDriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Status = EFI_UNSUPPORTED;
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // destroy this sock and related Tcp protocol control
+ // block
+ //
+ Sock = SOCK_FROM_THIS (Tcp);
+
+ SockDestroyChild (Sock);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/NetworkPkg/TcpDxe/TcpDriver.h b/NetworkPkg/TcpDxe/TcpDriver.h
new file mode 100644
index 0000000000..9de4be617d
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDriver.h
@@ -0,0 +1,230 @@
+/** @file
+ The prototype of driver binding and service binding protocol for TCP driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP_DRIVER_H_
+#define _TCP_DRIVER_H_
+
+#define TCP_DRIVER_SIGNATURE SIGNATURE_32 ('T', 'C', 'P', 'D')
+
+#define TCP_PORT_KNOWN 1024
+#define TCP_PORT_USER_RESERVED 65535
+
+typedef struct _TCP_HEARTBEAT_TIMER {
+ EFI_EVENT TimerEvent;
+ INTN RefCnt;
+} TCP_HEARTBEAT_TIMER;
+
+typedef struct _TCP_SERVICE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE ControllerHandle;
+ EFI_HANDLE DriverBindingHandle;
+ UINT8 IpVersion;
+ IP_IO *IpIo;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ CHAR16 *MacString;
+ LIST_ENTRY SocketList;
+} TCP_SERVICE_DATA;
+
+typedef struct _TCP_PROTO_DATA {
+ TCP_SERVICE_DATA *TcpService;
+ TCP_CB *TcpPcb;
+} TCP_PROTO_DATA;
+
+#define TCP_SERVICE_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ TCP_SERVICE_DATA, \
+ ServiceBinding, \
+ TCP_DRIVER_SIGNATURE \
+ )
+
+//
+// Function prototype for the driver's entry point
+//
+
+/**
+ The entry point for Tcp driver, used to install Tcp driver on the ImageHandle.
+
+ @param[in] ImageHandle The firmware allocated handle for this driver image.
+ @param[in] SystemTable Pointer to the EFI system table.
+
+ @retval EFI_SUCCESS The driver loaded.
+ @retval other The driver did not load.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+//
+// Function prototypes for the Driver Binding Protocol
+//
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of the device to test.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific
+ child device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS The driver was added to ControllerHandle.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the
+ driver.
+ @retval other The driver cannot be added to ControllerHandle.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ The Callback funtion called after the TCP socket is created.
+
+ @param[in] This Pointer to the socket just created.
+ @param[in] Context The context of the socket.
+
+ @retval EFI_SUCCESS This protocol is installed successfully.
+ @retval other An error occured.
+
+**/
+EFI_STATUS
+TcpCreateSocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+/**
+ The callback function called before the TCP socket is to be destroyed.
+
+ @param[in] This The TCP socket to be destroyed.
+ @param[in] Context The context of the socket.
+
+**/
+VOID
+TcpDestroySocketCallback (
+ IN SOCKET *This,
+ IN VOID *Context
+ );
+
+//
+// Function ptototypes for the ServiceBinding Prococol
+//
+
+/**
+ Creates a child handle with a set of TCP services.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create.
+ If it is NULL, then a new handle is created.
+ If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a set of TCP services.
+
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The protocol was removed from ChildHandle.
+ @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed.
+ @retval EFI_INVALID_PARAMETER The child handle is not a valid UEFI Handle.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle
+ because its services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/NetworkPkg/TcpDxe/TcpDxe.inf b/NetworkPkg/TcpDxe/TcpDxe.inf
new file mode 100644
index 0000000000..67615388df
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpDxe.inf
@@ -0,0 +1,83 @@
+## @file TcpDxe.inf
+# Component description file for Tcp module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = TcpDxe
+ FILE_GUID = 1A7E4468-2F55-4a56-903C-01265EB7622B
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = TcpDriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ TcpDriver.c
+ SockImpl.c
+ SockInterface.c
+ TcpDispatcher.c
+ TcpOutput.c
+ TcpMain.c
+ SockImpl.h
+ TcpMisc.c
+ TcpProto.h
+ TcpOption.c
+ TcpInput.c
+ TcpFunc.h
+ TcpOption.h
+ TcpTimer.c
+ TcpMain.h
+ Socket.h
+ ComponentName.c
+ TcpIo.c
+ TcpDriver.h
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DevicePathLib
+ DebugLib
+ MemoryAllocationLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DpcLib
+ NetLib
+ IpIoLib
+
+
+[Protocols]
+ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/NetworkPkg/TcpDxe/TcpFunc.h b/NetworkPkg/TcpDxe/TcpFunc.h
new file mode 100644
index 0000000000..c23ba94e44
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpFunc.h
@@ -0,0 +1,724 @@
+/** @file
+ Declaration of external functions shared in TCP driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP_FUNC_H_
+#define _TCP_FUNC_H_
+
+#include "TcpOption.h"
+
+#define TCP_COMP_VAL(Min, Max, Default, Val) \
+ ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default))
+
+/**
+ Timeout handler prototype.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+typedef
+VOID
+(*TCP_TIMER_HANDLER) (
+ IN OUT TCP_CB *Tcb
+ );
+
+//
+// Functions in TcpMisc.c
+//
+
+/**
+ Initialize the Tcb locally related members.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpInitTcbLocal (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Initialize the peer related members.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seg Pointer to the segment that contains the peer's intial information.
+ @param[in] Opt Pointer to the options announced by the peer.
+
+**/
+VOID
+TcpInitTcbPeer (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg,
+ IN TCP_OPTION *Opt
+ );
+
+/**
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @param[in] Addr Pointer to the IP address needs to match.
+ @param[in] Port The port number needs to match.
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack.
+ IP_VERSION_6 indicates TCP is running on IP6 stack.
+
+
+ @retval TRUE The Tcb which matches the <Addr Port> pairs exists.
+ @retval FALSE Otherwise
+
+**/
+BOOLEAN
+TcpFindTcbByPeer (
+ IN EFI_IP_ADDRESS *Addr,
+ IN TCP_PORTNO Port,
+ IN UINT8 Version
+ );
+
+/**
+ Locate the TCP_CB related to the socket pair.
+
+ @param[in] LocalPort The local port number.
+ @param[in] LocalIp The local IP address.
+ @param[in] RemotePort The remote port number.
+ @param[in] RemoteIp The remote IP address.
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
+ IP_VERSION_6 indicates TCP is running on IP6 stack.
+ @param[in] Syn If TRUE, the listen sockets are searched.
+
+ @return Pointer to the related TCP_CB. If NULL, no match is found.
+
+**/
+TCP_CB *
+TcpLocateTcb (
+ IN TCP_PORTNO LocalPort,
+ IN EFI_IP_ADDRESS *LocalIp,
+ IN TCP_PORTNO RemotePort,
+ IN EFI_IP_ADDRESS *RemoteIp,
+ IN UINT8 Version,
+ IN BOOLEAN Syn
+ );
+
+/**
+ Insert a Tcb into the proper queue.
+
+ @param[in] Tcb Pointer to the TCP_CB to be inserted.
+
+ @retval 0 The Tcb was inserted successfully.
+ @retval -1 An error condition occurred.
+
+**/
+INTN
+TcpInsertTcb (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Clone a TCP_CB from Tcb.
+
+ @param[in] Tcb Pointer to the TCP_CB to be cloned.
+
+ @return Pointer to the new cloned TCP_CB. If NULL, an error condition occurred.
+
+**/
+TCP_CB *
+TcpCloneTcb (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Compute an ISS to be used by a new connection.
+
+ @return The result ISS.
+
+**/
+TCP_SEQNO
+TcpGetIss (
+ VOID
+ );
+
+/**
+ Get the local mss.
+
+ @param[in] Sock Pointer to the socket to get mss.
+
+ @return The mss size.
+
+**/
+UINT16
+TcpGetRcvMss (
+ IN SOCKET *Sock
+ );
+
+/**
+ Set the Tcb's state.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] State The state to be set.
+
+**/
+VOID
+TcpSetState (
+ IN TCP_CB *Tcb,
+ IN UINT8 State
+ );
+
+/**
+ Compute the TCP segment's checksum.
+
+ @param[in] Nbuf Pointer to the buffer that contains the TCP segment.
+ @param[in] HeadSum The checksum value of the fixed part of pseudo header.
+
+ @return The checksum value.
+
+**/
+UINT16
+TcpChecksum (
+ IN NET_BUF *Nbuf,
+ IN UINT16 HeadSum
+ );
+
+/**
+ Translate the information from the head of the received TCP
+ segment Nbuf contains, and fill it into a TCP_SEG structure.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.
+
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.
+
+**/
+TCP_SEG *
+TcpFormatNetbuf (
+ IN TCP_CB *Tcb,
+ IN OUT NET_BUF *Nbuf
+ );
+
+/**
+ Initialize an active connection,
+
+ @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a
+ connection.
+
+**/
+VOID
+TcpOnAppConnect (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Application has consumed some data, check whether
+ to send a window update ack or a delayed ack.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppConsume (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Initiate the connection close procedure, called when
+ applications want to close the connection.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppClose (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Check whether the application's newly delivered data can be sent out.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 The data has been sent out successfully.
+ @retval -1 The Tcb is not in a state that data is permitted to
+ be sent out.
+
+**/
+INTN
+TcpOnAppSend (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Abort the connection by sending a reset segment: called
+ when the application wants to abort the connection.
+
+ @param[in] Tcb Pointer to the TCP_CB of the TCP instance.
+
+**/
+VOID
+TcpOnAppAbort (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Reset the connection related with Tcb.
+
+ @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.
+
+**/
+VOID
+TcpResetConnection (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Set the Tcp variable data.
+
+ @param[in] TcpService Tcp service data.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+TcpSetVariableData (
+ IN TCP_SERVICE_DATA *TcpService
+ );
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in] TcpService Tcp service data.
+
+**/
+VOID
+TcpClearVariableData (
+ IN TCP_SERVICE_DATA *TcpService
+ );
+
+/**
+ Install the device path protocol on the TCP instance.
+
+ @param[in] Sock Pointer to the socket representing the TCP instance.
+
+ @retval EFI_SUCCESS The device path protocol installed.
+ @retval other Failed to install the device path protocol.
+
+**/
+EFI_STATUS
+TcpInstallDevicePath (
+ IN SOCKET *Sock
+ );
+
+
+//
+// Functions in TcpOutput.c
+//
+
+/**
+ Compute the sequence space left in the old receive window.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence space left in the old receive window.
+
+**/
+UINT32
+TcpRcvWinOld (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Compute the current receive window.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The size of the current receive window, in bytes.
+
+**/
+UINT32
+TcpRcvWinNow (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Get the maximum SndNxt.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence number of the maximum SndNxt.
+
+**/
+TCP_SEQNO
+TcpGetMaxSndNxt (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Compute how much data to send.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm
+ and send out data by force.
+
+ @return The length of the data that can be sent. If 0, no data can be sent.
+
+**/
+UINT32
+TcpDataToSend (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ );
+
+/**
+ Retransmit the segment from sequence Seq.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seq The sequence number of the segment to be retransmitted.
+
+ @retval 0 The retransmission succeeded.
+ @retval -1 An error condition occurred.
+
+**/
+INTN
+TcpRetransmit (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq
+ );
+
+/**
+ Check whether to send data/SYN/FIN and piggyback an ACK.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm
+ and send out data by force.
+
+ @return The number of bytes sent.
+
+**/
+INTN
+TcpToSendData (
+ IN OUT TCP_CB *Tcb,
+ IN INTN Force
+ );
+
+/**
+ Check whether to send an ACK or delayed ACK.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpToSendAck (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Send an ACK immediately.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSendAck (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Send a zero probe segment. It can be used by keepalive and zero window probe.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 The zero probe segment was sent out successfully.
+ @retval other An error condition occurred.
+
+**/
+INTN
+TcpSendZeroProbe (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Send a RESET segment in response to the segment received.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.
+ @param[in] Head TCP header of the segment that triggers the reset.
+ @param[in] Len Length of the segment that triggers the reset.
+ @param[in] Local Local IP address.
+ @param[in] Remote Remote peer's IP address.
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
+ IP_VERSION_6 indicates TCP is running on IP6 stack.
+
+ @retval 0 A reset is sent or no need to send it.
+ @retval -1 No reset is sent.
+
+**/
+INTN
+TcpSendReset (
+ IN TCP_CB *Tcb,
+ IN TCP_HEAD *Head,
+ IN INT32 Len,
+ IN EFI_IP_ADDRESS *Local,
+ IN EFI_IP_ADDRESS *Remote,
+ IN UINT8 Version
+ );
+
+/**
+ Verify that the segment is in good shape.
+
+ @param[in] Nbuf Buffer that contains the segment to be checked.
+
+ @retval 0 The segment is broken.
+ @retval 1 The segment is in good shape.
+
+**/
+INTN
+TcpVerifySegment (
+ IN NET_BUF *Nbuf
+ );
+
+//
+// Functions from TcpInput.c
+//
+
+/**
+ Process the received ICMP error messages for TCP.
+
+ @param[in] Nbuf Buffer that contains part of the TCP segment without IP header
+ truncated from the ICMP error packet.
+ @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet.
+ @param[in] Src Source address of the ICMP error message.
+ @param[in] Dst Destination address of the ICMP error message.
+ @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates
+ IP6 stack.
+
+**/
+VOID
+TcpIcmpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT8 IcmpErr,
+ IN EFI_IP_ADDRESS *Src,
+ IN EFI_IP_ADDRESS *Dst,
+ IN UINT8 Version
+ );
+
+/**
+ Process the received TCP segments.
+
+ @param[in] Nbuf Buffer that contains received TCP segment without an IP header.
+ @param[in] Src Source address of the segment, or the peer's IP address.
+ @param[in] Dst Destination address of the segment, or the local end's IP
+ address.
+ @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates
+ IP6 stack.
+
+ @retval 0 The segment processed successfully. It is either accepted or
+ discarded. But no connection is reset by the segment.
+ @retval -1 A connection is reset by the segment.
+
+**/
+INTN
+TcpInput (
+ IN NET_BUF *Nbuf,
+ IN EFI_IP_ADDRESS *Src,
+ IN EFI_IP_ADDRESS *Dst,
+ IN UINT8 Version
+ );
+
+//
+// Functions in TcpTimer.c
+//
+
+/**
+ Close the TCP connection.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClose (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Heart beat timer handler, queues the DPC at TPL_CALLBACK.
+
+ @param[in] Event Timer event signaled, ignored.
+ @param[in] Context Context of the timer event, ignored.
+
+**/
+VOID
+EFIAPI
+TcpTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Enable a TCP timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Timer The index of the timer to be enabled.
+ @param[in] TimeOut The timeout value of this timer.
+
+**/
+VOID
+TcpSetTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer,
+ IN UINT32 TimeOut
+ );
+
+/**
+ Clear one TCP timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Timer The index of the timer to be cleared.
+
+**/
+VOID
+TcpClearTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer
+ );
+
+/**
+ Clear all TCP timers.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClearAllTimer (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Enable the window prober timer and set the timeout value.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetProbeTimer (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Enable the keepalive timer and set the timeout value.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetKeepaliveTimer (
+ IN OUT TCP_CB *Tcb
+ );
+
+//
+// Functions in TcpIo.c
+//
+
+/**
+ Packet receive callback function provided to IP_IO. Used to call
+ the proper function to handle the packet received by IP.
+
+ @param[in] Status Result of the receive request.
+ @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR.
+ @param[in] NetSession The IP session for the received packet.
+ @param[in] Pkt Packet received.
+ @param[in] Context The data provided by the user for the received packet when
+ the callback is registered in IP_IO_OPEN_DATA::RcvdContext.
+ This is an optional parameter that may be NULL.
+
+**/
+VOID
+EFIAPI
+TcpRxCallback (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpErr,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Pkt,
+ IN VOID *Context OPTIONAL
+ );
+
+/**
+ Send the segment to IP via IpIo function.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the TCP segment to be sent.
+ @param[in] Src Source address of the TCP segment.
+ @param[in] Dest Destination address of the TCP segment.
+ @param[in] Version IP_VERSION_4 or IP_VERSION_6
+
+ @retval 0 The segment was sent out successfully.
+ @retval -1 The segment failed to be sent.
+
+**/
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN EFI_IP_ADDRESS *Src,
+ IN EFI_IP_ADDRESS *Dest,
+ IN UINT8 Version
+ );
+
+/**
+ Refresh the remote peer's Neighbor Cache State if already exists.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Neighbor Source address of the TCP segment.
+ @param[in] Timeout Time in 100-ns units that this entry will remain
+ in the neighbor cache. A value of zero means that
+ the entry is permanent. A value of non-zero means
+ that the entry is dynamic and will be deleted
+ after Timeout.
+
+ @retval EFI_SUCCESS Successfully updated the neighbor relationship.
+ @retval EFI_NOT_STARTED The IpIo is not configured.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_NOT_FOUND This entry is not in the neighbor table.
+
+**/
+EFI_STATUS
+Tcp6RefreshNeighbor (
+ IN TCP_CB *Tcb,
+ IN EFI_IP_ADDRESS *Neighbor,
+ IN UINT32 Timeout
+ );
+
+//
+// Functions in TcpDispatcher.c
+//
+
+/**
+ The procotol handler provided to the socket layer, used to
+ dispatch the socket level requests by calling the corresponding
+ TCP layer functions.
+
+ @param[in] Sock Pointer to the socket of this TCP instance.
+ @param[in] Request The code of this operation request.
+ @param[in] Data Pointer to the operation specific data passed in
+ together with the operation request. This is an
+ optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The socket request completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+EFI_STATUS
+TcpDispatcher (
+ IN SOCKET *Sock,
+ IN UINT8 Request,
+ IN VOID *Data OPTIONAL
+ );
+
+#endif
diff --git a/NetworkPkg/TcpDxe/TcpInput.c b/NetworkPkg/TcpDxe/TcpInput.c
new file mode 100644
index 0000000000..e63469adb9
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpInput.c
@@ -0,0 +1,1592 @@
+/** @file
+ TCP input process routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+/**
+ Check whether the sequence number of the incoming segment is acceptable.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seg Pointer to the incoming segment.
+
+ @retval 1 The sequence number is acceptable.
+ @retval 0 The sequence number is not acceptable.
+
+**/
+INTN
+TcpSeqAcceptable (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) &&
+ TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd));
+}
+
+/**
+ NewReno fast recovery defined in RFC3782.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seg Segment that triggers the fast recovery.
+
+**/
+VOID
+TcpFastRecover (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ UINT32 FlightSize;
+ UINT32 Acked;
+
+ //
+ // Step 1: Three duplicate ACKs and not in fast recovery
+ //
+ if (Tcb->CongestState != TCP_CONGEST_RECOVER) {
+
+ //
+ // Step 1A: Invoking fast retransmission.
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+
+ Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss));
+ Tcb->Recover = Tcb->SndNxt;
+
+ Tcb->CongestState = TCP_CONGEST_RECOVER;
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+
+ //
+ // Step 2: Entering fast retransmission
+ //
+ TcpRetransmit (Tcb, Tcb->SndUna);
+ Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss;
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpFastRecover: enter fast retransmission for TCB %p, recover point is %d\n",
+ Tcb,
+ Tcb->Recover)
+ );
+ return;
+ }
+
+ //
+ // During fast recovery, execute Step 3, 4, 5 of RFC3782
+ //
+ if (Seg->Ack == Tcb->SndUna) {
+
+ //
+ // Step 3: Fast Recovery,
+ // If this is a duplicated ACK, increse Cwnd by SMSS.
+ //
+
+ // Step 4 is skipped here only to be executed later
+ // by TcpToSendData
+ //
+ Tcb->CWnd += Tcb->SndMss;
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpFastRecover: received another duplicated ACK (%d) for TCB %p\n",
+ Seg->Ack,
+ Tcb)
+ );
+
+ } else {
+
+ //
+ // New data is ACKed, check whether it is a
+ // full ACK or partial ACK
+ //
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) {
+
+ //
+ // Step 5 - Full ACK:
+ // deflate the congestion window, and exit fast recovery
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+
+ Tcb->CWnd = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss);
+
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpFastRecover: received a full ACK(%d) for TCB %p, exit fast recovery\n",
+ Seg->Ack,
+ Tcb)
+ );
+
+ } else {
+
+ //
+ // Step 5 - Partial ACK:
+ // fast retransmit the first unacknowledge field
+ // , then deflate the CWnd
+ //
+ TcpRetransmit (Tcb, Seg->Ack);
+ Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna);
+
+ //
+ // Deflate the CWnd by the amount of new data
+ // ACKed by SEG.ACK. If more than one SMSS data
+ // is ACKed, add back SMSS byte to CWnd after
+ //
+ if (Acked >= Tcb->SndMss) {
+ Acked -= Tcb->SndMss;
+
+ }
+
+ Tcb->CWnd -= Acked;
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpFastRecover: received a partial ACK(%d) for TCB %p\n",
+ Seg->Ack,
+ Tcb)
+ );
+
+ }
+ }
+}
+
+/**
+ NewReno fast loss recovery defined in RFC3792.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seg Segment that triggers the fast loss recovery.
+
+**/
+VOID
+TcpFastLossRecover (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ //
+ // New data is ACKed, check whether it is a
+ // full ACK or partial ACK
+ //
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) {
+
+ //
+ // Full ACK: exit the loss recovery.
+ //
+ Tcb->LossTimes = 0;
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpFastLossRecover: received a full ACK(%d) for TCB %p\n",
+ Seg->Ack,
+ Tcb)
+ );
+
+ } else {
+
+ //
+ // Partial ACK:
+ // fast retransmit the first unacknowledge field.
+ //
+ TcpRetransmit (Tcb, Seg->Ack);
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpFastLossRecover: received a partial ACK(%d) for TCB %p\n",
+ Seg->Ack,
+ Tcb)
+ );
+ }
+ }
+}
+
+/**
+ Compute the RTT as specified in RFC2988.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Measure Currently measured RTT in heartbeats.
+
+**/
+VOID
+TcpComputeRtt (
+ IN OUT TCP_CB *Tcb,
+ IN UINT32 Measure
+ )
+{
+ INT32 Var;
+
+ //
+ // Step 2.3: Compute the RTO for subsequent RTT measurement.
+ //
+ if (Tcb->SRtt != 0) {
+
+ Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT);
+
+ if (Var < 0) {
+ Var = -Var;
+ }
+
+ Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2;
+ Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure;
+
+ } else {
+ //
+ // Step 2.2: compute the first RTT measure
+ //
+ Tcb->SRtt = Measure << TCP_RTT_SHIFT;
+ Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1);
+ }
+
+ Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT;
+
+ //
+ // Step 2.4: Limit the RTO to at least 1 second
+ // Step 2.5: Limit the RTO to a maxium value that
+ // is at least 60 second
+ //
+ if (Tcb->Rto < TCP_RTO_MIN) {
+ Tcb->Rto = TCP_RTO_MIN;
+
+ } else if (Tcb->Rto > TCP_RTO_MAX) {
+ Tcb->Rto = TCP_RTO_MAX;
+
+ }
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpComputeRtt: new RTT for TCB %p computed SRTT: %d RTTVAR: %d RTO: %d\n",
+ Tcb,
+ Tcb->SRtt,
+ Tcb->RttVar,
+ Tcb->Rto)
+ );
+
+}
+
+/**
+ Trim the data; SYN and FIN to fit into the window defined by Left and Right.
+
+ @param[in] Nbuf The buffer that contains a received TCP segment without an IP header.
+ @param[in] Left The sequence number of the window's left edge.
+ @param[in] Right The sequence number of the window's right edge.
+
+**/
+VOID
+TcpTrimSegment (
+ IN NET_BUF *Nbuf,
+ IN TCP_SEQNO Left,
+ IN TCP_SEQNO Right
+ )
+{
+ TCP_SEG *Seg;
+ TCP_SEQNO Urg;
+ UINT32 Drop;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ //
+ // If the segment is completely out of window,
+ // truncate every thing, include SYN and FIN.
+ //
+ if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {
+
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);
+
+ Seg->Seq = Seg->End;
+ NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);
+ return;
+ }
+
+ //
+ // Adjust the buffer header
+ //
+ if (TCP_SEQ_LT (Seg->Seq, Left)) {
+
+ Drop = TCP_SUB_SEQ (Left, Seg->Seq);
+ Urg = Seg->Seq + Seg->Urg;
+ Seg->Seq = Left;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);
+ Drop--;
+ }
+
+ //
+ // Adjust the urgent point
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) {
+
+ if (TCP_SEQ_LT (Urg, Seg->Seq)) {
+
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);
+ } else {
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq);
+ }
+ }
+
+ if (Drop != 0) {
+ NetbufTrim (Nbuf, Drop, NET_BUF_HEAD);
+ }
+ }
+
+ //
+ // Adjust the buffer tail
+ //
+ if (TCP_SEQ_GT (Seg->End, Right)) {
+
+ Drop = TCP_SUB_SEQ (Seg->End, Right);
+ Seg->End = Right;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);
+ Drop--;
+ }
+
+ if (Drop != 0) {
+ NetbufTrim (Nbuf, Drop, NET_BUF_TAIL);
+ }
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+}
+
+/**
+ Trim off the data outside the tcb's receive window.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the NET_BUF containing the received tcp segment.
+
+**/
+VOID
+TcpTrimInWnd (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);
+}
+
+/**
+ Process the data and FIN flag, and check whether to deliver
+ data to the socket layer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 No error occurred to deliver data.
+ @retval -1 An error condition occurred. The proper response is to reset the
+ connection.
+
+**/
+INTN
+TcpDeliverData (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ TCP_SEQNO Seq;
+ TCP_SEG *Seg;
+ UINT32 Urgent;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ //
+ // make sure there is some data queued,
+ // and TCP is in a proper state
+ //
+ if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) {
+
+ return 0;
+ }
+
+ //
+ // Deliver data to the socket layer
+ //
+ Entry = Tcb->RcvQue.ForwardLink;
+ Seq = Tcb->RcvNxt;
+
+ while (Entry != &Tcb->RcvQue) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+ ASSERT (Nbuf->Tcp == NULL);
+
+ if (TCP_SEQ_GT (Seg->Seq, Seq)) {
+ break;
+ }
+
+ Entry = Entry->ForwardLink;
+ Seq = Seg->End;
+ Tcb->RcvNxt = Seq;
+
+ RemoveEntryList (&Nbuf->List);
+
+ //
+ // RFC793 Eighth step: process FIN in sequence
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+
+ //
+ // The peer sends to us junky data after FIN,
+ // reset the connection.
+ //
+ if (!IsListEmpty (&Tcb->RcvQue)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpDeliverData: data received after FIN from peer of TCB %p, reset connection\n",
+ Tcb)
+ );
+
+ NetbufFree (Nbuf);
+ return -1;
+ }
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpDeliverData: processing FIN from peer of TCB %p\n",
+ Tcb)
+ );
+
+ switch (Tcb->State) {
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+
+ TcpSetState (Tcb, TCP_CLOSE_WAIT);
+ break;
+
+ case TCP_FIN_WAIT_1:
+
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_CLOSING);
+ break;
+ }
+
+ //
+ // fall through
+ //
+ case TCP_FIN_WAIT_2:
+
+ TcpSetState (Tcb, TCP_TIME_WAIT);
+ TcpClearAllTimer (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "Connection closed immediately because app disables TIME_WAIT timer for %p\n",
+ Tcb)
+ );
+
+ TcpSendAck (Tcb);
+ TcpClose (Tcb);
+ }
+ break;
+
+ case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ //
+ // The peer sends to us junk FIN byte. Discard
+ // the buffer then reset the connection
+ //
+ NetbufFree (Nbuf);
+ return -1;
+ break;
+ default:
+ break;
+ }
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ Seg->End--;
+ }
+
+ //
+ // Don't delay the ack if PUSH flag is on.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+
+ if (Nbuf->TotalSize != 0) {
+ Urgent = 0;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&
+ TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)
+ ) {
+
+ if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) {
+ Urgent = Nbuf->TotalSize;
+ } else {
+ Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1;
+ }
+ }
+
+ SockDataRcvd (Tcb->Sk, Nbuf, Urgent);
+ }
+
+ if (TCP_FIN_RCVD (Tcb->State)) {
+
+ SockNoMoreData (Tcb->Sk);
+ }
+
+ NetbufFree (Nbuf);
+ }
+
+ return 0;
+}
+
+/**
+ Store the data into the reassemble queue.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the buffer containing the data to be queued.
+
+**/
+VOID
+TcpQueueData (
+ IN OUT TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ TCP_SEG *Seg;
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Prev;
+ LIST_ENTRY *Cur;
+ NET_BUF *Node;
+
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
+
+ NET_GET_REF (Nbuf);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Head = &Tcb->RcvQue;
+
+ //
+ // Fast path to process normal case. That is,
+ // no out-of-order segments are received.
+ //
+ if (IsListEmpty (Head)) {
+
+ InsertTailList (Head, &Nbuf->List);
+ return;
+ }
+
+ //
+ // Find the point to insert the buffer
+ //
+ for (Prev = Head, Cur = Head->ForwardLink;
+ Cur != Head;
+ Prev = Cur, Cur = Cur->ForwardLink
+ ) {
+
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) {
+ break;
+ }
+ }
+
+ //
+ // Check whether the current segment overlaps with the
+ // previous segment.
+ //
+ if (Prev != Head) {
+ Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
+
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) {
+
+ if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) {
+
+ NetbufFree (Nbuf);
+ return;
+ }
+
+ TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End);
+ }
+ }
+
+ InsertHeadList (Prev, &Nbuf->List);
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ //
+ // Check the segments after the insert point.
+ //
+ while (Cur != Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) {
+
+ Cur = Cur->ForwardLink;
+
+ RemoveEntryList (&Node->List);
+ NetbufFree (Node);
+ continue;
+ }
+
+ if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) {
+
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) {
+
+ RemoveEntryList (&Nbuf->List);
+ NetbufFree (Nbuf);
+ return;
+ }
+
+ TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq);
+ break;
+ }
+
+ Cur = Cur->ForwardLink;
+ }
+}
+
+
+/**
+ Adjust the send queue or the retransmit queue.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Ack The acknowledge seuqence number of the received segment.
+
+**/
+VOID
+TcpAdjustSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Ack
+ )
+{
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Cur;
+ NET_BUF *Node;
+ TCP_SEG *Seg;
+
+ Head = &Tcb->SndQue;
+ Cur = Head->ForwardLink;
+
+ while (Cur != Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Node);
+
+ if (TCP_SEQ_GEQ (Seg->Seq, Ack)) {
+ break;
+ }
+
+ //
+ // Remove completely ACKed segments
+ //
+ if (TCP_SEQ_LEQ (Seg->End, Ack)) {
+ Cur = Cur->ForwardLink;
+
+ RemoveEntryList (&Node->List);
+ NetbufFree (Node);
+ continue;
+ }
+
+ TcpTrimSegment (Node, Ack, Seg->End);
+ break;
+ }
+}
+
+/**
+ Process the received TCP segments.
+
+ @param[in] Nbuf Buffer that contains received a TCP segment without an IP header.
+ @param[in] Src Source address of the segment, or the peer's IP address.
+ @param[in] Dst Destination address of the segment, or the local end's IP
+ address.
+ @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates
+ IP6 stack.
+
+ @retval 0 Segment processed successfully. It is either accepted or
+ discarded. However, no connection is reset by the segment.
+ @retval -1 A connection is reset by the segment.
+
+**/
+INTN
+TcpInput (
+ IN NET_BUF *Nbuf,
+ IN EFI_IP_ADDRESS *Src,
+ IN EFI_IP_ADDRESS *Dst,
+ IN UINT8 Version
+ )
+{
+ TCP_CB *Tcb;
+ TCP_CB *Parent;
+ TCP_OPTION Option;
+ TCP_HEAD *Head;
+ INT32 Len;
+ TCP_SEG *Seg;
+ TCP_SEQNO Right;
+ TCP_SEQNO Urg;
+ UINT16 Checksum;
+
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Parent = NULL;
+ Tcb = NULL;
+
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (Head != NULL);
+ Len = Nbuf->TotalSize - (Head->HeadLen << 2);
+
+ if ((Head->HeadLen < 5) || (Len < 0)) {
+
+ DEBUG ((EFI_D_INFO, "TcpInput: received an mal-formated packet\n"));
+ goto DISCARD;
+ }
+
+ if (Version == IP_VERSION_4) {
+ Checksum = NetPseudoHeadChecksum (Src->Addr[0], Dst->Addr[0], 6, 0);
+ } else {
+ Checksum = NetIp6PseudoHeadChecksum (&Src->v6, &Dst->v6, 6, 0);
+ }
+
+ Checksum = TcpChecksum (Nbuf, Checksum);
+
+ if (Checksum != 0) {
+ DEBUG ((EFI_D_ERROR, "TcpInput: received a checksum error packet\n"));
+ goto DISCARD;
+ }
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) {
+ Len++;
+ }
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) {
+ Len++;
+ }
+
+ Tcb = TcpLocateTcb (
+ Head->DstPort,
+ Dst,
+ Head->SrcPort,
+ Src,
+ Version,
+ (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)
+ );
+
+ if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) {
+ DEBUG ((EFI_D_INFO, "TcpInput: send reset because no TCB find\n"));
+
+ Tcb = NULL;
+ goto SEND_RESET;
+ }
+
+ Seg = TcpFormatNetbuf (Tcb, Nbuf);
+
+ //
+ // RFC1122 recommended reaction to illegal option
+ // (in fact, an illegal option length) is reset.
+ //
+ if (TcpParseOption (Nbuf->Tcp, &Option) == -1) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: reset the peer because of mal-format option for Tcb %p\n",
+ Tcb)
+ );
+
+ goto SEND_RESET;
+ }
+
+ //
+ // From now on, the segment is headless
+ //
+ NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ //
+ // Process the segment in LISTEN state.
+ //
+ if (Tcb->State == TCP_LISTEN) {
+ //
+ // First step: Check RST
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: discard a reset segment for TCB %p in listening\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ //
+ // Second step: Check ACK.
+ // Any ACK sent to TCP in LISTEN is reseted.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: send reset because of segment with ACK for TCB %p in listening\n",
+ Tcb)
+ );
+
+ goto SEND_RESET;
+ }
+
+ //
+ // Third step: Check SYN
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ //
+ // create a child TCB to handle the data
+ //
+ Parent = Tcb;
+
+ Tcb = TcpCloneTcb (Parent);
+ if (Tcb == NULL) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: discard a segment because failed to clone a child for TCB%p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpInput: create a child for TCB %p in listening\n",
+ Tcb)
+ );
+
+ //
+ // init the TCB structure
+ //
+ IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, Dst);
+ IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, Src);
+ Tcb->LocalEnd.Port = Head->DstPort;
+ Tcb->RemoteEnd.Port = Head->SrcPort;
+
+ TcpInitTcbLocal (Tcb);
+ TcpInitTcbPeer (Tcb, Seg, &Option);
+
+ TcpSetState (Tcb, TCP_SYN_RCVD);
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ goto StepSix;
+ }
+
+ goto DISCARD;
+
+ } else if (Tcb->State == TCP_SYN_SENT) {
+ //
+ // First step: Check ACK bit
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: send reset because of wrong ACK received for TCB %p in SYN_SENT\n",
+ Tcb)
+ );
+
+ goto SEND_RESET;
+ }
+
+ //
+ // Second step: Check RST bit
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: connection reset by peer for TCB %p in SYN_SENT\n",
+ Tcb)
+ );
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+ goto DROP_CONNECTION;
+ } else {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: discard a reset segment because of no ACK for TCB %p in SYN_SENT\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+ }
+
+ //
+ // Third step: Check security and precedence. Skipped
+ //
+
+ //
+ // Fourth step: Check SYN. Pay attention to sitimulatous open
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ TcpInitTcbPeer (Tcb, Seg, &Option);
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+
+ Tcb->SndUna = Seg->Ack;
+ }
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+
+ if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) {
+
+ TcpSetState (Tcb, TCP_ESTABLISHED);
+
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);
+ TcpDeliverData (Tcb);
+
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)
+ ) {
+
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ }
+
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpInput: connection established for TCB %p in SYN_SENT\n",
+ Tcb)
+ );
+
+ goto StepSix;
+ } else {
+ //
+ // Received a SYN segment without ACK, simultanous open.
+ //
+ TcpSetState (Tcb, TCP_SYN_RCVD);
+
+ ASSERT (Tcb->SndNxt == Tcb->Iss + 1);
+ TcpAdjustSndQue (Tcb, Tcb->SndNxt);
+
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: simultanous open for TCB %p in SYN_SENT\n",
+ Tcb)
+ );
+
+ goto StepSix;
+ }
+ }
+
+ goto DISCARD;
+ }
+
+ //
+ // Process segment in SYN_RCVD or TCP_CONNECTED states
+ //
+
+ //
+ // Clear probe timer since the RecvWindow is opened.
+ //
+ if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) {
+ TcpClearTimer (Tcb, TCP_TIMER_PROBE);
+ Tcb->ProbeTimerOn = FALSE;
+ }
+
+ //
+ // First step: Check whether SEG.SEQ is acceptable
+ //
+ if (TcpSeqAcceptable (Tcb, Seg) == 0) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: sequence acceptance test failed for segment of TCB %p\n",
+ Tcb)
+ );
+
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+ TcpSendAck (Tcb);
+ }
+
+ goto DISCARD;
+ }
+
+ if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) &&
+ (Tcb->RcvWl2 == Seg->End) &&
+ !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)
+ ) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+
+ //
+ // Second step: Check the RST
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+
+ DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb));
+
+ if (Tcb->State == TCP_SYN_RCVD) {
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED);
+
+ //
+ // This TCB comes from either a LISTEN TCB,
+ // or active open TCB with simultanous open.
+ // Do NOT signal user CONNECTION refused
+ // if it comes from a LISTEN TCB.
+ //
+ } else if ((Tcb->State == TCP_ESTABLISHED) ||
+ (Tcb->State == TCP_FIN_WAIT_1) ||
+ (Tcb->State == TCP_FIN_WAIT_2) ||
+ (Tcb->State == TCP_CLOSE_WAIT)
+ ) {
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+
+ } else {
+ }
+
+ goto DROP_CONNECTION;
+ }
+
+ //
+ // Trim the data and flags.
+ //
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ //
+ // Third step: Check security and precedence, Ignored
+ //
+
+ //
+ // Fourth step: Check the SYN bit.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: connection reset because received extra SYN for TCB %p\n",
+ Tcb)
+ );
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+ goto RESET_THEN_DROP;
+ }
+ //
+ // Fifth step: Check the ACK
+ //
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: segment discard because of no ACK for connected TCB %p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ } else {
+ if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick == 0) {
+ Tcp6RefreshNeighbor (Tcb, Src, TCP6_KEEP_NEIGHBOR_TIME * TICKS_PER_SECOND);
+ Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK;
+ }
+ }
+
+ if (Tcb->State == TCP_SYN_RCVD) {
+
+ if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) {
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);
+ Tcb->SndWl1 = Seg->Seq;
+ Tcb->SndWl2 = Seg->Ack;
+ TcpSetState (Tcb, TCP_ESTABLISHED);
+
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);
+ TcpDeliverData (Tcb);
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpInput: connection established for TCB %p in SYN_RCVD\n",
+ Tcb)
+ );
+
+ //
+ // Continue the process as ESTABLISHED state
+ //
+ } else {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: send reset because of wrong ACK for TCB %p in SYN_RCVD\n",
+ Tcb)
+ );
+
+ goto SEND_RESET;
+ }
+ }
+
+ if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: ignore the out-of-data ACK for connected TCB %p\n",
+ Tcb)
+ );
+
+ goto StepSix;
+
+ } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: discard segment for future ACK for connected TCB %p\n",
+ Tcb)
+ );
+
+ TcpSendAck (Tcb);
+ goto DISCARD;
+ }
+
+ //
+ // From now on: SND.UNA <= SEG.ACK <= SND.NXT.
+ //
+ if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) {
+ //
+ // update TsRecent as specified in page 16 RFC1323.
+ // RcvWl2 equals to the variable "LastAckSent"
+ // defined there.
+ //
+ if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) {
+
+ Tcb->TsRecent = Option.TSVal;
+ Tcb->TsRecentAge = mTcpTick;
+ }
+
+ TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr));
+
+ } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN);
+
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ }
+
+ if (Seg->Ack == Tcb->SndNxt) {
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+ } else {
+
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+ }
+
+ //
+ // Count duplicate acks.
+ //
+ if ((Seg->Ack == Tcb->SndUna) &&
+ (Tcb->SndUna != Tcb->SndNxt) &&
+ (Seg->Wnd == Tcb->SndWnd) &&
+ (0 == Len)
+ ) {
+
+ Tcb->DupAck++;
+ } else {
+
+ Tcb->DupAck = 0;
+ }
+
+ //
+ // Congestion avoidance, fast recovery and fast retransmission.
+ //
+ if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) ||
+ (Tcb->CongestState == TCP_CONGEST_LOSS)
+ ) {
+
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ if (Tcb->CWnd < Tcb->Ssthresh) {
+
+ Tcb->CWnd += Tcb->SndMss;
+ } else {
+
+ Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1);
+ }
+
+ Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale);
+ }
+
+ if (Tcb->CongestState == TCP_CONGEST_LOSS) {
+ TcpFastLossRecover (Tcb, Seg);
+ }
+ } else {
+
+ TcpFastRecover (Tcb, Seg);
+ }
+
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ TcpAdjustSndQue (Tcb, Seg->Ack);
+ Tcb->SndUna = Seg->Ack;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&
+ TCP_SEQ_LT (Tcb->SndUp, Seg->Ack)
+ ) {
+
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);
+ }
+ }
+
+ //
+ // Update window info
+ //
+ if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) ||
+ ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))
+ ) {
+
+ Right = Seg->Ack + Seg->Wnd;
+
+ if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) {
+
+ if ((Tcb->SndWl1 == Seg->Seq) &&
+ (Tcb->SndWl2 == Seg->Ack) &&
+ (Len == 0)
+ ) {
+
+ goto NO_UPDATE;
+ }
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: peer shrinks the window for connected TCB %p\n",
+ Tcb)
+ );
+
+ if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && (TCP_SEQ_LT (Right, Tcb->Recover))) {
+
+ Tcb->Recover = Right;
+ }
+
+ if ((Tcb->CongestState == TCP_CONGEST_LOSS) && (TCP_SEQ_LT (Right, Tcb->LossRecover))) {
+
+ Tcb->LossRecover = Right;
+ }
+
+ if (TCP_SEQ_LT (Right, Tcb->SndNxt)) {
+
+ Tcb->SndNxt = Right;
+
+ if (Right == Tcb->SndUna) {
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+ TcpSetProbeTimer (Tcb);
+ }
+ }
+ }
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);
+ Tcb->SndWl1 = Seg->Seq;
+ Tcb->SndWl2 = Seg->Ack;
+ }
+
+NO_UPDATE:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && (Tcb->SndUna == Tcb->SndNxt)) {
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpInput: local FIN is ACKed by peer for connected TCB %p\n",
+ Tcb)
+ );
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED);
+ }
+
+ //
+ // Transit the state if proper.
+ //
+ switch (Tcb->State) {
+ case TCP_FIN_WAIT_1:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_FIN_WAIT_2);
+
+ TcpClearAllTimer (Tcb);
+ TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout);
+ }
+
+ case TCP_FIN_WAIT_2:
+
+ break;
+
+ case TCP_CLOSE_WAIT:
+ break;
+
+ case TCP_CLOSING:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_TIME_WAIT);
+
+ TcpClearAllTimer (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "Connection closed immediately because app disables TIME_WAIT timer for %p\n",
+ Tcb)
+ );
+
+ TcpClose (Tcb);
+ }
+ }
+ break;
+
+ case TCP_LAST_ACK:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_CLOSED);
+ }
+
+ break;
+
+ case TCP_TIME_WAIT:
+
+ TcpSendAck (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "Connection closed immediately because app disables TIME_WAIT timer for %p\n",
+ Tcb)
+ );
+
+ TcpClose (Tcb);
+ }
+ break;
+
+ default:
+ break;
+ }
+ //
+ // Sixth step: Check the URG bit.update the Urg point
+ // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact.
+ //
+StepSix:
+
+ Tcb->Idle = 0;
+ TcpSetKeepaliveTimer (Tcb);
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && !TCP_FIN_RCVD (Tcb->State)) {
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpInput: received urgent data from peer for connected TCB %p\n",
+ Tcb)
+ );
+
+ Urg = Seg->Seq + Seg->Urg;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && TCP_SEQ_GT (Urg, Tcb->RcvUp)) {
+
+ Tcb->RcvUp = Urg;
+ } else {
+
+ Tcb->RcvUp = Urg;
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG);
+ }
+ }
+ //
+ // Seventh step: Process the segment data
+ //
+ if (Seg->End != Seg->Seq) {
+
+ if (TCP_FIN_RCVD (Tcb->State)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: connection reset because data is lost for connected TCB %p\n",
+ Tcb)
+ );
+
+ goto RESET_THEN_DROP;
+ }
+
+ if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: connection reset because data is lost for connected TCB %p\n",
+ Tcb)
+ );
+
+ goto RESET_THEN_DROP;
+ }
+
+ TcpQueueData (Tcb, Nbuf);
+ if (TcpDeliverData (Tcb) == -1) {
+ goto RESET_THEN_DROP;
+ }
+
+ if (!IsListEmpty (&Tcb->RcvQue)) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+ }
+
+ //
+ // Eighth step: check the FIN.
+ // This step is moved to TcpDeliverData. FIN will be
+ // processed in sequence there. Check the comments in
+ // the beginning of the file header for information.
+ //
+
+ //
+ // Tcb is a new child of the listening Parent,
+ // commit it.
+ //
+ if (Parent != NULL) {
+ Tcb->Parent = Parent;
+ TcpInsertTcb (Tcb);
+ }
+
+ if ((Tcb->State != TCP_CLOSED) &&
+ (TcpToSendData (Tcb, 0) == 0) &&
+ (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0))
+ ) {
+
+ TcpToSendAck (Tcb);
+ }
+
+ NetbufFree (Nbuf);
+ return 0;
+
+RESET_THEN_DROP:
+ TcpSendReset (Tcb, Head, Len, Dst, Src, Version);
+
+DROP_CONNECTION:
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ NetbufFree (Nbuf);
+ TcpClose (Tcb);
+
+ return -1;
+
+SEND_RESET:
+
+ TcpSendReset (Tcb, Head, Len, Dst, Src, Version);
+
+DISCARD:
+
+ //
+ // Tcb is a child of Parent, and it doesn't survive
+ //
+ DEBUG ((EFI_D_WARN, "TcpInput: Discard a packet\n"));
+ NetbufFree (Nbuf);
+
+ if ((Parent != NULL) && (Tcb != NULL)) {
+
+ ASSERT (Tcb->Sk != NULL);
+ TcpClose (Tcb);
+ }
+
+ return 0;
+}
+
+/**
+ Process the received ICMP error messages for TCP.
+
+ @param[in] Nbuf The buffer that contains part of the TCP segment without an IP header
+ truncated from the ICMP error packet.
+ @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet.
+ @param[in] Src Source address of the ICMP error message.
+ @param[in] Dst Destination address of the ICMP error message.
+ @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates
+ IP6 stack.
+
+**/
+VOID
+TcpIcmpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT8 IcmpErr,
+ IN EFI_IP_ADDRESS *Src,
+ IN EFI_IP_ADDRESS *Dst,
+ IN UINT8 Version
+ )
+{
+ TCP_HEAD *Head;
+ TCP_CB *Tcb;
+ TCP_SEQNO Seq;
+ EFI_STATUS IcmpErrStatus;
+ BOOLEAN IcmpErrIsHard;
+ BOOLEAN IcmpErrNotify;
+
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (Head != NULL);
+
+ Tcb = TcpLocateTcb (
+ Head->DstPort,
+ Dst,
+ Head->SrcPort,
+ Src,
+ Version,
+ FALSE
+ );
+ if (Tcb == NULL || Tcb->State == TCP_CLOSED) {
+
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Validate the sequence number.
+ //
+ Seq = NTOHL (Head->Seq);
+ if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) {
+
+ goto CLEAN_EXIT;
+ }
+
+ IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, Tcb->Sk->IpVersion, &IcmpErrIsHard, &IcmpErrNotify);
+
+ if (IcmpErrNotify) {
+
+ SOCK_ERROR (Tcb->Sk, IcmpErrStatus);
+ }
+
+ if (IcmpErrIsHard) {
+
+ TcpClose (Tcb);
+ }
+
+CLEAN_EXIT:
+
+ NetbufFree (Nbuf);
+}
diff --git a/NetworkPkg/TcpDxe/TcpIo.c b/NetworkPkg/TcpDxe/TcpIo.c
new file mode 100644
index 0000000000..cecb6d19c5
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpIo.c
@@ -0,0 +1,190 @@
+/** @file
+ Implementation of I/O interfaces between TCP and IpIoLib.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+/**
+ Packet receive callback function provided to IP_IO, used to call
+ the proper function to handle the packet received by IP.
+
+ @param[in] Status Result of the receive request.
+ @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR.
+ @param[in] NetSession The IP session for the received packet.
+ @param[in] Pkt Packet received.
+ @param[in] Context The data provided by the user for the received packet when
+ the callback is registered in IP_IO_OPEN_DATA::RcvdContext.
+ This is an optional parameter that may be NULL.
+
+**/
+VOID
+EFIAPI
+TcpRxCallback (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpErr,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Pkt,
+ IN VOID *Context OPTIONAL
+ )
+{
+ if (EFI_SUCCESS == Status) {
+ TcpInput (Pkt, &NetSession->Source, &NetSession->Dest, NetSession->IpVersion);
+ } else {
+ TcpIcmpInput (
+ Pkt,
+ IcmpErr,
+ &NetSession->Source,
+ &NetSession->Dest,
+ NetSession->IpVersion
+ );
+ }
+}
+
+/**
+ Send the segment to IP via IpIo function.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the TCP segment to be sent.
+ @param[in] Src Source address of the TCP segment.
+ @param[in] Dest Destination address of the TCP segment.
+ @param[in] Version IP_VERSION_4 or IP_VERSION_6
+
+ @retval 0 The segment was sent out successfully.
+ @retval -1 The segment failed to send.
+
+**/
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN EFI_IP_ADDRESS *Src,
+ IN EFI_IP_ADDRESS *Dest,
+ IN UINT8 Version
+ )
+{
+ EFI_STATUS Status;
+ IP_IO *IpIo;
+ IP_IO_OVERRIDE Override;
+ SOCKET *Sock;
+ VOID *IpSender;
+ TCP_PROTO_DATA *TcpProto;
+
+ if (NULL == Tcb) {
+
+ IpIo = NULL;
+ IpSender = IpIoFindSender (&IpIo, Version, Src);
+
+ if (IpSender == NULL) {
+ DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n"));
+ return -1;
+ }
+
+ if (Version == IP_VERSION_6) {
+ //
+ // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the
+ // destination address if the dest is already specified through the
+ // configuration data. Here we get the IpIo we need and use the default IP
+ // instance in this IpIo to send the packet. The dest address is configured
+ // to be the unspecified address for the default IP instance.
+ //
+ IpSender = NULL;
+ }
+ } else {
+
+ Sock = Tcb->Sk;
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+ IpIo = TcpProto->TcpService->IpIo;
+ IpSender = Tcb->IpInfo;
+
+ if (Version == IP_VERSION_6) {
+ //
+ // It's IPv6 and this TCP segment belongs to a solid TCB, in such case
+ // the destination address can't be overridden, so reset the Dest to NULL.
+ //
+ Dest = NULL;
+ }
+ }
+
+ ASSERT (Version == IpIo->IpVersion);
+
+ if (Version == IP_VERSION_4) {
+ Override.Ip4OverrideData.TypeOfService = 0;
+ Override.Ip4OverrideData.TimeToLive = 255;
+ Override.Ip4OverrideData.DoNotFragment = FALSE;
+ Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_TCP;
+ ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS));
+ } else {
+ Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_TCP;
+ Override.Ip6OverrideData.HopLimit = 255;
+ Override.Ip6OverrideData.FlowLabel = 0;
+ }
+
+ Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status));
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ Refresh the remote peer's Neighbor Cache State if already exists.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Neighbor Source address of the TCP segment.
+ @param[in] Timeout Time in 100-ns units that this entry will remain
+ in the neighbor cache. A value of zero means that
+ the entry is permanent. A value of non-zero means
+ that the entry is dynamic and will be deleted
+ after Timeout.
+
+ @retval EFI_SUCCESS Successfully updated the neighbor relationship.
+ @retval EFI_NOT_STARTED The IpIo is not configured.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
+ @retval EFI_NOT_FOUND This entry is not in the neighbor table.
+
+**/
+EFI_STATUS
+Tcp6RefreshNeighbor (
+ IN TCP_CB *Tcb,
+ IN EFI_IP_ADDRESS *Neighbor,
+ IN UINT32 Timeout
+ )
+{
+ IP_IO *IpIo;
+ SOCKET *Sock;
+ TCP_PROTO_DATA *TcpProto;
+
+ if (NULL == Tcb) {
+ IpIo = NULL;
+ IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor);
+
+ if (IpIo == NULL) {
+ DEBUG ((EFI_D_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n"));
+ return EFI_NOT_STARTED;
+ }
+
+ } else {
+ Sock = Tcb->Sk;
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+ IpIo = TcpProto->TcpService->IpIo;
+ }
+
+ return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout);
+}
+
diff --git a/NetworkPkg/TcpDxe/TcpMain.c b/NetworkPkg/TcpDxe/TcpMain.c
new file mode 100644
index 0000000000..55a6d5b7d2
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpMain.c
@@ -0,0 +1,1074 @@
+/** @file
+ Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+/**
+ Check the integrity of the data buffer.
+
+ @param[in] DataLen The total length of the data buffer.
+ @param[in] FragmentCount The fragment count of the fragment table.
+ @param[in] FragmentTable Pointer to the fragment table of the data
+ buffer.
+
+ @retval EFI_SUCCESS The integrity check passed.
+ @retval EFI_INVALID_PARAMETER The integrity check failed.
+
+**/
+EFI_STATUS
+TcpChkDataBuf (
+ IN UINT32 DataLen,
+ IN UINT32 FragmentCount,
+ IN EFI_TCP4_FRAGMENT_DATA *FragmentTable
+ )
+{
+ UINT32 Index;
+
+ UINT32 Len;
+
+ for (Index = 0, Len = 0; Index < FragmentCount; Index++) {
+ Len = Len + FragmentTable[Index].FragmentLength;
+ }
+
+ if (DataLen != Len) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get the current operational status.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[out] Tcp4State Pointer to the buffer to receive the current TCP
+ state. Optional parameter that may be NULL.
+ @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP
+ configuration. Optional parameter that may be NULL.
+ @param[out] Ip4ModeData Pointer to the buffer to receive the current
+ IPv4 configuration. Optional parameter that may be NULL.
+ @param[out] MnpConfigData Pointer to the buffer to receive the current MNP
+ configuration data indirectly used by the TCPv4
+ Instance. Optional parameter that may be NULL.
+ @param[out] SnpModeData Pointer to the buffer to receive the current SNP
+ configuration data indirectly used by the TCPv4
+ Instance. Optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4GetModeData (
+ IN CONST EFI_TCP4_PROTOCOL *This,
+ OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL,
+ OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ TCP4_MODE_DATA TcpMode;
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ TcpMode.Tcp4State = Tcp4State;
+ TcpMode.Tcp4ConfigData = Tcp4ConfigData;
+ TcpMode.Ip4ModeData = Ip4ModeData;
+ TcpMode.MnpConfigData = MnpConfigData;
+ TcpMode.SnpModeData = SnpModeData;
+
+ return SockGetMode (Sock, &TcpMode);
+}
+
+/**
+ Initialize or brutally reset the operational parameters for
+ this EFI TCPv4 instance.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] TcpConfigData Pointer to the configure data to configure the
+ instance. Optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The operational settings were set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Configure (
+ IN EFI_TCP4_PROTOCOL * This,
+ IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL
+ )
+{
+ EFI_TCP4_OPTION *Option;
+ SOCKET *Sock;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+ IP4_ADDR SubnetMask;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Tcp protocol related parameter check will be conducted here
+ //
+ if (NULL != TcpConfigData) {
+
+ CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
+ if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TcpConfigData->AccessPoint.ActiveFlag && (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!TcpConfigData->AccessPoint.UseDefaultAddress) {
+
+ CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR));
+ CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR));
+ if (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (SubnetMask))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Option = TcpConfigData->ControlOption;
+ if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ if (NULL == TcpConfigData) {
+ return SockFlush (Sock);
+ }
+
+ Status = SockConfigure (Sock, TcpConfigData);
+
+ if (EFI_NO_MAPPING == Status) {
+ Sock->ConfigureState = SO_NO_MAPPING;
+ }
+
+ return Status;
+}
+
+/**
+ Add or delete routing entries.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] DeleteRoute If TRUE, delete the specified route from routing
+ table; if FALSE, add the specified route to
+ routing table.
+ @param[in] SubnetAddress The destination network.
+ @param[in] SubnetMask The subnet mask for the destination network.
+ @param[in] GatewayAddress The gateway address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the
+ entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED This route is already in the routing table.
+ @retval EFI_UNSUPPORTED The TCP driver does not support this operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Routes (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ )
+{
+ SOCKET *Sock;
+ TCP4_ROUTE_INFO RouteInfo;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ RouteInfo.DeleteRoute = DeleteRoute;
+ RouteInfo.SubnetAddress = SubnetAddress;
+ RouteInfo.SubnetMask = SubnetMask;
+ RouteInfo.GatewayAddress = GatewayAddress;
+
+ return SockRoute (Sock, &RouteInfo);
+}
+
+/**
+ Initiate a non-blocking TCP connection request for an active TCP instance.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] ConnectionToken Pointer to the connection token to return when
+ the TCP three way handshake finishes.
+
+ @retval EFI_SUCCESS The connection request successfully
+ initiated.
+ @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instance is not configured as an active one,
+ or it is not in Tcp4StateClosed state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to
+ initiate the active open.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Connect (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockConnect (Sock, ConnectionToken);
+}
+
+/**
+ Listen on the passive instance to accept an incoming connection request.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] ListenToken Pointer to the listen token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The listen token was queued successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not
+ in Tcp4StateListen state or a same listen token
+ has already existed in the listen token queue of
+ this TCP instance.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish
+ the operation.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Accept (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockAccept (Sock, ListenToken);
+}
+
+/**
+ Queues outgoing data into the transmit queue
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] Token Pointer to the completion token to queue to the
+ transmit queue.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A transmit completion token with the same
+ Token-> CompletionToken.Event was already in the
+ transmission queue. * The current instance is in
+ Tcp4StateClosed state. * The current instance is
+ a passive one and it is in Tcp4StateListen
+ state. * User has called Close() to disconnect
+ this connection.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a
+ resource shortage.
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or
+ address.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Transmit (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.TxData ||
+ 0 == Token->Packet.TxData->FragmentCount ||
+ 0 == Token->Packet.TxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = TcpChkDataBuf (
+ Token->Packet.TxData->DataLength,
+ Token->Packet.TxData->FragmentCount,
+ Token->Packet.TxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockSend (Sock, Token);
+}
+
+/**
+ Place an asynchronous receive request into the receiving queue.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A receive completion token with the same
+ Token->CompletionToken.Event was already in the
+ receive queue. * The current instance is in
+ Tcp4StateClosed state. * The current instance is
+ a passive one and it is in Tcp4StateListen
+ state. * User has called Close() to disconnect
+ this connection.
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection,
+ and there is no any buffered data in the receive
+ buffer of this instance.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Receive (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.RxData ||
+ 0 == Token->Packet.RxData->FragmentCount ||
+ 0 == Token->Packet.RxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = TcpChkDataBuf (
+ Token->Packet.RxData->DataLength,
+ Token->Packet.RxData->FragmentCount,
+ Token->Packet.RxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockRcv (Sock, Token);
+
+}
+
+/**
+ Disconnecting a TCP connection gracefully or reset a TCP connection.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] CloseToken Pointer to the close token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE: *
+ Configure() has been called with TcpConfigData
+ set to NULL, and this function has not returned.
+ * Previous Close() call on this instance has not
+ finished.
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the
+ operation.
+ @retval EFI_DEVICE_ERROR Any unexpected category error not belonging to those
+ listed above.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Close (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);
+}
+
+/**
+ Abort an asynchronous connection, listen, transmission or receive request.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ Connect(), Accept(), Transmit() or Receive(). If
+ NULL, all pending tokens issued by the four
+ functions listed above will be aborted.
+
+ @retval EFI_UNSUPPORTED The operation is not supported in the current
+ implementation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Poll to receive incoming data and transmit outgoing segments.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or
+ receive queue. Consider increasing the polling
+ rate.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Poll (
+ IN EFI_TCP4_PROTOCOL *This
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);
+
+ return Status;
+}
+
+/**
+ Get the current operational status.
+
+ The GetModeData() function copies the current operational settings of this EFI TCPv6
+ Protocol instance into user-supplied buffers. This function can also be used to retrieve
+ the operational setting of underlying drivers such as IPv6, MNP, or SNP.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[out] Tcp6State The buffer in which the current TCP state is
+ returned. Optional parameter that may be NULL.
+ @param[out] Tcp6ConfigData The buffer in which the current TCP configuration
+ is returned. Optional parameter that may be NULL.
+ @param[out] Ip6ModeData The buffer in which the current IPv6 configuration
+ data used by the TCP instance is returned.
+ Optional parameter that may be NULL.
+ @param[out] MnpConfigData The buffer in which the current MNP configuration
+ data indirectly used by the TCP instance is returned.
+ Optional parameter that may be NULL.
+ @param[out] SnpModeData The buffer in which the current SNP mode data
+ indirectly used by the TCP instance is returned.
+ Optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't
+ been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6GetModeData (
+ IN EFI_TCP6_PROTOCOL *This,
+ OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL,
+ OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL,
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ TCP6_MODE_DATA TcpMode;
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ TcpMode.Tcp6State = Tcp6State;
+ TcpMode.Tcp6ConfigData = Tcp6ConfigData;
+ TcpMode.Ip6ModeData = Ip6ModeData;
+ TcpMode.MnpConfigData = MnpConfigData;
+ TcpMode.SnpModeData = SnpModeData;
+
+ return SockGetMode (Sock, &TcpMode);
+}
+
+/**
+ Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.
+
+ The Configure() function does the following:
+ - Initialize this TCP instance, i.e., initialize the communication end settings and
+ specify active open or passive open for an instance.
+ - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush
+ transmission and receiving buffer directly without informing the communication peer.
+
+ No other TCPv6 Protocol operation except Poll() can be executed by this instance until
+ it is configured properly. For an active TCP instance, after a proper configuration it
+ may call Connect() to initiate a three-way handshake. For a passive TCP instance,
+ its state transits to Tcp6StateListen after configuration, and Accept() may be
+ called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,
+ the instance is reset. The resetting process will be done brutally, the state machine will
+ be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,
+ and no traffic is allowed through this instance.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance.
+ If Tcp6ConfigData is set to NULL, the instance is reset.
+
+ @retval EFI_SUCCESS The operational settings were set, changed, or reset
+ successfully.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for
+ use.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:
+ - This is NULL.
+ - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor
+ one of the configured IP addresses in the underlying IPv6 driver.
+ - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast
+ IPv6 address.
+ - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or
+ Tcp6ConfigData->AccessPoint.RemotePort is zero when
+ Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.
+ - A same access point has been configured in other TCP
+ instance properly.
+ @retval EFI_ACCESS_DENIED Configuring a TCP instance when it is configured without
+ calling Configure() with NULL to reset it.
+ @retval EFI_UNSUPPORTED One or more of the control options are not supported in
+ the implementation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when
+ executing Configure().
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Configure (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL
+ )
+{
+ EFI_TCP6_OPTION *Option;
+ SOCKET *Sock;
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS *Ip;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Tcp protocol related parameter check will be conducted here
+ //
+ if (NULL != Tcp6ConfigData) {
+
+ Ip = &Tcp6ConfigData->AccessPoint.RemoteAddress;
+ if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Tcp6ConfigData->AccessPoint.ActiveFlag &&
+ (0 == Tcp6ConfigData->AccessPoint.RemotePort || NetIp6IsUnspecifiedAddr (Ip))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip = &Tcp6ConfigData->AccessPoint.StationAddress;
+ if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option = Tcp6ConfigData->ControlOption;
+ if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ if (NULL == Tcp6ConfigData) {
+ return SockFlush (Sock);
+ }
+
+ Status = SockConfigure (Sock, Tcp6ConfigData);
+
+ if (EFI_NO_MAPPING == Status) {
+ Sock->ConfigureState = SO_NO_MAPPING;
+ }
+
+ return Status;
+}
+
+/**
+ Initiate a nonblocking TCP connection request for an active TCP instance.
+
+ The Connect() function will initiate an active open to the remote peer configured
+ in a current TCP instance if it is configured active. If the connection succeeds or
+ fails due to any error, the ConnectionToken->CompletionToken.Event will be signaled
+ and ConnectionToken->CompletionToken.Status will be updated accordingly. This
+ function can only be called for the TCP instance in the Tcp6StateClosed state. The
+ instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.
+ If a TCP three-way handshake succeeds, its state will become Tcp6StateEstablished.
+ Otherwise, the state will return to Tcp6StateClosed.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] ConnectionToken Pointer to the connection token to return when the TCP three
+ way handshake finishes.
+
+ @retval EFI_SUCCESS The connection request successfully initiated and the state of
+ this TCP instance has been changed to Tcp6StateSynSent.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:
+ - This instance is not configured as an active one.
+ - This instance is not in Tcp6StateClosed state.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - ConnectionToken is NULL.
+ - ConnectionToken->CompletionToken.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Connect (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockConnect (Sock, ConnectionToken);
+}
+
+/**
+ Listen on the passive instance to accept an incoming connection request. This is a
+ nonblocking operation.
+
+ The Accept() function initiates an asynchronous accept request to wait for an incoming
+ connection on the passive TCP instance. If a remote peer successfully establishes a
+ connection with this instance, a new TCP instance will be created and its handle will
+ be returned in ListenToken->NewChildHandle. The newly created instance is configured
+ by inheriting the passive instance's configuration and is ready for use upon return.
+ The new instance is in the Tcp6StateEstablished state.
+
+ The ListenToken->CompletionToken.Event will be signaled when a new connection is
+ accepted, when a user aborts the listen or when a connection is reset.
+
+ This function only can be called when a current TCP instance is in Tcp6StateListen state.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] ListenToken Pointer to the listen token to return when operation finishes.
+
+
+ @retval EFI_SUCCESS The listen token queued successfully.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:
+ - This instance is not a passive instance.
+ - This instance is not in Tcp6StateListen state.
+ - The same listen token has already existed in the listen
+ token queue of this TCP instance.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - ListenToken is NULL.
+ - ListentToken->CompletionToken.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation.
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to a category listed above.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Accept (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_LISTEN_TOKEN *ListenToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockAccept (Sock, ListenToken);
+}
+
+/**
+ Queues outgoing data into the transmit queue.
+
+ The Transmit() function queues a sending request to this TCP instance along with the
+ user data. The status of the token is updated and the event in the token will be
+ signaled once the data is sent out or an error occurs.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Token Pointer to the completion token to queue to the transmit queue.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a
+ source address for this instance, but no source address was
+ available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token->CompletionToken.Event is NULL.
+ - Token->Packet.TxData is NULL.
+ - Token->Packet.FragmentCount is zero.
+ - Token->Packet.DataLength is not equal to the sum of fragment lengths.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:
+ - A transmit completion token with the same Token->
+ CompletionToken.Event was already in the
+ transmission queue.
+ - The current instance is in Tcp6StateClosed state.
+ - The current instance is a passive one and it is in
+ Tcp6StateListen state.
+ - User has called Close() to disconnect this connection.
+ @retval EFI_NOT_READY The completion token could not be queued because the
+ transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of resource
+ shortage.
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Transmit (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.TxData ||
+ 0 == Token->Packet.TxData->FragmentCount ||
+ 0 == Token->Packet.TxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = TcpChkDataBuf (
+ Token->Packet.TxData->DataLength,
+ Token->Packet.TxData->FragmentCount,
+ (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.TxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockSend (Sock, Token);
+}
+
+/**
+ Places an asynchronous receive request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue. This
+ function is always asynchronous. The caller must allocate the Token->CompletionToken.Event
+ and the FragmentBuffer used to receive data. The caller also must fill the DataLength that
+ represents the whole length of all FragmentBuffer. When the receive operation completes, the
+ EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData
+ fields, and the Token->CompletionToken.Event is signaled. If data obtained, the data and its length
+ will be copied into the FragmentTable; at the same time the full length of received data will
+ be recorded in the DataLength fields. Providing a proper notification function and context
+ for the event enables the user to receive the notification and receiving status. That
+ notification function is guaranteed to not be re-entered.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the receive data
+ descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token->CompletionToken.Event is NULL.
+ - Token->Packet.RxData is NULL.
+ - Token->Packet.RxData->DataLength is 0.
+ - The Token->Packet.RxData->DataLength is not the
+ sum of all FragmentBuffer length in FragmentTable.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of
+ system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI TCPv6 Protocol instance has been reset to startup defaults.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ - A receive completion token with the same Token->CompletionToken.Event
+ was already in the receive queue.
+ - The current instance is in Tcp6StateClosed state.
+ - The current instance is a passive one and it is in
+ Tcp6StateListen state.
+ - User has called Close() to disconnect this connection.
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection and there is no
+ buffered data in the receive buffer of this instance.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Receive (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.RxData ||
+ 0 == Token->Packet.RxData->FragmentCount ||
+ 0 == Token->Packet.RxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = TcpChkDataBuf (
+ Token->Packet.RxData->DataLength,
+ Token->Packet.RxData->FragmentCount,
+ (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.RxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockRcv (Sock, Token);
+}
+
+/**
+ Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a
+ nonblocking operation.
+
+ Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered
+ transmission data will be sent by the TCP driver, and the current instance will have a graceful close
+ working flow described as RFC 793 if AbortOnClose is set to FALSE. Otherwise, a rest packet
+ will be sent by TCP driver to fast disconnect this connection. When the close operation completes
+ successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous
+ operations are signaled, and any buffers used for TCP network traffic are flushed.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] CloseToken Pointer to the close token to return when operation finishes.
+
+ @retval EFI_SUCCESS The Close() was called successfully.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:
+ - CloseToken or CloseToken->CompletionToken.Event is already in use.
+ - Previous Close() call on this instance has not finished.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - CloseToken is NULL.
+ - CloseToken->CompletionToken.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation.
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to error categories given above.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Close (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_CLOSE_TOKEN *CloseToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);
+}
+
+/**
+ Abort an asynchronous connection, listen, transmission, or receive request.
+
+ The Cancel() function aborts a pending connection, listen, transmit, or
+ receive request.
+
+ If Token is not NULL and the token is in the connection, listen, transmission,
+ or receive queue when it is being cancelled, its Token->Status will be set
+ to EFI_ABORTED, and then Token->Event will be signaled.
+
+ If the token is not in one of the queues, which usually means that the
+ asynchronous operation has completed, EFI_NOT_FOUND is returned.
+
+ If Token is NULL all asynchronous token issued by Connect(), Accept(),
+ Transmit(), and Receive() will be aborted.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_TCP6_PROTOCOL.Connect(),
+ EFI_TCP6_PROTOCOL.Accept(),
+ EFI_TCP6_PROTOCOL.Transmit() or
+ EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending
+ tokens issued by above four functions will be aborted. Type
+ EFI_TCP6_COMPLETION_TOKEN is defined in
+ EFI_TCP_PROTOCOL.Connect().
+
+ @retval EFI_UNSUPPORTED The implementation does not support this function.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Cancel (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Poll to receive incoming data and transmit outgoing segments.
+
+ The Poll() function increases the rate that data is moved between the network
+ and application, and can be called when the TCP instance is created successfully.
+ Its use is optional.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data is processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Poll (
+ IN EFI_TCP6_PROTOCOL *This
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);
+
+ return Status;
+}
+
diff --git a/NetworkPkg/TcpDxe/TcpMain.h b/NetworkPkg/TcpDxe/TcpMain.h
new file mode 100644
index 0000000000..fabffc21d7
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpMain.h
@@ -0,0 +1,758 @@
+/** @file
+ Declaration of protocol interfaces in EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.
+ It is the common head file for all Tcp*.c in TCP driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP_MAIN_H_
+#define _TCP_MAIN_H_
+
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+#include <Library/IpIoLib.h>
+#include <Library/DevicePathLib.h>
+
+#include "Socket.h"
+#include "TcpProto.h"
+#include "TcpDriver.h"
+#include "TcpFunc.h"
+
+extern UINT16 mTcp4RandomPort;
+extern UINT16 mTcp6RandomPort;
+extern CHAR16 *mTcpStateName[];
+extern EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2;
+
+extern LIST_ENTRY mTcpRunQue;
+extern LIST_ENTRY mTcpListenQue;
+extern TCP_SEQNO mTcpGlobalIss;
+extern UINT32 mTcpTick;
+
+///
+/// 30 seconds.
+///
+#define TCP6_KEEP_NEIGHBOR_TIME 30
+///
+/// 5 seconds, since 1 tick equals 200ms.
+///
+#define TCP6_REFRESH_NEIGHBOR_TICK 25
+
+#define TCP_EXPIRE_TIME 65535
+
+///
+/// The implementation selects the initial send sequence number and the unit to
+/// be added when it is increased.
+///
+#define TCP_BASE_ISS 0x4d7e980b
+#define TCP_ISS_INCREMENT_1 2048
+#define TCP_ISS_INCREMENT_2 100
+
+typedef union {
+ EFI_TCP4_CONFIG_DATA Tcp4CfgData;
+ EFI_TCP6_CONFIG_DATA Tcp6CfgData;
+} TCP_CONFIG_DATA;
+
+typedef union {
+ EFI_TCP4_ACCESS_POINT Tcp4Ap;
+ EFI_TCP6_ACCESS_POINT Tcp6Ap;
+} TCP_ACCESS_POINT;
+
+typedef struct _TCP4_MODE_DATA {
+ EFI_TCP4_CONNECTION_STATE *Tcp4State;
+ EFI_TCP4_CONFIG_DATA *Tcp4ConfigData;
+ EFI_IP4_MODE_DATA *Ip4ModeData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE *SnpModeData;
+} TCP4_MODE_DATA;
+
+typedef struct _TCP6_MODE_DATA {
+ EFI_TCP6_CONNECTION_STATE *Tcp6State;
+ EFI_TCP6_CONFIG_DATA *Tcp6ConfigData;
+ EFI_IP6_MODE_DATA *Ip6ModeData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE *SnpModeData;
+} TCP6_MODE_DATA;
+
+typedef struct _TCP4_ROUTE_INFO {
+ BOOLEAN DeleteRoute;
+ EFI_IPv4_ADDRESS *SubnetAddress;
+ EFI_IPv4_ADDRESS *SubnetMask;
+ EFI_IPv4_ADDRESS *GatewayAddress;
+} TCP4_ROUTE_INFO;
+
+//
+// EFI_TCP4_PROTOCOL definitions.
+//
+
+/**
+ Get the current operational status.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[out] Tcp4State Pointer to the buffer to receive the current TCP
+ state. Optional parameter that may be NULL.
+ @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP
+ configuration. Optional parameter that may be NULL.
+ @param[out] Ip4ModeData Pointer to the buffer to receive the current
+ IPv4 configuration. Optional parameter that may be NULL.
+ @param[out] MnpConfigData Pointer to the buffer to receive the current MNP
+ configuration data indirectly used by the TCPv4
+ Instance. Optional parameter that may be NULL.
+ @param[out] SnpModeData Pointer to the buffer to receive the current SNP
+ configuration data indirectly used by the TCPv4
+ Instance. Optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4GetModeData (
+ IN CONST EFI_TCP4_PROTOCOL *This,
+ OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL,
+ OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+/**
+ Initialize or brutally reset the operational parameters for
+ this EFI TCPv4 instance.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] TcpConfigData Pointer to the configure data to configure the
+ instance. Optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The operational settings are set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED Configuring the TCP instance when it is already
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Configure (
+ IN EFI_TCP4_PROTOCOL * This,
+ IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL
+ );
+
+/**
+ Add or delete routing entries.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] DeleteRoute If TRUE, delete the specified route from routing
+ table; if FALSE, add the specified route to
+ routing table.
+ @param[in] SubnetAddress The destination network.
+ @param[in] SubnetMask The subnet mask for the destination network.
+ @param[in] GatewayAddress The gateway address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the
+ entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED This route is already in the routing table.
+ @retval EFI_UNSUPPORTED The TCP driver does not support this operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Routes (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ );
+
+/**
+ Initiate a nonblocking TCP connection request for an active TCP instance.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] ConnectionToken Pointer to the connection token to return when
+ the TCP three way handshake finishes.
+
+ @retval EFI_SUCCESS The connection request is successfully
+ initiated.
+ @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instance is not configured as an active one
+ or it is not in Tcp4StateClosed state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to
+ initiate the active open.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Connect (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken
+ );
+
+/**
+ Listen on the passive instance to accept an incoming connection request.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] ListenToken Pointer to the listen token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The listen token has been queued successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not
+ in Tcp4StateListen state, or a same listen token
+ has already existed in the listen token queue of
+ this TCP instance.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish
+ the operation.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Accept (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken
+ );
+
+/**
+ Queues outgoing data into the transmit queue
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param[in] Token Pointer to the completion token to queue to the
+ transmit queue
+
+ @retval EFI_SUCCESS The data has been queued for transmission
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A transmit completion token with the same
+ Token-> CompletionToken.Event was already in the
+ transmission queue. * The current instance is in
+ Tcp4StateClosed state * The current instance is
+ a passive one and it is in Tcp4StateListen
+ state. * User has called Close() to disconnect
+ this connection.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a
+ resource shortage.
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or
+ address.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Transmit (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ );
+
+/**
+ Place an asynchronous receive request into the receiving queue.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A receive completion token with the same
+ Token->CompletionToken.Event was already in the
+ receive queue. * The current instance is in
+ Tcp4StateClosed state. * The current instance is
+ a passive one and it is in Tcp4StateListen
+ state. * User has called Close() to disconnect
+ this connection.
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection
+ and there is no buffered data in the receive
+ buffer of this instance.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Receive (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ );
+
+/**
+ Disconnecting a TCP connection gracefully or reset a TCP connection.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] CloseToken Pointer to the close token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE: *
+ Configure() has been called with TcpConfigData
+ set to NULL and this function has not returned.
+ * Previous Close() call on this instance has not
+ finished.
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the
+ operation.
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error
+ categories given above.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Close (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken
+ );
+
+/**
+ Abort an asynchronous connection, listen, transmission or receive request.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ Connect(), Accept(), Transmit() or Receive(). If
+ NULL, all pending tokens issued by the above four
+ functions will be aborted.
+
+ @retval EFI_UNSUPPORTED The operation is not supported in the current
+ implementation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Poll to receive incoming data and transmit outgoing segments.
+
+ @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or
+ receive queue. Consider increasing the polling
+ rate.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Poll (
+ IN EFI_TCP4_PROTOCOL *This
+ );
+
+//
+// EFI_TCP6_PROTOCOL definitions.
+//
+
+/**
+ Get the current operational status.
+
+ The GetModeData() function copies the current operational settings of this EFI TCPv6
+ Protocol instance into user-supplied buffers. This function can also be used to retrieve
+ the operational setting of underlying drivers such as IPv6, MNP, or SNP.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[out] Tcp6State The buffer in which the current TCP state is
+ returned. Optional parameter that may be NULL.
+ @param[out] Tcp6ConfigData The buffer in which the current TCP configuration
+ is returned. Optional parameter that may be NULL.
+ @param[out] Ip6ModeData The buffer in which the current IPv6 configuration
+ data used by the TCP instance is returned.
+ Optional parameter that may be NULL.
+ @param[out] MnpConfigData The buffer in which the current MNP configuration
+ data used indirectly by the TCP instance is returned.
+ Optional parameter that may be NULL.
+ @param[out] SnpModeData The buffer in which the current SNP mode data
+ used indirectly by the TCP instance is returned.
+ Optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't
+ been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6GetModeData (
+ IN EFI_TCP6_PROTOCOL *This,
+ OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL,
+ OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL,
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+/**
+ Initialize or brutally reset the operational parameters for this EFI TCPv6 instance.
+
+ The Configure() function does the following:
+ - Initialize this TCP instance, i.e., initialize the communication end settings and
+ specify active open or passive open for an instance.
+ - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush
+ transmission and receiving buffer directly without informing the communication peer.
+
+ No other TCPv6 Protocol operation except Poll() can be executed by this instance until
+ it is configured properly. For an active TCP instance, after a proper configuration it
+ may call Connect() to initiates the three-way handshake. For a passive TCP instance,
+ its state will transit to Tcp6StateListen after configuration, and Accept() may be
+ called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL,
+ the instance is reset. Resetting process will be done brutally, the state machine will
+ be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed,
+ and no traffic is allowed through this instance.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance.
+ If Tcp6ConfigData is set to NULL, the instance is reset.
+
+ @retval EFI_SUCCESS The operational settings were set, changed, or reset
+ successfully.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for
+ use.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE:
+ - This is NULL.
+ - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor
+ one of the configured IP addresses in the underlying IPv6 driver.
+ - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast
+ IPv6 address.
+ - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or
+ Tcp6ConfigData->AccessPoint.RemotePort is zero when
+ Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE.
+ - A same access point has been configured in other TCP
+ instance properly.
+ @retval EFI_ACCESS_DENIED Configuring TCP instance when it is configured without
+ calling Configure() with NULL to reset it.
+ @retval EFI_UNSUPPORTED One or more of the control options are not supported in
+ the implementation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when
+ executing Configure().
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Configure (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL
+ );
+
+/**
+ Initiate a nonblocking TCP connection request for an active TCP instance.
+
+ The Connect() function will initiate an active open to the remote peer configured
+ in current TCP instance if it is configured active. If the connection succeeds or
+ fails due to an error, the ConnectionToken->CompletionToken.Event will be signaled,
+ and ConnectionToken->CompletionToken.Status will be updated accordingly. This
+ function can only be called for the TCP instance in Tcp6StateClosed state. The
+ instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS.
+ If TCP three-way handshake succeeds, its state will become Tcp6StateEstablished;
+ otherwise, the state will return to Tcp6StateClosed.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] ConnectionToken Pointer to the connection token to return when the TCP
+ three-way handshake finishes.
+
+ @retval EFI_SUCCESS The connection request successfully initiated and the state of
+ this TCP instance has been changed to Tcp6StateSynSent.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:
+ - This instance is not configured as an active instance.
+ - This instance is not in Tcp6StateClosed state.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - ConnectionToken is NULL.
+ - ConnectionToken->CompletionToken.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Connect (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken
+ );
+
+/**
+ Listen on the passive instance to accept an incoming connection request. This is a
+ nonblocking operation.
+
+ The Accept() function initiates an asynchronous accept request to wait for an incoming
+ connection on the passive TCP instance. If a remote peer successfully establishes a
+ connection with this instance, a new TCP instance will be created and its handle will
+ be returned in ListenToken->NewChildHandle. The newly created instance is configured
+ by inheriting the passive instance's configuration, and is ready for use upon return.
+ The new instance is in the Tcp6StateEstablished state.
+
+ The ListenToken->CompletionToken.Event will be signaled when a new connection is
+ accepted, user aborts the listen or connection is reset.
+
+ This function only can be called when the current TCP instance is in Tcp6StateListen state.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] ListenToken Pointer to the listen token to return when the operation finishes.
+
+
+ @retval EFI_SUCCESS The listen token was been queued successfully.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:
+ - This instance is not a passive instance.
+ - This instance is not in Tcp6StateListen state.
+ - The same listen token has already existed in the listen
+ token queue of this TCP instance.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - ListenToken is NULL.
+ - ListentToken->CompletionToken.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation.
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error
+ categories given above.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Accept (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_LISTEN_TOKEN *ListenToken
+ );
+
+/**
+ Queues outgoing data into the transmit queue.
+
+ The Transmit() function queues a sending request to this TCP instance along with the
+ user data. The status of the token is updated and the event in the token will be
+ signaled once the data is sent out or some error occurs.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Token Pointer to the completion token to queue to the transmit queue.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a
+ source address for this instance, but no source address was
+ available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token->CompletionToken.Event is NULL.
+ - Token->Packet.TxData is NULL.
+ - Token->Packet.FragmentCount is zero.
+ - Token->Packet.DataLength is not equal to the sum of fragment lengths.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE:
+ - A transmit completion token with the same Token->
+ CompletionToken.Event was already in the
+ transmission queue.
+ - The current instance is in Tcp6StateClosed state.
+ - The current instance is a passive one and it is in
+ Tcp6StateListen state.
+ - User has called Close() to disconnect this connection.
+ @retval EFI_NOT_READY The completion token could not be queued because the
+ transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a resource
+ shortage.
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Transmit (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_IO_TOKEN *Token
+ );
+
+/**
+ Places an asynchronous receive request into the receiving queue.
+
+ The Receive() function places a completion token into the receive packet queue. This
+ function is always asynchronous. The caller must allocate the Token->CompletionToken.Event
+ and the FragmentBuffer used to receive data. The caller also must fill the DataLength, which
+ represents the whole length of all FragmentBuffer. When the receive operation completes, the
+ EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData
+ fields, and the Token->CompletionToken.Event is signaled. If data is obtained, the data and its length
+ will be copied into the FragmentTable. At the same time the full length of received data will
+ be recorded in the DataLength fields. Providing a proper notification function and context
+ for the event enables the user to receive the notification and receiving status. That
+ notification function is guaranteed to not be re-entered.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the receive data
+ descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source
+ address for this instance, but no source address was available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ - This is NULL.
+ - Token is NULL.
+ - Token->CompletionToken.Event is NULL.
+ - Token->Packet.RxData is NULL.
+ - Token->Packet.RxData->DataLength is 0.
+ - The Token->Packet.RxData->DataLength is not the
+ sum of all FragmentBuffer length in FragmentTable.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of
+ system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI TCPv6 Protocol instance has been reset to startup defaults.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ - A receive completion token with the same Token->CompletionToken.Event
+ was already in the receive queue.
+ - The current instance is in Tcp6StateClosed state.
+ - The current instance is a passive one and it is in
+ Tcp6StateListen state.
+ - The user has called Close() to disconnect this connection.
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection, and there is no
+ buffered data in the receive buffer of this instance.
+ @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Receive (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_IO_TOKEN *Token
+ );
+
+/**
+ Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a
+ nonblocking operation.
+
+ Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered
+ transmission data will be sent by the TCP driver, and the current instance will have a graceful close
+ working flow described as RFC 793 if AbortOnClose is set to FALSE, otherwise, a rest packet
+ will be sent by TCP driver to fast disconnect this connection. When the close operation completes
+ successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous
+ operations are signaled, and any buffers used for TCP network traffic are flushed.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] CloseToken Pointer to the close token to return when operation finishes.
+
+ @retval EFI_SUCCESS The Close() was called successfully.
+ @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE:
+ - CloseToken or CloseToken->CompletionToken.Event is already in use.
+ - Previous Close() call on this instance has not finished.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ - This is NULL.
+ - CloseToken is NULL.
+ - CloseToken->CompletionToken.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation.
+ @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error categories given above.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Close (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_CLOSE_TOKEN *CloseToken
+ );
+
+/**
+ Abort an asynchronous connection, listen, transmission or receive request.
+
+ The Cancel() function aborts a pending connection, listen, transmit or
+ receive request.
+
+ If Token is not NULL and the token is in the connection, listen, transmission
+ or receive queue when it is being cancelled, its Token->Status will be set
+ to EFI_ABORTED and then Token->Event will be signaled.
+
+ If the token is not in one of the queues, which usually means that the
+ asynchronous operation has completed, EFI_NOT_FOUND is returned.
+
+ If Token is NULL all asynchronous token issued by Connect(), Accept(),
+ Transmit() and Receive() will be aborted.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_TCP6_PROTOCOL.Connect(),
+ EFI_TCP6_PROTOCOL.Accept(),
+ EFI_TCP6_PROTOCOL.Transmit() or
+ EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending
+ tokens issued by above four functions will be aborted. Type
+ EFI_TCP6_COMPLETION_TOKEN is defined in
+ EFI_TCP_PROTOCOL.Connect().
+
+ @retval EFI_UNSUPPORTED The implementation does not support this function.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Cancel (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ Poll to receive incoming data and transmit outgoing segments.
+
+ The Poll() function increases the rate that data is moved between the network
+ and application and can be called when the TCP instance is created successfully.
+ Its use is optional.
+
+ @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data is processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue.
+ Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Poll (
+ IN EFI_TCP6_PROTOCOL *This
+ );
+
+#endif
diff --git a/NetworkPkg/TcpDxe/TcpMisc.c b/NetworkPkg/TcpDxe/TcpMisc.c
new file mode 100644
index 0000000000..492ec35fb8
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpMisc.c
@@ -0,0 +1,1281 @@
+/** @file
+ Misc support routines for TCP driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+LIST_ENTRY mTcpRunQue = {
+ &mTcpRunQue,
+ &mTcpRunQue
+};
+
+LIST_ENTRY mTcpListenQue = {
+ &mTcpListenQue,
+ &mTcpListenQue
+};
+
+TCP_SEQNO mTcpGlobalIss = TCP_BASE_ISS;
+
+CHAR16 *mTcpStateName[] = {
+ L"TCP_CLOSED",
+ L"TCP_LISTEN",
+ L"TCP_SYN_SENT",
+ L"TCP_SYN_RCVD",
+ L"TCP_ESTABLISHED",
+ L"TCP_FIN_WAIT_1",
+ L"TCP_FIN_WAIT_2",
+ L"TCP_CLOSING",
+ L"TCP_TIME_WAIT",
+ L"TCP_CLOSE_WAIT",
+ L"TCP_LAST_ACK"
+};
+
+
+/**
+ Initialize the Tcb local related members.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpInitTcbLocal (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ //
+ // Compute the checksum of the fixed parts of pseudo header
+ //
+ if (Tcb->Sk->IpVersion == IP_VERSION_4) {
+ Tcb->HeadSum = NetPseudoHeadChecksum (
+ Tcb->LocalEnd.Ip.Addr[0],
+ Tcb->RemoteEnd.Ip.Addr[0],
+ 0x06,
+ 0
+ );
+ } else {
+ Tcb->HeadSum = NetIp6PseudoHeadChecksum (
+ &Tcb->LocalEnd.Ip.v6,
+ &Tcb->RemoteEnd.Ip.v6,
+ 0x06,
+ 0
+ );
+ }
+
+ Tcb->Iss = TcpGetIss ();
+ Tcb->SndUna = Tcb->Iss;
+ Tcb->SndNxt = Tcb->Iss;
+
+ Tcb->SndWl2 = Tcb->Iss;
+ Tcb->SndWnd = 536;
+
+ Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);
+
+ //
+ // First window size is never scaled
+ //
+ Tcb->RcvWndScale = 0;
+
+ Tcb->ProbeTimerOn = FALSE;
+}
+
+/**
+ Initialize the peer related members.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seg Pointer to the segment that contains the peer's intial info.
+ @param[in] Opt Pointer to the options announced by the peer.
+
+**/
+VOID
+TcpInitTcbPeer (
+ IN OUT TCP_CB *Tcb,
+ IN TCP_SEG *Seg,
+ IN TCP_OPTION *Opt
+ )
+{
+ UINT16 RcvMss;
+
+ ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL));
+ ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = Tcb->SndWnd;
+ Tcb->SndWl1 = Seg->Seq;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ Tcb->SndWl2 = Seg->Ack;
+ } else {
+ Tcb->SndWl2 = Tcb->Iss + 1;
+ }
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {
+ Tcb->SndMss = (UINT16) MAX (64, Opt->Mss);
+
+ RcvMss = TcpGetRcvMss (Tcb->Sk);
+ if (Tcb->SndMss > RcvMss) {
+ Tcb->SndMss = RcvMss;
+ }
+
+ } else {
+ //
+ // One end doesn't support MSS option, use default.
+ //
+ Tcb->RcvMss = 536;
+ }
+
+ Tcb->CWnd = Tcb->SndMss;
+
+ Tcb->Irs = Seg->Seq;
+ Tcb->RcvNxt = Tcb->Irs + 1;
+
+ Tcb->RcvWl2 = Tcb->RcvNxt;
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {
+
+ Tcb->SndWndScale = Opt->WndScale;
+
+ Tcb->RcvWndScale = TcpComputeScale (Tcb);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);
+
+ } else {
+ //
+ // One end doesn't support window scale option. use zero.
+ //
+ Tcb->RcvWndScale = 0;
+ }
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);
+
+ //
+ // Compute the effective SndMss per RFC1122
+ // section 4.2.2.6. If timestamp option is
+ // enabled, it will always occupy 12 bytes.
+ //
+ Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;
+ }
+}
+
+/**
+ Check whether one IP address equals the other.
+
+ @param[in] Ip1 Pointer to IP address to be checked.
+ @param[in] Ip2 Pointer to IP address to be checked.
+ @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,
+ IP_VERSION_6 indicates the IP address is an IPv6 address.
+
+ @retval TRUE Ip1 equals Ip2.
+ @retval FALSE Ip1 does not equal Ip2.
+
+**/
+BOOLEAN
+TcpIsIpEqual (
+ IN EFI_IP_ADDRESS *Ip1,
+ IN EFI_IP_ADDRESS *Ip2,
+ IN UINT8 Version
+ )
+{
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));
+
+ if (Version == IP_VERSION_4) {
+ return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]);
+ } else {
+ return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6);
+ }
+}
+
+/**
+ Check whether one IP address is filled with ZERO.
+
+ @param[in] Ip Pointer to the IP address to be checked.
+ @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address,
+ IP_VERSION_6 indicates the IP address is an IPv6 address.
+
+ @retval TRUE Ip is all zero address.
+ @retval FALSE Ip is not all zero address.
+
+**/
+BOOLEAN
+TcpIsIpZero (
+ IN EFI_IP_ADDRESS *Ip,
+ IN UINT8 Version
+ )
+{
+ ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6));
+
+ if (Version == IP_VERSION_4) {
+ return (BOOLEAN) (Ip->Addr[0] == 0);
+ } else {
+ return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) &&
+ (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0));
+ }
+}
+
+/**
+ Locate a listen TCB that matchs the Local and Remote.
+
+ @param[in] Local Pointer to the local (IP, Port).
+ @param[in] Remote Pointer to the remote (IP, Port).
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
+ IP_VERSION_6 indicates TCP is running on IP6 stack.
+
+ @return Pointer to the TCP_CB with the least number of wildcards,
+ if NULL no match is found.
+
+**/
+TCP_CB *
+TcpLocateListenTcb (
+ IN TCP_PEER *Local,
+ IN TCP_PEER *Remote,
+ IN UINT8 Version
+ )
+{
+ LIST_ENTRY *Entry;
+ TCP_CB *Node;
+ TCP_CB *Match;
+ INTN Last;
+ INTN Cur;
+
+ Last = 4;
+ Match = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if ((Version != Node->Sk->IpVersion) ||
+ (Local->Port != Node->LocalEnd.Port) ||
+ !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) ||
+ !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version)
+ ) {
+
+ continue;
+ }
+
+ //
+ // Compute the number of wildcard
+ //
+ Cur = 0;
+ if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) {
+ Cur++;
+ }
+
+ if (Node->RemoteEnd.Port == 0) {
+ Cur++;
+ }
+
+ if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) {
+ Cur++;
+ }
+
+ if (Cur < Last) {
+ if (Cur == 0) {
+ return Node;
+ }
+
+ Last = Cur;
+ Match = Node;
+ }
+ }
+
+ return Match;
+}
+
+/**
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @param[in] Addr Pointer to the IP address needs to match.
+ @param[in] Port The port number needs to match.
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
+ IP_VERSION_6 indicates TCP is running on IP6 stack.
+
+
+ @retval TRUE The Tcb which matches the <Addr Port> pair exists.
+ @retval FALSE Otherwise
+
+**/
+BOOLEAN
+TcpFindTcbByPeer (
+ IN EFI_IP_ADDRESS *Addr,
+ IN TCP_PORTNO Port,
+ IN UINT8 Version
+ )
+{
+ TCP_PORTNO LocalPort;
+ LIST_ENTRY *Entry;
+ TCP_CB *Tcb;
+
+ ASSERT ((Addr != NULL) && (Port != 0));
+
+ LocalPort = HTONS (Port);
+
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if ((Version == Tcb->Sk->IpVersion) &&
+ TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&
+ (LocalPort == Tcb->LocalEnd.Port)
+ ) {
+
+ return TRUE;
+ }
+ }
+
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if ((Version == Tcb->Sk->IpVersion) &&
+ TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) &&
+ (LocalPort == Tcb->LocalEnd.Port)
+ ) {
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Locate the TCP_CB related to the socket pair.
+
+ @param[in] LocalPort The local port number.
+ @param[in] LocalIp The local IP address.
+ @param[in] RemotePort The remote port number.
+ @param[in] RemoteIp The remote IP address.
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
+ IP_VERSION_6 indicates TCP is running on IP6 stack.
+ @param[in] Syn If TRUE, the listen sockets are searched.
+
+ @return Pointer to the related TCP_CB. If NULL, no match is found.
+
+**/
+TCP_CB *
+TcpLocateTcb (
+ IN TCP_PORTNO LocalPort,
+ IN EFI_IP_ADDRESS *LocalIp,
+ IN TCP_PORTNO RemotePort,
+ IN EFI_IP_ADDRESS *RemoteIp,
+ IN UINT8 Version,
+ IN BOOLEAN Syn
+ )
+{
+ TCP_PEER Local;
+ TCP_PEER Remote;
+ LIST_ENTRY *Entry;
+ TCP_CB *Tcb;
+
+ Local.Port = LocalPort;
+ Remote.Port = RemotePort;
+
+ CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS));
+
+ //
+ // First check for exact match.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if ((Version == Tcb->Sk->IpVersion) &&
+ TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) &&
+ TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version)
+ ) {
+
+ RemoveEntryList (&Tcb->List);
+ InsertHeadList (&mTcpRunQue, &Tcb->List);
+
+ return Tcb;
+ }
+ }
+
+ //
+ // Only check the listen queue when the SYN flag is on.
+ //
+ if (Syn) {
+ return TcpLocateListenTcb (&Local, &Remote, Version);
+ }
+
+ return NULL;
+}
+
+/**
+ Insert a Tcb into the proper queue.
+
+ @param[in] Tcb Pointer to the TCP_CB to be inserted.
+
+ @retval 0 The Tcb was inserted successfully.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpInsertTcb (
+ IN TCP_CB *Tcb
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Head;
+ TCP_CB *Node;
+ TCP_PROTO_DATA *TcpProto;
+
+ ASSERT (
+ (Tcb != NULL) &&
+ (
+ (Tcb->State == TCP_LISTEN) ||
+ (Tcb->State == TCP_SYN_SENT) ||
+ (Tcb->State == TCP_SYN_RCVD) ||
+ (Tcb->State == TCP_CLOSED)
+ )
+ );
+
+ if (Tcb->LocalEnd.Port == 0) {
+ return -1;
+ }
+
+ Head = &mTcpRunQue;
+
+ if (Tcb->State == TCP_LISTEN) {
+ Head = &mTcpListenQue;
+ }
+
+ //
+ // Check that the Tcb isn't already on the list.
+ //
+ NET_LIST_FOR_EACH (Entry, Head) {
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd, Tcb->Sk->IpVersion) &&
+ TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion)
+ ) {
+
+ return -1;
+ }
+ }
+
+ InsertHeadList (Head, &Tcb->List);
+
+ TcpProto = (TCP_PROTO_DATA *) Tcb->Sk->ProtoReserved;
+ TcpSetVariableData (TcpProto->TcpService);
+
+ return 0;
+}
+
+/**
+ Clone a TCP_CB from Tcb.
+
+ @param[in] Tcb Pointer to the TCP_CB to be cloned.
+
+ @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred.
+
+**/
+TCP_CB *
+TcpCloneTcb (
+ IN TCP_CB *Tcb
+ )
+{
+ TCP_CB *Clone;
+
+ Clone = AllocateZeroPool (sizeof (TCP_CB));
+
+ if (Clone == NULL) {
+ return NULL;
+ }
+
+ CopyMem (Clone, Tcb, sizeof (TCP_CB));
+
+ //
+ // Increase the reference count of the shared IpInfo.
+ //
+ NET_GET_REF (Tcb->IpInfo);
+
+ InitializeListHead (&Clone->List);
+ InitializeListHead (&Clone->SndQue);
+ InitializeListHead (&Clone->RcvQue);
+
+ Clone->Sk = SockClone (Tcb->Sk);
+ if (Clone->Sk == NULL) {
+ DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n"));
+ FreePool (Clone);
+ return NULL;
+ }
+
+ ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;
+
+ return Clone;
+}
+
+/**
+ Compute an ISS to be used by a new connection.
+
+ @return The resulting ISS.
+
+**/
+TCP_SEQNO
+TcpGetIss (
+ VOID
+ )
+{
+ mTcpGlobalIss += TCP_ISS_INCREMENT_1;
+ return mTcpGlobalIss;
+}
+
+/**
+ Get the local mss.
+
+ @param[in] Sock Pointer to the socket to get mss.
+
+ @return The mss size.
+
+**/
+UINT16
+TcpGetRcvMss (
+ IN SOCKET *Sock
+ )
+{
+ EFI_IP4_MODE_DATA Ip4Mode;
+ EFI_IP6_MODE_DATA Ip6Mode;
+ EFI_IP4_PROTOCOL *Ip4;
+ EFI_IP6_PROTOCOL *Ip6;
+ TCP_PROTO_DATA *TcpProto;
+
+ ASSERT (Sock != NULL);
+
+ ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA));
+ ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA));
+
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+
+ if (Sock->IpVersion == IP_VERSION_4) {
+ Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4;
+ ASSERT (Ip4 != NULL);
+ Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL);
+
+ return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD));
+ } else {
+ Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6;
+ ASSERT (Ip6 != NULL);
+ Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL);
+
+ return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD));
+ }
+}
+
+/**
+ Set the Tcb's state.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] State The state to be set.
+
+**/
+VOID
+TcpSetState (
+ IN TCP_CB *Tcb,
+ IN UINT8 State
+ )
+{
+ ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));
+ ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *)));
+
+ DEBUG (
+ (EFI_D_INFO,
+ "Tcb (%p) state %s --> %s\n",
+ Tcb,
+ mTcpStateName[Tcb->State],
+ mTcpStateName[State])
+ );
+
+ Tcb->State = State;
+
+ switch (State) {
+ case TCP_ESTABLISHED:
+
+ SockConnEstablished (Tcb->Sk);
+
+ if (Tcb->Parent != NULL) {
+ //
+ // A new connection is accepted by a listening socket. Install
+ // the device path.
+ //
+ TcpInstallDevicePath (Tcb->Sk);
+ }
+
+ break;
+
+ case TCP_CLOSED:
+
+ SockConnClosed (Tcb->Sk);
+
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ Compute the TCP segment's checksum.
+
+ @param[in] Nbuf Pointer to the buffer that contains the TCP segment.
+ @param[in] HeadSum The checksum value of the fixed part of pseudo header.
+
+ @return The checksum value.
+
+**/
+UINT16
+TcpChecksum (
+ IN NET_BUF *Nbuf,
+ IN UINT16 HeadSum
+ )
+{
+ UINT16 Checksum;
+
+ Checksum = NetbufChecksum (Nbuf);
+ Checksum = NetAddChecksum (Checksum, HeadSum);
+
+ Checksum = NetAddChecksum (
+ Checksum,
+ HTONS ((UINT16) Nbuf->TotalSize)
+ );
+
+ return (UINT16) (~Checksum);
+}
+
+/**
+ Translate the information from the head of the received TCP
+ segment Nbuf contents and fill it into a TCP_SEG structure.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Nbuf Pointer to the buffer contains the TCP segment.
+
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.
+
+**/
+TCP_SEG *
+TcpFormatNetbuf (
+ IN TCP_CB *Tcb,
+ IN OUT NET_BUF *Nbuf
+ )
+{
+ TCP_SEG *Seg;
+ TCP_HEAD *Head;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (Head != NULL);
+
+ Nbuf->Tcp = Head;
+
+ Seg->Seq = NTOHL (Head->Seq);
+ Seg->Ack = NTOHL (Head->Ack);
+ Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));
+
+ Seg->Urg = NTOHS (Head->Urg);
+ Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale);
+ Seg->Flag = Head->Flag;
+
+ //
+ // SYN and FIN flag occupy one sequence space each.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ //
+ // RFC requires that the initial window not be scaled.
+ //
+ Seg->Wnd = NTOHS (Head->Wnd);
+ Seg->End++;
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ Seg->End++;
+ }
+
+ return Seg;
+}
+
+/**
+ Initialize an active connection.
+
+ @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a
+ connection.
+
+**/
+VOID
+TcpOnAppConnect (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ TcpInitTcbLocal (Tcb);
+ TcpSetState (Tcb, TCP_SYN_SENT);
+
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);
+ TcpToSendData (Tcb, 1);
+}
+
+/**
+ Initiate the connection close procedure, called when
+ applications want to close the connection.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppClose (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ ASSERT (Tcb != NULL);
+
+ if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpOnAppClose: connection reset because data is lost for TCB %p\n",
+ Tcb)
+ );
+
+ TcpResetConnection (Tcb);
+ TcpClose (Tcb);
+ return;
+ }
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ case TCP_LISTEN:
+ case TCP_SYN_SENT:
+ TcpSetState (Tcb, TCP_CLOSED);
+ break;
+
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+ TcpSetState (Tcb, TCP_FIN_WAIT_1);
+ break;
+
+ case TCP_CLOSE_WAIT:
+ TcpSetState (Tcb, TCP_LAST_ACK);
+ break;
+ default:
+ break;
+ }
+
+ TcpToSendData (Tcb, 1);
+}
+
+/**
+ Check whether the application's newly delivered data can be sent out.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 The data has been sent out successfully.
+ @retval -1 The Tcb is not in a state that data is permitted to
+ be sent out.
+
+**/
+INTN
+TcpOnAppSend (
+ IN OUT TCP_CB *Tcb
+ )
+{
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ return -1;
+
+ case TCP_LISTEN:
+ return -1;
+
+ case TCP_SYN_SENT:
+ case TCP_SYN_RCVD:
+ return 0;
+
+ case TCP_ESTABLISHED:
+ case TCP_CLOSE_WAIT:
+ TcpToSendData (Tcb, 0);
+ return 0;
+
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ return -1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ Application has consumed some data. Check whether
+ to send a window update ack or a delayed ack.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpOnAppConsume (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT32 TcpOld;
+
+ switch (Tcb->State) {
+ case TCP_ESTABLISHED:
+ TcpOld = TcpRcvWinOld (Tcb);
+ if (TcpRcvWinNow (Tcb) > TcpOld) {
+
+ if (TcpOld < Tcb->RcvMss) {
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",
+ Tcb)
+ );
+
+ TcpSendAck (Tcb);
+ } else if (Tcb->DelayedAck == 0) {
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n",
+ Tcb)
+ );
+
+ Tcb->DelayedAck = 1;
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ Abort the connection by sending a reset segment. Called
+ when the application wants to abort the connection.
+
+ @param[in] Tcb Pointer to the TCP_CB of the TCP instance.
+
+**/
+VOID
+TcpOnAppAbort (
+ IN TCP_CB *Tcb
+ )
+{
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpOnAppAbort: connection reset issued by application for TCB %p\n",
+ Tcb)
+ );
+
+ switch (Tcb->State) {
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSE_WAIT:
+ TcpResetConnection (Tcb);
+ break;
+ default:
+ break;
+ }
+
+ TcpSetState (Tcb, TCP_CLOSED);
+}
+
+/**
+ Reset the connection related with Tcb.
+
+ @param[in] Tcb Pointer to the TCP_CB of the connection to be reset.
+
+**/
+VOID
+TcpResetConnection (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_HEAD *Nhead;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return ;
+ }
+
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_TAIL
+ );
+
+ ASSERT (Nhead != NULL);
+
+ Nbuf->Tcp = Nhead;
+
+ Nhead->Flag = TCP_FLG_RST;
+ Nhead->Seq = HTONL (Tcb->SndNxt);
+ Nhead->Ack = HTONL (Tcb->RcvNxt);
+ Nhead->SrcPort = Tcb->LocalEnd.Port;
+ Nhead->DstPort = Tcb->RemoteEnd.Port;
+ Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);
+ Nhead->Res = 0;
+ Nhead->Wnd = HTONS (0xFFFF);
+ Nhead->Checksum = 0;
+ Nhead->Urg = 0;
+ Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);
+
+ TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);
+
+ NetbufFree (Nbuf);
+}
+
+/**
+ Set the Tcp variable data.
+
+ @param[in] TcpService Tcp service data.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+TcpSetVariableData (
+ IN TCP_SERVICE_DATA *TcpService
+ )
+{
+ EFI_GUID *ServiceBindingGuid;
+ UINT32 NumConfiguredInstance;
+ LIST_ENTRY *Entry;
+ TCP_CB *TcpPcb;
+ TCP_PROTO_DATA *TcpProto;
+ UINTN VariableDataSize;
+ EFI_TCP4_VARIABLE_DATA *Tcp4VariableData;
+ EFI_TCP4_SERVICE_POINT *Tcp4ServicePoint;
+ EFI_TCP6_VARIABLE_DATA *Tcp6VariableData;
+ EFI_TCP6_SERVICE_POINT *Tcp6ServicePoint;
+ VOID *VariableData;
+ CHAR16 *NewMacString;
+ EFI_STATUS Status;
+
+ if (TcpService->IpVersion == IP_VERSION_4) {
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ } else {
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ }
+
+ NumConfiguredInstance = 0;
+ Tcp4VariableData = NULL;
+ Tcp6VariableData = NULL;
+
+ //
+ // Go through the running queue to count the instances.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == TcpService) {
+ //
+ // This tcp instance belongs to the TcpService.
+ //
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Go through the listening queue to count the instances.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == TcpService) {
+ //
+ // This tcp instance belongs to the TcpService.
+ //
+ NumConfiguredInstance++;
+ }
+ }
+
+ Tcp4ServicePoint = NULL;
+ Tcp6ServicePoint = NULL;
+
+ //
+ // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,
+ // we should add extra buffers for the service points only if the number of configured
+ // children is more than one.
+ //
+ if (TcpService->IpVersion == IP_VERSION_4) {
+ VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);
+ }
+
+ Tcp4VariableData = AllocateZeroPool (VariableDataSize);
+ if (Tcp4VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Tcp4VariableData->DriverHandle = TcpService->DriverBindingHandle;
+ Tcp4VariableData->ServiceCount = NumConfiguredInstance;
+
+ Tcp4ServicePoint = &Tcp4VariableData->Services[0];
+ VariableData = Tcp4VariableData;
+ } else {
+ VariableDataSize = sizeof (EFI_TCP6_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_TCP6_SERVICE_POINT) * (NumConfiguredInstance - 1);
+ }
+
+ Tcp6VariableData = AllocateZeroPool (VariableDataSize);
+ if (Tcp6VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Tcp6VariableData->DriverHandle = TcpService->DriverBindingHandle;
+ Tcp6VariableData->ServiceCount = NumConfiguredInstance;
+
+ Tcp6ServicePoint = &Tcp6VariableData->Services[0];
+ VariableData = Tcp6VariableData;
+ }
+
+ //
+ // Go through the running queue to fill the service points.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == TcpService) {
+ //
+ // This tcp instance belongs to the TcpService.
+ //
+ if (TcpService->IpVersion == IP_VERSION_4) {
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
+ CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
+ CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
+
+ Tcp4ServicePoint++;
+ } else {
+ Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);
+ Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);
+ Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
+
+ Tcp6ServicePoint++;
+ }
+ }
+ }
+
+ //
+ // Go through the listening queue to fill the service points.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == TcpService) {
+ //
+ // This tcp instance belongs to the TcpService.
+ //
+ if (TcpService->IpVersion == IP_VERSION_4) {
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
+ CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
+ CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
+
+ Tcp4ServicePoint++;
+ } else {
+ Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip);
+ Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
+ IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip);
+ Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
+
+ Tcp6ServicePoint++;
+ }
+ }
+ }
+
+ //
+ // Get the mac string.
+ //
+ Status = NetLibGetMacString (
+ TcpService->ControllerHandle,
+ TcpService->DriverBindingHandle,
+ &NewMacString
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (TcpService->MacString != NULL) {
+ //
+ // The variable is set already. We're going to update it.
+ //
+ if (StrCmp (TcpService->MacString, NewMacString) != 0) {
+ //
+ // The mac address is changed. Delete the previous variable first.
+ //
+ gRT->SetVariable (
+ TcpService->MacString,
+ ServiceBindingGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+ }
+
+ FreePool (TcpService->MacString);
+ }
+
+ TcpService->MacString = NewMacString;
+
+ Status = gRT->SetVariable (
+ TcpService->MacString,
+ ServiceBindingGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableDataSize,
+ VariableData
+ );
+
+ON_ERROR:
+
+ FreePool (VariableData);
+
+ return Status;
+}
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in] TcpService Tcp service data.
+
+**/
+VOID
+TcpClearVariableData (
+ IN TCP_SERVICE_DATA *TcpService
+ )
+{
+ EFI_GUID *ServiceBindingGuid;
+
+ ASSERT (TcpService->MacString != NULL);
+
+ if (TcpService->IpVersion == IP_VERSION_4) {
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ } else {
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ }
+
+ gRT->SetVariable (
+ TcpService->MacString,
+ ServiceBindingGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+
+ FreePool (TcpService->MacString);
+ TcpService->MacString = NULL;
+}
+
+/**
+ Install the device path protocol on the TCP instance.
+
+ @param[in] Sock Pointer to the socket representing the TCP instance.
+
+ @retval EFI_SUCCESS The device path protocol was installed.
+ @retval other Failed to install the device path protocol.
+
+**/
+EFI_STATUS
+TcpInstallDevicePath (
+ IN SOCKET *Sock
+ )
+{
+ TCP_PROTO_DATA *TcpProto;
+ TCP_SERVICE_DATA *TcpService;
+ TCP_CB *Tcb;
+ IPv4_DEVICE_PATH Ip4DPathNode;
+ IPv6_DEVICE_PATH Ip6DPathNode;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS Status;
+ TCP_PORTNO LocalPort;
+ TCP_PORTNO RemotePort;
+
+ TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+ TcpService = TcpProto->TcpService;
+ Tcb = TcpProto->TcpPcb;
+
+ LocalPort = NTOHS (Tcb->LocalEnd.Port);
+ RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ if (Sock->IpVersion == IP_VERSION_4) {
+ NetLibCreateIPv4DPathNode (
+ &Ip4DPathNode,
+ TcpService->ControllerHandle,
+ Tcb->LocalEnd.Ip.Addr[0],
+ LocalPort,
+ Tcb->RemoteEnd.Ip.Addr[0],
+ RemotePort,
+ EFI_IP_PROTO_TCP,
+ Tcb->UseDefaultAddr
+ );
+
+ DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode;
+ } else {
+ NetLibCreateIPv6DPathNode (
+ &Ip6DPathNode,
+ TcpService->ControllerHandle,
+ &Tcb->LocalEnd.Ip.v6,
+ LocalPort,
+ &Tcb->RemoteEnd.Ip.v6,
+ RemotePort,
+ EFI_IP_PROTO_TCP
+ );
+
+ DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode;
+ }
+
+ Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath);
+ if (Sock->DevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->InstallProtocolInterface (
+ &Sock->SockHandle,
+ &gEfiDevicePathProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ Sock->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Sock->DevicePath);
+ Sock->DevicePath = NULL;
+ }
+
+ return Status;
+}
+
diff --git a/NetworkPkg/TcpDxe/TcpOption.c b/NetworkPkg/TcpDxe/TcpOption.c
new file mode 100644
index 0000000000..bacce1070d
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpOption.c
@@ -0,0 +1,374 @@
+/** @file
+ Routines to process TCP option.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+/**
+ Get a UINT16 value from buffer.
+
+ @param[in] Buf Pointer to input buffer.
+
+ @return The UINT16 value obtained from the buffer.
+
+**/
+UINT16
+TcpGetUint16 (
+ IN UINT8 *Buf
+ )
+{
+ UINT16 Value;
+ CopyMem (&Value, Buf, sizeof (UINT16));
+ return NTOHS (Value);
+}
+
+/**
+ Get a UINT32 value from buffer.
+
+ @param[in] Buf Pointer to input buffer.
+
+ @return The UINT32 value obtained from the buffer.
+
+**/
+UINT32
+TcpGetUint32 (
+ IN UINT8 *Buf
+ )
+{
+ UINT32 Value;
+ CopyMem (&Value, Buf, sizeof (UINT32));
+ return NTOHL (Value);
+}
+
+/**
+ Put a UINT32 value in buffer.
+
+ @param[out] Buf Pointer to the buffer.
+ @param[in] Data The UINT32 Date to put in the buffer.
+
+**/
+VOID
+TcpPutUint32 (
+ OUT UINT8 *Buf,
+ IN UINT32 Data
+ )
+{
+ Data = HTONL (Data);
+ CopyMem (Buf, &Data, sizeof (UINT32));
+}
+
+/**
+ Compute the window scale value according to the given buffer size.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The scale value.
+
+**/
+UINT8
+TcpComputeScale (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT8 Scale;
+ UINT32 BufSize;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ BufSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+
+ Scale = 0;
+ while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) {
+
+ Scale++;
+ }
+
+ return Scale;
+}
+
+/**
+ Build the TCP option in three-way handshake.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpSynBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ UINT8 *Data;
+ UINT16 Len;
+
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
+
+ Len = 0;
+
+ //
+ // Add a timestamp option if not disabled by the application
+ // and it is the first SYN segment, or the peer has sent
+ // us its timestamp.
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) &&
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))
+ ) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_TS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data != NULL);
+ Len += TCP_OPTION_TS_ALIGNED_LEN;
+
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
+ TcpPutUint32 (Data + 4, mTcpTick);
+ TcpPutUint32 (Data + 8, 0);
+ }
+
+ //
+ // Build window scale option, only when configured
+ // to send WS option, and either we are doing active
+ // open or we have received WS option from peer.
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) &&
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))
+ ) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_WS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data != NULL);
+
+ Len += TCP_OPTION_WS_ALIGNED_LEN;
+ TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb));
+ }
+
+ //
+ // Build the MSS option.
+ //
+ Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1);
+ ASSERT (Data != NULL);
+
+ Len += TCP_OPTION_MSS_LEN;
+ TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss);
+
+ return Len;
+}
+
+/**
+ Build the TCP option in synchronized states.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ UINT8 *Data;
+ UINT16 Len;
+
+ ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));
+ Len = 0;
+
+ //
+ // Build the Timestamp option.
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) &&
+ !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)
+ ) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_TS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data != NULL);
+ Len += TCP_OPTION_TS_ALIGNED_LEN;
+
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
+ TcpPutUint32 (Data + 4, mTcpTick);
+ TcpPutUint32 (Data + 8, Tcb->TsRecent);
+ }
+
+ return Len;
+}
+
+/**
+ Parse the supported options.
+
+ @param[in] Tcp Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Option Pointer to the TCP_OPTION used to store the
+ successfully pasrsed options.
+
+ @retval 0 The options are successfully pasrsed.
+ @retval -1 Ilegal option was found.
+
+**/
+INTN
+TcpParseOption (
+ IN TCP_HEAD *Tcp,
+ IN OUT TCP_OPTION *Option
+ )
+{
+ UINT8 *Head;
+ UINT8 TotalLen;
+ UINT8 Cur;
+ UINT8 Type;
+ UINT8 Len;
+
+ ASSERT ((Tcp != NULL) && (Option != NULL));
+
+ Option->Flag = 0;
+
+ TotalLen = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD));
+ if (TotalLen <= 0) {
+ return 0;
+ }
+
+ Head = (UINT8 *) (Tcp + 1);
+
+ //
+ // Fast process of the timestamp option.
+ //
+ if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) {
+
+ Option->TSVal = TcpGetUint32 (Head + 4);
+ Option->TSEcr = TcpGetUint32 (Head + 8);
+ Option->Flag = TCP_OPTION_RCVD_TS;
+
+ return 0;
+ }
+ //
+ // Slow path to process the options.
+ //
+ Cur = 0;
+
+ while (Cur < TotalLen) {
+ Type = Head[Cur];
+
+ switch (Type) {
+ case TCP_OPTION_MSS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) {
+
+ return -1;
+ }
+
+ Option->Mss = TcpGetUint16 (&Head[Cur + 2]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS);
+
+ Cur += TCP_OPTION_MSS_LEN;
+ break;
+
+ case TCP_OPTION_WS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) {
+
+ return -1;
+ }
+
+ Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS);
+
+ Cur += TCP_OPTION_WS_LEN;
+ break;
+
+ case TCP_OPTION_TS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) {
+
+ return -1;
+ }
+
+ Option->TSVal = TcpGetUint32 (&Head[Cur + 2]);
+ Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS);
+
+ Cur += TCP_OPTION_TS_LEN;
+ break;
+
+ case TCP_OPTION_NOP:
+ Cur++;
+ break;
+
+ case TCP_OPTION_EOP:
+ Cur = TotalLen;
+ break;
+
+ default:
+ Len = Head[Cur + 1];
+
+ if ((TotalLen - Cur) < Len || Len < 2) {
+ return -1;
+ }
+
+ Cur = (UINT8) (Cur + Len);
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+/**
+ Check the segment against PAWS.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] TSVal The timestamp value.
+
+ @retval 1 The segment passed the PAWS check.
+ @retval 0 The segment failed to pass the PAWS check.
+
+**/
+UINT32
+TcpPawsOK (
+ IN TCP_CB *Tcb,
+ IN UINT32 TSVal
+ )
+{
+ //
+ // PAWS as defined in RFC1323, buggy...
+ //
+ if (TCP_TIME_LT (TSVal, Tcb->TsRecent) &&
+ TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)
+ ) {
+
+ return 0;
+
+ }
+
+ return 1;
+}
diff --git a/NetworkPkg/TcpDxe/TcpOption.h b/NetworkPkg/TcpDxe/TcpOption.h
new file mode 100644
index 0000000000..0ccadb9536
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpOption.h
@@ -0,0 +1,145 @@
+/** @file
+ Tcp option's routine header file.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP_OPTION_H_
+#define _TCP_OPTION_H_
+
+//
+// Supported TCP option types and their length.
+//
+#define TCP_OPTION_EOP 0 ///< End Of oPtion
+#define TCP_OPTION_NOP 1 ///< No-Option.
+#define TCP_OPTION_MSS 2 ///< Maximum Segment Size
+#define TCP_OPTION_WS 3 ///< Window scale
+#define TCP_OPTION_TS 8 ///< Timestamp
+#define TCP_OPTION_MSS_LEN 4 ///< Length of MSS option
+#define TCP_OPTION_WS_LEN 3 ///< Length of window scale option
+#define TCP_OPTION_TS_LEN 10 ///< Length of timestamp option
+#define TCP_OPTION_WS_ALIGNED_LEN 4 ///< Length of window scale option, aligned
+#define TCP_OPTION_TS_ALIGNED_LEN 12 ///< Length of timestamp option, aligned
+
+//
+// recommend format of timestamp window scale
+// option for fast process.
+//
+#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \
+ (TCP_OPTION_NOP << 16) | \
+ (TCP_OPTION_TS << 8) | \
+ (TCP_OPTION_TS_LEN))
+
+#define TCP_OPTION_WS_FAST ((TCP_OPTION_NOP << 24) | \
+ (TCP_OPTION_WS << 16) | \
+ (TCP_OPTION_WS_LEN << 8))
+
+#define TCP_OPTION_MSS_FAST ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16))
+
+//
+// Other misc definations
+//
+#define TCP_OPTION_RCVD_MSS 0x01
+#define TCP_OPTION_RCVD_WS 0x02
+#define TCP_OPTION_RCVD_TS 0x04
+#define TCP_OPTION_MAX_WS 14 ///< Maxium window scale value
+#define TCP_OPTION_MAX_WIN 0xffff ///< Max window size in TCP header
+
+///
+/// The structure to store the parse option value.
+/// ParseOption only parses the options, doesn't process them.
+///
+typedef struct _TCP_OPTION {
+ UINT8 Flag; ///< Flag such as TCP_OPTION_RCVD_MSS
+ UINT8 WndScale; ///< The WndScale received
+ UINT16 Mss; ///< The Mss received
+ UINT32 TSVal; ///< The TSVal field in a timestamp option
+ UINT32 TSEcr; ///< The TSEcr field in a timestamp option
+} TCP_OPTION;
+
+/**
+ Compute the window scale value according to the given buffer size.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The scale value.
+
+**/
+UINT8
+TcpComputeScale (
+ IN TCP_CB *Tcb
+ );
+
+/**
+ Build the TCP option in three-way handshake.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpSynBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+/**
+ Build the TCP option in synchronized states.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+/**
+ Parse the supported options.
+
+ @param[in] Tcp Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Option Pointer to the TCP_OPTION used to store the
+ successfully pasrsed options.
+
+ @retval 0 The options successfully pasrsed.
+ @retval -1 Ilegal option was found.
+
+**/
+INTN
+TcpParseOption (
+ IN TCP_HEAD *Tcp,
+ IN OUT TCP_OPTION *Option
+ );
+
+/**
+ Check the segment against PAWS.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] TSVal The timestamp value.
+
+ @retval 1 The segment passed the PAWS check.
+ @retval 0 The segment failed to pass the PAWS check.
+
+**/
+UINT32
+TcpPawsOK (
+ IN TCP_CB *Tcb,
+ IN UINT32 TSVal
+ );
+
+#endif
diff --git a/NetworkPkg/TcpDxe/TcpOutput.c b/NetworkPkg/TcpDxe/TcpOutput.c
new file mode 100644
index 0000000000..c038213484
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpOutput.c
@@ -0,0 +1,1219 @@
+/** @file
+ TCP output process routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+UINT8 mTcpOutFlag[] = {
+ 0, // TCP_CLOSED
+ 0, // TCP_LISTEN
+ TCP_FLG_SYN, // TCP_SYN_SENT
+ TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD
+ TCP_FLG_ACK, // TCP_ESTABLISHED
+ TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1
+ TCP_FLG_ACK, // TCP_FIN_WAIT_2
+ TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING
+ TCP_FLG_ACK, // TCP_TIME_WAIT
+ TCP_FLG_ACK, // TCP_CLOSE_WAIT
+ TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK
+};
+
+/**
+ Compute the sequence space left in the old receive window.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence space left in the old receive window.
+
+**/
+UINT32
+TcpRcvWinOld (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT32 OldWin;
+
+ OldWin = 0;
+
+ if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {
+
+ OldWin = TCP_SUB_SEQ (
+ Tcb->RcvWl2 + Tcb->RcvWnd,
+ Tcb->RcvNxt
+ );
+ }
+
+ return OldWin;
+}
+
+/**
+ Compute the current receive window.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The size of the current receive window, in bytes.
+
+**/
+UINT32
+TcpRcvWinNow (
+ IN TCP_CB *Tcb
+ )
+{
+ SOCKET *Sk;
+ UINT32 Win;
+ UINT32 Increase;
+ UINT32 OldWin;
+
+ Sk = Tcb->Sk;
+ ASSERT (Sk != NULL);
+
+ OldWin = TcpRcvWinOld (Tcb);
+
+ Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF);
+
+ Increase = 0;
+ if (Win > OldWin) {
+ Increase = Win - OldWin;
+ }
+
+ //
+ // Receiver's SWS: don't advertise a bigger window
+ // unless it can be increased by at least one Mss or
+ // half of the receive buffer.
+ //
+ if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {
+
+ return Win;
+ }
+
+ return OldWin;
+}
+
+/**
+ Compute the value to fill in the window size field of the outgoing segment.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Syn The flag to indicate whether the outgoing segment
+ is a SYN segment.
+
+ @return The value of the local receive window size used to fill the outgoing segment.
+
+**/
+UINT16
+TcpComputeWnd (
+ IN OUT TCP_CB *Tcb,
+ IN BOOLEAN Syn
+ )
+{
+ UINT32 Wnd;
+
+ //
+ // RFC requires that initial window not be scaled
+ //
+ if (Syn) {
+
+ Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);
+ } else {
+
+ Wnd = TcpRcvWinNow (Tcb);
+
+ Tcb->RcvWnd = Wnd;
+ }
+
+ Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);
+ return NTOHS ((UINT16) Wnd);
+}
+
+/**
+ Get the maximum SndNxt.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence number of the maximum SndNxt.
+
+**/
+TCP_SEQNO
+TcpGetMaxSndNxt (
+ IN TCP_CB *Tcb
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+
+ if (IsListEmpty (&Tcb->SndQue)) {
+ return Tcb->SndNxt;
+ }
+
+ Entry = Tcb->SndQue.BackLink;
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));
+ return TCPSEG_NETBUF (Nbuf)->End;
+}
+
+/**
+ Compute how much data to send.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Force If TRUE, to ignore the sender's SWS avoidance algorithm and send
+ out data by force.
+
+ @return The length of the data can be sent. If 0, no data can be sent.
+
+**/
+UINT32
+TcpDataToSend (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ )
+{
+ SOCKET *Sk;
+ UINT32 Win;
+ UINT32 Len;
+ UINT32 Left;
+ UINT32 Limit;
+
+ Sk = Tcb->Sk;
+ ASSERT (Sk != NULL);
+
+ //
+ // TCP should NOT send data beyond the send window
+ // and congestion window. The right edge of send
+ // window is defined as SND.WL2 + SND.WND. The right
+ // edge of congestion window is defined as SND.UNA +
+ // CWND.
+ //
+ Win = 0;
+ Limit = Tcb->SndWl2 + Tcb->SndWnd;
+
+ if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {
+
+ Limit = Tcb->SndUna + Tcb->CWnd;
+ }
+
+ if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {
+ Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);
+ }
+
+ //
+ // The data to send contains two parts: the data on the
+ // socket send queue, and the data on the TCB's send
+ // buffer. The later can be non-zero if the peer shrinks
+ // its advertised window.
+ //
+ Left = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);
+
+ Len = MIN (Win, Left);
+
+ if (Len > Tcb->SndMss) {
+ Len = Tcb->SndMss;
+ }
+
+ if ((Force != 0)|| (Len == 0 && Left == 0)) {
+ return Len;
+ }
+
+ if (Len == 0 && Left != 0) {
+ goto SetPersistTimer;
+ }
+
+ //
+ // Sender's SWS avoidance: Don't send a small segment unless
+ // a)A full-sized segment can be sent,
+ // b)At least one-half of the maximum sized windows that
+ // the other end has ever advertised.
+ // c)It can send everything it has, and either it isn't
+ // expecting an ACK, or the Nagle algorithm is disabled.
+ //
+ if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {
+
+ return Len;
+ }
+
+ if ((Len == Left) &&
+ ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))
+ ) {
+
+ return Len;
+ }
+
+ //
+ // RFC1122 suggests to set a timer when SWSA forbids TCP
+ // sending more data, and combines it with a probe timer.
+ //
+SetPersistTimer:
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpDataToSend: enter persistent state for TCB %p\n",
+ Tcb)
+ );
+
+ if (!Tcb->ProbeTimerOn) {
+ TcpSetProbeTimer (Tcb);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ Build the TCP header of the TCP segment and transmit the segment by IP.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Nbuf Pointer to the buffer containing the segment to be
+ sent out.
+
+ @retval 0 The segment was sent out successfully.
+ @retval -1 An error condition occurred.
+
+**/
+INTN
+TcpTransmitSegment (
+ IN OUT TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ UINT16 Len;
+ TCP_HEAD *Head;
+ TCP_SEG *Seg;
+ BOOLEAN Syn;
+ UINT32 DataLen;
+
+ ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0));
+
+ DataLen = Nbuf->TotalSize;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);
+
+ if (Syn) {
+
+ Len = TcpSynBuildOption (Tcb, Nbuf);
+ } else {
+
+ Len = TcpBuildOption (Tcb, Nbuf);
+ }
+
+ ASSERT ((Len % 4 == 0) && (Len <= 40));
+
+ Len += sizeof (TCP_HEAD);
+
+ Head = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Head != NULL);
+
+ Nbuf->Tcp = Head;
+
+ Head->SrcPort = Tcb->LocalEnd.Port;
+ Head->DstPort = Tcb->RemoteEnd.Port;
+ Head->Seq = NTOHL (Seg->Seq);
+ Head->Ack = NTOHL (Tcb->RcvNxt);
+ Head->HeadLen = (UINT8) (Len >> 2);
+ Head->Res = 0;
+ Head->Wnd = TcpComputeWnd (Tcb, Syn);
+ Head->Checksum = 0;
+
+ //
+ // Check whether to set the PSH flag.
+ //
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);
+
+ if (DataLen != 0) {
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&
+ TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)
+ ) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
+
+ } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
+ }
+ }
+
+ //
+ // Check whether to set the URG flag and the urgent pointer.
+ //
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);
+
+ if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {
+
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);
+ } else {
+
+ Seg->Urg = (UINT16) MIN (
+ TCP_SUB_SEQ (Tcb->SndUp,
+ Seg->Seq),
+ 0xffff
+ );
+ }
+ }
+
+ Head->Flag = Seg->Flag;
+ Head->Urg = NTOHS (Seg->Urg);
+ Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);
+
+ //
+ // Update the TCP session's control information.
+ //
+ Tcb->RcvWl2 = Tcb->RcvNxt;
+ if (Syn) {
+ Tcb->RcvWnd = NTOHS (Head->Wnd);
+ }
+
+ //
+ // Clear the delayedack flag.
+ //
+ Tcb->DelayedAck = 0;
+
+ return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);
+}
+
+/**
+ Get a segment from the Tcb's SndQue.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seq The sequence number of the segment.
+ @param[in] Len The maximum length of the segment.
+
+ @return Pointer to the segment. If NULL, some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *Head;
+ LIST_ENTRY *Cur;
+ NET_BUF *Node;
+ TCP_SEG *Seg;
+ NET_BUF *Nbuf;
+ TCP_SEQNO End;
+ UINT8 *Data;
+ UINT8 Flag;
+ INT32 Offset;
+ INT32 CopyLen;
+
+ ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));
+
+ //
+ // Find the segment that contains the Seq.
+ //
+ Head = &Tcb->SndQue;
+
+ Node = NULL;
+ Seg = NULL;
+
+ NET_LIST_FOR_EACH (Cur, Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Node);
+
+ if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {
+
+ break;
+ }
+ }
+
+ if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) {
+ return NULL;
+ }
+
+ //
+ // Return the buffer if it can be returned without
+ // adjustment:
+ //
+ if ((Seg->Seq == Seq) &&
+ TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&
+ !NET_BUF_SHARED (Node)
+ ) {
+
+ NET_GET_REF (Node);
+ return Node;
+ }
+
+ //
+ // Create a new buffer and copy data there.
+ //
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ Flag = Seg->Flag;
+ End = Seg->End;
+
+ if (TCP_SEQ_LT (Seq + Len, Seg->End)) {
+ End = Seq + Len;
+ }
+
+ CopyLen = TCP_SUB_SEQ (End, Seq);
+ Offset = TCP_SUB_SEQ (Seq, Seg->Seq);
+
+ //
+ // If SYN is set and out of the range, clear the flag.
+ // Becuase the sequence of the first byte is SEG.SEQ+1,
+ // adjust Offset by -1. If SYN is in the range, copy
+ // one byte less.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ if (TCP_SEQ_LT (Seg->Seq, Seq)) {
+
+ TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);
+ Offset--;
+ } else {
+
+ CopyLen--;
+ }
+ }
+
+ //
+ // If FIN is set and in the range, copy one byte less,
+ // and if it is out of the range, clear the flag.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+
+ if (Seg->End == End) {
+
+ CopyLen--;
+ } else {
+
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
+ }
+ }
+
+ ASSERT (CopyLen >= 0);
+
+ //
+ // Copy data to the segment
+ //
+ if (CopyLen != 0) {
+ Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);
+ ASSERT (Data != NULL);
+
+ if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {
+ goto OnError;
+ }
+ }
+
+ CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));
+
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;
+ TCPSEG_NETBUF (Nbuf)->End = End;
+ TCPSEG_NETBUF (Nbuf)->Flag = Flag;
+
+ return Nbuf;
+
+OnError:
+ NetbufFree (Nbuf);
+ return NULL;
+}
+
+/**
+ Get a segment from the Tcb's socket buffer.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seq The sequence number of the segment.
+ @param[in] Len The maximum length of the segment.
+
+ @return Pointer to the segment. If NULL, some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSock (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+ UINT8 *Data;
+ UINT32 DataGet;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));
+
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n",
+ Tcb)
+ );
+
+ return NULL;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ DataGet = 0;
+
+ if (Len != 0) {
+ //
+ // copy data to the segment.
+ //
+ Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);
+ ASSERT (Data != NULL);
+
+ DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);
+ }
+
+ NET_GET_REF (Nbuf);
+
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;
+ TCPSEG_NETBUF (Nbuf)->End = Seq + Len;
+
+ InsertTailList (&(Tcb->SndQue), &(Nbuf->List));
+
+ if (DataGet != 0) {
+
+ SockDataSent (Tcb->Sk, DataGet);
+ }
+
+ return Nbuf;
+}
+
+/**
+ Get a segment starting from sequence Seq of a maximum
+ length of Len.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seq The sequence number of the segment.
+ @param[in] Len The maximum length of the segment.
+
+ @return Pointer to the segment. If NULL, some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegment (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+
+ ASSERT (Tcb != NULL);
+
+ //
+ // Compare the SndNxt with the max sequence number sent.
+ //
+ if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {
+
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
+ } else {
+
+ Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+ return Nbuf;
+}
+
+/**
+ Retransmit the segment from sequence Seq.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Seq The sequence number of the segment to be retransmitted.
+
+ @retval 0 Retransmission succeeded.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpRetransmit (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq
+ )
+{
+ NET_BUF *Nbuf;
+ UINT32 Len;
+
+ //
+ // Compute the maxium length of retransmission. It is
+ // limited by three factors:
+ // 1. Less than SndMss
+ // 2. Must in the current send window
+ // 3. Will not change the boundaries of queued segments.
+ //
+ if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",
+ Tcb)
+ );
+
+ return 0;
+ }
+
+ Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);
+ Len = MIN (Len, Tcb->SndMss);
+
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
+ goto OnError;
+ }
+
+ //
+ // The retransmitted buffer may be on the SndQue,
+ // trim TCP head because all the buffers on SndQue
+ // are headless.
+ //
+ ASSERT (Nbuf->Tcp != NULL);
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ NetbufFree (Nbuf);
+ return 0;
+
+OnError:
+ if (Nbuf != NULL) {
+ NetbufFree (Nbuf);
+ }
+
+ return -1;
+}
+
+/**
+ Verify that all the segments in SndQue are in good shape.
+
+ @param[in] Head Pointer to the head node of the SndQue.
+
+ @retval 0 At least one segment is broken.
+ @retval 1 All segments in the specific queue are in good shape.
+
+**/
+INTN
+TcpCheckSndQue (
+ IN LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ TCP_SEQNO Seq;
+
+ if (IsListEmpty (Head)) {
+ return 1;
+ }
+ //
+ // Initialize the Seq.
+ //
+ Entry = Head->ForwardLink;
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Seq = TCPSEG_NETBUF (Nbuf)->Seq;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (TcpVerifySegment (Nbuf) == 0) {
+ return 0;
+ }
+
+ //
+ // All the node in the SndQue should has:
+ // SEG.SEQ = LAST_SEG.END
+ //
+ if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {
+ return 0;
+ }
+
+ Seq = TCPSEG_NETBUF (Nbuf)->End;
+ }
+
+ return 1;
+}
+
+/**
+ Check whether to send data/SYN/FIN and piggyback an ACK.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm
+ and send out data by force.
+
+ @return The number of bytes sent.
+
+**/
+INTN
+TcpToSendData (
+ IN OUT TCP_CB *Tcb,
+ IN INTN Force
+ )
+{
+ UINT32 Len;
+ INTN Sent;
+ UINT8 Flag;
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+ TCP_SEQNO Seq;
+ TCP_SEQNO End;
+
+ ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));
+
+ Sent = 0;
+
+ if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {
+
+ return 0;
+ }
+
+ do {
+ //
+ // Compute how much data can be sent
+ //
+ Len = TcpDataToSend (Tcb, Force);
+ Seq = Tcb->SndNxt;
+
+ ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0])));
+ Flag = mTcpOutFlag[Tcb->State];
+
+ if ((Flag & TCP_FLG_SYN) != 0) {
+
+ Seq = Tcb->Iss;
+ Len = 0;
+ }
+
+ //
+ // Only send a segment without data if SYN or
+ // FIN is set.
+ //
+ if ((Len == 0) && ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {
+ return Sent;
+ }
+
+ Nbuf = TcpGetSegment (Tcb, Seq, Len);
+
+ if (Nbuf == NULL) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpToSendData: failed to get a segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto OnError;
+ }
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ //
+ // Set the TcpSeg in Nbuf.
+ //
+ Len = Nbuf->TotalSize;
+ End = Seq + Len;
+ if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {
+ End++;
+ }
+
+ if ((Flag & TCP_FLG_FIN) != 0) {
+ //
+ // Send FIN if all data is sent, and FIN is
+ // in the window
+ //
+ if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&
+ (GET_SND_DATASIZE (Tcb->Sk) == 0) &&
+ TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)
+ ) {
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpToSendData: send FIN to peer for TCB %p in state %s\n",
+ Tcb,
+ mTcpStateName[Tcb->State])
+ );
+
+ End++;
+ } else {
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
+ }
+ }
+
+ Seg->Seq = Seq;
+ Seg->End = End;
+ Seg->Flag = Flag;
+
+ ASSERT (TcpVerifySegment (Nbuf) != 0);
+ ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);
+
+ //
+ // Don't send an empty segment here.
+ //
+ if (Seg->End == Seg->Seq) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpToSendData: created a empty segment for TCB %p, free it now\n",
+ Tcb)
+ );
+
+ NetbufFree (Nbuf);
+ return Sent;
+ }
+
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ if ((Flag & TCP_FLG_FIN) != 0) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
+ }
+
+ goto OnError;
+ }
+
+ Sent += TCP_SUB_SEQ (End, Seq);
+
+ //
+ // All the buffers in the SndQue are headless.
+ //
+ ASSERT (Nbuf->Tcp != NULL);
+
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ NetbufFree (Nbuf);
+
+ //
+ // Update the status in TCB.
+ //
+ Tcb->DelayedAck = 0;
+
+ if ((Flag & TCP_FLG_FIN) != 0) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
+ }
+
+ if (TCP_SEQ_GT (End, Tcb->SndNxt)) {
+ Tcb->SndNxt = End;
+ }
+
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+ }
+
+ //
+ // Enable RTT measurement only if not in retransmit.
+ // Karn's algorithm requires not to update RTT when in loss.
+ //
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpToSendData: set RTT measure sequence %d for TCB %p\n",
+ Seq,
+ Tcb)
+ );
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ Tcb->RttSeq = Seq;
+ Tcb->RttMeasure = 0;
+ }
+
+ } while (Len == Tcb->SndMss);
+
+ return Sent;
+
+OnError:
+ if (Nbuf != NULL) {
+ NetbufFree (Nbuf);
+ }
+
+ return Sent;
+}
+
+/**
+ Send an ACK immediately.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSendAck (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Seg->Seq = Tcb->SndNxt;
+ Seg->End = Tcb->SndNxt;
+ Seg->Flag = TCP_FLG_ACK;
+
+ if (TcpTransmitSegment (Tcb, Nbuf) == 0) {
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ Tcb->DelayedAck = 0;
+ }
+
+ NetbufFree (Nbuf);
+}
+
+/**
+ Send a zero probe segment. It can be used by keepalive and zero window probe.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 The zero probe segment was sent out successfully.
+ @retval other An error condition occurred.
+
+**/
+INTN
+TcpSendZeroProbe (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+ INTN Result;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ //
+ // SndNxt-1 is out of window. The peer should respond
+ // with an ACK.
+ //
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Seg->Seq = Tcb->SndNxt - 1;
+ Seg->End = Tcb->SndNxt - 1;
+ Seg->Flag = TCP_FLG_ACK;
+
+ Result = TcpTransmitSegment (Tcb, Nbuf);
+ NetbufFree (Nbuf);
+
+ return Result;
+}
+
+/**
+ Check whether to send an ACK or delayed ACK.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpToSendAck (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ UINT32 TcpNow;
+
+ //
+ // Generally, TCP should send a delayed ACK unless:
+ // 1. ACK at least every other FULL sized segment received.
+ // 2. Packets received out of order.
+ // 3. Receiving window is open.
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Tcb->DelayedAck >= 1)) {
+ TcpSendAck (Tcb);
+ return;
+ }
+
+ TcpNow = TcpRcvWinNow (Tcb);
+
+ if (TcpNow > TcpRcvWinOld (Tcb)) {
+ TcpSendAck (Tcb);
+ return;
+ }
+
+ DEBUG (
+ (EFI_D_INFO,
+ "TcpToSendAck: scheduled a delayed ACK for TCB %p\n",
+ Tcb)
+ );
+
+ //
+ // Schedule a delayed ACK.
+ //
+ Tcb->DelayedAck++;
+}
+
+/**
+ Send a RESET segment in response to the segment received.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance. May be NULL.
+ @param[in] Head TCP header of the segment that triggers the reset.
+ @param[in] Len Length of the segment that triggers the reset.
+ @param[in] Local Local IP address.
+ @param[in] Remote Remote peer's IP address.
+ @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,
+ IP_VERSION_6 indicates TCP is running on IP6 stack.
+
+ @retval 0 A reset was sent or there is no need to send it.
+ @retval -1 No reset is sent.
+
+**/
+INTN
+TcpSendReset (
+ IN TCP_CB *Tcb,
+ IN TCP_HEAD *Head,
+ IN INT32 Len,
+ IN EFI_IP_ADDRESS *Local,
+ IN EFI_IP_ADDRESS *Remote,
+ IN UINT8 Version
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_HEAD *Nhead;
+ UINT16 HeadSum;
+
+ //
+ // Don't respond to a Reset with reset.
+ //
+ if ((Head->Flag & TCP_FLG_RST) != 0) {
+ return 0;
+ }
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_TAIL
+ );
+
+ ASSERT (Nhead != NULL);
+
+ Nbuf->Tcp = Nhead;
+ Nhead->Flag = TCP_FLG_RST;
+
+ //
+ // Derive Seq/ACK from the segment if no TCB
+ // is associated with it, otherwise derive from the Tcb.
+ //
+ if (Tcb == NULL) {
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {
+ Nhead->Seq = Head->Ack;
+ Nhead->Ack = 0;
+ } else {
+ Nhead->Seq = 0;
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
+ Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);
+ }
+ } else {
+
+ Nhead->Seq = HTONL (Tcb->SndNxt);
+ Nhead->Ack = HTONL (Tcb->RcvNxt);
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
+ }
+
+ Nhead->SrcPort = Head->DstPort;
+ Nhead->DstPort = Head->SrcPort;
+ Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);
+ Nhead->Res = 0;
+ Nhead->Wnd = HTONS (0xFFFF);
+ Nhead->Checksum = 0;
+ Nhead->Urg = 0;
+
+ if (Version == IP_VERSION_4) {
+ HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0);
+ } else {
+ HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0);
+ }
+
+ Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);
+
+ TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version);
+
+ NetbufFree (Nbuf);
+
+ return 0;
+}
+
+/**
+ Verify that the segment is in good shape.
+
+ @param[in] Nbuf The buffer that contains the segment to be checked.
+
+ @retval 0 The segment is broken.
+ @retval 1 The segment is in good shape.
+
+**/
+INTN
+TcpVerifySegment (
+ IN NET_BUF *Nbuf
+ )
+{
+ TCP_HEAD *Head;
+ TCP_SEG *Seg;
+ UINT32 Len;
+
+ if (Nbuf == NULL) {
+ return 1;
+ }
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Len = Nbuf->TotalSize;
+ Head = Nbuf->Tcp;
+
+ if (Head != NULL) {
+ if (Head->Flag != Seg->Flag) {
+ return 0;
+ }
+
+ Len -= (Head->HeadLen << 2);
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ Len++;
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ Len++;
+ }
+
+ if (Seg->Seq + Len != Seg->End) {
+ return 0;
+ }
+
+ return 1;
+}
+
diff --git a/NetworkPkg/TcpDxe/TcpProto.h b/NetworkPkg/TcpDxe/TcpProto.h
new file mode 100644
index 0000000000..88dfbb9d5c
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpProto.h
@@ -0,0 +1,342 @@
+/** @file
+ TCP protocol header file.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _TCP_PROTO_H_
+#define _TCP_PROTO_H_
+
+///
+/// Tcp states don't change their order. It is used as an
+/// index to mTcpOutFlag and other macros.
+///
+#define TCP_CLOSED 0
+#define TCP_LISTEN 1
+#define TCP_SYN_SENT 2
+#define TCP_SYN_RCVD 3
+#define TCP_ESTABLISHED 4
+#define TCP_FIN_WAIT_1 5
+#define TCP_FIN_WAIT_2 6
+#define TCP_CLOSING 7
+#define TCP_TIME_WAIT 8
+#define TCP_CLOSE_WAIT 9
+#define TCP_LAST_ACK 10
+
+
+///
+/// Flags in the TCP header
+///
+#define TCP_FLG_FIN 0x01
+#define TCP_FLG_SYN 0x02
+#define TCP_FLG_RST 0x04
+#define TCP_FLG_PSH 0x08
+#define TCP_FLG_ACK 0x10
+#define TCP_FLG_URG 0x20
+
+ //
+ // mask for all the flags
+ //
+#define TCP_FLG_FLAG 0x3F
+
+
+#define TCP_CONNECT_REFUSED (-1) ///< TCP error status
+#define TCP_CONNECT_RESET (-2) ///< TCP error status
+#define TCP_CONNECT_CLOSED (-3) ///< TCP error status
+
+//
+// Current congestion status as suggested by RFC3782.
+//
+#define TCP_CONGEST_RECOVER 1 ///< During the NewReno fast recovery.
+#define TCP_CONGEST_LOSS 2 ///< Retxmit because of retxmit time out.
+#define TCP_CONGEST_OPEN 3 ///< TCP is opening its congestion window.
+
+//
+// TCP control flags
+//
+#define TCP_CTRL_NO_NAGLE 0x0001 ///< Disable Nagle algorithm
+#define TCP_CTRL_NO_KEEPALIVE 0x0002 ///< Disable keepalive timer.
+#define TCP_CTRL_NO_WS 0x0004 ///< Disable window scale option.
+#define TCP_CTRL_RCVD_WS 0x0008 ///< Received a wnd scale option in syn.
+#define TCP_CTRL_NO_TS 0x0010 ///< Disable Timestamp option.
+#define TCP_CTRL_RCVD_TS 0x0020 ///< Received a Timestamp option in syn.
+#define TCP_CTRL_SND_TS 0x0040 ///< Send Timestamp option to remote.
+#define TCP_CTRL_SND_URG 0x0080 ///< In urgent send mode.
+#define TCP_CTRL_RCVD_URG 0x0100 ///< In urgent receive mode.
+#define TCP_CTRL_SND_PSH 0x0200 ///< In PUSH send mode.
+#define TCP_CTRL_FIN_SENT 0x0400 ///< FIN is sent.
+#define TCP_CTRL_FIN_ACKED 0x0800 ///< FIN is ACKed.
+#define TCP_CTRL_TIMER_ON 0x1000 ///< At least one of the timer is on.
+#define TCP_CTRL_RTT_ON 0x2000 ///< The RTT measurement is on.
+#define TCP_CTRL_ACK_NOW 0x4000 ///< Send the ACK now, don't delay.
+
+//
+// Timer related values
+//
+#define TCP_TIMER_CONNECT 0 ///< Connection establishment timer.
+#define TCP_TIMER_REXMIT 1 ///< Retransmit timer.
+#define TCP_TIMER_PROBE 2 ///< Window probe timer.
+#define TCP_TIMER_KEEPALIVE 3 ///< Keepalive timer.
+#define TCP_TIMER_FINWAIT2 4 ///< FIN_WAIT_2 timer.
+#define TCP_TIMER_2MSL 5 ///< TIME_WAIT timer.
+#define TCP_TIMER_NUMBER 6 ///< The total number of the TCP timer.
+#define TCP_TICK 200 ///< Every TCP tick is 200ms.
+#define TCP_TICK_HZ 5 ///< The frequence of TCP tick.
+#define TCP_RTT_SHIFT 3 ///< SRTT & RTTVAR scaled by 8.
+#define TCP_RTO_MIN TCP_TICK_HZ ///< The minium value of RTO.
+#define TCP_RTO_MAX (TCP_TICK_HZ * 60) ///< The maxium value of RTO.
+#define TCP_FOLD_RTT 4 ///< Timeout threshod to fold RTT.
+
+//
+// Default values for some timers
+//
+#define TCP_MAX_LOSS 12 ///< Default max times to retxmit.
+#define TCP_KEEPALIVE_IDLE_MIN (TCP_TICK_HZ * 60 * 60 * 2) ///< First keepalive.
+#define TCP_KEEPALIVE_PERIOD (TCP_TICK_HZ * 60)
+#define TCP_MAX_KEEPALIVE 8
+#define TCP_FIN_WAIT2_TIME (2 * TCP_TICK_HZ)
+#define TCP_TIME_WAIT_TIME (2 * TCP_TICK_HZ)
+#define TCP_PAWS_24DAY (24 * 24 * 60 * 60 * TCP_TICK_HZ)
+#define TCP_CONNECT_TIME (75 * TCP_TICK_HZ)
+
+//
+// The header space to be reserved before TCP data to accomodate :
+// 60byte IP head + 60byte TCP head + link layer head
+//
+#define TCP_MAX_HEAD 192
+
+//
+// Value ranges for some control option
+//
+#define TCP_RCV_BUF_SIZE (2 * 1024 * 1024)
+#define TCP_RCV_BUF_SIZE_MIN (8 * 1024)
+#define TCP_SND_BUF_SIZE (2 * 1024 * 1024)
+#define TCP_SND_BUF_SIZE_MIN (8 * 1024)
+#define TCP_BACKLOG 10
+#define TCP_BACKLOG_MIN 5
+#define TCP_MAX_LOSS_MIN 6
+#define TCP_CONNECT_TIME_MIN (60 * TCP_TICK_HZ)
+#define TCP_MAX_KEEPALIVE_MIN 4
+#define TCP_KEEPALIVE_IDLE_MAX (TCP_TICK_HZ * 60 * 60 * 4)
+#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30)
+#define TCP_FIN_WAIT2_TIME_MAX (4 * TCP_TICK_HZ)
+#define TCP_TIME_WAIT_TIME_MAX (60 * TCP_TICK_HZ)
+
+///
+/// TCP_CONNECTED: both ends have synchronized their ISN.
+///
+#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD)
+
+#define TCP_FIN_RCVD(State) \
+ ( \
+ ((State) == TCP_CLOSE_WAIT) || \
+ ((State) == TCP_LAST_ACK) || \
+ ((State) == TCP_CLOSING) || \
+ ((State) == TCP_TIME_WAIT) \
+ )
+
+#define TCP_LOCAL_CLOSED(State) \
+ ( \
+ ((State) == TCP_FIN_WAIT_1) || \
+ ((State) == TCP_FIN_WAIT_2) || \
+ ((State) == TCP_CLOSING) || \
+ ((State) == TCP_TIME_WAIT) || \
+ ((State) == TCP_LAST_ACK) \
+ )
+
+//
+// Get the TCP_SEG point from a net buffer's ProtoData.
+//
+#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData))
+
+//
+// Macros to compare sequence no
+//
+#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0)
+#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0)
+#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0)
+#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0)
+
+//
+// TCP_SEQ_BETWEEN return whether b <= m <= e
+//
+#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b))
+
+//
+// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2
+//
+#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2)))
+
+//
+// Check whether Flag is on
+//
+#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0))
+//
+// Set and Clear operation on a Flag
+//
+#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag))
+#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag))
+
+//
+// Test whether two peers are equal
+//
+#define TCP_PEER_EQUAL(Pa, Pb, Ver) \
+ (((Pa)->Port == (Pb)->Port) && TcpIsIpEqual(&((Pa)->Ip), &((Pb)->Ip), Ver))
+
+//
+// Test whether Pa matches Pb, or Pa is more specific
+// than pb. Zero means wildcard.
+//
+#define TCP_PEER_MATCH(Pa, Pb, Ver) \
+ ( \
+ (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)) && \
+ (TcpIsIpZero (&((Pb)->Ip), Ver) || TcpIsIpEqual (&((Pb)->Ip), &((Pa)->Ip), Ver)) \
+ )
+
+#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer)))
+#define TCP_SET_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) | (1 << (Timer))))
+#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) & (~(1 << (Timer)))))
+
+
+#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0)
+#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0)
+#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb)))
+
+#define TCP_MAX_WIN 0xFFFFU
+
+///
+/// TCP segmentation data.
+///
+typedef struct _TCP_SEG {
+ TCP_SEQNO Seq; ///< Starting sequence number.
+ TCP_SEQNO End; ///< The sequence of the last byte + 1, include SYN/FIN. End-Seq = SEG.LEN.
+ TCP_SEQNO Ack; ///< ACK field in the segment.
+ UINT8 Flag; ///< TCP header flags.
+ UINT16 Urg; ///< Valid if URG flag is set.
+ UINT32 Wnd; ///< TCP window size field.
+} TCP_SEG;
+
+///
+/// Network endpoint, IP plus Port structure.
+///
+typedef struct _TCP_PEER {
+ EFI_IP_ADDRESS Ip; ///< IP address, in network byte order.
+ TCP_PORTNO Port; ///< Port number, in network byte order.
+} TCP_PEER;
+
+typedef struct _TCP_CONTROL_BLOCK TCP_CB;
+
+///
+/// TCP control block: it includes various states.
+///
+struct _TCP_CONTROL_BLOCK {
+ LIST_ENTRY List; ///< Back and forward link entry
+ TCP_CB *Parent; ///< The parent TCP_CB structure
+
+ SOCKET *Sk; ///< The socket it controled.
+ TCP_PEER LocalEnd; ///< Local endpoint.
+ TCP_PEER RemoteEnd;///< Remote endpoint.
+
+ LIST_ENTRY SndQue; ///< Retxmission queue.
+ LIST_ENTRY RcvQue; ///< Reassemble queue.
+ UINT32 CtrlFlag; ///< Control flags, such as NO_NAGLE.
+ INT32 Error; ///< Soft error status, such as TCP_CONNECT_RESET.
+
+ //
+ // RFC793 and RFC1122 defined variables
+ //
+ UINT8 State; ///< TCP state, such as SYN_SENT, LISTEN.
+ UINT8 DelayedAck; ///< Number of delayed ACKs.
+ UINT16 HeadSum; ///< Checksum of the fixed parts of pesudo
+ ///< header: Src IP, Dst IP, 0, Protocol,
+ ///< do not include the TCP length.
+
+ TCP_SEQNO Iss; ///< Initial Sending Sequence.
+ TCP_SEQNO SndUna; ///< First unacknowledged data.
+ TCP_SEQNO SndNxt; ///< Next data sequence to send.
+ TCP_SEQNO SndPsh; ///< Send PUSH point.
+ TCP_SEQNO SndUp; ///< Send urgent point.
+ UINT32 SndWnd; ///< Window advertised by the remote peer.
+ UINT32 SndWndMax; ///< Max send window advertised by the peer.
+ TCP_SEQNO SndWl1; ///< Seq number used for last window update.
+ TCP_SEQNO SndWl2; ///< Ack no of last window update.
+ UINT16 SndMss; ///< Max send segment size.
+ TCP_SEQNO RcvNxt; ///< Next sequence no to receive.
+ UINT32 RcvWnd; ///< Window advertised by the local peer.
+ TCP_SEQNO RcvWl2; ///< The RcvNxt (or ACK) of last window update.
+ ///< It is necessary because of delayed ACK.
+
+ TCP_SEQNO RcvUp; ///< Urgent point;
+ TCP_SEQNO Irs; ///< Initial Receiving Sequence.
+ UINT16 RcvMss; ///< Max receive segment size.
+ UINT16 EnabledTimer; ///< Which timer is currently enabled.
+ UINT32 Timer[TCP_TIMER_NUMBER]; ///< When the timer will expire.
+ INT32 NextExpire; ///< Countdown offset for the nearest timer.
+ UINT32 Idle; ///< How long the connection is in idle.
+ UINT32 ProbeTime; ///< The time out value for current window prober.
+ BOOLEAN ProbeTimerOn;///< If TRUE, the probe time is on.
+
+ //
+ // RFC1323 defined variables, about window scale,
+ // timestamp and PAWS
+ //
+ UINT8 SndWndScale; ///< Wndscale received from the peer.
+ UINT8 RcvWndScale; ///< Wndscale used to scale local buffer.
+ UINT32 TsRecent; ///< TsRecent to echo to the remote peer.
+ UINT32 TsRecentAge; ///< When this TsRecent is updated.
+
+ //
+ // RFC2988 defined variables. about RTT measurement
+ //
+ TCP_SEQNO RttSeq; ///< The seq of measured segment now.
+ UINT32 RttMeasure; ///< Currently measured RTT in heartbeats.
+ UINT32 SRtt; ///< Smoothed RTT, scaled by 8.
+ UINT32 RttVar; ///< RTT variance, scaled by 8.
+ UINT32 Rto; ///< Current RTO, not scaled.
+
+ //
+ // RFC2581, and 3782 variables.
+ // Congestion control + NewReno fast recovery.
+ //
+ UINT32 CWnd; ///< Sender's congestion window.
+ UINT32 Ssthresh; ///< Slow start threshold.
+ TCP_SEQNO Recover; ///< Recover point for NewReno.
+ UINT16 DupAck; ///< Number of duplicate ACKs.
+ UINT8 CongestState; ///< The current congestion state(RFC3782).
+ UINT8 LossTimes; ///< Number of retxmit timeouts in a row.
+ TCP_SEQNO LossRecover; ///< Recover point for retxmit.
+
+ //
+ // configuration parameters, for EFI_TCP4_PROTOCOL specification
+ //
+ UINT32 KeepAliveIdle; ///< Idle time before sending first probe.
+ UINT32 KeepAlivePeriod; ///< Interval for subsequent keep alive probe.
+ UINT8 MaxKeepAlive; ///< Maxium keep alive probe times.
+ UINT8 KeepAliveProbes; ///< The number of keep alive probe.
+ UINT16 MaxRexmit; ///< The maxium number of retxmit before abort.
+ UINT32 FinWait2Timeout; ///< The FIN_WAIT_2 timeout.
+ UINT32 TimeWaitTimeout; ///< The TIME_WAIT timeout.
+ UINT32 ConnectTimeout; ///< The connect establishment timeout.
+
+ //
+ // configuration for tcp provided by user
+ //
+ BOOLEAN UseDefaultAddr;
+ UINT8 Tos;
+ UINT8 Ttl;
+ EFI_IPv4_ADDRESS SubnetMask;
+
+ IP_IO_IP_INFO *IpInfo; ///< Pointer reference to Ip used to send pkt
+ UINT32 Tick; ///< 1 tick = 200ms
+};
+
+#endif
diff --git a/NetworkPkg/TcpDxe/TcpTimer.c b/NetworkPkg/TcpDxe/TcpTimer.c
new file mode 100644
index 0000000000..cc52ad924c
--- /dev/null
+++ b/NetworkPkg/TcpDxe/TcpTimer.c
@@ -0,0 +1,593 @@
+/** @file
+ TCP timer related functions.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "TcpMain.h"
+
+UINT32 mTcpTick = 1000;
+
+/**
+ Connect timeout handler.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpConnectTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for TCP retransmission timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpRexmitTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for window probe timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpProbeTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for keepalive timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpKeepaliveTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for FIN_WAIT_2 timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpFinwait2Timeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+/**
+ Timeout handler for 2MSL timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+Tcp2MSLTimeout (
+ IN OUT TCP_CB *Tcb
+ );
+
+TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {
+ TcpConnectTimeout,
+ TcpRexmitTimeout,
+ TcpProbeTimeout,
+ TcpKeepaliveTimeout,
+ TcpFinwait2Timeout,
+ Tcp2MSLTimeout,
+};
+
+/**
+ Close the TCP connection.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClose (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ NetbufFreeList (&Tcb->SndQue);
+ NetbufFreeList (&Tcb->RcvQue);
+
+ TcpSetState (Tcb, TCP_CLOSED);
+}
+
+/**
+ Backoff the RTO.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpBackoffRto (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ //
+ // Fold the RTT estimate if too many times, the estimate
+ // may be wrong, fold it. So the next time a valid
+ // measurement is sampled, we can start fresh.
+ //
+ if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {
+ Tcb->RttVar += Tcb->SRtt >> 2;
+ Tcb->SRtt = 0;
+ }
+
+ Tcb->Rto <<= 1;
+
+ if (Tcb->Rto < TCP_RTO_MIN) {
+
+ Tcb->Rto = TCP_RTO_MIN;
+ } else if (Tcb->Rto > TCP_RTO_MAX) {
+
+ Tcb->Rto = TCP_RTO_MAX;
+ }
+}
+
+/**
+ Connect timeout handler.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpConnectTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ if (!TCP_CONNECTED (Tcb->State)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n",
+ Tcb)
+ );
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ if (TCP_SYN_RCVD == Tcb->State) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",
+ Tcb)
+ );
+
+ TcpResetConnection (Tcb);
+
+ }
+
+ TcpClose (Tcb);
+ }
+}
+
+
+/**
+ Timeout handler for TCP retransmission timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpRexmitTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ UINT32 FlightSize;
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpRexmitTimeout: transmission timeout for TCB %p\n",
+ Tcb)
+ );
+
+ //
+ // Set the congestion window. FlightSize is the
+ // amount of data that has been sent but not
+ // yet ACKed.
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+ Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);
+
+ Tcb->CWnd = Tcb->SndMss;
+ Tcb->LossRecover = Tcb->SndNxt;
+
+ Tcb->LossTimes++;
+ if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",
+ Tcb)
+ );
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ TcpClose (Tcb);
+ return ;
+ }
+
+ TcpBackoffRto (Tcb);
+ TcpRetransmit (Tcb, Tcb->SndUna);
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+
+ Tcb->CongestState = TCP_CONGEST_LOSS;
+
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+}
+
+/**
+ Timeout handler for window probe timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpProbeTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ //
+ // This is the timer for sender's SWSA. RFC1122 requires
+ // a timer set for sender's SWSA, and suggest combine it
+ // with window probe timer. If data is sent, don't set
+ // the probe timer, since retransmit timer is on.
+ //
+ if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {
+
+ ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);
+ Tcb->ProbeTimerOn = FALSE;
+ return ;
+ }
+
+ TcpSendZeroProbe (Tcb);
+ TcpSetProbeTimer (Tcb);
+}
+
+/**
+ Timeout handler for keepalive timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpKeepaliveTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ Tcb->KeepAliveProbes++;
+
+ //
+ // Too many Keep-alive probes, drop the connection
+ //
+ if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ TcpClose (Tcb);
+ return ;
+ }
+
+ TcpSendZeroProbe (Tcb);
+ TcpSetKeepaliveTimer (Tcb);
+}
+
+/**
+ Timeout handler for FIN_WAIT_2 timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpFinwait2Timeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",
+ Tcb)
+ );
+
+ TcpClose (Tcb);
+}
+
+/**
+ Timeout handler for 2MSL timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+Tcp2MSLTimeout (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ DEBUG (
+ (EFI_D_WARN,
+ "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",
+ Tcb)
+ );
+
+ TcpClose (Tcb);
+}
+
+/**
+ Update the timer status and the next expire time according to the timers
+ to expire in a specific future time slot.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpUpdateTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ UINT16 Index;
+
+ //
+ // Don't use a too large value to init NextExpire
+ // since mTcpTick wraps around as sequence no does.
+ //
+ Tcb->NextExpire = TCP_EXPIRE_TIME;
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
+
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
+
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
+ TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)
+ ) {
+
+ Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
+ }
+ }
+}
+
+/**
+ Enable a TCP timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Timer The index of the timer to be enabled.
+ @param[in] TimeOut The timeout value of this timer.
+
+**/
+VOID
+TcpSetTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer,
+ IN UINT32 TimeOut
+ )
+{
+ TCP_SET_TIMER (Tcb->EnabledTimer, Timer);
+ Tcb->Timer[Timer] = mTcpTick + TimeOut;
+
+ TcpUpdateTimer (Tcb);
+}
+
+/**
+ Clear one TCP timer.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] Timer The index of the timer to be cleared.
+
+**/
+VOID
+TcpClearTimer (
+ IN OUT TCP_CB *Tcb,
+ IN UINT16 Timer
+ )
+{
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);
+ TcpUpdateTimer (Tcb);
+}
+
+/**
+ Clear all TCP timers.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpClearAllTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ Tcb->EnabledTimer = 0;
+ TcpUpdateTimer (Tcb);
+}
+
+/**
+ Enable the window prober timer and set the timeout value.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetProbeTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ if (!Tcb->ProbeTimerOn) {
+ Tcb->ProbeTime = Tcb->Rto;
+ Tcb->ProbeTimerOn = TRUE;
+
+ } else {
+ Tcb->ProbeTime <<= 1;
+ }
+
+ if (Tcb->ProbeTime < TCP_RTO_MIN) {
+
+ Tcb->ProbeTime = TCP_RTO_MIN;
+ } else if (Tcb->ProbeTime > TCP_RTO_MAX) {
+
+ Tcb->ProbeTime = TCP_RTO_MAX;
+ }
+
+ TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);
+}
+
+/**
+ Enable the keepalive timer and set the timeout value.
+
+ @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
+
+**/
+VOID
+TcpSetKeepaliveTimer (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {
+ return ;
+
+ }
+
+ //
+ // Set the timer to KeepAliveIdle if either
+ // 1. the keepalive timer is off
+ // 2. The keepalive timer is on, but the idle
+ // is less than KeepAliveIdle, that means the
+ // connection is alive since our last probe.
+ //
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||
+ (Tcb->Idle < Tcb->KeepAliveIdle)
+ ) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);
+ Tcb->KeepAliveProbes = 0;
+
+ } else {
+
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);
+ }
+}
+
+/**
+ Heart beat timer handler.
+
+ @param[in] Context Context of the timer event, ignored.
+
+**/
+VOID
+EFIAPI
+TcpTickingDpc (
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ TCP_CB *Tcb;
+ INT16 Index;
+
+ mTcpTick++;
+ mTcpGlobalIss += TCP_ISS_INCREMENT_2;
+
+ //
+ // Don't use LIST_FOR_EACH, which isn't delete safe.
+ //
+ for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {
+
+ Next = Entry->ForwardLink;
+
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (Tcb->State == TCP_CLOSED) {
+ continue;
+ }
+ //
+ // The connection is doing RTT measurement.
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+ Tcb->RttMeasure++;
+ }
+
+ Tcb->Idle++;
+
+ if (Tcb->DelayedAck != 0) {
+ TcpSendAck (Tcb);
+ }
+
+ if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) {
+ Tcb->Tick--;
+ }
+
+ //
+ // No timer is active or no timer expired
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {
+
+ continue;
+ }
+
+ //
+ // Call the timeout handler for each expired timer.
+ //
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
+
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {
+ //
+ // disable the timer before calling the handler
+ // in case the handler enables it again.
+ //
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);
+ mTcpTimerHandler[Index](Tcb);
+
+ //
+ // The Tcb may have been deleted by the timer, or
+ // no other timer is set.
+ //
+ if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {
+ break;
+ }
+ }
+ }
+
+ //
+ // If the Tcb still exist or some timer is set, update the timer
+ //
+ if (Index == TCP_TIMER_NUMBER) {
+ TcpUpdateTimer (Tcb);
+ }
+ }
+}
+
+/**
+ Heart beat timer handler, queues the DPC at TPL_CALLBACK.
+
+ @param[in] Event Timer event signaled, ignored.
+ @param[in] Context Context of the timer event, ignored.
+
+**/
+VOID
+EFIAPI
+TcpTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
+}
+
diff --git a/NetworkPkg/Udp6Dxe/ComponentName.c b/NetworkPkg/Udp6Dxe/ComponentName.c
new file mode 100644
index 0000000000..d7df0965a0
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/ComponentName.c
@@ -0,0 +1,313 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for UDP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Udp6Impl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName = {
+ Udp6ComponentNameGetDriverName,
+ Udp6ComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Udp6ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Udp6ComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdp6DriverNameTable[] = {
+ {
+ "eng;en",
+ L"UDP6 Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mUdp6DriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gUdp6ComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.c b/NetworkPkg/Udp6Dxe/Udp6Driver.c
new file mode 100644
index 0000000000..1cfd5f1b4d
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Driver.c
@@ -0,0 +1,556 @@
+/** @file
+ Driver Binding functions and Service Binding functions for the Network driver module.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Udp6Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gUdp6DriverBinding = {
+ Udp6DriverBindingSupported,
+ Udp6DriverBindingStart,
+ Udp6DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding = {
+ Udp6ServiceBindingCreateChild,
+ Udp6ServiceBindingDestroyChild
+};
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+Udp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ //
+ // Test for the Udp6ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+ //
+ // Test for the Ip6ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+/**
+ Start this driver on ControllerHandle.
+
+ This service is called by the EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind the driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle.
+ @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP6_SERVICE_DATA *Udp6Service;
+
+ //
+ // Allocate Private Context Data Structure.
+ //
+ Udp6Service = AllocateZeroPool (sizeof (UDP6_SERVICE_DATA));
+ if (Udp6Service == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ Status = Udp6CreateService (Udp6Service, This->DriverBindingHandle, ControllerHandle);
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ //
+ // Install the Udp6ServiceBindingProtocol on the ControllerHandle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ &Udp6Service->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Udp6CleanService (Udp6Service);
+ goto EXIT;
+ } else {
+ Status = Udp6SetVariableData (Udp6Service);
+ }
+
+EXIT:
+ if (EFI_ERROR (Status)) {
+ if (Udp6Service != NULL) {
+ FreePool (Udp6Service);
+ }
+ }
+ return Status;
+}
+
+/**
+ Stop this driver on ControllerHandle.
+
+ This service is called by the EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop(), it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop the driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number
+ of children is zero stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional.
+
+ @retval EFI_SUCCES This driver is removed ControllerHandle.
+ @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UDP6_SERVICE_DATA *Udp6Service;
+ UDP6_INSTANCE_DATA *Instance;
+
+ //
+ // Find the NicHandle where UDP6 ServiceBinding Protocol is installed.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Retrieve the UDP6 ServiceBinding Protocol.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (ServiceBinding);
+
+ if (NumberOfChildren == 0) {
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ &Udp6Service->ServiceBinding,
+ NULL
+ );
+
+ Udp6ClearVariableData (Udp6Service);
+
+ Udp6CleanService (Udp6Service);
+
+ FreePool (Udp6Service);
+ } else {
+
+ while (!IsListEmpty (&Udp6Service->ChildrenList)) {
+ Instance = NET_LIST_HEAD (&Udp6Service->ChildrenList, UDP6_INSTANCE_DATA, Link);
+
+ Status = ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ UDP6_SERVICE_DATA *Udp6Service;
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ VOID *Ip6;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Allocate the instance private data structure.
+ //
+ Instance = AllocateZeroPool (sizeof (UDP6_INSTANCE_DATA));
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Udp6InitInstance (Udp6Service, Instance);
+
+ //
+ // Add an IpInfo for this instance.
+ //
+ Instance->IpInfo = IpIoAddIp (Udp6Service->IpIo);
+ if (Instance->IpInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Udp6Protocol for this instance.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiUdp6ProtocolGuid,
+ &Instance->Udp6Proto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Instance->ChildHandle = *ChildHandle;
+
+ //
+ // Open the default Ip6 protocol in the IP_IO BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Udp6Service->IpIo->ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ (VOID **) &Ip6,
+ gUdp6DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Link this instance into the service context data and increase the ChildrenNumber.
+ //
+ InsertTailList (&Udp6Service->ChildrenList, &Instance->Link);
+ Udp6Service->ChildrenNumber++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Instance->ChildHandle != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->ChildHandle,
+ &gEfiUdp6ProtocolGuid,
+ &Instance->Udp6Proto,
+ NULL
+ );
+ }
+
+ if (Instance->IpInfo != NULL) {
+ IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);
+ }
+
+ Udp6CleanInstance (Instance);
+
+ FreePool (Instance);
+
+ return Status;
+}
+
+/**
+ Destroys a child handle with a set of I/O services.
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle.
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ UDP6_SERVICE_DATA *Udp6Service;
+ EFI_UDP6_PROTOCOL *Udp6Proto;
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Try to get the Udp6 protocol from the ChildHandle.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiUdp6ProtocolGuid,
+ (VOID **) &Udp6Proto,
+ gUdp6DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (Udp6Proto);
+
+ if (Instance->Destroyed) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Use the Destroyed flag to avoid the re-entering of the following code.
+ //
+ Instance->Destroyed = TRUE;
+
+ //
+ // Close the Ip6 protocol.
+ //
+ gBS->CloseProtocol (
+ Udp6Service->IpIo->ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ gUdp6DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle
+ );
+
+ //
+ // Uninstall the Udp6Protocol previously installed on the ChildHandle.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiUdp6ProtocolGuid,
+ (VOID *) &Instance->Udp6Proto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Instance->Destroyed = FALSE;
+ return Status;
+ }
+
+ //
+ // Reset the configuration in case the instance's consumer forgets to do this.
+ //
+ Udp6Proto->Configure (Udp6Proto, NULL);
+
+ //
+ // Remove the IpInfo this instance consumes.
+ //
+ IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Remove this instance from the service context data's ChildrenList.
+ //
+ RemoveEntryList (&Instance->Link);
+ Udp6Service->ChildrenNumber--;
+
+ //
+ // Clean the instance.
+ //
+ Udp6CleanInstance (Instance);
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including
+ both device drivers and bus drivers.
+
+ The entry point for Udp6 driver that installs the driver binding
+ and component name protocol on its ImageHandle.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Install the Udp6DriverBinding and Udp6ComponentName protocols.
+ //
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gUdp6DriverBinding,
+ ImageHandle,
+ &gUdp6ComponentName,
+ &gUdp6ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Initialize the UDP random port.
+ //
+ mUdp6RandomPort = (UINT16)(
+ ((UINT16) NetRandomInitSeed ()) %
+ UDP6_PORT_KNOWN +
+ UDP6_PORT_KNOWN
+ );
+ }
+
+ return Status;
+}
+
+
diff --git a/NetworkPkg/Udp6Dxe/Udp6Driver.h b/NetworkPkg/Udp6Dxe/Udp6Driver.h
new file mode 100644
index 0000000000..e5a923be68
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Driver.h
@@ -0,0 +1,182 @@
+/** @file
+ Driver Binding functions and Service Binding functions for the Network driver module.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _UDP6_DRIVER_H_
+#define _UDP6_DRIVER_H_
+
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DevicePath.h>
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+Udp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Start this driver on ControllerHandle.
+
+ This service is called by the EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to bind a driver to.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle.
+ @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stop this driver on ControllerHandle.
+
+ This service is called by the EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop(), it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop the driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero, stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional.
+
+ @retval EFI_SUCCESS This driver removed ControllerHandle.
+ @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ Creates a child handle and installs a protocol.
+
+ The CreateChild() function installs a protocol on ChildHandle.
+ If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
+ If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
+
+ @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
+ @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,
+ then a new handle is created. If it is a pointer to an existing UEFI handle,
+ then the protocol is added to the existing UEFI handle.
+
+ @retval EFI_SUCCES The protocol was added to ChildHandle.
+ @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN OUT EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroys a child handle with a set of I/O services.
+ The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
+ that was installed by CreateChild() from ChildHandle. If the removed protocol is the
+ last protocol on ChildHandle, then ChildHandle is destroyed.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle.
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
+
diff --git a/NetworkPkg/Udp6Dxe/Udp6Dxe.inf b/NetworkPkg/Udp6Dxe/Udp6Dxe.inf
new file mode 100644
index 0000000000..30b2bc1cc7
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Dxe.inf
@@ -0,0 +1,63 @@
+## @file Udp6Dxe.inf
+# Component description file for Udp6 module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Udp6Dxe
+ FILE_GUID = D912C7BC-F098-4367-92BA-E911083C7B0E
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = Udp6DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ Udp6Driver.h
+ Udp6Driver.c
+ Udp6Impl.c
+ Udp6Impl.h
+ ComponentName.c
+ Udp6Main.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ UefiLib
+ DebugLib
+ IpIoLib
+ NetLib
+ DpcLib
+
+
+[Protocols]
+ gEfiIp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUdp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUdp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.c b/NetworkPkg/Udp6Dxe/Udp6Impl.c
new file mode 100644
index 0000000000..8e259319b9
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Impl.c
@@ -0,0 +1,2131 @@
+/** @file
+ Udp6 driver's whole implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Udp6Impl.h"
+
+UINT16 mUdp6RandomPort;
+
+/**
+ This function checks and timeouts the I/O datagrams holding by the corresponding
+ service context.
+
+ @param[in] Event The event this function is registered to.
+ @param[in] Context The context data registered during the creation of
+ the Event.
+
+**/
+VOID
+EFIAPI
+Udp6CheckTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ This function finds the udp instance by the specified <Address, Port> pair.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in] Address Pointer to the specified IPv6 address.
+ @param[in] Port The udp port number.
+
+ @retval TRUE The specified <Address, Port> pair is found.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp6FindInstanceByPort (
+ IN LIST_ENTRY *InstanceList,
+ IN EFI_IPv6_ADDRESS *Address,
+ IN UINT16 Port
+ );
+
+/**
+ This function is the packet transmitting notify function registered to the IpIo
+ interface. It's called to signal the udp TxToken when the IpIo layer completes
+ transmitting of the udp datagram.
+
+ @param[in] Status The completion status of the output udp datagram.
+ @param[in] Context Pointer to the context data.
+ @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending.
+ @param[in] NotifyData Pointer to the notify data.
+
+**/
+VOID
+EFIAPI
+Udp6DgramSent (
+ IN EFI_STATUS Status,
+ IN VOID *Context,
+ IN IP_IO_IP_PROTOCOL Sender,
+ IN VOID *NotifyData
+ );
+
+/**
+ This function processes the received datagram passed up by the IpIo layer.
+
+ @param[in] Status The status of this udp datagram.
+ @param[in] IcmpError The IcmpError code, only available when Status is
+ EFI_ICMP_ERROR.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA.
+ @param[in] Packet Pointer to the NET_BUF containing the received udp
+ datagram.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp6DgramRcvd (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet,
+ IN VOID *Context
+ );
+
+/**
+ This function cancle the token specified by Arg in the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the token to be cancelled, if NULL, all
+ the tokens in this Map will be cancelled.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token
+ is not the same as that in the Item if Arg is not
+ NULL.
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is
+ cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6CancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ );
+
+/**
+ This function check if the received udp datagram matches with the Instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted
+ from the received udp datagram.
+
+ @retval TRUE The udp datagram matches the receiving requirements of the Instance.
+ @retval FALSE The udp datagram doe not match the receiving requirements of the Instance.
+
+**/
+BOOLEAN
+Udp6MatchDgram (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN EFI_UDP6_SESSION_DATA *Udp6Session
+ );
+
+/**
+ This function removes the Wrap specified by Context and releases relevant resources.
+
+ @param[in] Event The Event this notify function is registered to.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp6RecycleRxDataWrap (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ This function wraps the Packet into RxData.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this
+ datagram.
+
+ @return Pointer to the structure wrapping the RxData and the Packet.
+
+**/
+UDP6_RXDATA_WRAP *
+Udp6WrapRxData (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN NET_BUF *Packet,
+ IN EFI_UDP6_RECEIVE_DATA *RxData
+ );
+
+/**
+ This function enqueues the received datagram into the instances' receiving queues.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this
+ datagram.
+
+ @return The times this datagram is enqueued.
+
+**/
+UINTN
+Udp6EnqueueDgram (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN NET_BUF *Packet,
+ IN EFI_UDP6_RECEIVE_DATA *RxData
+ );
+
+/**
+ This function delivers the datagrams enqueued in the instances.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+
+**/
+VOID
+Udp6DeliverDgram (
+ IN UDP6_SERVICE_DATA *Udp6Service
+ );
+
+/**
+ This function demultiplexes the received udp datagram to the apropriate instances.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from
+ the received datagram.
+ @param[in] Packet Pointer to the buffer containing the received udp
+ datagram.
+
+**/
+VOID
+Udp6Demultiplex (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ );
+
+/**
+ This function handles the received Icmp Error message and demultiplexes it to the
+ instance.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+ @param[in] IcmpError The icmp error code.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted
+ from the received Icmp Error packet.
+ @param[in, out] Packet Pointer to the Icmp Error packet.
+
+**/
+VOID
+Udp6IcmpHandler (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN OUT NET_BUF *Packet
+ );
+
+/**
+ This function builds and sends out a icmp port unreachable message.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet
+ causes this icmp error message.
+ @param[in] Udp6Header Pointer to the udp header of the datagram causes
+ this icmp error message.
+
+**/
+VOID
+Udp6SendPortUnreach (
+ IN IP_IO *IpIo,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN VOID *Udp6Header
+ );
+
+/**
+ Find the key in the netmap
+
+ @param[in] Map The netmap to search within.
+ @param[in] Key The key to search.
+
+ @return The point to the item contains the Key, or NULL if Key isn't in the map.
+
+**/
+NET_MAP_ITEM *
+Udp6MapMultiCastAddr (
+ IN NET_MAP *Map,
+ IN VOID *Key
+ );
+
+/**
+ Create the Udp service context data.
+
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.
+ @param[in] ImageHandle The image handle of this udp6 driver.
+ @param[in] ControllerHandle The controller handle this udp6 driver binds on.
+
+ @retval EFI_SUCCESS The udp6 service context data was created and
+ initialized.
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.
+ @retval Others An error condition occurred.
+
+**/
+EFI_STATUS
+Udp6CreateService (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_STATUS Status;
+ IP_IO_OPEN_DATA OpenData;
+
+ ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA));
+
+ Udp6Service->Signature = UDP6_SERVICE_DATA_SIGNATURE;
+ Udp6Service->ServiceBinding = mUdp6ServiceBinding;
+ Udp6Service->ImageHandle = ImageHandle;
+ Udp6Service->ControllerHandle = ControllerHandle;
+ Udp6Service->ChildrenNumber = 0;
+
+ InitializeListHead (&Udp6Service->ChildrenList);
+
+ //
+ // Create the IpIo for this service context.
+ //
+ Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6);
+ if (Udp6Service->IpIo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Set the OpenData used to open the IpIo.
+ //
+ CopyMem (
+ &OpenData.IpConfigData.Ip6CfgData,
+ &mIp6IoDefaultIpConfigData,
+ sizeof (EFI_IP6_CONFIG_DATA)
+ );
+ OpenData.RcvdContext = (VOID *) Udp6Service;
+ OpenData.SndContext = NULL;
+ OpenData.PktRcvdNotify = Udp6DgramRcvd;
+ OpenData.PktSentNotify = Udp6DgramSent;
+
+ //
+ // Configure and start the IpIo.
+ //
+ Status = IpIoOpen (Udp6Service->IpIo, &OpenData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create the event for Udp timeout checking.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ Udp6CheckTimeout,
+ Udp6Service,
+ &Udp6Service->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Start the timeout timer event.
+ //
+ Status = gBS->SetTimer (
+ Udp6Service->TimeoutEvent,
+ TimerPeriodic,
+ UDP6_TIMEOUT_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Udp6Service->TimeoutEvent != NULL) {
+ gBS->CloseEvent (Udp6Service->TimeoutEvent);
+ }
+
+ IpIoDestroy (Udp6Service->IpIo);
+
+ return Status;
+}
+
+
+/**
+ Clean the Udp service context data.
+
+ @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA.
+
+**/
+VOID
+Udp6CleanService (
+ IN OUT UDP6_SERVICE_DATA *Udp6Service
+ )
+{
+ //
+ // Close the TimeoutEvent timer.
+ //
+ gBS->CloseEvent (Udp6Service->TimeoutEvent);
+
+ //
+ // Destroy the IpIo.
+ //
+ IpIoDestroy (Udp6Service->IpIo);
+}
+
+
+/**
+ This function checks and times out the I/O datagrams listed in the
+ UDP6_SERVICE_DATA which is specified by the input parameter Context.
+
+
+ @param[in] Event The event this function registered to.
+ @param[in] Context The context data registered during the creation of
+ the Event.
+
+**/
+VOID
+EFIAPI
+Udp6CheckTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UDP6_SERVICE_DATA *Udp6Service;
+ LIST_ENTRY *Entry;
+ UDP6_INSTANCE_DATA *Instance;
+ LIST_ENTRY *WrapEntry;
+ LIST_ENTRY *NextEntry;
+ UDP6_RXDATA_WRAP *Wrap;
+
+ Udp6Service = (UDP6_SERVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
+ //
+ // Iterate all the instances belonging to this service context.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
+ NET_CHECK_SIGNATURE (Instance, UDP6_INSTANCE_DATA_SIGNATURE);
+
+ if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {
+ //
+ // Skip this instance if it's not configured or no receive timeout.
+ //
+ continue;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {
+ //
+ // Iterate all the rxdatas belonging to this udp instance.
+ //
+ Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP6_RXDATA_WRAP, Link);
+
+ if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) {
+ //
+ // Remove this RxData if it timeouts.
+ //
+ Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);
+ } else {
+ Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10;
+ }
+ }
+ }
+}
+
+
+/**
+ This function intializes the new created udp instance.
+
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.
+ @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA.
+
+**/
+VOID
+Udp6InitInstance (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN OUT UDP6_INSTANCE_DATA *Instance
+ )
+{
+ //
+ // Set the signature.
+ //
+ Instance->Signature = UDP6_INSTANCE_DATA_SIGNATURE;
+
+ //
+ // Init the lists.
+ //
+ InitializeListHead (&Instance->Link);
+ InitializeListHead (&Instance->RcvdDgramQue);
+ InitializeListHead (&Instance->DeliveredDgramQue);
+
+ //
+ // Init the NET_MAPs.
+ //
+ NetMapInit (&Instance->TxTokens);
+ NetMapInit (&Instance->RxTokens);
+ NetMapInit (&Instance->McastIps);
+
+ //
+ // Save the pointer to the UDP6_SERVICE_DATA, and initialize other members.
+ //
+ Instance->Udp6Service = Udp6Service;
+ CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL));
+ Instance->IcmpError = EFI_SUCCESS;
+ Instance->Configured = FALSE;
+ Instance->IsNoMapping = FALSE;
+ Instance->Destroyed = FALSE;
+}
+
+
+/**
+ This function cleans the udp instance.
+
+ @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean.
+
+**/
+VOID
+Udp6CleanInstance (
+ IN OUT UDP6_INSTANCE_DATA *Instance
+ )
+{
+ NetMapClean (&Instance->McastIps);
+ NetMapClean (&Instance->RxTokens);
+ NetMapClean (&Instance->TxTokens);
+}
+
+
+/**
+ This function finds the udp instance by the specified <Address, Port> pair.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in] Address Pointer to the specified IPv6 address.
+ @param[in] Port The udp port number.
+
+ @retval TRUE The specified <Address, Port> pair is found.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp6FindInstanceByPort (
+ IN LIST_ENTRY *InstanceList,
+ IN EFI_IPv6_ADDRESS *Address,
+ IN UINT16 Port
+ )
+{
+ LIST_ENTRY *Entry;
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_UDP6_CONFIG_DATA *ConfigData;
+
+ NET_LIST_FOR_EACH (Entry, InstanceList) {
+ //
+ // Iterate all the udp instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
+ ConfigData = &Instance->ConfigData;
+
+ if (!Instance->Configured || ConfigData->AcceptAnyPort) {
+ //
+ // If the instance is not configured, or the configdata of the instance indicates
+ // this instance accepts any port, skip it.
+ //
+ continue;
+ }
+
+ if (EFI_IP6_EQUAL (&ConfigData->StationAddress, Address) &&
+ (ConfigData->StationPort == Port)
+ ) {
+ //
+ // If both the address and the port are the same, return TRUE.
+ //
+ return TRUE;
+ }
+ }
+
+ //
+ // Return FALSE when matching fails.
+ //
+ return FALSE;
+}
+
+
+/**
+ This function tries to bind the udp instance according to the configured port
+ allocation stragety.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in] ConfigData Pointer to the ConfigData of the instance to be
+ bound.
+
+ @retval EFI_SUCCESS The bound operation completed successfully.
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is
+ already used by other instance.
+ @retval EFI_OUT_OF_RESOURCES No available port resources.
+
+**/
+EFI_STATUS
+Udp6Bind (
+ IN LIST_ENTRY *InstanceList,
+ IN EFI_UDP6_CONFIG_DATA *ConfigData
+ )
+{
+ EFI_IPv6_ADDRESS *StationAddress;
+ UINT16 StartPort;
+
+ if (ConfigData->AcceptAnyPort) {
+ return EFI_SUCCESS;
+ }
+
+ StationAddress = &ConfigData->StationAddress;
+
+ if (ConfigData->StationPort != 0) {
+
+ if (!ConfigData->AllowDuplicatePort &&
+ Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)
+ ) {
+ //
+ // Do not allow duplicate ports and the port is already used by other instance.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+ } else {
+ //
+ // Select a random port for this instance.
+ //
+ if (ConfigData->AllowDuplicatePort) {
+ //
+ // Just pick up the random port if the instance allows duplicate port.
+ //
+ ConfigData->StationPort = mUdp6RandomPort;
+ } else {
+
+ StartPort = mUdp6RandomPort;
+
+ while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) {
+
+ mUdp6RandomPort++;
+ if (mUdp6RandomPort == 0) {
+ mUdp6RandomPort = UDP6_PORT_KNOWN;
+ }
+
+ if (mUdp6RandomPort == StartPort) {
+ //
+ // No available port.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ ConfigData->StationPort = mUdp6RandomPort;
+ }
+
+ mUdp6RandomPort++;
+ if (mUdp6RandomPort == 0) {
+ mUdp6RandomPort = UDP6_PORT_KNOWN;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function is used to check whether the NewConfigData has any un-reconfigurable
+ parameters changed compared to the OldConfigData.
+
+ @param[in] OldConfigData Pointer to the current ConfigData the udp instance
+ uses.
+ @param[in] NewConfigData Pointer to the new ConfigData.
+
+ @retval TRUE The instance is reconfigurable according to the NewConfigData.
+ @retval FALSE Otherwise.
+
+**/
+BOOLEAN
+Udp6IsReconfigurable (
+ IN EFI_UDP6_CONFIG_DATA *OldConfigData,
+ IN EFI_UDP6_CONFIG_DATA *NewConfigData
+ )
+{
+ if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||
+ (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||
+ (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)
+ ) {
+ //
+ // The receiving filter parameters cannot be changed.
+ //
+ return FALSE;
+ }
+
+ if ((!NewConfigData->AcceptAnyPort) &&
+ (NewConfigData->StationPort != OldConfigData->StationPort)
+ ) {
+ //
+ // The port is not changeable.
+ //
+ return FALSE;
+ }
+
+ if (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) {
+ //
+ // The StationAddress is not the same.
+ //
+ return FALSE;
+ }
+
+
+ if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) {
+ //
+ // The remoteaddress is not the same.
+ //
+ return FALSE;
+ }
+
+ if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) &&
+ (NewConfigData->RemotePort != OldConfigData->RemotePort)
+ ) {
+ //
+ // The RemotePort differs if it's designated in the configdata.
+ //
+ return FALSE;
+ }
+
+ //
+ // All checks pass, return TRUE.
+ //
+ return TRUE;
+}
+
+
+/**
+ This function builds the Ip6 configdata from the Udp6ConfigData.
+
+ @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA.
+ @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA.
+
+**/
+VOID
+Udp6BuildIp6ConfigData (
+ IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData,
+ IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData
+ )
+{
+ CopyMem (
+ Ip6ConfigData,
+ &mIp6IoDefaultIpConfigData,
+ sizeof (EFI_IP6_CONFIG_DATA)
+ );
+ Ip6ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP;
+ Ip6ConfigData->AcceptPromiscuous = Udp6ConfigData->AcceptPromiscuous;
+ IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress);
+ IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress);
+ //
+ // Use the -1 magic number to disable the receiving process of the ip instance.
+ //
+ Ip6ConfigData->ReceiveTimeout = (UINT32) (-1);
+}
+
+
+/**
+ This function validates the TxToken. It returns the error code according to the spec.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] TxToken Pointer to the token to be checked.
+
+ @retval EFI_SUCCESS The TxToken is valid.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ Token.Event is NULL;
+ Token.Packet.TxData is NULL;
+ Token.Packet.TxData.FragmentCount is zero;
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths;
+ One or more of the
+ Token.Packet.TxData.FragmentTable[].FragmentLength
+ fields is zero;
+ One or more of the
+ Token.Packet.TxData.FragmentTable[].FragmentBuffer
+ fields is NULL;
+ UdpSessionData.DestinationAddress are not valid
+ unicast IPv6 addresses if the UdpSessionData is
+ not NULL;
+ UdpSessionData.DestinationPort and
+ ConfigData.RemotePort are all zero if the
+ UdpSessionData is not NULL.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size.
+
+**/
+EFI_STATUS
+Udp6ValidateTxToken (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN EFI_UDP6_COMPLETION_TOKEN *TxToken
+ )
+{
+ EFI_UDP6_TRANSMIT_DATA *TxData;
+ UINT32 Index;
+ UINT32 TotalLen;
+ EFI_UDP6_CONFIG_DATA *ConfigData;
+ EFI_UDP6_SESSION_DATA *UdpSessionData;
+
+
+ if (TxToken->Event == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TxData = TxToken->Packet.TxData;
+
+ if ((TxData == NULL) || (TxData->FragmentCount == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TotalLen = 0;
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+
+ if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||
+ (TxData->FragmentTable[Index].FragmentLength == 0)
+ ) {
+ //
+ // If the FragmentBuffer is NULL, or the FragmentLeng is zero.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TotalLen += TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ if (TotalLen != TxData->DataLength) {
+ //
+ // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the
+ // DataLength.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ConfigData = &Instance->ConfigData;
+ UdpSessionData = TxData->UdpSessionData;
+
+ if (UdpSessionData != NULL) {
+
+ if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {
+ //
+ // Ambiguous; no avalaible DestinationPort for this token.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&
+ NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)
+ ) {
+ //
+ // The DestinationAddress is not specificed.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) &&
+ !NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)
+ ) {
+ //
+ // The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress
+ // is not zero too.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ } else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) {
+ //
+ // The configured RemoteAddress is all zero, and the user doesn't override the
+ // destination address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TxData->DataLength > UDP6_MAX_DATA_SIZE) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function checks whether the specified Token duplicates the one in the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to
+ the Token.
+ @param[in] Context Pointer to the Token to be checked.
+
+ @retval EFI_SUCCESS The Token specified by Context differs from the
+ one in the Item.
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_UDP6_COMPLETION_TOKEN *Token;
+ EFI_UDP6_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) Context;
+ TokenInItem = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;
+
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
+ //
+ // The Token duplicates with the TokenInItem in case either the two pointers are the
+ // same, or the Events of these two tokens are the same.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function calculates the checksum for the Packet, utilizing the pre-calculated
+ pseudo HeadSum to reduce some overhead.
+
+ @param[in] Packet Pointer to the NET_BUF contains the udp datagram.
+ @param[in] HeadSum Checksum of the pseudo header, execpt the length
+ field.
+
+ @return The 16-bit checksum of this udp datagram.
+
+**/
+UINT16
+Udp6Checksum (
+ IN NET_BUF *Packet,
+ IN UINT16 HeadSum
+ )
+{
+ UINT16 Checksum;
+
+ Checksum = NetbufChecksum (Packet);
+ Checksum = NetAddChecksum (Checksum, HeadSum);
+
+ Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));
+ Checksum = (UINT16) (~Checksum);
+ return Checksum;
+}
+
+
+/**
+ This function removes the specified Token from the TokenMap.
+
+ @param[in] TokenMap Pointer to the NET_MAP containing the tokens.
+ @param[in] Token Pointer to the Token to be removed.
+
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.
+
+**/
+EFI_STATUS
+Udp6RemoveToken (
+ IN NET_MAP *TokenMap,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ //
+ // Find the Token first.
+ //
+ Item = NetMapFindKey (TokenMap, (VOID *) Token);
+
+ if (Item != NULL) {
+ //
+ // Remove the token if it's found in the map.
+ //
+ NetMapRemoveItem (TokenMap, Item, NULL);
+
+ return EFI_SUCCESS;
+ }
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ This function is the packet transmitting notify function registered to the IpIo
+ interface. It's called to signal the udp TxToken when IpIo layer completes the
+ transmitting of the udp datagram.
+
+ @param[in] Status The completion status of the output udp datagram.
+ @param[in] Context Pointer to the context data.
+ @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending.
+ @param[in] NotifyData Pointer to the notify data.
+
+**/
+VOID
+EFIAPI
+Udp6DgramSent (
+ IN EFI_STATUS Status,
+ IN VOID *Context,
+ IN IP_IO_IP_PROTOCOL Sender,
+ IN VOID *NotifyData
+ )
+{
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_UDP6_COMPLETION_TOKEN *Token;
+
+ Instance = (UDP6_INSTANCE_DATA *) Context;
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) NotifyData;
+
+ if (Udp6RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {
+ //
+ // The token may be cancelled. Only signal it if the remove operation succeeds.
+ //
+ Token->Status = Status;
+ gBS->SignalEvent (Token->Event);
+ DispatchDpc ();
+ }
+}
+
+
+/**
+ This function processes the received datagram passed up by the IpIo layer.
+
+ @param[in] Status The status of this udp datagram.
+ @param[in] IcmpError The IcmpError code, only available when Status is
+ EFI_ICMP_ERROR.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA.
+ @param[in] Packet Pointer to the NET_BUF containing the received udp
+ datagram.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp6DgramRcvd (
+ IN EFI_STATUS Status,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet,
+ IN VOID *Context
+ )
+{
+ NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);
+
+ //
+ // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.
+ //
+ if (Status == EFI_SUCCESS) {
+
+ //
+ // Demultiplex the received datagram.
+ //
+ Udp6Demultiplex ((UDP6_SERVICE_DATA *) Context, NetSession, Packet);
+ } else {
+ //
+ // Handle the ICMP6 Error packet.
+ //
+ Udp6IcmpHandler ((UDP6_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);
+ }
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the rx token's events
+ // that are signaled with received data.
+ //
+ DispatchDpc ();
+}
+
+
+/**
+ This function removes the multicast group specified by Arg from the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg, it's the pointer to a
+ multicast IPv6 Address. This parameter is
+ optional and may be NULL.
+
+ @retval EFI_SUCCESS The multicast address is removed.
+ @retval EFI_ABORTED The specified multicast address is removed, and the
+ Arg is not NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6LeaveGroup (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ EFI_IPv6_ADDRESS *McastIp;
+
+ McastIp = Arg;
+
+ if ((McastIp != NULL) &&
+ !EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key))
+ ) {
+ //
+ // McastIp is not NULL and the multicast address contained in the Item
+ // is not the same as McastIp.
+ //
+ return EFI_SUCCESS;
+ }
+
+ FreePool (Item->Key);
+
+ //
+ // Remove this Item.
+ //
+ NetMapRemoveItem (Map, Item, NULL);
+
+ if (McastIp != NULL) {
+ //
+ // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.
+ //
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function cancle the token specified by Arg in the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the token to be cancelled. If NULL, all
+ the tokens in this Map will be cancelled.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token
+ is not the same as that in the Item, if Arg is not
+ NULL.
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is
+ cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6CancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ EFI_UDP6_COMPLETION_TOKEN *TokenToCancel;
+ NET_BUF *Packet;
+ IP_IO *IpIo;
+
+ if ((Arg != NULL) && (Item->Key != Arg)) {
+ return EFI_SUCCESS;
+ }
+
+ if (Item->Value != NULL) {
+ //
+ // If the token is a transmit token, the corresponding Packet is recorded in
+ // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken
+ // will invoke Udp6DgramSent, the token will be signaled and this Item will
+ // be removed from the Map there.
+ //
+ Packet = (NET_BUF *) (Item->Value);
+ IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));
+
+ IpIoCancelTxToken (IpIo, Packet);
+ } else {
+ //
+ // The token is a receive token. Abort it and remove it from the Map.
+ //
+ TokenToCancel = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key;
+ NetMapRemoveItem (Map, Item, NULL);
+
+ TokenToCancel->Status = EFI_ABORTED;
+ gBS->SignalEvent (TokenToCancel->Event);
+ }
+
+ if (Arg != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function removes all the Wrap datas in the RcvdDgramQue.
+
+ @param[in] Instance Pointer to the Udp6 Instance.
+
+**/
+VOID
+Udp6FlushRcvdDgram (
+ IN UDP6_INSTANCE_DATA *Instance
+ )
+{
+ UDP6_RXDATA_WRAP *Wrap;
+
+ while (!IsListEmpty (&Instance->RcvdDgramQue)) {
+ //
+ // Iterate all the Wraps in the RcvdDgramQue.
+ //
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);
+
+ //
+ // The Wrap will be removed from the RcvdDgramQue by this function call.
+ //
+ Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap);
+ }
+}
+
+
+
+/**
+ Cancel Udp6 tokens from the Udp6 instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Token Pointer to the token to be canceled. If NULL, all
+ tokens in this instance will be cancelled.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The Token is cancelled.
+ @retval EFI_NOT_FOUND The Token is not found.
+
+**/
+EFI_STATUS
+Udp6InstanceCancelToken (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Cancel this token from the TxTokens map.
+ //
+ Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token);
+
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ //
+ // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from
+ // the TxTokens and returns success.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Try to cancel this token from the RxTokens map in condition either the Token
+ // is NULL or the specified Token is not in TxTokens.
+ //
+ Status = NetMapIterate (&Instance->RxTokens, Udp6CancelTokens, Token);
+
+ if ((Token != NULL) && (Status == EFI_SUCCESS)) {
+ //
+ // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the
+ // TxTokens nor the RxTokens, or say, it's not found.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT ((Token != NULL) ||
+ ((0 == NetMapGetCount (&Instance->TxTokens)) &&
+ (0 == NetMapGetCount (&Instance->RxTokens)))
+ );
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function checks if the received udp datagram matches with the Instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted
+ from the received udp datagram.
+
+ @retval TRUE The udp datagram matches the receiving requirements of the Instance.
+ @retval FALSE The udp datagram does not matche the receiving requirements of the Instance.
+
+**/
+BOOLEAN
+Udp6MatchDgram (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN EFI_UDP6_SESSION_DATA *Udp6Session
+ )
+{
+ EFI_UDP6_CONFIG_DATA *ConfigData;
+ EFI_IPv6_ADDRESS Destination;
+
+ ConfigData = &Instance->ConfigData;
+
+ if (ConfigData->AcceptPromiscuous) {
+ //
+ // Always matches if this instance is in the promiscuous state.
+ //
+ return TRUE;
+ }
+
+ if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) ||
+ ((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort))
+ ) {
+ //
+ // The local port or the remote port doesn't match.
+ //
+ return FALSE;
+ }
+
+ if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) &&
+ !EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress)
+ ) {
+ //
+ // This datagram doesn't come from the instance's specified sender.
+ //
+ return FALSE;
+ }
+
+ if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) ||
+ EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress)
+ ) {
+ //
+ // The instance is configured to receive datagrams destinated to any station IP or
+ // the destination address of this datagram matches the configured station IP.
+ //
+ return TRUE;
+ }
+
+ IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress);
+
+ if (IP6_IS_MULTICAST (&Destination) &&
+ (NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination))
+ ) {
+ //
+ // It's a multicast packet and the multicast address is accepted by this instance.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ This function removes the Wrap specified by Context and release relevant resources.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp6RecycleRxDataWrap (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UDP6_RXDATA_WRAP *Wrap;
+
+ Wrap = (UDP6_RXDATA_WRAP *) Context;
+
+ //
+ // Remove the Wrap from the list it belongs to.
+ //
+ RemoveEntryList (&Wrap->Link);
+
+ //
+ // Free the Packet associated with this Wrap.
+ //
+ NetbufFree (Wrap->Packet);
+
+ //
+ // Close the event.
+ //
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);
+
+ FreePool (Wrap);
+}
+
+
+/**
+ This function wraps the Packet into RxData.
+
+ @param[in] Instance Pointer to the instance context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this
+ datagram.
+
+ @return Pointer to the structure wrapping the RxData and the Packet.
+
+**/
+UDP6_RXDATA_WRAP *
+Udp6WrapRxData (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN NET_BUF *Packet,
+ IN EFI_UDP6_RECEIVE_DATA *RxData
+ )
+{
+ EFI_STATUS Status;
+ UDP6_RXDATA_WRAP *Wrap;
+
+ //
+ // Allocate buffer for the Wrap.
+ //
+ Wrap = AllocateZeroPool (sizeof (UDP6_RXDATA_WRAP) +
+ (Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA));
+ if (Wrap == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Wrap->Link);
+
+ CopyMem (&Wrap->RxData, RxData, sizeof(EFI_UDP6_RECEIVE_DATA));
+ //
+ // Create the Recycle event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Udp6RecycleRxDataWrap,
+ Wrap,
+ &Wrap->RxData.RecycleSignal
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Wrap);
+ return NULL;
+ }
+
+ Wrap->Packet = Packet;
+ Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;
+
+ return Wrap;
+}
+
+
+/**
+ This function enqueues the received datagram into the instances' receiving queues.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+ @param[in] Packet Pointer to the buffer containing the received
+ datagram.
+ @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this
+ datagram.
+
+ @return The times this datagram is enqueued.
+
+**/
+UINTN
+Udp6EnqueueDgram (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN NET_BUF *Packet,
+ IN EFI_UDP6_RECEIVE_DATA *RxData
+ )
+{
+ LIST_ENTRY *Entry;
+ UDP6_INSTANCE_DATA *Instance;
+ UDP6_RXDATA_WRAP *Wrap;
+ UINTN Enqueued;
+
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
+ //
+ // Iterate the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ if (Udp6MatchDgram (Instance, &RxData->UdpSession)) {
+ //
+ // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.
+ //
+ Wrap = Udp6WrapRxData (Instance, Packet, RxData);
+ if (Wrap == NULL) {
+ continue;
+ }
+
+ NET_GET_REF (Packet);
+
+ InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link);
+
+ Enqueued++;
+ }
+ }
+
+ return Enqueued;
+}
+
+
+/**
+ This function delivers the received datagrams to the specified instance.
+
+ @param[in] Instance Pointer to the instance context data.
+
+**/
+VOID
+Udp6InstanceDeliverDgram (
+ IN UDP6_INSTANCE_DATA *Instance
+ )
+{
+ UDP6_RXDATA_WRAP *Wrap;
+ EFI_UDP6_COMPLETION_TOKEN *Token;
+ NET_BUF *Dup;
+ EFI_UDP6_RECEIVE_DATA *RxData;
+ EFI_TPL OldTpl;
+
+ if (!IsListEmpty (&Instance->RcvdDgramQue) &&
+ !NetMapIsEmpty (&Instance->RxTokens)
+ ) {
+
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link);
+
+ if (NET_BUF_SHARED (Wrap->Packet)) {
+ //
+ // Duplicate the Packet if it is shared between instances.
+ //
+ Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);
+ if (Dup == NULL) {
+ return;
+ }
+
+ NetbufFree (Wrap->Packet);
+
+ Wrap->Packet = Dup;
+ }
+
+ NetListRemoveHead (&Instance->RcvdDgramQue);
+
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
+
+ //
+ // Build the FragmentTable and set the FragmentCount in RxData.
+ //
+ RxData = &Wrap->RxData;
+ RxData->FragmentCount = Wrap->Packet->BlockOpNum;
+
+ NetbufBuildExt (
+ Wrap->Packet,
+ (NET_FRAGMENT *) RxData->FragmentTable,
+ &RxData->FragmentCount
+ );
+
+ Token->Status = EFI_SUCCESS;
+ Token->Packet.RxData = &Wrap->RxData;
+
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link);
+ gBS->RestoreTPL (OldTpl);
+
+ gBS->SignalEvent (Token->Event);
+ }
+}
+
+
+/**
+ This function delivers the datagrams enqueued in the instances.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+
+**/
+VOID
+Udp6DeliverDgram (
+ IN UDP6_SERVICE_DATA *Udp6Service
+ )
+{
+ LIST_ENTRY *Entry;
+ UDP6_INSTANCE_DATA *Instance;
+
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
+ //
+ // Iterate the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ //
+ // Deliver the datagrams of this instance.
+ //
+ Udp6InstanceDeliverDgram (Instance);
+ }
+}
+
+
+/**
+ This function demultiplexes the received udp datagram to the appropriate instances.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from
+ the received datagram.
+ @param[in] Packet Pointer to the buffer containing the received udp
+ datagram.
+
+**/
+VOID
+Udp6Demultiplex (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_UDP_HEADER *Udp6Header;
+ UINT16 HeadSum;
+ EFI_UDP6_RECEIVE_DATA RxData;
+ EFI_UDP6_SESSION_DATA *Udp6Session;
+ UINTN Enqueued;
+
+ //
+ // Get the datagram header from the packet buffer.
+ //
+ Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Udp6Header != NULL);
+
+ if (Udp6Header->Checksum != 0) {
+ //
+ // check the checksum.
+ //
+ HeadSum = NetIp6PseudoHeadChecksum (
+ &NetSession->Source.v6,
+ &NetSession->Dest.v6,
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+
+ if (Udp6Checksum (Packet, HeadSum) != 0) {
+ //
+ // Wrong checksum.
+ //
+ return;
+ }
+ }
+
+ gRT->GetTime (&RxData.TimeStamp, NULL);
+
+ Udp6Session = &RxData.UdpSession;
+ Udp6Session->SourcePort = NTOHS (Udp6Header->SrcPort);
+ Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort);
+
+ IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source);
+ IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest);
+
+ //
+ // Trim the UDP header.
+ //
+ NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE);
+
+ RxData.DataLength = (UINT32) Packet->TotalSize;
+
+ //
+ // Try to enqueue this datagram into the instances.
+ //
+ Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData);
+
+ if (Enqueued == 0) {
+ //
+ // Send the port unreachable ICMP packet before we free this NET_BUF
+ //
+ Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header);
+ }
+
+ //
+ // Try to free the packet before deliver it.
+ //
+ NetbufFree (Packet);
+
+ if (Enqueued > 0) {
+ //
+ // Deliver the datagram.
+ //
+ Udp6DeliverDgram (Udp6Service);
+ }
+}
+
+
+/**
+ This function builds and sends out a icmp port unreachable message.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet
+ causes this icmp error message.
+ @param[in] Udp6Header Pointer to the udp header of the datagram causes
+ this icmp error message.
+
+**/
+VOID
+Udp6SendPortUnreach (
+ IN IP_IO *IpIo,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN VOID *Udp6Header
+ )
+{
+ NET_BUF *Packet;
+ UINT32 Len;
+ IP6_ICMP_ERROR_HEAD *IcmpErrHdr;
+ UINT8 *Ptr;
+ IP_IO_OVERRIDE Override;
+ IP_IO_IP_INFO *IpSender;
+ EFI_IP6_MODE_DATA *Ip6ModeData;
+ EFI_STATUS Status;
+ EFI_IP6_PROTOCOL *Ip6Protocol;
+
+ Ip6ModeData = NULL;
+
+ //
+ // An ICMPv6 error message MUST NOT be originated as A packet destined to
+ // 1) an IPv6 multicast address 2) The IPv6 Unspecified Address
+ //
+ if (NetSession->IpVersion == IP_VERSION_6) {
+ if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) ||
+ IP6_IS_MULTICAST (&NetSession->Dest.v6)
+ ) {
+ goto EXIT;
+ }
+ }
+
+
+ IpSender = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest);
+
+ //
+ // Get the Ipv6 Mode Data.
+ //
+ Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA));
+ ASSERT (Ip6ModeData != NULL);
+
+ //
+ // If not finding the related IpSender use the default IpIo to send out
+ // the port unreachable ICMP message.
+ //
+ if (IpSender == NULL) {
+ Ip6Protocol = IpIo->Ip.Ip6;
+ } else {
+ Ip6Protocol = IpSender->Ip.Ip6;
+ }
+
+ Status = Ip6Protocol->GetModeData (
+ Ip6Protocol,
+ Ip6ModeData,
+ NULL,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+ //
+ // The ICMP6 packet length, includes whole invoking packet and ICMP6 error header.
+ //
+ Len = NetSession->IpHdrLen +
+ NTOHS(((EFI_UDP_HEADER *) Udp6Header)->Length) +
+ sizeof (IP6_ICMP_ERROR_HEAD);
+
+ //
+ // If the ICMP6 packet length larger than IP MTU, adjust its length to MTU.
+ //
+ if (Ip6ModeData->MaxPacketSize < Len) {
+ Len = Ip6ModeData->MaxPacketSize;
+ }
+
+ //
+ // Allocate buffer for the icmp error message.
+ //
+ Packet = NetbufAlloc (Len);
+ if (Packet == NULL) {
+ goto EXIT;
+ }
+
+ //
+ // Allocate space for the IP6_ICMP_ERROR_HEAD.
+ //
+ IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);
+ ASSERT (IcmpErrHdr != NULL);
+
+ //
+ // Set the required fields for the icmp port unreachable message.
+ //
+ IcmpErrHdr->Head.Type = ICMP_V6_DEST_UNREACHABLE;
+ IcmpErrHdr->Head.Code = ICMP_V6_PORT_UNREACHABLE;
+ IcmpErrHdr->Head.Checksum = 0;
+ IcmpErrHdr->Fourth = 0;
+
+ //
+ // Copy as much of invoking Packet as possible without the ICMPv6 packet
+ // exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains
+ // the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD
+ // for pointer movement that fact should be considered.
+ //
+ Ptr = (VOID *) &IcmpErrHdr->Head;
+ Ptr = (UINT8 *) (UINTN) ((UINTN) Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER));
+ CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen);
+ CopyMem (
+ Ptr + NetSession->IpHdrLen,
+ Udp6Header,
+ Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER)
+ );
+
+ //
+ // Set the checksum as zero, and IP6 driver will calcuate it with pseudo header.
+ //
+ IcmpErrHdr->Head.Checksum = 0;
+
+ //
+ // Fill the override data.
+ //
+ Override.Ip6OverrideData.FlowLabel = 0;
+ Override.Ip6OverrideData.HopLimit = 255;
+ Override.Ip6OverrideData.Protocol = IP6_ICMP;
+
+ //
+ // Send out this icmp packet.
+ //
+ IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override);
+
+ NetbufFree (Packet);
+
+EXIT:
+ if (Ip6ModeData != NULL) {
+ FreePool (Ip6ModeData);
+ }
+}
+
+
+/**
+ This function handles the received Icmp Error message and de-multiplexes it to the
+ instance.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+ @param[in] IcmpError The icmp error code.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted
+ from the received Icmp Error packet.
+ @param[in, out] Packet Pointer to the Icmp Error packet.
+
+**/
+VOID
+Udp6IcmpHandler (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN UINT8 IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN OUT NET_BUF *Packet
+ )
+{
+ EFI_UDP_HEADER *Udp6Header;
+ EFI_UDP6_SESSION_DATA Udp6Session;
+ LIST_ENTRY *Entry;
+ UDP6_INSTANCE_DATA *Instance;
+
+ Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Udp6Header != NULL);
+
+ IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source);
+ IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest);
+
+ Udp6Session.SourcePort = NTOHS (Udp6Header->DstPort);
+ Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort);
+
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
+ //
+ // Iterate all the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ if (Udp6MatchDgram (Instance, &Udp6Session)) {
+ //
+ // Translate the Icmp Error code according to the udp spec.
+ //
+ Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL);
+
+ if (IcmpError > ICMP_ERR_UNREACH_PORT) {
+ Instance->IcmpError = EFI_ICMP_ERROR;
+ }
+
+ //
+ // Notify the instance with the received Icmp Error.
+ //
+ Udp6ReportIcmpError (Instance);
+
+ break;
+ }
+ }
+
+ NetbufFree (Packet);
+}
+
+
+/**
+ This function reports the received ICMP error.
+
+ @param[in] Instance Pointer to the udp instance context data.
+
+**/
+VOID
+Udp6ReportIcmpError (
+ IN UDP6_INSTANCE_DATA *Instance
+ )
+{
+ EFI_UDP6_COMPLETION_TOKEN *Token;
+
+ if (NetMapIsEmpty (&Instance->RxTokens)) {
+ //
+ // There are no receive tokens to deliver the ICMP error.
+ //
+ return;
+ }
+
+ if (EFI_ERROR (Instance->IcmpError)) {
+ //
+ // Try to get a RxToken from the RxTokens map.
+ //
+ Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
+
+ if (Token != NULL) {
+ //
+ // Report the error through the Token.
+ //
+ Token->Status = Instance->IcmpError;
+ gBS->SignalEvent (Token->Event);
+
+ //
+ // Clear the IcmpError.
+ //
+ Instance->IcmpError = EFI_SUCCESS;
+ }
+ }
+}
+
+
+/**
+ This function is a dummy ext-free function for the NET_BUF created for the output
+ udp datagram.
+
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp6NetVectorExtFree (
+ IN VOID *Context
+ )
+{
+}
+
+
+/**
+ Set the Udp6 variable data.
+
+ @param[in] Udp6Service Udp6 service data.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the
+ variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Udp6SetVariableData (
+ IN UDP6_SERVICE_DATA *Udp6Service
+ )
+{
+ UINT32 NumConfiguredInstance;
+ LIST_ENTRY *Entry;
+ UINTN VariableDataSize;
+ EFI_UDP6_VARIABLE_DATA *Udp6VariableData;
+ EFI_UDP6_SERVICE_POINT *Udp6ServicePoint;
+ UDP6_INSTANCE_DATA *Udp6Instance;
+ CHAR16 *NewMacString;
+ EFI_STATUS Status;
+
+ NumConfiguredInstance = 0;
+
+ //
+ // Go through the children list to count the configured children.
+ //
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
+ Udp6Instance = NET_LIST_USER_STRUCT_S (
+ Entry,
+ UDP6_INSTANCE_DATA,
+ Link,
+ UDP6_INSTANCE_DATA_SIGNATURE
+ );
+
+ if (Udp6Instance->Configured) {
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Calculate the size of the Udp6VariableData. As there may be no Udp6 child,
+ // we should add extra buffer for the service points only if the number of configured
+ // children is more than 1.
+ //
+ VariableDataSize = sizeof (EFI_UDP6_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_UDP6_SERVICE_POINT) * (NumConfiguredInstance - 1);
+ }
+
+ Udp6VariableData = AllocateZeroPool (VariableDataSize);
+ if (Udp6VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Udp6VariableData->DriverHandle = Udp6Service->ImageHandle;
+ Udp6VariableData->ServiceCount = NumConfiguredInstance;
+
+ Udp6ServicePoint = &Udp6VariableData->Services[0];
+
+ //
+ // Go through the children list to fill the configured children's address pairs.
+ //
+ NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) {
+ Udp6Instance = NET_LIST_USER_STRUCT_S (
+ Entry,
+ UDP6_INSTANCE_DATA,
+ Link,
+ UDP6_INSTANCE_DATA_SIGNATURE
+ );
+
+ if (Udp6Instance->Configured) {
+ Udp6ServicePoint->InstanceHandle = Udp6Instance->ChildHandle;
+ Udp6ServicePoint->LocalPort = Udp6Instance->ConfigData.StationPort;
+ Udp6ServicePoint->RemotePort = Udp6Instance->ConfigData.RemotePort;
+
+ IP6_COPY_ADDRESS (
+ &Udp6ServicePoint->LocalAddress,
+ &Udp6Instance->ConfigData.StationAddress
+ );
+ IP6_COPY_ADDRESS (
+ &Udp6ServicePoint->RemoteAddress,
+ &Udp6Instance->ConfigData.RemoteAddress
+ );
+ Udp6ServicePoint++;
+ }
+ }
+
+ //
+ // Get the MAC string.
+ //
+ Status = NetLibGetMacString (
+ Udp6Service->ControllerHandle,
+ Udp6Service->ImageHandle,
+ &NewMacString
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ if (Udp6Service->MacString != NULL) {
+ //
+ // The variable is set already, we're going to update it.
+ //
+ if (StrCmp (Udp6Service->MacString, NewMacString) != 0) {
+ //
+ // The MAC address is changed, delete the previous variable first.
+ //
+ gRT->SetVariable (
+ Udp6Service->MacString,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+ }
+
+ FreePool (Udp6Service->MacString);
+ }
+
+ Udp6Service->MacString = NewMacString;
+
+ Status = gRT->SetVariable (
+ Udp6Service->MacString,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableDataSize,
+ (VOID *) Udp6VariableData
+ );
+
+EXIT:
+
+ FreePool (Udp6VariableData);
+
+ return Status;
+}
+
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in, out] Udp6Service Udp6 service data.
+
+**/
+VOID
+Udp6ClearVariableData (
+ IN OUT UDP6_SERVICE_DATA *Udp6Service
+ )
+{
+ ASSERT (Udp6Service->MacString != NULL);
+
+ gRT->SetVariable (
+ Udp6Service->MacString,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+
+ FreePool (Udp6Service->MacString);
+ Udp6Service->MacString = NULL;
+}
+
+
+/**
+ Find the key in the netmap.
+
+ @param[in] Map The netmap to search within.
+ @param[in] Key The key to search.
+
+ @return The point to the item contains the Key, or NULL, if Key isn't in the map.
+
+**/
+NET_MAP_ITEM *
+Udp6MapMultiCastAddr (
+ IN NET_MAP *Map,
+ IN VOID *Key
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_MAP_ITEM *Item;
+ EFI_IPv6_ADDRESS *Addr;
+
+ ASSERT (Map != NULL);
+ NET_LIST_FOR_EACH (Entry, &Map->Used) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+ Addr = (EFI_IPv6_ADDRESS *) Item->Key;
+ if (EFI_IP6_EQUAL (Addr, Key)) {
+ return Item;
+ }
+ }
+ return NULL;
+}
+
diff --git a/NetworkPkg/Udp6Dxe/Udp6Impl.h b/NetworkPkg/Udp6Dxe/Udp6Impl.h
new file mode 100644
index 0000000000..108e30b71c
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Impl.h
@@ -0,0 +1,673 @@
+/** @file
+ Udp6 driver's whole implementation and internal data structures.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _UDP6_IMPL_H_
+#define _UDP6_IMPL_H_
+
+#include <Uefi.h>
+
+#include <Protocol/Ip6.h>
+#include <Protocol/Udp6.h>
+
+#include <Library/IpIoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DpcLib.h>
+
+#include "Udp6Driver.h"
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName;
+extern EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding;
+extern EFI_UDP6_PROTOCOL mUdp6Protocol;
+extern UINT16 mUdp6RandomPort;
+
+//
+// Define time out 50 milliseconds
+//
+#define UDP6_TIMEOUT_INTERVAL (50 * TICKS_PER_MS)
+#define UDP6_HEADER_SIZE sizeof (EFI_UDP_HEADER)
+#define UDP6_MAX_DATA_SIZE 65507
+#define UDP6_PORT_KNOWN 1024
+
+#define UDP6_SERVICE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', '6')
+#define UDP6_INSTANCE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', 'S')
+
+#define UDP6_SERVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ UDP6_SERVICE_DATA, \
+ ServiceBinding, \
+ UDP6_SERVICE_DATA_SIGNATURE \
+ )
+
+#define UDP6_INSTANCE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ UDP6_INSTANCE_DATA, \
+ Udp6Proto, \
+ UDP6_INSTANCE_DATA_SIGNATURE \
+ )
+//
+// Udp6 service contest data
+//
+typedef struct _UDP6_SERVICE_DATA {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE ControllerHandle;
+ LIST_ENTRY ChildrenList;
+ UINTN ChildrenNumber;
+ IP_IO *IpIo;
+ EFI_EVENT TimeoutEvent;
+ CHAR16 *MacString;
+} UDP6_SERVICE_DATA;
+
+typedef struct _UDP6_INSTANCE_DATA {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ UDP6_SERVICE_DATA *Udp6Service;
+ EFI_UDP6_PROTOCOL Udp6Proto;
+ EFI_UDP6_CONFIG_DATA ConfigData;
+ EFI_HANDLE ChildHandle;
+ BOOLEAN Configured;
+ BOOLEAN IsNoMapping;
+ NET_MAP TxTokens;
+ NET_MAP RxTokens;
+ NET_MAP McastIps;
+ LIST_ENTRY RcvdDgramQue;
+ LIST_ENTRY DeliveredDgramQue;
+ UINT16 HeadSum;
+ EFI_STATUS IcmpError;
+ IP_IO_IP_INFO *IpInfo;
+ BOOLEAN Destroyed;
+} UDP6_INSTANCE_DATA;
+
+typedef struct _UDP6_RXDATA_WRAP {
+ LIST_ENTRY Link;
+ NET_BUF *Packet;
+ UINT32 TimeoutTick;
+ EFI_UDP6_RECEIVE_DATA RxData;
+} UDP6_RXDATA_WRAP;
+
+/**
+ Clean the Udp service context data.
+
+ @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA.
+
+**/
+VOID
+Udp6CleanService (
+ IN OUT UDP6_SERVICE_DATA *Udp6Service
+ );
+
+/**
+ Create the Udp service context data.
+
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.
+ @param[in] ImageHandle The image handle of this udp6 driver.
+ @param[in] ControllerHandle The controller handle this udp6 driver binds on.
+
+ @retval EFI_SUCCESS The udp6 service context data was created and
+ initialized.
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.
+ @retval Others An error condition occurred.
+
+**/
+EFI_STATUS
+Udp6CreateService (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+/**
+ Set the Udp6 variable data.
+
+ @param[in] Udp6Service Udp6 service data.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the
+ variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Udp6SetVariableData (
+ IN UDP6_SERVICE_DATA *Udp6Service
+ );
+
+/**
+ This function cleans the udp instance.
+
+ @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean.
+
+**/
+VOID
+Udp6CleanInstance (
+ IN OUT UDP6_INSTANCE_DATA *Instance
+ );
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in, out] Udp6Service Udp6 service data.
+
+**/
+VOID
+Udp6ClearVariableData (
+ IN OUT UDP6_SERVICE_DATA *Udp6Service
+ );
+
+/**
+ This function intializes the new created udp instance.
+
+ @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA.
+ @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA.
+
+**/
+VOID
+Udp6InitInstance (
+ IN UDP6_SERVICE_DATA *Udp6Service,
+ IN OUT UDP6_INSTANCE_DATA *Instance
+ );
+
+/**
+ This function reports the received ICMP error.
+
+ @param[in] Instance Pointer to the udp instance context data.
+
+**/
+VOID
+Udp6ReportIcmpError (
+ IN UDP6_INSTANCE_DATA *Instance
+ );
+
+/**
+ This function copies the current operational settings of this EFI UDPv6 Protocol
+ instance into user-supplied buffers. This function is used optionally to retrieve
+ the operational mode data of underlying networks or drivers.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[out] Udp6ConfigData The buffer in which the current UDP configuration
+ data is returned. This parameter is optional and
+ may be NULL.
+ @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol
+ mode data is returned. This parameter is optional
+ and may be NULL.
+ @param[out] MnpConfigData The buffer in which the current managed network
+ configuration data is returned. This parameter
+ is optional and may be NULL.
+ @param[out] SnpModeData The buffer in which the simple network mode data
+ is returned. This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration
+ data is available because this instance has not
+ been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6GetModeData (
+ IN EFI_UDP6_PROTOCOL *This,
+ OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL,
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+/**
+ This function is used to do the following:
+ Initialize and start this instance of the EFI UDPv6 Protocol.
+ Change the filtering rules and operational parameters.
+ Reset this instance of the EFI UDPv6 Protocol.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] UdpConfigData Pointer to the buffer to set the configuration
+ data. This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The configuration settings were set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set
+ to true and there is no address available for IP6
+ driver to binding source address to this
+ instance.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ This is NULL.
+ UdpConfigData.StationAddress is not a valid
+ unicast IPv6 address.
+ UdpConfigData.RemoteAddress is not a valid unicast
+ IPv6 address, if it is not zero.
+ @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already
+ started/configured and must be stopped/reset
+ before it can be reconfigured. Only TrafficClass,
+ HopLimit, ReceiveTimeout, and TransmitTimeout can
+ be reconfigured without stopping the current
+ instance of the EFI UDPv6 Protocol.
+ @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE, and
+ UdpConfigData.StationPort is already used by another
+ instance.
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate
+ memory for this EFI UDPv6 Protocol instance.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and
+ this instance was not opened.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Configure (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL
+ );
+
+/**
+ This function places a sending request to this instance of the EFI UDPv6 Protocol,
+ alongside the transmit data that was filled by the user.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] Token Pointer to the completion token that will be
+ placed into the transmit queue.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for
+ choosing a source address for this instance, but
+ no source address was available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ This is NULL. Token is NULL. Token.Event is NULL.
+ Token.Packet.TxData is NULL.
+ Token.Packet.TxData.FragmentCount is zero.
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths.
+ One or more of the
+ Token.Packet.TxData.FragmentTable[]
+ .FragmentLength fields is zero.
+ One or more of the
+ Token.Packet.TxData.FragmentTable[]
+ .FragmentBuffer fields is NULL.
+ One or more of the
+ Token.Packet.TxData.UdpSessionData.
+ DestinationAddres are not valid unicast IPv6
+ addresses, if the UdpSessionData is not NULL.
+ Token.Packet.TxData.UdpSessionData.
+ DestinationAddres is NULL
+ Token.Packet.TxData.UdpSessionData.
+ DestinatioPort is zero.
+ Token.Packet.TxData.UdpSessionData is
+ NULL and this instance's
+ UdpConfigData.RemoteAddress is unspecified.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same
+ Token.Event is already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_NOT_FOUND There is no route to the destination network or
+ address.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size. Or the length of the IP header + UDP
+ header + data length is greater than MTU if
+ DoNotFragment is TRUE.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Transmit (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token
+ );
+
+/**
+ This function places a completion token into the receive packet queue. This function
+ is always asynchronous.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token is cached.
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Token is NULL.
+ Token.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources (usually
+ memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI UDPv6 Protocol instance has been reset to
+ startup defaults.
+ @retval EFI_ACCESS_DENIED A receive completion token with the same
+ Token.Event is already in the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Receive (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token
+ );
+
+/**
+ This function is used to abort a pending transmit or receive request.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_UDP6_PROTOCOL.Transmit() or
+ EFI_UDP6_PROTOCOL.Receive(). This parameter is
+ optional and may be NULL.
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and
+ Token.Event is signaled. When Token is NULL, all
+ pending requests are aborted and their events are
+ signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O
+ request is not found in the transmit or receive
+ queue. It either completed or was not issued by
+ Transmit() or Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Cancel (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ This function can be used by network drivers and applications to increase the rate that
+ data packets are moved between the communications device and the transmit/receive queues.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or
+ receive queue.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Poll (
+ IN EFI_UDP6_PROTOCOL *This
+ );
+
+/**
+ This function is used to enable and disable the multicast group filtering.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join a multicast group. Set to
+ FALSE to leave one or all multicast groups.
+ @param[in] MulticastAddress Pointer to multicast group address to join or
+ leave. This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been
+ started.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. JoinFlag is TRUE and
+ MulticastAddress is NULL. JoinFlag is TRUE and
+ *MulticastAddress is not a valid multicast
+ address.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table
+ (when JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when
+ JoinFlag is FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Groups (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL
+ );
+
+/**
+ This function tries to bind the udp instance according to the configured port
+ allocation stragety.
+
+ @param[in] InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param[in] ConfigData Pointer to the ConfigData of the instance to be
+ bound.
+
+ @retval EFI_SUCCESS The bound operation completed successfully.
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is
+ already used by another instance.
+ @retval EFI_OUT_OF_RESOURCES No available port resources.
+
+**/
+EFI_STATUS
+Udp6Bind (
+ IN LIST_ENTRY *InstanceList,
+ IN EFI_UDP6_CONFIG_DATA *ConfigData
+ );
+
+/**
+ This function builds the Ip6 configdata from the Udp6ConfigData.
+
+ @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA.
+ @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA.
+
+**/
+VOID
+Udp6BuildIp6ConfigData (
+ IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData,
+ IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData
+ );
+
+/**
+ This function checks whether the specified Token duplicates with the one in the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to
+ the Token.
+ @param[in] Context Pointer to the Token to be checked.
+
+ @retval EFI_SUCCESS The Token specified by Context differs from the
+ one in the Item.
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ );
+
+/**
+ This function removes the specified Token from the TokenMap.
+
+ @param[in] TokenMap Pointer to the NET_MAP containing the tokens.
+ @param[in] Token Pointer to the Token to be removed.
+
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.
+
+**/
+EFI_STATUS
+Udp6RemoveToken (
+ IN NET_MAP *TokenMap,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token
+ );
+
+/**
+ This function is used to check whether the NewConfigData has any un-reconfigurable
+ parameters changed compared to the OldConfigData.
+
+ @param[in] OldConfigData Pointer to the current ConfigData the udp instance
+ uses.
+ @param[in] NewConfigData Pointer to the new ConfigData.
+
+ @retval TRUE The instance is reconfigurable according to NewConfigData.
+ @retval FALSE The instance is not reconfigurable according to NewConfigData.
+
+**/
+BOOLEAN
+Udp6IsReconfigurable (
+ IN EFI_UDP6_CONFIG_DATA *OldConfigData,
+ IN EFI_UDP6_CONFIG_DATA *NewConfigData
+ );
+
+/**
+ This function removes the multicast group specified by Arg from the Map.
+
+ @param[in] Map Pointer to the NET_MAP.
+ @param[in] Item Pointer to the NET_MAP_ITEM.
+ @param[in] Arg Pointer to the Arg. It is the pointer to a
+ multicast IPv6 Address. This parameter is
+ optional and may be NULL.
+
+ @retval EFI_SUCCESS The multicast address is removed.
+ @retval EFI_ABORTED The specified multicast address is removed, and the
+ Arg is not NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6LeaveGroup (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ );
+
+/**
+ This function validates the TxToken, it returns the error code according to the spec.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] TxToken Pointer to the token to be checked.
+
+ @retval EFI_SUCCESS The TxToken is valid.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ Token.Event is NULL.
+ Token.Packet.TxData is NULL.
+ Token.Packet.TxData.FragmentCount is zero.
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths.
+ One or more of the
+ Token.Packet.TxData.FragmentTable[].FragmentLength
+ fields is zero.
+ One or more of the
+ Token.Packet.TxData.FragmentTable[].FragmentBuffer
+ fields is NULL.
+ UdpSessionData.DestinationAddress are not valid
+ unicast IPv6 addresses if the UdpSessionData is
+ not NULL.
+ UdpSessionData.DestinationPort and
+ ConfigData.RemotePort are all zero if the
+ UdpSessionData is not NULL.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size.
+
+**/
+EFI_STATUS
+Udp6ValidateTxToken (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN EFI_UDP6_COMPLETION_TOKEN *TxToken
+ );
+
+/**
+ This function is a dummy ext-free function for the NET_BUF created for the output
+ udp datagram.
+
+ @param[in] Context Pointer to the context data.
+
+**/
+VOID
+EFIAPI
+Udp6NetVectorExtFree (
+ IN VOID *Context
+ );
+
+/**
+ This function calculates the checksum for the Packet, utilizing the pre-calculated
+ pseudo header to reduce overhead.
+
+ @param[in] Packet Pointer to the NET_BUF contains the udp datagram.
+ @param[in] HeadSum Checksum of the pseudo header execpt the length
+ field.
+
+ @return The 16-bit checksum of this udp datagram.
+
+**/
+UINT16
+Udp6Checksum (
+ IN NET_BUF *Packet,
+ IN UINT16 HeadSum
+ );
+
+/**
+ This function delivers the received datagrams to the specified instance.
+
+ @param[in] Instance Pointer to the instance context data.
+
+**/
+VOID
+Udp6InstanceDeliverDgram (
+ IN UDP6_INSTANCE_DATA *Instance
+ );
+
+/**
+ Cancel Udp6 tokens from the Udp6 instance.
+
+ @param[in] Instance Pointer to the udp instance context data.
+ @param[in] Token Pointer to the token to be canceled. If NULL, all
+ tokens in this instance will be cancelled.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The Token is cancelled.
+ @retval EFI_NOT_FOUND The Token is not found.
+
+**/
+EFI_STATUS
+Udp6InstanceCancelToken (
+ IN UDP6_INSTANCE_DATA *Instance,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+/**
+ This function removes all the Wrap datas in the RcvdDgramQue.
+
+ @param[in] Instance Pointer to the Udp6 Instance.
+
+**/
+VOID
+Udp6FlushRcvdDgram (
+ IN UDP6_INSTANCE_DATA *Instance
+ );
+
+#endif
+
diff --git a/NetworkPkg/Udp6Dxe/Udp6Main.c b/NetworkPkg/Udp6Dxe/Udp6Main.c
new file mode 100644
index 0000000000..0cad596276
--- /dev/null
+++ b/NetworkPkg/Udp6Dxe/Udp6Main.c
@@ -0,0 +1,855 @@
+/** @file
+ Contains all EFI_UDP6_PROTOCOL interfaces.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Udp6Impl.h"
+
+EFI_UDP6_PROTOCOL mUdp6Protocol = {
+ Udp6GetModeData,
+ Udp6Configure,
+ Udp6Groups,
+ Udp6Transmit,
+ Udp6Receive,
+ Udp6Cancel,
+ Udp6Poll
+};
+
+
+/**
+ This function copies the current operational settings of this EFI UDPv6 Protocol
+ instance into user-supplied buffers. This function is used optionally to retrieve
+ the operational mode data of underlying networks or drivers.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[out] Udp6ConfigData The buffer in which the current UDP configuration
+ data is returned. This parameter is optional and
+ may be NULL.
+ @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol
+ mode data is returned. This parameter is optional
+ and may be NULL.
+ @param[out] MnpConfigData The buffer in which the current managed network
+ configuration data is returned. This parameter is
+ optional and may be NULL.
+ @param[out] SnpModeData The buffer in which the simple network mode data
+ is returned. This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration
+ data is available because this instance has not
+ been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6GetModeData (
+ IN EFI_UDP6_PROTOCOL *This,
+ OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL,
+ OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_IP6_PROTOCOL *Ip;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured && (Udp6ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Udp6ConfigData != NULL) {
+ //
+ // Set the Udp6ConfigData.
+ //
+ CopyMem (Udp6ConfigData, &Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA));
+ }
+
+ Ip = Instance->IpInfo->Ip.Ip6;
+
+ //
+ // Get the underlying Ip6ModeData, MnpConfigData and SnpModeData.
+ //
+ Status = Ip->GetModeData (Ip, Ip6ModeData, MnpConfigData, SnpModeData);
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function is used to do the following:
+ Initialize and start this instance of the EFI UDPv6 Protocol.
+ Change the filtering rules and operational parameters.
+ Reset this instance of the EFI UDPv6 Protocol.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] UdpConfigData Pointer to the buffer to set the configuration
+ data. This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The configuration settings were set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set
+ to true and there is no address available for the IP6
+ driver to bind a source address to this instance.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ This is NULL.
+ UdpConfigData.StationAddress is not a valid
+ unicast IPv6 address.
+ UdpConfigData.RemoteAddress is not a valid unicast
+ IPv6 address if it is not zero.
+ @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already
+ started/configured and must be stopped/reset
+ before it can be reconfigured. Only TrafficClass,
+ HopLimit, ReceiveTimeout, and TransmitTimeout can
+ be reconfigured without stopping the current
+ instance of the EFI UDPv6 Protocol.
+ @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE and
+ UdpConfigData.StationPort is already used by another
+ instance.
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate
+ memory for this EFI UDPv6 Protocol instance.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and
+ this instance was not opened.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Configure (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP6_INSTANCE_DATA *Instance;
+ UDP6_SERVICE_DATA *Udp6Service;
+ EFI_TPL OldTpl;
+ EFI_IPv6_ADDRESS StationAddress;
+ EFI_IPv6_ADDRESS RemoteAddress;
+ EFI_IP6_CONFIG_DATA Ip6ConfigData;
+ EFI_IPv6_ADDRESS LocalAddr;
+ EFI_IPv6_ADDRESS RemoteAddr;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured && (UdpConfigData == NULL)) {
+ return EFI_SUCCESS;
+ }
+
+ Udp6Service = Instance->Udp6Service;
+ Status = EFI_SUCCESS;
+ ASSERT (Udp6Service != NULL);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (UdpConfigData != NULL) {
+
+ IP6_COPY_ADDRESS (&StationAddress, &UdpConfigData->StationAddress);
+ IP6_COPY_ADDRESS (&RemoteAddress, &UdpConfigData->RemoteAddress);
+
+ if ((!NetIp6IsUnspecifiedAddr (&StationAddress) && !NetIp6IsValidUnicast (&StationAddress)) ||
+ (!NetIp6IsUnspecifiedAddr (&RemoteAddress) && !NetIp6IsValidUnicast (&RemoteAddress))
+ ){
+ //
+ // If not use default address, and StationAddress is not a valid unicast
+ // if it is not IPv6 address or RemoteAddress is not a valid unicast IPv6
+ // address if it is not 0.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (Instance->Configured) {
+ //
+ // The instance is already configured, try to do the re-configuration.
+ //
+ if (!Udp6IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {
+ //
+ // If the new configuration data wants to change some unreconfigurable
+ // settings, return EFI_ALREADY_STARTED.
+ //
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Save the reconfigurable parameters.
+ //
+ Instance->ConfigData.TrafficClass = UdpConfigData->TrafficClass;
+ Instance->ConfigData.HopLimit = UdpConfigData->HopLimit;
+ Instance->ConfigData.ReceiveTimeout = UdpConfigData->ReceiveTimeout;
+ Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;
+ } else {
+ //
+ // Construct the Ip configuration data from the UdpConfigData.
+ //
+ Udp6BuildIp6ConfigData (UdpConfigData, &Ip6ConfigData);
+
+ //
+ // Configure the Ip instance wrapped in the IpInfo.
+ //
+ Status = IpIoConfigIp (Instance->IpInfo, &Ip6ConfigData);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NO_MAPPING) {
+ Instance->IsNoMapping = TRUE;
+ }
+
+ goto ON_EXIT;
+ }
+
+ Instance->IsNoMapping = FALSE;
+
+ //
+ // Save the configuration data.
+ //
+ CopyMem (
+ &Instance->ConfigData,
+ UdpConfigData,
+ sizeof (EFI_UDP6_CONFIG_DATA)
+ );
+ IP6_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip6ConfigData.StationAddress);
+ //
+ // Try to allocate the required port resource.
+ //
+ Status = Udp6Bind (&Udp6Service->ChildrenList, &Instance->ConfigData);
+ if (EFI_ERROR (Status)) {
+ //
+ // Reset the ip instance if bind fails.
+ //
+ IpIoConfigIp (Instance->IpInfo, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // Pre calculate the checksum for the pseudo head, ignore the UDP length first.
+ //
+ IP6_COPY_ADDRESS (&LocalAddr, &Instance->ConfigData.StationAddress);
+ IP6_COPY_ADDRESS (&RemoteAddr, &Instance->ConfigData.RemoteAddress);
+
+ Instance->HeadSum = NetIp6PseudoHeadChecksum (
+ &LocalAddr,
+ &RemoteAddr,
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+
+ Instance->Configured = TRUE;
+ }
+ } else {
+ //
+ // UdpConfigData is NULL, reset the instance.
+ //
+ Instance->Configured = FALSE;
+ Instance->IsNoMapping = FALSE;
+
+ //
+ // Reset the Ip instance wrapped in the IpInfo.
+ //
+ IpIoConfigIp (Instance->IpInfo, NULL);
+
+ //
+ // Cancel all the user tokens.
+ //
+ Status = Instance->Udp6Proto.Cancel (&Instance->Udp6Proto, NULL);
+
+ //
+ // Remove the buffered RxData for this instance.
+ //
+ Udp6FlushRcvdDgram (Instance);
+
+ ASSERT (IsListEmpty (&Instance->DeliveredDgramQue));
+ }
+
+ Status = Udp6SetVariableData (Instance->Udp6Service);
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function is used to enable and disable the multicast group filtering.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] JoinFlag Set to TRUE to join a multicast group. Set to
+ FALSE to leave one or all multicast groups.
+ @param[in] MulticastAddress Pointer to multicast group address to join or
+ leave. This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been
+ started.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ JoinFlag is TRUE and MulticastAddress is NULL.
+ JoinFlag is TRUE and *MulticastAddress is not a
+ valid multicast address.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table
+ (when JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when
+ JoinFlag is FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Groups (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_IP6_PROTOCOL *Ip;
+ EFI_TPL OldTpl;
+ EFI_IPv6_ADDRESS *McastIp;
+
+ if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ McastIp = NULL;
+
+ if (JoinFlag) {
+ if (!IP6_IS_MULTICAST (MulticastAddress)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ McastIp = AllocateCopyPool (sizeof (EFI_IPv6_ADDRESS), MulticastAddress);
+ if (McastIp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Ip = Instance->IpInfo->Ip.Ip6;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Invoke the Ip instance the Udp6 instance consumes to do the group operation.
+ //
+ Status = Ip->Groups (Ip, JoinFlag, MulticastAddress);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Keep a local copy of the configured multicast IPs because IpIo receives
+ // datagrams from the 0 station address IP instance and then UDP delivers to
+ // the matched instance. This copy of multicast IPs is used to avoid receive
+ // the mutlicast datagrams destinated to multicast IPs the other instances configured.
+ //
+ if (JoinFlag) {
+
+ Status = NetMapInsertTail (&Instance->McastIps, (VOID *) McastIp, NULL);
+ } else {
+
+ NetMapIterate (&Instance->McastIps, Udp6LeaveGroup, MulticastAddress);
+ }
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ if (EFI_ERROR (Status)) {
+ if (McastIp != NULL) {
+ FreePool (McastIp);
+ }
+ }
+
+ return Status;
+}
+
+
+
+/**
+ This function places a sending request to this instance of the EFI UDPv6 Protocol,
+ alongside the transmit data that was filled by the user.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] Token Pointer to the completion token that will be
+ placed into the transmit queue.
+
+ @retval EFI_SUCCESS The data was queued for transmission.
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for
+ choosing a source address for this instance, but
+ no source address was available for use.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE:
+ This is NULL.
+ Token is NULL. Token.Event is NULL.
+ Token.Packet.TxData is NULL.
+ Token.Packet.TxData.FragmentCount is zero.
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths.
+ One or more of the
+ Token.Packet.TxData.FragmentTable[].FragmentLength
+ fields is zero.
+ One or more of the
+ Token.Packet.TxData.FragmentTable[].FragmentBuffer
+ fields is NULL. One or more of the
+ Token.Packet.TxData.UdpSessionData.DestinationAddres
+ are not valid unicast IPv6
+ addresses if the UdpSessionData is not NULL.
+ Token.Packet.TxData.UdpSessionData.
+ DestinationAddress is NULL
+ Token.Packet.TxData.UdpSessionData.
+ DestinatioPort
+ is zero.
+ Token.Packet.TxData.UdpSessionData is NULL and this
+ instance's UdpConfigData.RemoteAddress is unspecified.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same
+ Token.Event is already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_NOT_FOUND There is no route to the destination network or
+ address.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size. Or, the length of the IP header + UDP
+ header + data length is greater than MTU if
+ DoNotFragment is TRUE.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Transmit (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ NET_BUF *Packet;
+ EFI_UDP_HEADER *Udp6Header;
+ EFI_UDP6_CONFIG_DATA *ConfigData;
+ EFI_IPv6_ADDRESS Source;
+ EFI_IPv6_ADDRESS Destination;
+ EFI_UDP6_TRANSMIT_DATA *TxData;
+ EFI_UDP6_SESSION_DATA *UdpSessionData;
+ UDP6_SERVICE_DATA *Udp6Service;
+ IP_IO_OVERRIDE Override;
+ UINT16 HeadSum;
+ EFI_IP_ADDRESS IpDestAddr;
+
+ if ((This == NULL) || (Token == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Validate the Token, if the token is invalid return the error code.
+ //
+ Status = Udp6ValidateTxToken (Instance, Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) ||
+ EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token))
+ ){
+ //
+ // Try to find a duplicate token in the two token maps, if found, return
+ // EFI_ACCESS_DENIED.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ TxData = Token->Packet.TxData;
+
+ //
+ // Create a net buffer to hold the user buffer and the udp header.
+ //
+ Packet = NetbufFromExt (
+ (NET_FRAGMENT *) TxData->FragmentTable,
+ TxData->FragmentCount,
+ UDP6_HEADER_SIZE,
+ 0,
+ Udp6NetVectorExtFree,
+ NULL
+ );
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Store the IpIo in ProtoData.
+ //
+ Udp6Service = Instance->Udp6Service;
+ *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp6Service->IpIo);
+
+ Udp6Header = (EFI_UDP_HEADER *) NetbufAllocSpace (Packet, UDP6_HEADER_SIZE, TRUE);
+ ASSERT (Udp6Header != NULL);
+ ConfigData = &Instance->ConfigData;
+
+ //
+ // Fill the udp header.
+ //
+ Udp6Header->SrcPort = HTONS (ConfigData->StationPort);
+ Udp6Header->DstPort = HTONS (ConfigData->RemotePort);
+ Udp6Header->Length = HTONS ((UINT16) Packet->TotalSize);
+ Udp6Header->Checksum = 0;
+ //
+ // Set the UDP Header in NET_BUF, this UDP header is for IP6 can fast get the
+ // Udp header for pseudoHeadCheckSum.
+ //
+ Packet->Udp = Udp6Header;
+ UdpSessionData = TxData->UdpSessionData;
+
+ if (UdpSessionData != NULL) {
+ //
+ // Set the Destination according to the specified
+ // UdpSessionData.
+ //
+
+ if (UdpSessionData->DestinationPort != 0) {
+ Udp6Header->DstPort = HTONS (UdpSessionData->DestinationPort);
+ }
+
+ IP6_COPY_ADDRESS (&Source, &ConfigData->StationAddress);
+ if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress)) {
+ IP6_COPY_ADDRESS (&Destination, &UdpSessionData->DestinationAddress);
+ } else {
+ IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);
+ }
+
+ //
+ //Calculate the pseudo head checksum using the overridden parameters.
+ //
+ if (!NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress)) {
+ HeadSum = NetIp6PseudoHeadChecksum (
+ &Source,
+ &Destination,
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+
+ //
+ // calculate the checksum.
+ //
+ Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);
+ if (Udp6Header->Checksum == 0) {
+ //
+ // If the calculated checksum is 0, fill the Checksum field with all ones.
+ //
+ Udp6Header->Checksum = 0XFFFF;
+ }
+ } else {
+ //
+ // Set the checksum is zero if the ConfigData->StationAddress is unspcified
+ // and the Ipv6 will fill the correct value of this checksum.
+ //
+ Udp6Header->Checksum = 0;
+
+ }
+ } else {
+ //
+ // UdpSessionData is NULL, use the address and port information previously configured.
+ //
+ IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress);
+
+ HeadSum = Instance->HeadSum;
+ //
+ // calculate the checksum.
+ //
+ Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum);
+ if (Udp6Header->Checksum == 0) {
+ //
+ // If the calculated checksum is 0, fill the Checksum field with all ones.
+ //
+ Udp6Header->Checksum = 0xffff;
+ }
+ }
+
+
+
+ //
+ // Fill the IpIo Override data.
+ //
+ Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_UDP;
+ Override.Ip6OverrideData.HopLimit = ConfigData->HopLimit;
+ Override.Ip6OverrideData.FlowLabel = 0;
+
+ //
+ // Save the token into the TxToken map.
+ //
+ Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);
+ if (EFI_ERROR (Status)) {
+ goto FREE_PACKET;
+ }
+
+ //
+ // Send out this datagram through IpIo.
+ //
+ if (UdpSessionData != NULL){
+ IP6_COPY_ADDRESS (&(IpDestAddr.v6), &Destination);
+ } else {
+ ZeroMem (&IpDestAddr.v6, sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ Status = IpIoSend (
+ Udp6Service->IpIo,
+ Packet,
+ Instance->IpInfo,
+ Instance,
+ Token,
+ &IpDestAddr,
+ &Override
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Remove this token from the TxTokens.
+ //
+ Udp6RemoveToken (&Instance->TxTokens, Token);
+ }
+
+FREE_PACKET:
+
+ NetbufFree (Packet);
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function places a completion token into the receive packet queue. This function
+ is always asynchronous.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. Token is NULL. Token.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources (usually
+ memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI UDPv6 Protocol instance has been reset to
+ startup defaults.
+ @retval EFI_ACCESS_DENIED A receive completion token with the same
+ Token.Event is already in the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Receive (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) ||
+ EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token))
+ ){
+ //
+ // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or
+ // RxTokens map.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ Token->Packet.RxData = NULL;
+
+ //
+ // Save the token into the RxTokens map.
+ //
+ Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_NOT_READY;
+ goto ON_EXIT;
+ }
+
+ //
+ // If there is an icmp error, report it.
+ //
+ Udp6ReportIcmpError (Instance);
+
+ //
+ // Try to delivered the received datagrams.
+ //
+ Udp6InstanceDeliverDgram (Instance);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of Token->Event.
+ //
+ DispatchDpc ();
+
+ON_EXIT:
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function is used to abort a pending transmit or receive request.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+ @param[in] Token Pointer to a token that has been issued by
+ EFI_UDP6_PROTOCOL.Transmit() or
+ EFI_UDP6_PROTOCOL.Receive(). This parameter is
+ optional and may be NULL.
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted, and
+ Token.Event was signaled. When Token is NULL, all
+ pending requests are aborted and their events are
+ signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O
+ request is not found in the transmit or receive
+ queue. It is either completed or not issued by
+ Transmit() or Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Cancel (
+ IN EFI_UDP6_PROTOCOL *This,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Cancle the tokens specified by Token for this instance.
+ //
+ Status = Udp6InstanceCancelToken (Instance, Token);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
+ //
+ DispatchDpc ();
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function can be used by network drivers and applications to increase the rate that
+ data packets are moved between the communications device and the transmit/receive queues.
+
+ @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or
+ receive queue.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp6Poll (
+ IN EFI_UDP6_PROTOCOL *This
+ )
+{
+ UDP6_INSTANCE_DATA *Instance;
+ EFI_IP6_PROTOCOL *Ip;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP6_INSTANCE_DATA_FROM_THIS (This);
+ Ip = Instance->IpInfo->Ip.Ip6;
+
+ //
+ // Invode the Ip instance consumed by the udp instance to do the poll operation.
+ //
+ return Ip->Poll (Ip);
+}
diff --git a/NetworkPkg/UefiPxeBcDxe/ComponentName.c b/NetworkPkg/UefiPxeBcDxe/ComponentName.c
new file mode 100644
index 0000000000..4228519d18
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/ComponentName.c
@@ -0,0 +1,312 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for UefiPxeBc driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = {
+ PxeBcComponentNameGetDriverName,
+ PxeBcComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = {
+ {
+ "eng;en",
+ L"UEFI PXE Base Code Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the driver.
+
+ This function retrieves the user-readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user-readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2(
+ Language,
+ This->SupportedLanguages,
+ mPxeBcDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gPxeBcComponentName)
+ );
+}
+
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
new file mode 100644
index 0000000000..dd1d76d820
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
@@ -0,0 +1,1170 @@
+/** @file
+ Boot functions implementation for UefiPxeBc Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+ Display the string of the boot item.
+
+ If the length of the boot item string beyond 70 Char, just display 70 Char.
+
+ @param[in] Str The pointer to the string.
+ @param[in] Len The length of the string.
+
+**/
+VOID
+PxeBcDisplayBootItem (
+ IN UINT8 *Str,
+ IN UINT8 Len
+ )
+{
+ UINT8 Tmp;
+
+ //
+ // Cut off the chars behind 70th.
+ //
+ Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);
+ Tmp = Str[Len];
+ Str[Len] = 0;
+ AsciiPrint ("%a \n", Str);
+
+ //
+ // Restore the original 70th char.
+ //
+ Str[Len] = Tmp;
+}
+
+
+/**
+ Select and maintain the boot prompt if needed.
+
+ @param[in] Private Pointer to PxeBc private data.
+
+ @retval EFI_SUCCESS Selected boot prompt done.
+ @retval EFI_TIMEOUT Selected boot prompt timed out.
+ @retval EFI_NOT_FOUND The proxy offer is not Pxe10.
+ @retval EFI_ABORTED User cancelled the operation.
+ @retval EFI_NOT_READY Reading the input key from the keyboard has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootPrompt (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ PXEBC_DHCP_PACKET_CACHE *Cache;
+ PXEBC_VENDOR_OPTION *VendorOpt;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_EVENT TimeoutEvent;
+ EFI_EVENT DescendEvent;
+ EFI_INPUT_KEY InputKey;
+ EFI_STATUS Status;
+ UINT32 OfferType;
+ UINT8 Timeout;
+ UINT8 *Prompt;
+ UINT8 PromptLen;
+ INT32 SecCol;
+ INT32 SecRow;
+
+ TimeoutEvent = NULL;
+ DescendEvent = NULL;
+ Mode = Private->PxeBc.Mode;
+ Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
+ OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
+
+ //
+ // Only ProxyPxe10 offer needs boot prompt.
+ //
+ if (OfferType != PxeOfferTypeProxyPxe10) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
+ //
+ ASSERT (!Mode->UsingIpv6);
+
+ VendorOpt = &Cache->Dhcp4.VendorOpt;
+ if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
+ return EFI_SUCCESS;
+ }
+
+ Timeout = VendorOpt->MenuPrompt->Timeout;
+ Prompt = VendorOpt->MenuPrompt->Prompt;
+ PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
+
+ //
+ // The valid scope of Timeout refers to PXE2.1 spec.
+ //
+ if (Timeout == 0) {
+ return EFI_SUCCESS;
+ }
+ if (Timeout == 255) {
+ return EFI_TIMEOUT;
+ }
+
+ //
+ // Create and start a timer as timeout event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (
+ TimeoutEvent,
+ TimerRelative,
+ Timeout * TICKS_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Create and start a periodic timer as descend event by second.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &DescendEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->SetTimer (
+ DescendEvent,
+ TimerPeriodic,
+ TICKS_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Display the boot item and cursor on the screen.
+ //
+ SecCol = gST->ConOut->Mode->CursorColumn;
+ SecRow = gST->ConOut->Mode->CursorRow;
+
+ PxeBcDisplayBootItem (Prompt, PromptLen);
+
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
+ AsciiPrint ("(%d) ", Timeout--);
+
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
+ gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
+ AsciiPrint ("(%d) ", Timeout--);
+ }
+ if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
+ gBS->Stall (10 * TICKS_PER_MS);
+ continue;
+ }
+ //
+ // Parse the input key by user.
+ //
+ if (InputKey.ScanCode == 0) {
+
+ switch (InputKey.UnicodeChar) {
+
+ case CTRL ('c'):
+ Status = EFI_ABORTED;
+ break;
+
+ case CTRL ('m'):
+ case 'm':
+ case 'M':
+ Status = EFI_TIMEOUT;
+ break;
+
+ default:
+ continue;
+ }
+
+ } else {
+
+ switch (InputKey.ScanCode) {
+
+ case SCAN_F8:
+ Status = EFI_TIMEOUT;
+ break;
+
+ case SCAN_ESC:
+ Status = EFI_ABORTED;
+ break;
+
+ default:
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ //
+ // Reset the cursor on the screen.
+ //
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
+
+ON_EXIT:
+ if (DescendEvent != NULL) {
+ gBS->CloseEvent (DescendEvent);
+ }
+ if (TimeoutEvent != NULL) {
+ gBS->CloseEvent (TimeoutEvent);
+ }
+
+ return Status;
+}
+
+
+/**
+ Select the boot menu by user's input.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[out] Type The type of the menu.
+ @param[in] UseDefaultItem Use default item or not.
+
+ @retval EFI_ABORTED User cancel operation.
+ @retval EFI_SUCCESS Select the boot menu success.
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootMenu (
+ IN PXEBC_PRIVATE_DATA *Private,
+ OUT UINT16 *Type,
+ IN BOOLEAN UseDefaultItem
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ PXEBC_DHCP_PACKET_CACHE *Cache;
+ PXEBC_VENDOR_OPTION *VendorOpt;
+ EFI_INPUT_KEY InputKey;
+ UINT32 OfferType;
+ UINT8 MenuSize;
+ UINT8 MenuNum;
+ INT32 TopRow;
+ UINT16 Select;
+ UINT16 LastSelect;
+ UINT8 Index;
+ BOOLEAN Finish;
+ CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE];
+ PXEBC_BOOT_MENU_ENTRY *MenuItem;
+ PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM];
+
+ Finish = FALSE;
+ Select = 1;
+ Index = 0;
+ *Type = 0;
+ Mode = Private->PxeBc.Mode;
+ Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
+ OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
+
+ //
+ // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
+ //
+ ASSERT (!Mode->UsingIpv6);
+ ASSERT (OfferType == PxeOfferTypeProxyPxe10);
+
+ VendorOpt = &Cache->Dhcp4.VendorOpt;
+ if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Display the boot menu on the screen.
+ //
+ SetMem (Blank, sizeof(Blank), ' ');
+
+ MenuSize = VendorOpt->BootMenuLen;
+ MenuItem = VendorOpt->BootMenu;
+
+ if (MenuSize == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {
+ ASSERT (MenuItem != NULL);
+ MenuArray[Index] = MenuItem;
+ MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
+ MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
+ Index++;
+ }
+
+ if (UseDefaultItem) {
+ ASSERT (MenuArray[0] != NULL);
+ CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));
+ *Type = NTOHS (*Type);
+ return EFI_SUCCESS;
+ }
+
+ MenuNum = Index;
+
+ for (Index = 0; Index < MenuNum; Index++) {
+ ASSERT (MenuArray[Index] != NULL);
+ PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
+ }
+
+ TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
+
+ //
+ // Select the boot item by user in the boot menu.
+ //
+ do {
+ //
+ // Highlight selected row.
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
+ ASSERT (Select < PXEBC_MENU_MAX_NUM);
+ ASSERT (MenuArray[Select] != NULL);
+ Blank[MenuArray[Select]->DescLen] = 0;
+ AsciiPrint ("%a\r", Blank);
+ PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
+ LastSelect = Select;
+
+ while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
+ gBS->Stall (10 * TICKS_PER_MS);
+ }
+
+ if (InputKey.ScanCode != 0) {
+ switch (InputKey.UnicodeChar) {
+ case CTRL ('c'):
+ InputKey.ScanCode = SCAN_ESC;
+ break;
+
+ case CTRL ('j'): /* linefeed */
+ case CTRL ('m'): /* return */
+ Finish = TRUE;
+ break;
+
+ case CTRL ('i'): /* tab */
+ case ' ':
+ case 'd':
+ case 'D':
+ InputKey.ScanCode = SCAN_DOWN;
+ break;
+
+ case CTRL ('h'): /* backspace */
+ case 'u':
+ case 'U':
+ InputKey.ScanCode = SCAN_UP;
+ break;
+
+ default:
+ InputKey.ScanCode = 0;
+ }
+ }
+
+ switch (InputKey.ScanCode) {
+ case SCAN_LEFT:
+ case SCAN_UP:
+ if (Select != 0) {
+ Select--;
+ }
+ break;
+
+ case SCAN_DOWN:
+ case SCAN_RIGHT:
+ if (++Select == MenuNum) {
+ Select--;
+ }
+ break;
+
+ case SCAN_PAGE_UP:
+ case SCAN_HOME:
+ Select = 0;
+ break;
+
+ case SCAN_PAGE_DOWN:
+ case SCAN_END:
+ Select = (UINT16) (MenuNum - 1);
+ break;
+
+ case SCAN_ESC:
+ return EFI_ABORTED;
+ }
+
+ //
+ // Unhighlight the last selected row.
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
+ ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);
+ ASSERT (MenuArray[LastSelect] != NULL);
+ Blank[MenuArray[LastSelect]->DescLen] = 0;
+ AsciiPrint ("%a\r", Blank);
+ PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
+ } while (!Finish);
+
+ //
+ // Swap the byte order.
+ //
+ ASSERT (Select < PXEBC_MENU_MAX_NUM);
+ ASSERT (MenuArray[Select] != NULL);
+ CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
+ *Type = NTOHS (*Type);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse out the boot information from the last Dhcp4 reply packet.
+
+ @param[in, out] Private Pointer to PxeBc private data.
+ @param[out] BufferSize Size of the boot file to be downloaded.
+
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.
+ @retval Others Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcDhcp4BootInfo (
+ IN OUT PXEBC_PRIVATE_DATA *Private,
+ OUT UINT64 *BufferSize
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;
+ UINT16 Value;
+
+ PxeBc = &Private->PxeBc;
+ Mode = PxeBc->Mode;
+ Status = EFI_SUCCESS;
+ *BufferSize = 0;
+
+ //
+ // Get the last received Dhcp4 reply packet.
+ //
+ if (Mode->PxeReplyReceived) {
+ Cache4 = &Private->PxeReply.Dhcp4;
+ } else if (Mode->ProxyOfferReceived) {
+ Cache4 = &Private->ProxyOffer.Dhcp4;
+ } else {
+ Cache4 = &Private->DhcpAck.Dhcp4;
+ }
+
+ //
+ // Parse the boot server Ipv4 address by next server address.
+ // If this field isn't available, use option 54 instead.
+ //
+ CopyMem (
+ &Private->ServerIp,
+ &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ if (Private->ServerIp.Addr[0] == 0) {
+ CopyMem (
+ &Private->ServerIp,
+ Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ //
+ // Parse the boot file name by option.
+ //
+ ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
+ Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;
+
+ if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
+ //
+ // Parse the boot file size by option.
+ //
+ CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
+ Value = NTOHS (Value);
+ //
+ // The field of boot file size is 512 bytes in unit.
+ //
+ *BufferSize = 512 * Value;
+ } else {
+ //
+ // Get the bootfile size by tftp command if no option available.
+ //
+ Status = PxeBc->Mtftp (
+ PxeBc,
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+ NULL,
+ FALSE,
+ BufferSize,
+ &Private->BlockSize,
+ &Private->ServerIp,
+ Private->BootFileName,
+ NULL,
+ FALSE
+ );
+ }
+
+ //
+ // Save the value of boot file size.
+ //
+ Private->BootFileSize = (UINTN) *BufferSize;
+
+ //
+ // Display all the information: boot server address, boot file name and boot file size.
+ //
+ AsciiPrint ("\n Server IP address is ");
+ PxeBcShowIp4Addr (&Private->ServerIp.v4);
+ AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
+ AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
+
+ return Status;
+}
+
+
+/**
+ Parse out the boot information from the last Dhcp6 reply packet.
+
+ @param[in, out] Private Pointer to PxeBc private data.
+ @param[out] BufferSize Size of the boot file to be downloaded.
+
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.
+ @retval EFI_BUFFER_TOO_SMALL
+ @retval Others Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcDhcp6BootInfo (
+ IN OUT PXEBC_PRIVATE_DATA *Private,
+ OUT UINT64 *BufferSize
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;
+ UINT16 Value;
+
+ PxeBc = &Private->PxeBc;
+ Mode = PxeBc->Mode;
+ Status = EFI_SUCCESS;
+ *BufferSize = 0;
+
+ //
+ // Get the last received Dhcp6 reply packet.
+ //
+ if (Mode->PxeReplyReceived) {
+ Cache6 = &Private->PxeReply.Dhcp6;
+ } else if (Mode->ProxyOfferReceived) {
+ Cache6 = &Private->ProxyOffer.Dhcp6;
+ } else {
+ Cache6 = &Private->DhcpAck.Dhcp6;
+ }
+
+ ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
+
+ //
+ // Parse (m)tftp server ip address and bootfile name.
+ //
+ Status = PxeBcExtractBootFileUrl (
+ &Private->BootFileName,
+ &Private->ServerIp.v6,
+ (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
+ NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Parse the value of boot file size.
+ //
+ if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
+ //
+ // Parse it out if have the boot file parameter option.
+ //
+ Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // The field of boot file size is 512 bytes in unit.
+ //
+ *BufferSize = 512 * Value;
+ } else {
+ //
+ // Send get file size command by tftp if option unavailable.
+ //
+ Status = PxeBc->Mtftp (
+ PxeBc,
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+ NULL,
+ FALSE,
+ BufferSize,
+ &Private->BlockSize,
+ &Private->ServerIp,
+ Private->BootFileName,
+ NULL,
+ FALSE
+ );
+ }
+
+ //
+ // Save the value of boot file size.
+ //
+ Private->BootFileSize = (UINTN) *BufferSize;
+
+ //
+ // Display all the information: boot server address, boot file name and boot file size.
+ //
+ AsciiPrint ("\n Server IP address is ");
+ PxeBcShowIp6Addr (&Private->ServerIp.v6);
+ AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
+ AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
+
+ return Status;
+}
+
+
+/**
+ Extract the discover information and boot server entry from the
+ cached packets if unspecified.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Type The type of bootstrap to perform.
+ @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
+ @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.
+ @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+ @retval EFI_SUCCESS Successfully extracted the information.
+ @retval EFI_DEVICE_ERROR Failed to extract the information.
+
+**/
+EFI_STATUS
+PxeBcExtractDiscoverInfo (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info,
+ OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,
+ OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;
+ PXEBC_VENDOR_OPTION *VendorOpt;
+ PXEBC_BOOT_SVR_ENTRY *Entry;
+ BOOLEAN IsFound;
+
+ Mode = Private->PxeBc.Mode;
+
+ if (Mode->UsingIpv6) {
+ Info->IpCnt = 1;
+ Info->UseUCast = TRUE;
+
+ Info->SrvList[0].Type = Type;
+ Info->SrvList[0].AcceptAnyResponse = FALSE;
+
+ //
+ // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
+ //
+ CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+
+ *SrvList = Info->SrvList;
+ } else {
+ Entry = NULL;
+ IsFound = FALSE;
+ Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
+ VendorOpt = &Cache4->VendorOpt;
+
+ if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
+ //
+ // Address is not acquired or no discovery options.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Parse the boot server entry from the vendor option in the last cached packet.
+ //
+ Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
+ Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
+ Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
+ Info->UseUCast = Info->MustUseList;
+
+ if (Info->UseMCast) {
+ //
+ // Get the multicast discover ip address from vendor option if has.
+ //
+ CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
+ }
+
+ Info->IpCnt = 0;
+
+ if (Info->MustUseList) {
+ Entry = VendorOpt->BootSvr;
+
+ while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
+ if (Entry->Type == HTONS (Type)) {
+ IsFound = TRUE;
+ break;
+ }
+ Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
+ }
+
+ if (!IsFound) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Info->IpCnt = Entry->IpCnt;
+ }
+
+ *BootEntry = Entry;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Build the discover packet and send out for boot server.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Type PxeBc option boot item type.
+ @param[in] Layer Pointer to option boot item layer.
+ @param[in] UseBis Use BIS or not.
+ @param[in] DestIp Pointer to the destination address.
+ @param[in] IpCount The count of the server address.
+ @param[in] SrvList Pointer to the server address list.
+
+ @retval EFI_SUCCESS Successfully discovered boot file.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.
+ @retval Others Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDiscoverBootServer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN UINT16 IpCount,
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
+ )
+{
+ if (Private->PxeBc.Mode->UsingIpv6) {
+ return PxeBcDhcp6Discover (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ DestIp
+ );
+ } else {
+ return PxeBcDhcp4Discover (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ DestIp,
+ IpCount,
+ SrvList
+ );
+ }
+}
+
+
+/**
+ Discover all the boot information for boot file.
+
+ @param[in, out] Private Pointer to PxeBc private data.
+ @param[out] BufferSize Size of the boot file to be downloaded.
+
+ @retval EFI_SUCCESS Successfully obtained all the boot information .
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
+ @retval EFI_ABORTED User cancel current operation.
+ @retval Others Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcDiscoverBootFile (
+ IN OUT PXEBC_PRIVATE_DATA *Private,
+ OUT UINT64 *BufferSize
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ UINT16 Type;
+ UINT16 Layer;
+ BOOLEAN UseBis;
+
+ PxeBc = &Private->PxeBc;
+ Mode = PxeBc->Mode;
+ Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
+ Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
+
+ //
+ // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
+ // other pxe boot information.
+ //
+ Status = PxeBc->Dhcp (PxeBc, TRUE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Select a boot server from boot server list.
+ //
+ Status = PxeBcSelectBootPrompt (Private);
+
+ if (Status == EFI_SUCCESS) {
+ //
+ // Choose by user's input.
+ //
+ Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
+ } else if (Status == EFI_TIMEOUT) {
+ //
+ // Choose by default item.
+ //
+ Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
+ }
+
+ if (!EFI_ERROR (Status)) {
+
+ if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
+ //
+ // Local boot(PXE bootstrap server) need abort
+ //
+ return EFI_ABORTED;
+ }
+
+ //
+ // Start to discover the boot server to get (m)tftp server ip address, bootfile
+ // name and bootfile size.
+ //
+ UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
+ Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Parse the boot information.
+ //
+ if (Mode->UsingIpv6) {
+ Status = PxeBcDhcp6BootInfo (Private, BufferSize);
+ } else {
+ Status = PxeBcDhcp4BootInfo (Private, BufferSize);
+ }
+
+ return Status;
+}
+
+
+/**
+ Install PxeBaseCodeCallbackProtocol if not installed before.
+
+ @param[in, out] Private Pointer to PxeBc private data.
+ @param[out] NewMakeCallback If TRUE, it is a new callback.
+ Otherwise, it is not new callback.
+ @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully.
+ @retval Others Failed to install PxeBaseCodeCallbackProtocol.
+
+**/
+EFI_STATUS
+PxeBcInstallCallback (
+ IN OUT PXEBC_PRIVATE_DATA *Private,
+ OUT BOOLEAN *NewMakeCallback
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_STATUS Status;
+
+ //
+ // Check whether PxeBaseCodeCallbackProtocol already installed.
+ //
+ PxeBc = &Private->PxeBc;
+ Status = gBS->HandleProtocol (
+ Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ (VOID **) &Private->PxeBcCallback
+ );
+ if (Status == EFI_UNSUPPORTED) {
+
+ CopyMem (
+ &Private->LoadFileCallback,
+ &gPxeBcCallBackTemplate,
+ sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
+ );
+
+ //
+ // Install a default callback if user didn't offer one.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->LoadFileCallback
+ );
+
+ (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);
+
+ Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
+ if (EFI_ERROR (Status)) {
+ PxeBc->Stop (PxeBc);
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Uninstall PxeBaseCodeCallbackProtocol.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] NewMakeCallback If TRUE, it is a new callback.
+ Otherwise, it is not new callback.
+
+**/
+VOID
+PxeBcUninstallCallback (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN BOOLEAN NewMakeCallback
+ )
+{
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+
+ PxeBc = &Private->PxeBc;
+
+ if (NewMakeCallback) {
+
+ NewMakeCallback = FALSE;
+
+ PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
+
+ gBS->UninstallProtocolInterface (
+ Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ &Private->LoadFileCallback
+ );
+ }
+}
+
+
+/**
+ Download one of boot file in the list, and it's special for IPv6.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in, out] BufferSize Size of user buffer for input;
+ required buffer size for output.
+ @param[in] Buffer Pointer to user buffer.
+
+ @retval EFI_SUCCESS Read one of boot file in the list successfully.
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
+ @retval EFI_NOT_FOUND There is no proper boot file available.
+ @retval Others Failed to download boot file in the list.
+
+**/
+EFI_STATUS
+PxeBcReadBootFileList (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN OUT UINT64 *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+
+ PxeBc = &Private->PxeBc;
+
+ //
+ // Try to download the boot file if everything is ready.
+ //
+ if (Buffer != NULL) {
+ Status = PxeBc->Mtftp (
+ PxeBc,
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+ Buffer,
+ FALSE,
+ BufferSize,
+ &Private->BlockSize,
+ &Private->ServerIp,
+ Private->BootFileName,
+ NULL,
+ FALSE
+ );
+
+
+ } else {
+ Status = EFI_BUFFER_TOO_SMALL;
+ }
+
+ return Status;
+}
+
+
+/**
+ Load boot file into user buffer.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in, out] BufferSize Size of user buffer for input;
+ required buffer size for output.
+ @param[in] Buffer Pointer to user buffer.
+
+ @retval EFI_SUCCESS Get all the boot information successfully.
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
+ @retval EFI_ABORTED User cancelled the current operation.
+ @retval Others Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcLoadBootFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ BOOLEAN NewMakeCallback;
+ UINT64 RequiredSize;
+ UINT64 CurrentSize;
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_PXE_BASE_CODE_MODE *PxeBcMode;
+
+ NewMakeCallback = FALSE;
+ PxeBc = &Private->PxeBc;
+ PxeBcMode = &Private->Mode;
+ CurrentSize = *BufferSize;
+ RequiredSize = 0;
+
+ //
+ // Install pxebc callback protocol if hasn't been installed yet.
+ //
+ Status = PxeBcInstallCallback (Private, &NewMakeCallback);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ if (Private->BootFileSize == 0) {
+ //
+ // Discover the boot information about the bootfile if hasn't.
+ //
+ Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
+
+ if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
+ //
+ // It's error if the required buffer size is beyond the system scope.
+ //
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ } else if (RequiredSize > 0) {
+ //
+ // Get the right buffer size of the bootfile required.
+ //
+ if (CurrentSize < RequiredSize || Buffer == NULL) {
+ //
+ // It's buffer too small if the size of user buffer is smaller than the required.
+ //
+ CurrentSize = RequiredSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ CurrentSize = RequiredSize;
+ } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {
+ //
+ // Try to download another bootfile in list if failed to get the filesize of the last one.
+ // It's special for the case of IPv6.
+ //
+ Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
+ goto ON_EXIT;
+ }
+ } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {
+ //
+ // It's buffer too small if the size of user buffer is smaller than the required.
+ //
+ CurrentSize = Private->BootFileSize;
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+
+ //
+ // Begin to download the bootfile if everything is ready.
+ //
+ AsciiPrint ("\n Downloading NBP file...\n");
+ if (PxeBcMode->UsingIpv6) {
+ Status = PxeBcReadBootFileList (
+ Private,
+ &CurrentSize,
+ Buffer
+ );
+ } else {
+ Status = PxeBc->Mtftp (
+ PxeBc,
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+ Buffer,
+ FALSE,
+ &CurrentSize,
+ &Private->BlockSize,
+ &Private->ServerIp,
+ Private->BootFileName,
+ NULL,
+ FALSE
+ );
+ }
+
+ON_EXIT:
+ *BufferSize = (UINTN) CurrentSize;
+ PxeBcUninstallCallback(Private, NewMakeCallback);
+
+ if (Status == EFI_SUCCESS) {
+ AsciiPrint ("\n Succeed to download NBP file.\n");
+ return EFI_SUCCESS;
+ } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
+ AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");
+ } else if (Status == EFI_DEVICE_ERROR) {
+ AsciiPrint ("\n PXE-E07: Network device error.\n");
+ } else if (Status == EFI_OUT_OF_RESOURCES) {
+ AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");
+ } else if (Status == EFI_NO_MEDIA) {
+ AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");
+ } else if (Status == EFI_NO_RESPONSE) {
+ AsciiPrint ("\n PXE-E16: No offer received.\n");
+ } else if (Status == EFI_TIMEOUT) {
+ AsciiPrint ("\n PXE-E18: Server response timeout.\n");
+ } else if (Status == EFI_ABORTED) {
+ AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");
+ } else if (Status == EFI_ICMP_ERROR) {
+ AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");
+ } else if (Status == EFI_TFTP_ERROR) {
+ AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");
+ } else if (Status != EFI_BUFFER_TOO_SMALL) {
+ AsciiPrint ("\n PXE-E99: Unexpected network error.\n");
+ }
+
+ return Status;
+}
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
new file mode 100644
index 0000000000..ef18907aa6
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
@@ -0,0 +1,100 @@
+/** @file
+ Boot functions declaration for UefiPxeBc Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_BOOT_H__
+#define __EFI_PXEBC_BOOT_H__
+
+#define PXEBC_DISPLAY_MAX_LINE 70
+#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE 8
+#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE 4
+
+#define PXEBC_IS_SIZE_OVERFLOWED(x) ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF))
+
+
+/**
+ Extract the discover information and boot server entry from the
+ cached packets if unspecified.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Type The type of bootstrap to perform.
+ @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
+ @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.
+ @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+ @retval EFI_SUCCESS Successfully extracted the information.
+ @retval EFI_DEVICE_ERROR Failed to extract the information.
+
+**/
+EFI_STATUS
+PxeBcExtractDiscoverInfo (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info,
+ OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,
+ OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList
+ );
+
+
+/**
+ Build the discover packet and send out for boot.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Type PxeBc option boot item type.
+ @param[in] Layer Pointer to option boot item layer.
+ @param[in] UseBis Use BIS or not.
+ @param[in] DestIp Pointer to the server address.
+ @param[in] IpCount The total count of the server address.
+ @param[in] SrvList Pointer to the server address list.
+
+ @retval EFI_SUCCESS Successfully discovered boot file.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.
+ @retval Others Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDiscoverBootServer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN UINT16 IpCount,
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
+ );
+
+
+/**
+ Load boot file into user buffer.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in, out] BufferSize Size of user buffer for input;
+ required buffer size for output.
+ @param[in] Buffer Pointer to user buffer.
+
+ @retval EFI_SUCCESS Successfully obtained all the boot information.
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
+ @retval EFI_ABORTED User cancelled the current operation.
+ @retval Others Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcLoadBootFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ );
+
+#endif
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
new file mode 100644
index 0000000000..08415d97b4
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
@@ -0,0 +1,1599 @@
+/** @file
+ Functions implementation related with DHCPv4 for UefiPxeBc Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+//
+// This is a map from the interested DHCP4 option tags' index to the tag value.
+//
+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {
+ PXEBC_DHCP4_TAG_BOOTFILE_LEN,
+ PXEBC_DHCP4_TAG_VENDOR,
+ PXEBC_DHCP4_TAG_OVERLOAD,
+ PXEBC_DHCP4_TAG_MSG_TYPE,
+ PXEBC_DHCP4_TAG_SERVER_ID,
+ PXEBC_DHCP4_TAG_CLASS_ID,
+ PXEBC_DHCP4_TAG_BOOTFILE
+};
+
+//
+// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec.
+//
+UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32};
+
+
+/**
+ Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.
+
+ @param[in] Buffer Pointer to the option buffer.
+ @param[in] Length Length of the option buffer.
+ @param[in] OptTag Tag of the required option.
+
+ @retval NULL Failed to find the required option.
+ @retval Others The position of the required option.
+
+**/
+EFI_DHCP4_PACKET_OPTION *
+PxeBcParseDhcp4Options (
+ IN UINT8 *Buffer,
+ IN UINT32 Length,
+ IN UINT8 OptTag
+ )
+{
+ EFI_DHCP4_PACKET_OPTION *Option;
+ UINT32 Offset;
+
+ Option = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+ Offset = 0;
+
+ while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) {
+
+ if (Option->OpCode == OptTag) {
+ //
+ // Found the required option.
+ //
+ return Option;
+ }
+
+ //
+ // Skip the current option to the next.
+ //
+ if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) {
+ Offset++;
+ } else {
+ Offset += Option->Length + 2;
+ }
+
+ Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Parse the PXE vender options and extract the information from them.
+
+ @param[in] Dhcp4Option Pointer to vendor options in buffer.
+ @param[in] VendorOption Pointer to structure to store information in vendor options.
+
+**/
+VOID
+PxeBcParseVendorOptions (
+ IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option,
+ IN PXEBC_VENDOR_OPTION *VendorOption
+ )
+{
+ UINT32 *BitMap;
+ UINT8 VendorOptionLen;
+ EFI_DHCP4_PACKET_OPTION *PxeOption;
+ UINT8 Offset;
+
+ BitMap = VendorOption->BitMap;
+ VendorOptionLen = Dhcp4Option->Length;
+ PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];
+ Offset = 0;
+
+ ASSERT (PxeOption != NULL);
+
+ while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) {
+ //
+ // Parse all the interesting PXE vendor options one by one.
+ //
+ switch (PxeOption->OpCode) {
+
+ case PXEBC_VENDOR_TAG_MTFTP_IP:
+
+ CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_CPORT:
+
+ CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_SPORT:
+
+ CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:
+
+ VendorOption->MtftpTimeout = *PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_MTFTP_DELAY:
+
+ VendorOption->MtftpDelay = *PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_DISCOVER_CTRL:
+
+ VendorOption->DiscoverCtrl = *PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_DISCOVER_MCAST:
+
+ CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+ break;
+
+ case PXEBC_VENDOR_TAG_BOOT_SERVERS:
+
+ VendorOption->BootSvrLen = PxeOption->Length;
+ VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_BOOT_MENU:
+
+ VendorOption->BootMenuLen = PxeOption->Length;
+ VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_MENU_PROMPT:
+
+ VendorOption->MenuPromptLen = PxeOption->Length;
+ VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_MCAST_ALLOC:
+
+ CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));
+ CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));
+ break;
+
+ case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:
+
+ VendorOption->CredTypeLen = PxeOption->Length;
+ VendorOption->CredType = (UINT32 *) PxeOption->Data;
+ break;
+
+ case PXEBC_VENDOR_TAG_BOOT_ITEM:
+
+ CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));
+ CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));
+ break;
+
+ default:
+ //
+ // Not interesting PXE vendor options.
+ //
+ break;
+ }
+
+ //
+ // Set the bit map for the special PXE options.
+ //
+ SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);
+
+ //
+ // Continue to the next option.
+ //
+ if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) {
+ Offset++;
+ } else {
+ Offset = (UINT8) (Offset + PxeOption->Length + 2);
+ }
+
+ PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);
+ }
+}
+
+
+/**
+ Build the options buffer for the DHCPv4 request packet.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[out] OptList Pointer to the option pointer array.
+ @param[in] Buffer Pointer to the buffer to contain the option list.
+ @param[in] NeedMsgType If TRUE, it is necessary to include the Msg type option.
+ Otherwise, it is not necessary.
+
+ @return Index The count of the built-in options.
+
+**/
+UINT32
+PxeBcBuildDhcp4Options (
+ IN PXEBC_PRIVATE_DATA *Private,
+ OUT EFI_DHCP4_PACKET_OPTION **OptList,
+ IN UINT8 *Buffer,
+ IN BOOLEAN NeedMsgType
+ )
+{
+ UINT32 Index;
+ PXEBC_DHCP4_OPTION_ENTRY OptEnt;
+ UINT16 Value;
+
+ Index = 0;
+ OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+
+ if (NeedMsgType) {
+ //
+ // Append message type.
+ //
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MSG_TYPE;
+ OptList[Index]->Length = 1;
+ OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;
+ OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append max message size.
+ //
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MAXMSG;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);
+ OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;
+ Value = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);
+ CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+ }
+
+ //
+ // Append parameter request list option.
+ //
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_PARA_LIST;
+ OptList[Index]->Length = 35;
+ OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+ OptEnt.Para->ParaList[0] = PXEBC_DHCP4_TAG_NETMASK;
+ OptEnt.Para->ParaList[1] = PXEBC_DHCP4_TAG_TIME_OFFSET;
+ OptEnt.Para->ParaList[2] = PXEBC_DHCP4_TAG_ROUTER;
+ OptEnt.Para->ParaList[3] = PXEBC_DHCP4_TAG_TIME_SERVER;
+ OptEnt.Para->ParaList[4] = PXEBC_DHCP4_TAG_NAME_SERVER;
+ OptEnt.Para->ParaList[5] = PXEBC_DHCP4_TAG_DNS_SERVER;
+ OptEnt.Para->ParaList[6] = PXEBC_DHCP4_TAG_HOSTNAME;
+ OptEnt.Para->ParaList[7] = PXEBC_DHCP4_TAG_BOOTFILE_LEN;
+ OptEnt.Para->ParaList[8] = PXEBC_DHCP4_TAG_DOMAINNAME;
+ OptEnt.Para->ParaList[9] = PXEBC_DHCP4_TAG_ROOTPATH;
+ OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;
+ OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;
+ OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;
+ OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;
+ OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;
+ OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;
+ OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;
+ OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;
+ OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;
+ OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;
+ OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;
+ OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;
+ OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;
+ OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;
+ OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;
+ OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;
+ OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID;
+ OptEnt.Para->ParaList[27] = 0x80;
+ OptEnt.Para->ParaList[28] = 0x81;
+ OptEnt.Para->ParaList[29] = 0x82;
+ OptEnt.Para->ParaList[30] = 0x83;
+ OptEnt.Para->ParaList[31] = 0x84;
+ OptEnt.Para->ParaList[32] = 0x85;
+ OptEnt.Para->ParaList[33] = 0x86;
+ OptEnt.Para->ParaList[34] = 0x87;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append UUID/Guid-based client identifier option
+ //
+ OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UUID;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);
+ OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;
+ OptEnt.Uuid->Type = 0;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
+ //
+ // Zero the Guid to indicate NOT programable if failed to get system Guid.
+ //
+ ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
+ }
+
+ //
+ // Append client network device interface option
+ //
+ OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UNDI;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);
+ OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
+
+ if (Private->Nii != NULL) {
+ OptEnt.Undi->Type = Private->Nii->Type;
+ OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
+ OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
+ } else {
+ OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
+ OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
+ OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
+ }
+
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client system architecture option
+ //
+ OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_ARCH;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);
+ OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
+ Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
+ CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append vendor class identify option
+ //
+ OptList[Index]->OpCode = PXEBC_DHCP4_TAG_CLASS_ID;
+ OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);
+ OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;
+ CopyMem (
+ OptEnt.Clid,
+ DEFAULT_CLASS_ID_DATA,
+ sizeof (PXEBC_DHCP4_OPTION_CLID)
+ );
+ PxeBcUintnToAscDecWithFormat (
+ EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
+ OptEnt.Clid->ArchitectureType,
+ sizeof (OptEnt.Clid->ArchitectureType)
+ );
+
+ if (Private->Nii != NULL) {
+ CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
+ PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
+ PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
+ }
+
+ Index++;
+
+ return Index;
+}
+
+
+/**
+ Create a template DHCPv4 packet as a seed.
+
+ @param[out] Seed Pointer to the seed packet.
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.
+
+**/
+VOID
+PxeBcSeedDhcp4Packet (
+ OUT EFI_DHCP4_PACKET *Seed,
+ IN EFI_UDP4_PROTOCOL *Udp4
+ )
+{
+ EFI_SIMPLE_NETWORK_MODE Mode;
+ EFI_DHCP4_HEADER *Header;
+
+ //
+ // Get IfType and HwAddressSize from SNP mode data.
+ //
+ Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);
+
+ Seed->Size = sizeof (EFI_DHCP4_PACKET);
+ Seed->Length = sizeof (Seed->Dhcp4);
+ Header = &Seed->Dhcp4.Header;
+ ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));
+ Header->OpCode = PXEBC_DHCP4_OPCODE_REQUEST;
+ Header->HwType = Mode.IfType;
+ Header->HwAddrLen = (UINT8) Mode.HwAddressSize;
+ CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);
+
+ Seed->Dhcp4.Magik = PXEBC_DHCP4_MAGIC;
+ Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP;
+}
+
+
+/**
+ Cache the DHCPv4 packet.
+
+ @param[in] Dst Pointer to the cache buffer for DHCPv4 packet.
+ @param[in] Src Pointer to the DHCPv4 packet to be cached.
+
+**/
+VOID
+PxeBcCacheDhcp4Packet (
+ IN EFI_DHCP4_PACKET *Dst,
+ IN EFI_DHCP4_PACKET *Src
+ )
+{
+ ASSERT (Dst->Size >= Src->Length);
+
+ CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
+ Dst->Length = Src->Length;
+}
+
+
+/**
+ Parse the cached DHCPv4 packet, including all the options.
+
+ @param[in] Cache4 Pointer to cached DHCPv4 packet.
+
+ @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully.
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp4Packet (
+ IN PXEBC_DHCP4_PACKET_CACHE *Cache4
+ )
+{
+ EFI_DHCP4_PACKET *Offer;
+ EFI_DHCP4_PACKET_OPTION **Options;
+ EFI_DHCP4_PACKET_OPTION *Option;
+ PXEBC_OFFER_TYPE OfferType;
+ UINTN Index;
+ BOOLEAN IsProxyOffer;
+ BOOLEAN IsPxeOffer;
+ UINT8 *Ptr8;
+
+ IsProxyOffer = FALSE;
+ IsPxeOffer = FALSE;
+
+ ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));
+ ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt));
+
+ Offer = &Cache4->Packet.Offer;
+ Options = Cache4->OptList;
+
+ //
+ // Parse DHCPv4 options in this offer, and store the pointers.
+ //
+ for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
+ Options[Index] = PxeBcParseDhcp4Options (
+ Offer->Dhcp4.Option,
+ GET_OPTION_BUFFER_LEN (Offer),
+ mInterestedDhcp4Tags[Index]
+ );
+ }
+
+ //
+ // The offer with "yiaddr" is a proxy offer.
+ //
+ if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {
+ IsProxyOffer = TRUE;
+ }
+
+ //
+ // The offer with "PXEClient" is a PXE offer.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];
+ if ((Option != NULL) && (Option->Length >= 9) &&
+ (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {
+ IsPxeOffer = TRUE;
+ }
+
+ //
+ // Parse PXE vendor options in this offer, and store the contents/pointers.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];
+ if (IsPxeOffer && Option != NULL) {
+ PxeBcParseVendorOptions (Option, &Cache4->VendorOpt);
+ }
+
+ //
+ // Check whether bootfilename and serverhostname overloaded, refers to rfc-2132 in details.
+ // If overloaded, parse the buffer as nested DHCPv4 options, or else just parse as bootfilename
+ // and serverhostname option.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];
+ if (Option != NULL && (Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {
+
+ Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = PxeBcParseDhcp4Options (
+ (UINT8 *) Offer->Dhcp4.Header.BootFileName,
+ sizeof (Offer->Dhcp4.Header.BootFileName),
+ PXEBC_DHCP4_TAG_BOOTFILE
+ );
+ //
+ // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null
+ // terminated string. So force to append null terminated character at the end of string.
+ //
+ if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+ Ptr8 = (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
+ Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length;
+ *Ptr8 = '\0';
+ }
+
+ } else if ((Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) &&
+ (Offer->Dhcp4.Header.BootFileName[0] != 0)) {
+ //
+ // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.
+ // Do not count dhcp option header here, or else will destory the serverhostname.
+ //
+ Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)
+ (&Offer->Dhcp4.Header.BootFileName[0] -
+ OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
+
+ }
+
+ //
+ // Determine offer type of the DHCPv4 packet.
+ //
+ Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];
+ if (Option == NULL || Option->Data[0] == 0) {
+ //
+ // It's a Bootp offer.
+ //
+ OfferType = PxeOfferTypeBootp;
+
+ Option = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];
+ if (Option == NULL) {
+ //
+ // If the Bootp offer without bootfilename, discard it.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+ } else {
+
+ if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {
+ //
+ // It's a PXE10 offer with PXEClient and discover vendor option.
+ //
+ OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10;
+ } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {
+ //
+ // It's a WFM11a offer with PXEClient and mtftp vendor option.
+ // But multi-cast download is not supported currently, so discard it.
+ //
+ return EFI_DEVICE_ERROR;
+ } else if (IsPxeOffer) {
+ //
+ // It's a BINL offer only with PXEClient.
+ //
+ OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
+ } else {
+ //
+ // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet.
+ //
+ OfferType = PxeOfferTypeDhcpOnly;
+ }
+ }
+
+ Cache4->OfferType = OfferType;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cache the DHCPv4 ack packet, and parse it on demand.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Ack Pointer to the DHCPv4 ack packet.
+ @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.
+
+**/
+VOID
+PxeBcCopyDhcp4Ack (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET *Ack,
+ IN BOOLEAN Verified
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+
+ Mode = Private->PxeBc.Mode;
+
+ PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack);
+
+ if (Verified) {
+ //
+ // Parse the ack packet and store it into mode data if needed.
+ //
+ PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4);
+ CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length);
+ Mode->DhcpAckReceived = TRUE;
+ }
+}
+
+
+/**
+ Cache the DHCPv4 proxy offer packet according to the received order.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] OfferIndex The received order of offer packets.
+
+**/
+VOID
+PxeBcCopyProxyOffer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 OfferIndex
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PACKET *Offer;
+
+ ASSERT (OfferIndex < Private->OfferNum);
+ ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
+
+ Mode = Private->PxeBc.Mode;
+ Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer;
+
+ //
+ // Cache the proxy offer packet and parse it.
+ //
+ PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer);
+ PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4);
+
+ //
+ // Store this packet into mode data.
+ //
+ CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length);
+ Mode->ProxyOfferReceived = TRUE;
+}
+
+
+/**
+ Retry to request bootfile name by the BINL offer.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Index The received order of offer packets.
+
+ @retval EFI_SUCCESS Successfully retried to request bootfile name.
+ @retval EFI_DEVICE_ERROR Failed to retry bootfile name.
+
+**/
+EFI_STATUS
+PxeBcRetryBinlOffer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 Index
+ )
+{
+ EFI_DHCP4_PACKET *Offer;
+ EFI_IP_ADDRESS ServerIp;
+ EFI_STATUS Status;
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;
+ EFI_DHCP4_PACKET *Reply;
+
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+ ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl ||
+ Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl);
+
+ Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
+
+ //
+ // Prefer to siaddr in header as next server address. If it's zero, then use option 54.
+ //
+ if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) {
+ CopyMem (
+ &ServerIp.Addr[0],
+ Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ } else {
+ CopyMem (
+ &ServerIp.Addr[0],
+ &Offer->Dhcp4.Header.ServerAddr,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ Private->IsDoDiscover = FALSE;
+ Cache4 = &Private->ProxyOffer.Dhcp4;
+ Reply = &Cache4->Packet.Offer;
+
+ //
+ // Send another request packet for bootfile name.
+ //
+ Status = PxeBcDhcp4Discover (
+ Private,
+ 0,
+ NULL,
+ FALSE,
+ &ServerIp,
+ 0,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Parse the reply for the last request packet.
+ //
+ Status = PxeBcParseDhcp4Packet (Cache4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Cache4->OfferType != PxeOfferTypeProxyPxe10 &&
+ Cache4->OfferType != PxeOfferTypeProxyWfm11a &&
+ Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
+ //
+ // This BINL ack doesn't have discovery option set or multicast option set
+ // or bootfile name specified.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Store the reply into mode data.
+ //
+ Private->PxeBc.Mode->ProxyOfferReceived = TRUE;
+ CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] RcvdOffer Pointer to the received offer packet.
+
+**/
+VOID
+PxeBcCacheDhcp4Offer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET *RcvdOffer
+ )
+{
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;
+ EFI_DHCP4_PACKET *Offer;
+ PXEBC_OFFER_TYPE OfferType;
+
+ ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM);
+ Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;
+ Offer = &Cache4->Packet.Offer;
+
+ //
+ // Cache the content of DHCPv4 packet firstly.
+ //
+ PxeBcCacheDhcp4Packet (Offer, RcvdOffer);
+
+ //
+ // Validate the DHCPv4 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) {
+ return;
+ }
+
+ //
+ // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
+ //
+ OfferType = Cache4->OfferType;
+ ASSERT (OfferType < PxeOfferTypeMax);
+
+ if (OfferType == PxeOfferTypeBootp) {
+ //
+ // It's a Bootp offer, only cache the first one, and discard the others.
+ //
+ if (Private->OfferCount[OfferType] == 0) {
+ Private->OfferIndex[OfferType][0] = Private->OfferNum;
+ Private->OfferCount[OfferType] = 1;
+ } else {
+ return;
+ }
+ } else {
+ ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
+ if (IS_PROXY_DHCP_OFFER (Offer)) {
+ //
+ // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
+ //
+ Private->IsProxyRecved = TRUE;
+
+ if (OfferType == PxeOfferTypeProxyBinl) {
+ //
+ // Cache all proxy BINL offers.
+ //
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+ Private->OfferCount[OfferType]++;
+ } else if (Private->OfferCount[OfferType] > 0) {
+ //
+ // Only cache the first PXE10/WFM11a offer, and discard the others.
+ //
+ Private->OfferIndex[OfferType][0] = Private->OfferNum;
+ Private->OfferCount[OfferType] = 1;
+ } else {
+ return ;
+ }
+ } else {
+ //
+ // It's a DHCPv4 offer with yiaddr, and cache them all.
+ //
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+ Private->OfferCount[OfferType]++;
+ }
+ }
+
+ Private->OfferNum++;
+}
+
+
+/**
+ Select an DHCPv4 offer, and record SelectIndex and SelectProxyType.
+
+ @param[in] Private Pointer to PxeBc private data.
+
+**/
+VOID
+PxeBcSelectDhcp4Offer (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ UINT32 Index;
+ UINT32 OfferIndex;
+ EFI_DHCP4_PACKET *Offer;
+
+ Private->SelectIndex = 0;
+
+ if (Private->IsOfferSorted) {
+ //
+ // Select offer by default policy.
+ //
+ if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
+ //
+ // 1. DhcpPxe10 offer
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
+ //
+ // 2. DhcpWfm11a offer
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+ Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
+ //
+ // 3. DhcpOnly offer and ProxyPxe10 offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+ Private->SelectProxyType = PxeOfferTypeProxyPxe10;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+ Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
+ //
+ // 4. DhcpOnly offer and ProxyWfm11a offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+ Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
+ //
+ // 5. DhcpBinl offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+ Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
+ //
+ // 6. DhcpOnly offer and ProxyBinl offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+ Private->SelectProxyType = PxeOfferTypeProxyBinl;
+
+ } else {
+ //
+ // 7. DhcpOnly offer with bootfilename.
+ //
+ for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
+ OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
+ if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+ Private->SelectIndex = OfferIndex + 1;
+ break;
+ }
+ }
+ //
+ // 8. Bootp offer with bootfilename.
+ //
+ OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0];
+ if (Private->SelectIndex == 0 &&
+ Private->OfferCount[PxeOfferTypeBootp] > 0 &&
+ Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+ Private->SelectIndex = OfferIndex + 1;
+ }
+ }
+ } else {
+ //
+ // Select offer by received order.
+ //
+ for (Index = 0; Index < Private->OfferNum; Index++) {
+
+ Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
+
+ if (IS_PROXY_DHCP_OFFER (Offer)) {
+ //
+ // Skip proxy offers
+ //
+ continue;
+ }
+
+ if (!Private->IsProxyRecved &&
+ Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly &&
+ Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
+ //
+ // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
+ //
+ continue;
+ }
+
+ //
+ // Record the index of the select offer.
+ //
+ Private->SelectIndex = Index + 1;
+ break;
+ }
+ }
+}
+
+
+/**
+ Handle the DHCPv4 offer packet.
+
+ @param[in] Private Pointer to PxeBc private data.
+
+ @retval EFI_SUCCESS Handled the DHCPv4 offer packet successfully.
+ @retval EFI_NO_RESPONSE No response to the following request packet.
+
+**/
+EFI_STATUS
+PxeBcHandleDhcp4Offer (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;
+ EFI_DHCP4_PACKET_OPTION **Options;
+ UINT32 Index;
+ EFI_DHCP4_PACKET *Offer;
+ PXEBC_OFFER_TYPE OfferType;
+ UINT32 ProxyIndex;
+ UINT32 SelectIndex;
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PACKET *Ack;
+
+ ASSERT (Private->SelectIndex > 0);
+ SelectIndex = (UINT32) (Private->SelectIndex - 1);
+ ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
+ Cache4 = &Private->OfferBuffer[SelectIndex].Dhcp4;
+ Options = Cache4->OptList;
+ Status = EFI_SUCCESS;
+
+ if (Cache4->OfferType == PxeOfferTypeDhcpBinl) {
+ //
+ // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
+ //
+ if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) {
+ Status = EFI_NO_RESPONSE;
+ }
+ } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) {
+
+ if (Private->IsProxyRecved) {
+ //
+ // DhcpOnly offer is selected, so need try to request bootfile name.
+ //
+ ProxyIndex = 0;
+ if (Private->IsOfferSorted) {
+ //
+ // The proxy offer should be determined if select by default policy.
+ // IsOfferSorted means all offers are labeled by OfferIndex.
+ //
+ ASSERT (Private->SelectProxyType < PxeOfferTypeMax);
+ ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
+
+ if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
+ //
+ // Try all the cached ProxyBinl offer one by one to request bootfile name.
+ //
+ for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
+ if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) {
+ break;
+ }
+ }
+ if (Index == Private->OfferCount[Private->SelectProxyType]) {
+ Status = EFI_NO_RESPONSE;
+ }
+ } else {
+ //
+ // For other proxy offers, only one is buffered.
+ //
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+ }
+ } else {
+ //
+ // The proxy offer should not be determined if select by received order.
+ //
+ Status = EFI_NO_RESPONSE;
+
+ for (Index = 0; Index < Private->OfferNum; Index++) {
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+ Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
+ OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType;
+ if (!IS_PROXY_DHCP_OFFER (Offer)) {
+ //
+ // Skip non proxy DHCPv4 offers.
+ //
+ continue;
+ }
+
+ if (OfferType == PxeOfferTypeProxyBinl) {
+ //
+ // Try all the cached ProxyBinl offer one by one to request bootfile name.
+ //
+ if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) {
+ continue;
+ }
+ }
+
+ Private->SelectProxyType = OfferType;
+ ProxyIndex = Index;
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
+ //
+ // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
+ //
+ PxeBcCopyProxyOffer (Private, ProxyIndex);
+ }
+ } else {
+ //
+ // Othewise, the bootfile name must be included in DhcpOnly offer.
+ //
+ ASSERT (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // All PXE boot information is ready by now.
+ //
+ Mode = Private->PxeBc.Mode;
+ Offer = &Cache4->Packet.Offer;
+ Ack = &Private->DhcpAck.Dhcp4.Packet.Ack;
+ if (Cache4->OfferType == PxeOfferTypeBootp) {
+ //
+ // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply
+ // should be taken as ack.
+ //
+ Ack = Offer;
+ }
+
+ PxeBcCopyDhcp4Ack (Private, Ack, TRUE);
+ Mode->DhcpDiscoverValid = TRUE;
+ }
+
+ return Status;
+}
+
+
+/**
+ EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
+ to intercept events that occurred in the configuration process.
+
+ @param[in] This Pointer to the EFI DHCPv4 Protocol.
+ @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().
+ @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver.
+ @param[in] Dhcp4Event The event that occurs in the current state, which usually means a
+ state transition.
+ @param[in] Packet The DHCPv4 packet that is going to be sent or already received.
+ @param[out] NewPacket The packet that is used to replace the above Packet.
+
+ @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
+ @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
+ driver will continue to wait for more DHCPOFFER packets until the
+ retry timeout expires.
+ @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process
+ and return to the Dhcp4Init or Dhcp4InitReboot state.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDhcp4CallBack (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP4_STATE CurrentState,
+ IN EFI_DHCP4_EVENT Dhcp4Event,
+ IN EFI_DHCP4_PACKET *Packet OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+ EFI_DHCP4_PACKET_OPTION *MaxMsgSize;
+ UINT16 Value;
+ EFI_STATUS Status;
+ BOOLEAN Received;
+
+ if ((Dhcp4Event != Dhcp4RcvdOffer) &&
+ (Dhcp4Event != Dhcp4SelectOffer) &&
+ (Dhcp4Event != Dhcp4SendDiscover) &&
+ (Dhcp4Event != Dhcp4RcvdAck)) {
+ return EFI_SUCCESS;
+ }
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = Private->PxeBc.Mode;
+ Callback = Private->PxeBcCallback;
+
+ //
+ // Override the Maximum DHCP Message Size.
+ //
+ MaxMsgSize = PxeBcParseDhcp4Options (
+ Packet->Dhcp4.Option,
+ GET_OPTION_BUFFER_LEN (Packet),
+ PXEBC_DHCP4_TAG_MAXMSG
+ );
+ if (MaxMsgSize != NULL) {
+ Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);
+ CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
+ }
+
+ //
+ // Callback to user if any packets sent or received.
+ //
+ if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) {
+ Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck);
+ Status = Callback->Callback (
+ Callback,
+ Private->Function,
+ Received,
+ Packet->Length,
+ (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4
+ );
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ return EFI_ABORTED;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ switch (Dhcp4Event) {
+
+ case Dhcp4SendDiscover:
+ //
+ // Cache the DHCPv4 discover packet to mode data directly.
+ // It need to check SendGuid as well as Dhcp4SendRequest.
+ //
+ CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length);
+
+ case Dhcp4SendRequest:
+ if (Mode->SendGUID) {
+ //
+ // Send the system Guid instead of the MAC address as the hardware address if required.
+ //
+ if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) {
+ //
+ // Zero the Guid to indicate NOT programable if failed to get system Guid.
+ //
+ ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));
+ }
+ Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);
+ }
+ break;
+
+ case Dhcp4RcvdOffer:
+ Status = EFI_NOT_READY;
+ if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
+ //
+ // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
+ // the OfferIndex and OfferCount.
+ //
+ PxeBcCacheDhcp4Offer (Private, Packet);
+ }
+ break;
+
+ case Dhcp4SelectOffer:
+ //
+ // Select offer by the default policy or by order, and record the SelectIndex
+ // and SelectProxyType.
+ //
+ PxeBcSelectDhcp4Offer (Private);
+
+ if (Private->SelectIndex == 0) {
+ Status = EFI_ABORTED;
+ } else {
+ *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;
+ }
+ break;
+
+ case Dhcp4RcvdAck:
+ //
+ // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data
+ // without verification.
+ //
+ ASSERT (Private->SelectIndex != 0);
+
+ PxeBcCopyDhcp4Ack (Private, Packet, FALSE);
+ break;
+
+ default:
+ break;
+ }
+
+ return Status;
+}
+
+
+/**
+ Build and send out the request packet for the bootfile, and parse the reply.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Type PxeBc option boot item type.
+ @param[in] Layer Pointer to option boot item layer.
+ @param[in] UseBis Use BIS or not.
+ @param[in] DestIp Pointer to the server address.
+ @param[in] IpCount The total count of the server address.
+ @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+ @retval EFI_SUCCESS Successfully discovered boot file.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.
+ @retval Others Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Discover (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN UINT16 IpCount,
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT Sport;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token;
+ BOOLEAN IsBCast;
+ EFI_STATUS Status;
+ UINT16 RepIndex;
+ UINT16 SrvIndex;
+ UINT16 TryIndex;
+ EFI_DHCP4_LISTEN_POINT ListenPoint;
+ EFI_DHCP4_PACKET *Response;
+ UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];
+ UINT32 OptCount;
+ EFI_DHCP4_PACKET_OPTION *PxeOpt;
+ PXEBC_OPTION_BOOT_ITEM *PxeBootItem;
+ UINT8 VendorOptLen;
+ UINT32 Xid;
+
+ Mode = Private->PxeBc.Mode;
+ Dhcp4 = Private->Dhcp4;
+ Status = EFI_SUCCESS;
+
+ ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));
+
+ //
+ // Use broadcast if destination address not specified.
+ //
+ if (DestIp == NULL) {
+ Sport = PXEBC_DHCP4_S_PORT;
+ IsBCast = TRUE;
+ } else {
+ Sport = PXEBC_BS_DISCOVER_PORT;
+ IsBCast = FALSE;
+ }
+
+ if (!UseBis && Layer != NULL) {
+ *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
+ }
+
+ //
+ // Build all the options for the request packet.
+ //
+ OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE);
+
+ if (Private->IsDoDiscover) {
+ //
+ // Add vendor option of PXE_BOOT_ITEM
+ //
+ VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);
+ OptList[OptCount] = AllocateZeroPool (VendorOptLen);
+ if (OptList[OptCount] == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ OptList[OptCount]->OpCode = PXEBC_DHCP4_TAG_VENDOR;
+ OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2);
+ PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;
+ PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM;
+ PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);
+ PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;
+ PxeBootItem->Type = HTONS (Type);
+ PxeOpt->Data[PxeOpt->Length] = PXEBC_DHCP4_TAG_EOP;
+
+ if (Layer != NULL) {
+ PxeBootItem->Layer = HTONS (*Layer);
+ }
+
+ OptCount++;
+ }
+
+ //
+ // Build the request packet with seed packet and option list.
+ //
+ Status = Dhcp4->Build (
+ Dhcp4,
+ &Private->SeedPacket,
+ 0,
+ NULL,
+ OptCount,
+ OptList,
+ &Token.Packet
+ );
+ //
+ // Free the vendor option of PXE_BOOT_ITEM.
+ //
+ if (Private->IsDoDiscover) {
+ FreePool (OptList[OptCount - 1]);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Mode->SendGUID) {
+ if (EFI_ERROR (PxeBcGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) {
+ //
+ // Zero the Guid to indicate NOT programable if failed to get system Guid.
+ //
+ ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));
+ }
+ Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);
+ }
+
+ //
+ // Set fields of the token for the request packet.
+ //
+ Xid = NET_RANDOM (NetRandomInitSeed ());
+ Token.Packet->Dhcp4.Header.Xid = HTONL (Xid);
+ Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0));
+ CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+
+ Token.RemotePort = Sport;
+
+ if (IsBCast) {
+ SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);
+ } else {
+ CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
+ }
+
+ CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
+
+ if (!IsBCast) {
+ Token.ListenPointCount = 1;
+ Token.ListenPoints = &ListenPoint;
+ Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT;
+ CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));
+ CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));
+ }
+
+ //
+ // Send out the request packet to discover the bootfile.
+ //
+ for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {
+
+ Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);
+ Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));
+
+ Status = Dhcp4->TransmitReceive (Dhcp4, &Token);
+ if (Token.Status != EFI_TIMEOUT) {
+ break;
+ }
+ }
+
+ if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {
+ //
+ // No server response our PXE request
+ //
+ Status = EFI_TIMEOUT;
+ }
+
+ if (!EFI_ERROR (Status)) {
+
+ RepIndex = 0;
+ SrvIndex = 0;
+ Response = Token.ResponseList;
+ //
+ // Find the right PXE Reply according to server address.
+ //
+ while (RepIndex < Token.ResponseCount) {
+
+ while (SrvIndex < IpCount) {
+ if (SrvList[SrvIndex].AcceptAnyResponse) {
+ break;
+ }
+ if ((SrvList[SrvIndex].Type == Type) &&
+ EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &Private->ServerIp)) {
+ break;
+ }
+ SrvIndex++;
+ }
+
+ if ((IpCount != SrvIndex) || (IpCount == 0)) {
+ break;
+ }
+
+ SrvIndex = 0;
+ RepIndex++;
+
+ Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
+ }
+
+ if (RepIndex < Token.ResponseCount) {
+ //
+ // Cache the right PXE reply packet here, set valid flag later.
+ // Especially for PXE discover packet, store it into mode data here.
+ //
+ if (Private->IsDoDiscover) {
+ PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response);
+ CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length);
+ } else {
+ PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response);
+ }
+ } else {
+ //
+ // Not found the right PXE reply packet.
+ //
+ Status = EFI_NOT_FOUND;
+ }
+ if (Token.ResponseList != NULL) {
+ FreePool (Token.ResponseList);
+ }
+ }
+
+ FreePool (Token.Packet);
+ return Status;
+}
+
+
+/**
+ Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL
+
+ @retval EFI_SUCCESS The D.O.R.A process successfully finished.
+ @retval Others Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Dora (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PROTOCOL *Dhcp4
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeMode;
+ EFI_DHCP4_CONFIG_DATA Config;
+ EFI_DHCP4_MODE_DATA Mode;
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];
+ UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];
+ UINT32 OptCount;
+ EFI_STATUS Status;
+
+ ASSERT (Dhcp4 != NULL);
+
+ Status = EFI_SUCCESS;
+ PxeMode = Private->PxeBc.Mode;
+
+ //
+ // Build option list for the request packet.
+ //
+ OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE);
+ ASSERT (OptCount> 0);
+
+ ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA));
+ ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+
+ Config.OptionCount = OptCount;
+ Config.OptionList = OptList;
+ Config.Dhcp4Callback = PxeBcDhcp4CallBack;
+ Config.CallbackContext = Private;
+ Config.DiscoverTryCount = PXEBC_DHCP_RETRIES;
+ Config.DiscoverTimeout = mPxeDhcpTimeout;
+
+ //
+ // Configure the DHCPv4 instance for PXE boot.
+ //
+ Status = Dhcp4->Configure (Dhcp4, &Config);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Initialize the record fields for DHCPv4 offer in private data.
+ //
+ Private->IsProxyRecved = FALSE;
+ Private->OfferNum = 0;
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+ //
+ // Start DHCPv4 D.O.R.A. process to acquire IPv4 address.
+ //
+ Status = Dhcp4->Start (Dhcp4, NULL);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ICMP_ERROR) {
+ PxeMode->IcmpErrorReceived = TRUE;
+ }
+ goto ON_EXIT;
+ }
+
+ //
+ // Get the acquired IPv4 address and store them.
+ //
+ Status = Dhcp4->GetModeData (Dhcp4, &Mode);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ ASSERT (Mode.State == Dhcp4Bound);
+
+ CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = PxeBcFlushStaionIp (Private, &Private->StationIp, &Private->SubnetMask);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Check the selected offer whether BINL retry is needed.
+ //
+ Status = PxeBcHandleDhcp4Offer (Private);
+
+ AsciiPrint ("\n Station IP address is ");
+
+ PxeBcShowIp4Addr (&Private->StationIp.v4);
+
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ Dhcp4->Stop (Dhcp4);
+ Dhcp4->Configure (Dhcp4, NULL);
+ } else {
+ ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+ Dhcp4->Configure (Dhcp4, &Config);
+ Private->IsAddressOk = TRUE;
+ }
+
+ return Status;
+}
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
new file mode 100644
index 0000000000..bc21b212a2
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
@@ -0,0 +1,389 @@
+/** @file
+ Functions declaration related with DHCPv4 for UefiPxeBc Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_DHCP4_H__
+#define __EFI_PXEBC_DHCP4_H__
+
+#define PXEBC_DHCP4_OPTION_MAX_NUM 16
+#define PXEBC_DHCP4_OPTION_MAX_SIZE 312
+#define PXEBC_DHCP4_PACKET_MAX_SIZE 1472
+#define PXEBC_DHCP4_S_PORT 67
+#define PXEBC_DHCP4_C_PORT 68
+#define PXEBC_BS_DOWNLOAD_PORT 69
+#define PXEBC_BS_DISCOVER_PORT 4011
+#define PXEBC_DHCP4_OPCODE_REQUEST 1
+#define PXEBC_DHCP4_OPCODE_REPLY 2
+#define PXEBC_DHCP4_MSG_TYPE_REQUEST 3
+#define PXEBC_DHCP4_MAGIC 0x63538263 // network byte order
+
+//
+// Dhcp Options
+//
+#define PXEBC_DHCP4_TAG_PAD 0 // Pad Option
+#define PXEBC_DHCP4_TAG_EOP 255 // End Option
+#define PXEBC_DHCP4_TAG_NETMASK 1 // Subnet Mask
+#define PXEBC_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC
+#define PXEBC_DHCP4_TAG_ROUTER 3 // Router option,
+#define PXEBC_DHCP4_TAG_TIME_SERVER 4 // Time Server
+#define PXEBC_DHCP4_TAG_NAME_SERVER 5 // Name Server
+#define PXEBC_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server
+#define PXEBC_DHCP4_TAG_HOSTNAME 12 // Host Name
+#define PXEBC_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size
+#define PXEBC_DHCP4_TAG_DUMP 14 // Merit Dump File
+#define PXEBC_DHCP4_TAG_DOMAINNAME 15 // Domain Name
+#define PXEBC_DHCP4_TAG_ROOTPATH 17 // Root path
+#define PXEBC_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path
+#define PXEBC_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size
+#define PXEBC_DHCP4_TAG_TTL 23 // Default IP Time-to-live
+#define PXEBC_DHCP4_TAG_BROADCAST 28 // Broadcast Address
+#define PXEBC_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain
+#define PXEBC_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers
+#define PXEBC_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers
+#define PXEBC_DHCP4_TAG_VENDOR 43 // Vendor Specific Information
+#define PXEBC_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address
+#define PXEBC_DHCP4_TAG_LEASE 51 // IP Address Lease Time
+#define PXEBC_DHCP4_TAG_OVERLOAD 52 // Option Overload
+#define PXEBC_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type
+#define PXEBC_DHCP4_TAG_SERVER_ID 54 // Server Identifier
+#define PXEBC_DHCP4_TAG_PARA_LIST 55 // Parameter Request List
+#define PXEBC_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size
+#define PXEBC_DHCP4_TAG_T1 58 // Renewal (T1) Time Value
+#define PXEBC_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value
+#define PXEBC_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier
+#define PXEBC_DHCP4_TAG_CLIENT_ID 61 // Client-identifier
+#define PXEBC_DHCP4_TAG_TFTP 66 // TFTP server name
+#define PXEBC_DHCP4_TAG_BOOTFILE 67 // Bootfile name
+#define PXEBC_PXE_DHCP4_TAG_ARCH 93
+#define PXEBC_PXE_DHCP4_TAG_UNDI 94
+#define PXEBC_PXE_DHCP4_TAG_UUID 97
+//
+// Sub-Options in Dhcp Vendor Option
+//
+#define PXEBC_VENDOR_TAG_MTFTP_IP 1
+#define PXEBC_VENDOR_TAG_MTFTP_CPORT 2
+#define PXEBC_VENDOR_TAG_MTFTP_SPORT 3
+#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT 4
+#define PXEBC_VENDOR_TAG_MTFTP_DELAY 5
+#define PXEBC_VENDOR_TAG_DISCOVER_CTRL 6
+#define PXEBC_VENDOR_TAG_DISCOVER_MCAST 7
+#define PXEBC_VENDOR_TAG_BOOT_SERVERS 8
+#define PXEBC_VENDOR_TAG_BOOT_MENU 9
+#define PXEBC_VENDOR_TAG_MENU_PROMPT 10
+#define PXEBC_VENDOR_TAG_MCAST_ALLOC 11
+#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES 12
+#define PXEBC_VENDOR_TAG_BOOT_ITEM 71
+
+#define PXEBC_BOOT_REQUEST_TIMEOUT 1
+#define PXEBC_BOOT_REQUEST_RETRIES 4
+
+#define PXEBC_DHCP4_OVERLOAD_FILE 1
+#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME 2
+
+
+//
+// The array index of the DHCP4 option tag interested
+//
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0
+#define PXEBC_DHCP4_TAG_INDEX_VENDOR 1
+#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD 2
+#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE 3
+#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID 4
+#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID 5
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE 6
+#define PXEBC_DHCP4_TAG_INDEX_MAX 7
+
+//
+// Dhcp4 and Dhcp6 share this definition, and corresponding
+// relatioinship is as follows:
+//
+// Dhcp4Discover <> Dhcp6Solicit
+// Dhcp4Offer <> Dhcp6Advertise
+// Dhcp4Request <> Dhcp6Request
+// Dhcp4Ack <> DHcp6Reply
+//
+typedef enum {
+ PxeOfferTypeDhcpOnly,
+ PxeOfferTypeDhcpPxe10,
+ PxeOfferTypeDhcpWfm11a,
+ PxeOfferTypeDhcpBinl,
+ PxeOfferTypeProxyPxe10,
+ PxeOfferTypeProxyWfm11a,
+ PxeOfferTypeProxyBinl,
+ PxeOfferTypeBootp,
+ PxeOfferTypeMax
+} PXEBC_OFFER_TYPE;
+
+#define BIT(x) (1 << x)
+#define CTRL(x) (0x1F & (x))
+#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:?????:????:??????"
+#define DEFAULT_UNDI_TYPE 1
+#define DEFAULT_UNDI_MAJOR 3
+#define DEFAULT_UNDI_MINOR 0
+
+#define MTFTP_VENDOR_OPTION_BIT_MAP \
+ (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \
+ BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY))
+
+#define DISCOVER_VENDOR_OPTION_BIT_MAP \
+ (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \
+ BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \
+ BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \
+ BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \
+ BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))
+
+#define IS_VALID_BOOT_PROMPT(x) \
+ ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \
+ == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))
+
+#define IS_VALID_BOOT_MENU(x) \
+ ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \
+ == BIT (PXEBC_VENDOR_TAG_BOOT_MENU))
+
+#define IS_VALID_MTFTP_VENDOR_OPTION(x) \
+ (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \
+ == MTFTP_VENDOR_OPTION_BIT_MAP)
+
+#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \
+ (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0)
+
+#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \
+ (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \
+ == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES))
+
+#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \
+ (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \
+ BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \
+ == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32))
+
+#define SET_VENDOR_OPTION_BIT_MAP(x, y) \
+ (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32)))
+
+#define GET_NEXT_DHCP_OPTION(Opt) \
+ (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \
+ sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)
+
+#define GET_OPTION_BUFFER_LEN(Pkt) \
+ ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)
+
+#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \
+ (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \
+ ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS))
+
+#define IS_PROXY_DHCP_OFFER(Offer) \
+ EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr)
+
+#define IS_DISABLE_BCAST_DISCOVER(x) \
+ (((x) & BIT (0)) == BIT (0))
+
+#define IS_DISABLE_MCAST_DISCOVER(x) \
+ (((x) & BIT (1)) == BIT (1))
+
+#define IS_ENABLE_USE_SERVER_LIST(x) \
+ (((x) & BIT (2)) == BIT (2))
+
+#define IS_ENABLE_BOOT_FILE_NAME(x) \
+ (((x) & BIT (3)) == BIT (3))
+
+
+#pragma pack(1)
+typedef struct {
+ UINT8 ParaList[135];
+} PXEBC_DHCP4_OPTION_PARA;
+
+typedef struct {
+ UINT16 Size;
+} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 MajorVer;
+ UINT8 MinorVer;
+} PXEBC_DHCP4_OPTION_UNDI;
+
+typedef struct {
+ UINT8 Type;
+} PXEBC_DHCP4_OPTION_MESG;
+
+typedef struct {
+ UINT16 Type;
+} PXEBC_DHCP4_OPTION_ARCH;
+
+typedef struct {
+ UINT8 ClassIdentifier[10];
+ UINT8 ArchitecturePrefix[5];
+ UINT8 ArchitectureType[5];
+ UINT8 Lit3[1];
+ UINT8 InterfaceName[4];
+ UINT8 Lit4[1];
+ UINT8 UndiMajor[3];
+ UINT8 UndiMinor[3];
+} PXEBC_DHCP4_OPTION_CLID;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 Guid[16];
+} PXEBC_DHCP4_OPTION_UUID;
+
+typedef struct {
+ UINT16 Type;
+ UINT16 Layer;
+} PXEBC_OPTION_BOOT_ITEM;
+
+#pragma pack()
+
+typedef union {
+ PXEBC_DHCP4_OPTION_PARA *Para;
+ PXEBC_DHCP4_OPTION_UNDI *Undi;
+ PXEBC_DHCP4_OPTION_ARCH *Arch;
+ PXEBC_DHCP4_OPTION_CLID *Clid;
+ PXEBC_DHCP4_OPTION_UUID *Uuid;
+ PXEBC_DHCP4_OPTION_MESG *Mesg;
+ PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize;
+} PXEBC_DHCP4_OPTION_ENTRY;
+
+typedef struct {
+ UINT16 Type;
+ UINT8 IpCnt;
+ EFI_IPv4_ADDRESS IpAddr[1];
+} PXEBC_BOOT_SVR_ENTRY;
+
+typedef struct {
+ UINT16 Type;
+ UINT8 DescLen;
+ UINT8 DescStr[1];
+} PXEBC_BOOT_MENU_ENTRY;
+
+typedef struct {
+ UINT8 Timeout;
+ UINT8 Prompt[1];
+} PXEBC_MENU_PROMPT;
+
+typedef struct {
+ UINT32 BitMap[8];
+ EFI_IPv4_ADDRESS MtftpIp;
+ UINT16 MtftpCPort;
+ UINT16 MtftpSPort;
+ UINT8 MtftpTimeout;
+ UINT8 MtftpDelay;
+ UINT8 DiscoverCtrl;
+ EFI_IPv4_ADDRESS DiscoverMcastIp;
+ EFI_IPv4_ADDRESS McastIpBase;
+ UINT16 McastIpBlock;
+ UINT16 McastIpRange;
+ UINT16 BootSrvType;
+ UINT16 BootSrvLayer;
+ PXEBC_BOOT_SVR_ENTRY *BootSvr;
+ UINT8 BootSvrLen;
+ PXEBC_BOOT_MENU_ENTRY *BootMenu;
+ UINT8 BootMenuLen;
+ PXEBC_MENU_PROMPT *MenuPrompt;
+ UINT8 MenuPromptLen;
+ UINT32 *CredType;
+ UINT8 CredTypeLen;
+} PXEBC_VENDOR_OPTION;
+
+typedef union {
+ EFI_DHCP4_PACKET Offer;
+ EFI_DHCP4_PACKET Ack;
+ UINT8 Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE];
+} PXEBC_DHCP4_PACKET;
+
+typedef struct {
+ PXEBC_DHCP4_PACKET Packet;
+ PXEBC_OFFER_TYPE OfferType;
+ EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX];
+ PXEBC_VENDOR_OPTION VendorOpt;
+} PXEBC_DHCP4_PACKET_CACHE;
+
+
+/**
+ Create a template DHCPv4 packet as a seed.
+
+ @param[out] Seed Pointer to the seed packet.
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.
+
+**/
+VOID
+PxeBcSeedDhcp4Packet (
+ OUT EFI_DHCP4_PACKET *Seed,
+ IN EFI_UDP4_PROTOCOL *Udp4
+ );
+
+
+/**
+ Parse the cached DHCPv4 packet, including all the options.
+
+ @param[in] Cache4 Pointer to cached DHCPv4 packet.
+
+ @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully.
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp4Packet (
+ IN PXEBC_DHCP4_PACKET_CACHE *Cache4
+ );
+
+
+/**
+ Build and send out the request packet for the bootfile, and parse the reply.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Type PxeBc option boot item type.
+ @param[in] Layer Pointer to option boot item layer.
+ @param[in] UseBis Use BIS or not.
+ @param[in] DestIp Pointer to the server address.
+ @param[in] IpCount The total count of the server address.
+ @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+ @retval EFI_SUCCESS Successfully discovered boot file.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.
+ @retval Others Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Discover (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN UINT16 IpCount,
+ IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
+ );
+
+
+/**
+ Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL
+
+ @retval EFI_SUCCESS The D.O.R.A process successfully finished.
+ @retval Others Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Dora (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PROTOCOL *Dhcp4
+ );
+
+#endif
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
new file mode 100644
index 0000000000..0b7cf1f947
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
@@ -0,0 +1,1531 @@
+/** @file
+ Functions implementation related with DHCPv6 for UefiPxeBc Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+ Parse out a DHCPv6 option by OptTag, and find the position in buffer.
+
+ @param[in] Buffer The pointer to the option buffer.
+ @param[in] Length Length of the option buffer.
+ @param[in] OptTag The required option tag.
+
+ @retval NULL Failed to parse the required option.
+ @retval Others The postion of the required option in buffer.
+
+**/
+EFI_DHCP6_PACKET_OPTION *
+PxeBcParseDhcp6Options (
+ IN UINT8 *Buffer,
+ IN UINT32 Length,
+ IN UINT16 OptTag
+ )
+{
+ EFI_DHCP6_PACKET_OPTION *Option;
+ UINT32 Offset;
+
+ Option = (EFI_DHCP6_PACKET_OPTION *) Buffer;
+ Offset = 0;
+
+ //
+ // OpLen and OpCode here are both stored in network order.
+ //
+ while (Offset < Length) {
+
+ if (NTOHS (Option->OpCode) == OptTag) {
+
+ return Option;
+ }
+
+ Offset += (NTOHS(Option->OpLen) + 4);
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Build the options buffer for the DHCPv6 request packet.
+
+ @param[in] Private The pointer to PxeBc private data.
+ @param[out] OptList The pointer to the option pointer array.
+ @param[in] Buffer The pointer to the buffer to contain the option list.
+
+ @return Index The count of the built-in options.
+
+**/
+UINT32
+PxeBcBuildDhcp6Options (
+ IN PXEBC_PRIVATE_DATA *Private,
+ OUT EFI_DHCP6_PACKET_OPTION **OptList,
+ IN UINT8 *Buffer
+ )
+{
+ PXEBC_DHCP6_OPTION_ENTRY OptEnt;
+ UINT32 Index;
+ UINT16 Value;
+
+ Index = 0;
+ OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer;
+
+ //
+ // Append client option request option
+ //
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO);
+ OptList[Index]->OpLen = HTONS (4);
+ OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;
+ OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);
+ OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client network device interface option
+ //
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI);
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_UNDI));
+ OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;
+
+ if (Private->Nii != NULL) {
+ OptEnt.Undi->Type = Private->Nii->Type;
+ OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
+ OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
+ } else {
+ OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
+ OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
+ OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
+ }
+
+ OptEnt.Undi->Reserved = 0;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client system architecture option
+ //
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH);
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));
+ OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;
+ Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
+ CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append vendor class option to store the PXE class identifier.
+ //
+ OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));
+ OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;
+ OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);
+ OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));
+ CopyMem (
+ &OptEnt.VendorClass->ClassId,
+ DEFAULT_CLASS_ID_DATA,
+ sizeof (PXEBC_CLASS_ID)
+ );
+ PxeBcUintnToAscDecWithFormat (
+ EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
+ OptEnt.VendorClass->ClassId.ArchitectureType,
+ sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
+ );
+
+ if (Private->Nii != NULL) {
+ CopyMem (
+ OptEnt.VendorClass->ClassId.InterfaceName,
+ Private->Nii->StringId,
+ sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
+ );
+ PxeBcUintnToAscDecWithFormat (
+ Private->Nii->MajorVer,
+ OptEnt.VendorClass->ClassId.UndiMajor,
+ sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
+ );
+ PxeBcUintnToAscDecWithFormat (
+ Private->Nii->MinorVer,
+ OptEnt.VendorClass->ClassId.UndiMinor,
+ sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
+ );
+ }
+
+ Index++;
+
+ return Index;
+}
+
+
+/**
+ Cache the DHCPv6 packet.
+
+ @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.
+ @param[in] Src The pointer to the DHCPv6 packet to be cached.
+
+**/
+VOID
+PxeBcCacheDhcp6Packet (
+ IN EFI_DHCP6_PACKET *Dst,
+ IN EFI_DHCP6_PACKET *Src
+ )
+{
+ ASSERT (Dst->Size >= Src->Length);
+
+ CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
+ Dst->Length = Src->Length;
+}
+
+
+/**
+ Free all the nodes in the list for boot file.
+
+ @param[in] Head The pointer to the head of list.
+
+**/
+VOID
+PxeBcFreeBootFileOption (
+ IN LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ PXEBC_DHCP6_OPTION_NODE *Node;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {
+ Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);
+ RemoveEntryList (Entry);
+ FreePool (Node);
+ }
+}
+
+
+/**
+ Parse the Boot File URL option.
+
+ @param[out] FileName The pointer to the boot file name.
+ @param[in, out] SrvAddr The pointer to the boot server address.
+ @param[in] BootFile The pointer to the boot file URL option data.
+ @param[in] Length The length of the boot file URL option data.
+
+ @retval EFI_ABORTED User cancel operation.
+ @retval EFI_SUCCESS Selected the boot menu successfully.
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileUrl (
+ OUT UINT8 **FileName,
+ IN OUT EFI_IPv6_ADDRESS *SrvAddr,
+ IN CHAR8 *BootFile,
+ IN UINT16 Length
+ )
+{
+ UINT16 PrefixLen;
+ UINT8 *BootFileNamePtr;
+ UINT8 *BootFileName;
+ UINT16 BootFileNameLen;
+ CHAR8 *TmpStr;
+ CHAR8 *ServerAddressOption;
+ CHAR8 *ServerAddress;
+ EFI_STATUS Status;
+
+ //
+ // The format of the Boot File URL option is:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | OPT_BOOTFILE_URL | option-len |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // . bootfile-url (variable length) .
+ // | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ //
+ // Based upon RFC 5970 and UEFI errata that will appear in chapter 21.3 of UEFI 2.3
+ // specification after 2.3 errata B and future UEFI Specifications after 2.3.
+ // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME
+ // As an example where the BOOTFILE_NAME is the EFI loader and
+ // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.
+ //
+ PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);
+
+ if (Length <= PrefixLen ||
+ CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ BootFile = BootFile + PrefixLen;
+ Length = (UINT16) (Length - PrefixLen);
+
+ TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);
+ if (TmpStr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (TmpStr, BootFile, Length);
+ TmpStr[Length] = '\0';
+
+ //
+ // Get the part of SERVER_ADDRESS string.
+ //
+ ServerAddressOption = TmpStr;
+ if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {
+ FreePool (TmpStr);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ServerAddressOption ++;
+ ServerAddress = ServerAddressOption;
+ while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {
+ ServerAddress++;
+ }
+
+ if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {
+ FreePool (TmpStr);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ServerAddress = '\0';
+
+ //
+ // Convert the string of server address to Ipv6 address format and store it.
+ //
+ Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);
+ if (EFI_ERROR (Status)) {
+ FreePool (TmpStr);
+ return Status;
+ }
+
+ //
+ // Get the part of BOOTFILE_NAME string.
+ //
+ BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1);
+ if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {
+ FreePool (TmpStr);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ++BootFileNamePtr;
+ BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);
+ if (BootFileNameLen != 0 || FileName != NULL) {
+ BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen);
+ if (BootFileName == NULL) {
+ FreePool (TmpStr);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (BootFileName, BootFileNamePtr, BootFileNameLen);
+ BootFileName[BootFileNameLen - 1] = '\0';
+ *FileName = BootFileName;
+ }
+
+
+ FreePool (TmpStr);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the Boot File Parameter option.
+
+ @param[in] BootFilePara The pointer to boot file parameter option data.
+ @param[out] BootFileSize The pointer to the parsed boot file size.
+
+ @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.
+ @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileParam (
+ IN CHAR8 *BootFilePara,
+ OUT UINT16 *BootFileSize
+ )
+{
+ UINT16 Length;
+ UINT8 Index;
+ UINT8 Digit;
+ UINT32 Size;
+
+ CopyMem (&Length, BootFilePara, sizeof (UINT16));
+ Length = NTOHS (Length);
+
+ //
+ // The BootFile Size should be 1~5 byte ASCII strings
+ //
+ if (Length < 1 || Length > 5) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Extract the value of BootFile Size.
+ //
+ BootFilePara = BootFilePara + sizeof (UINT16);
+ Size = 0;
+ for (Index = 0; Index < Length; Index++) {
+ if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {
+ return EFI_NOT_FOUND;
+ }
+
+ Size = (Size + Digit) * 10;
+ }
+
+ Size = Size / 10;
+ if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {
+ return EFI_NOT_FOUND;
+ }
+
+ *BootFileSize = (UINT16) Size;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the cached DHCPv6 packet, including all the options.
+
+ @param[in] Cache6 The pointer to a cached DHCPv6 packet.
+
+ @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp6Packet (
+ IN PXEBC_DHCP6_PACKET_CACHE *Cache6
+ )
+{
+ EFI_DHCP6_PACKET *Offer;
+ EFI_DHCP6_PACKET_OPTION **Options;
+ EFI_DHCP6_PACKET_OPTION *Option;
+ PXEBC_OFFER_TYPE OfferType;
+ BOOLEAN IsProxyOffer;
+ BOOLEAN IsPxeOffer;
+ UINT32 Offset;
+ UINT32 Length;
+ UINT32 EnterpriseNum;
+
+ IsProxyOffer = TRUE;
+ IsPxeOffer = FALSE;
+ Offer = &Cache6->Packet.Offer;
+ Options = Cache6->OptList;
+
+ ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
+
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);
+ Offset = 0;
+ Length = GET_DHCP6_OPTION_SIZE (Offer);
+
+ //
+ // OpLen and OpCode here are both stored in network order, since they are from original packet.
+ //
+ while (Offset < Length) {
+
+ if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {
+ Options[PXEBC_DHCP6_IDX_IA_NA] = Option;
+ } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {
+ //
+ // The server sends this option to inform the client about an URL to a boot file.
+ //
+ Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;
+ } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {
+ Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
+ } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {
+ Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;
+ }
+
+ Offset += (NTOHS (Option->OpLen) + 4);
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);
+ }
+
+ //
+ // The offer with assigned client address is a proxy offer.
+ // An ia_na option, embeded with valid ia_addr option and a status_code of success.
+ //
+ Option = Options[PXEBC_DHCP6_IDX_IA_NA];
+ if (Option != NULL && NTOHS(Option->OpLen) >= 12) {
+ Option = PxeBcParseDhcp6Options (
+ Option->Data + 12,
+ NTOHS (Option->OpLen),
+ PXEBC_DHCP6_OPT_STATUS_CODE
+ );
+ if (Option != NULL && Option->Data[0] == 0) {
+ IsProxyOffer = FALSE;
+ }
+ }
+
+ //
+ // The offer with "PXEClient" is a pxe offer.
+ //
+ Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];
+ EnterpriseNum = PXEBC_DHCP6_ENTERPRISE_NUM;
+ if (Option != NULL &&
+ NTOHS(Option->OpLen) >= 13 &&
+ CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&
+ CompareMem (&Option->Data[4], DEFAULT_CLASS_ID_DATA, 9) == 0) {
+ IsPxeOffer = TRUE;
+ }
+
+ //
+ // Determine offer type of the dhcp6 packet.
+ //
+ if (IsPxeOffer) {
+ //
+ // It's a binl offer only with PXEClient.
+ //
+ OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
+ } else {
+ //
+ // It's a dhcp only offer, which is a pure dhcp6 offer packet.
+ //
+ OfferType = PxeOfferTypeDhcpOnly;
+ }
+
+ Cache6->OfferType = OfferType;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cache the DHCPv6 ack packet, and parse it on demand.
+
+ @param[in] Private The pointer to PxeBc private data.
+ @param[in] Ack The pointer to the DHCPv6 ack packet.
+ @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.
+
+**/
+VOID
+PxeBcCopyDhcp6Ack (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP6_PACKET *Ack,
+ IN BOOLEAN Verified
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+
+ Mode = Private->PxeBc.Mode;
+
+ PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);
+
+ if (Verified) {
+ //
+ // Parse the ack packet and store it into mode data if needed.
+ //
+ PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);
+ CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);
+ Mode->DhcpAckReceived = TRUE;
+ }
+}
+
+
+/**
+ Cache the DHCPv6 proxy offer packet according to the received order.
+
+ @param[in] Private The pointer to PxeBc private data.
+ @param[in] OfferIndex The received order of offer packets.
+
+**/
+VOID
+PxeBcCopyDhcp6Proxy (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 OfferIndex
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP6_PACKET *Offer;
+
+ ASSERT (OfferIndex < Private->OfferNum);
+ ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
+
+ Mode = Private->PxeBc.Mode;
+ Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;
+
+ //
+ // Cache the proxy offer packet and parse it.
+ //
+ PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);
+ PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);
+
+ //
+ // Store this packet into mode data.
+ //
+ CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);
+ Mode->ProxyOfferReceived = TRUE;
+}
+
+
+/**
+ Retry to request bootfile name by the BINL offer.
+
+ @param[in] Private The pointer to PxeBc private data.
+ @param[in] Index The received order of offer packets.
+
+ @retval EFI_SUCCESS Successfully retried a request for the bootfile name.
+ @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.
+
+**/
+EFI_STATUS
+PxeBcRetryDhcp6Binl (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 Index
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ PXEBC_DHCP6_PACKET_CACHE *Offer;
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;
+ EFI_IP_ADDRESS ServerIp;
+ EFI_STATUS Status;
+
+ ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+ ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||
+ Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);
+
+ Mode = Private->PxeBc.Mode;
+ Private->IsDoDiscover = FALSE;
+ Offer = &Private->OfferBuffer[Index].Dhcp6;
+
+ ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
+ //
+ // Parse out the next server address from the last offer, and store it
+ //
+ Status = PxeBcExtractBootFileUrl (
+ NULL,
+ &ServerIp.v6,
+ (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
+ NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
+ //
+ Status = PxeBcDhcp6Discover (
+ Private,
+ 0,
+ NULL,
+ FALSE,
+ &ServerIp
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cache6 = &Private->ProxyOffer.Dhcp6;
+ Status = PxeBcParseDhcp6Packet (Cache6);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&
+ Cache6->OfferType != PxeOfferTypeProxyWfm11a &&
+ Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
+ //
+ // This BINL ack doesn't have discovery option set or multicast option set
+ // or bootfile name specified.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ Mode->ProxyOfferReceived = TRUE;
+ CopyMem (
+ &Mode->ProxyOffer.Dhcpv6,
+ &Cache6->Packet.Offer.Dhcp6,
+ Cache6->Packet.Offer.Length
+ );
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
+
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
+ @param[in] RcvdOffer The pointer to the received offer packet.
+
+**/
+VOID
+PxeBcCacheDhcp6Offer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP6_PACKET *RcvdOffer
+ )
+{
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;
+ EFI_DHCP6_PACKET *Offer;
+ PXEBC_OFFER_TYPE OfferType;
+
+ Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
+ Offer = &Cache6->Packet.Offer;
+
+ //
+ // Cache the content of DHCPv6 packet firstly.
+ //
+ PxeBcCacheDhcp6Packet (Offer, RcvdOffer);
+
+ //
+ // Validate the DHCPv6 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {
+ return ;
+ }
+
+ //
+ // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
+ //
+ OfferType = Cache6->OfferType;
+ ASSERT (OfferType < PxeOfferTypeMax);
+ ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
+
+ if (IS_PROXY_OFFER (OfferType)) {
+ //
+ // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
+ //
+ Private->IsProxyRecved = TRUE;
+
+ if (OfferType == PxeOfferTypeProxyBinl) {
+ //
+ // Cache all proxy BINL offers.
+ //
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+ Private->OfferCount[OfferType]++;
+ } else if (Private->OfferCount[OfferType] > 0) {
+ //
+ // Only cache the first PXE10/WFM11a offer, and discard the others.
+ //
+ Private->OfferIndex[OfferType][0] = Private->OfferNum;
+ Private->OfferCount[OfferType] = 1;
+ } else {
+ return;
+ }
+ } else {
+ //
+ // It's a DHCPv6 offer with yiaddr, and cache them all.
+ //
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+ Private->OfferCount[OfferType]++;
+ }
+
+ Private->OfferNum++;
+}
+
+
+/**
+ Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
+
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcSelectDhcp6Offer (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ UINT32 Index;
+ UINT32 OfferIndex;
+ PXEBC_OFFER_TYPE OfferType;
+
+ Private->SelectIndex = 0;
+
+ if (Private->IsOfferSorted) {
+ //
+ // Select offer by default policy.
+ //
+ if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
+ //
+ // 1. DhcpPxe10 offer
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
+ //
+ // 2. DhcpWfm11a offer
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+ Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
+ //
+ // 3. DhcpOnly offer and ProxyPxe10 offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+ Private->SelectProxyType = PxeOfferTypeProxyPxe10;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+ Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
+ //
+ // 4. DhcpOnly offer and ProxyWfm11a offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+ Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
+ //
+ // 5. DhcpBinl offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
+
+ } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+ Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
+ //
+ // 6. DhcpOnly offer and ProxyBinl offer.
+ //
+ Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+ Private->SelectProxyType = PxeOfferTypeProxyBinl;
+
+ } else {
+ //
+ // 7. DhcpOnly offer with bootfilename.
+ //
+ for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
+ OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
+ if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {
+ Private->SelectIndex = OfferIndex + 1;
+ break;
+ }
+ }
+ }
+ } else {
+ //
+ // Select offer by received order.
+ //
+ for (Index = 0; Index < Private->OfferNum; Index++) {
+
+ OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
+
+ if (IS_PROXY_OFFER (OfferType)) {
+ //
+ // Skip proxy offers
+ //
+ continue;
+ }
+
+ if (!Private->IsProxyRecved &&
+ OfferType == PxeOfferTypeDhcpOnly &&
+ Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
+ //
+ // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
+ //
+ continue;
+ }
+
+ Private->SelectIndex = Index + 1;
+ break;
+ }
+ }
+}
+
+
+/**
+ Handle the DHCPv6 offer packet.
+
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.
+ @retval EFI_NO_RESPONSE No response to the following request packet.
+
+**/
+EFI_STATUS
+PxeBcHandleDhcp6Offer (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;
+ EFI_STATUS Status;
+ PXEBC_OFFER_TYPE OfferType;
+ UINT32 ProxyIndex;
+ UINT32 SelectIndex;
+ UINT32 Index;
+
+ ASSERT (Private->SelectIndex > 0);
+ SelectIndex = (UINT32) (Private->SelectIndex - 1);
+ ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
+ Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6;
+ Status = EFI_SUCCESS;
+
+ if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {
+ //
+ // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
+ //
+ if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {
+ Status = EFI_NO_RESPONSE;
+ }
+ } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {
+
+ if (Private->IsProxyRecved) {
+ //
+ // DhcpOnly offer is selected, so need try to request bootfilename.
+ //
+ ProxyIndex = 0;
+ if (Private->IsOfferSorted) {
+ //
+ // The proxy offer should be determined if select by default policy.
+ // IsOfferSorted means all offers are labeled by OfferIndex.
+ //
+ ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
+
+ if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
+ //
+ // Try all the cached ProxyBinl offer one by one to request bootfilename.
+ //
+ for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
+
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
+ if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {
+ break;
+ }
+ }
+ if (Index == Private->OfferCount[Private->SelectProxyType]) {
+ Status = EFI_NO_RESPONSE;
+ }
+ } else {
+ //
+ // For other proxy offers (pxe10 or wfm11a), only one is buffered.
+ //
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+ }
+ } else {
+ //
+ // The proxy offer should not be determined if select by received order.
+ //
+ Status = EFI_NO_RESPONSE;
+
+ for (Index = 0; Index < Private->OfferNum; Index++) {
+
+ OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
+
+ if (!IS_PROXY_OFFER (OfferType)) {
+ //
+ // Skip non proxy dhcp offers.
+ //
+ continue;
+ }
+
+ if (OfferType == PxeOfferTypeProxyBinl) {
+ //
+ // Try all the cached ProxyBinl offer one by one to request bootfilename.
+ //
+ if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {
+ continue;
+ }
+ }
+
+ Private->SelectProxyType = OfferType;
+ ProxyIndex = Index;
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
+ //
+ // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
+ //
+ PxeBcCopyDhcp6Proxy (Private, ProxyIndex);
+ }
+ } else {
+ //
+ // Othewise, the bootfilename must be included in DhcpOnly offer.
+ //
+ ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // All PXE boot information is ready by now.
+ //
+ PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);
+ Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;
+ }
+
+ return Status;
+}
+
+
+/**
+ Unregister the address by Ip6Config protocol.
+
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcUnregisterIp6Address (
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {
+ //
+ // PXE driver change the policy of IP6 driver, it's a chance to recover.
+ // Keep the point and there is no enough requirements to do recovery.
+ //
+ }
+}
+
+
+/**
+ Register the ready address by Ip6Config protocol.
+
+ @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
+ @param[in] Address The pointer to the ready address.
+
+ @retval EFI_SUCCESS Registered the address succesfully.
+ @retval Others Failed to register the address.
+
+**/
+EFI_STATUS
+PxeBcRegisterIp6Address (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_IPv6_ADDRESS *Address
+ )
+{
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
+ EFI_IP6_CONFIG_POLICY Policy;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;
+ UINTN DataSize;
+ EFI_EVENT TimeOutEvt;
+ EFI_EVENT MappedEvt;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+ TimeOutEvt = NULL;
+ MappedEvt = NULL;
+ DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
+ Ip6Cfg = Private->Ip6Cfg;
+ Ip6 = Private->Ip6;
+
+ ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
+ CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Get and store the current policy of IP6 driver.
+ //
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypePolicy,
+ &DataSize,
+ &Private->Ip6Policy
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // There is no channel between IP6 and PXE driver about address setting,
+ // so it has to set the new address by Ip6ConfigProtocol manually.
+ //
+ Policy = Ip6ConfigPolicyManual;
+ Status = Ip6Cfg->SetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypePolicy,
+ sizeof(EFI_IP6_CONFIG_POLICY),
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // There is no need to recover later.
+ //
+ Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
+ goto ON_EXIT;
+ }
+
+ //
+ // Create a timer as setting address timeout event since DAD in IP6 driver.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeOutEvt
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Create a notify event to set address flag when DAD if IP6 driver succeeded.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &Private->IsAddressOk,
+ &MappedEvt
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Ip6Cfg->RegisterDataNotify (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ MappedEvt
+ );
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Ip6Cfg->SetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),
+ &CfgAddr
+ );
+ if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Start the 5 secondes timer to wait for setting address.
+ //
+ Status = EFI_NO_MAPPING;
+ gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT);
+
+ while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
+ Ip6->Poll (Ip6);
+ if (Private->IsAddressOk) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ON_EXIT:
+ if (MappedEvt != NULL) {
+ Ip6Cfg->UnregisterDataNotify (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ MappedEvt
+ );
+ gBS->CloseEvent (MappedEvt);
+ }
+ if (TimeOutEvt != NULL) {
+ gBS->CloseEvent (TimeOutEvt);
+ }
+ return Status;
+}
+
+
+/**
+ EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
+ to intercept events that occurred in the configuration process.
+
+ @param[in] This The pointer to the EFI DHCPv6 Protocol.
+ @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
+ @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
+ @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
+ state transition.
+ @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
+ @param[out] NewPacket The packet that is used to replace the Packet above.
+
+ @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
+ @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
+ driver will continue to wait for more packets.
+ @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDhcp6CallBack (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP6_STATE CurrentState,
+ IN EFI_DHCP6_EVENT Dhcp6Event,
+ IN EFI_DHCP6_PACKET *Packet,
+ OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+ EFI_DHCP6_PACKET *SelectAd;
+ EFI_STATUS Status;
+ BOOLEAN Received;
+
+ if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&
+ (Dhcp6Event != Dhcp6SelectAdvertise) &&
+ (Dhcp6Event != Dhcp6SendSolicit) &&
+ (Dhcp6Event != Dhcp6SendRequest) &&
+ (Dhcp6Event != Dhcp6RcvdReply)) {
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (Packet != NULL);
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = Private->PxeBc.Mode;
+ Callback = Private->PxeBcCallback;
+
+ //
+ // Callback to user when any traffic ocurred if has.
+ //
+ if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {
+ Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
+ Status = Callback->Callback (
+ Callback,
+ Private->Function,
+ Received,
+ Packet->Length,
+ (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6
+ );
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ return EFI_ABORTED;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ switch (Dhcp6Event) {
+
+ case Dhcp6SendSolicit:
+ //
+ // Cache the dhcp discover packet to mode data directly.
+ //
+ CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);
+ break;
+
+ case Dhcp6RcvdAdvertise:
+ Status = EFI_NOT_READY;
+ if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
+ //
+ // Cache the dhcp offers to OfferBuffer[] for select later, and record
+ // the OfferIndex and OfferCount.
+ //
+ PxeBcCacheDhcp6Offer (Private, Packet);
+ }
+ break;
+
+ case Dhcp6SendRequest:
+ //
+ // Store the request packet as seed packet for discover.
+ //
+ if (Private->Dhcp6Request != NULL) {
+ FreePool (Private->Dhcp6Request);
+ }
+ Private->Dhcp6Request = AllocateZeroPool (Packet->Size);
+ if (Private->Dhcp6Request != NULL) {
+ CopyMem (Private->Dhcp6Request, Packet, Packet->Size);
+ }
+ break;
+
+ case Dhcp6SelectAdvertise:
+ //
+ // Select offer by the default policy or by order, and record the SelectIndex
+ // and SelectProxyType.
+ //
+ PxeBcSelectDhcp6Offer (Private);
+
+ if (Private->SelectIndex == 0) {
+ Status = EFI_ABORTED;
+ } else {
+ ASSERT (NewPacket != NULL);
+ SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;
+ *NewPacket = AllocateZeroPool (SelectAd->Size);
+ ASSERT (*NewPacket != NULL);
+ CopyMem (*NewPacket, SelectAd, SelectAd->Size);
+ }
+ break;
+
+ case Dhcp6RcvdReply:
+ //
+ // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
+ // without verification.
+ //
+ ASSERT (Private->SelectIndex != 0);
+ PxeBcCopyDhcp6Ack (Private, Packet, FALSE);
+ break;
+
+ default:
+ ASSERT (0);
+ }
+
+ return Status;
+}
+
+
+/**
+ Build and send out the request packet for the bootfile, and parse the reply.
+
+ @param[in] Private The pointer to PxeBc private data.
+ @param[in] Type PxeBc option boot item type.
+ @param[in] Layer The pointer to option boot item layer.
+ @param[in] UseBis Use BIS or not.
+ @param[in] DestIp The pointer to the server address.
+
+ @retval EFI_SUCCESS Successfully discovered the boot file.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.
+ @retval Others Failed to discover the boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Discover (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS *DestIp
+ )
+{
+ EFI_PXE_BASE_CODE_UDP_PORT SrcPort;
+ EFI_PXE_BASE_CODE_UDP_PORT DestPort;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover;
+ UINTN DiscoverLen;
+ EFI_DHCP6_PACKET *Request;
+ UINTN RequestLen;
+ EFI_DHCP6_PACKET *Reply;
+ UINT8 *RequestOpt;
+ UINT8 *DiscoverOpt;
+ UINTN ReadSize;
+ UINT16 OpFlags;
+ UINT16 OpCode;
+ UINT16 OpLen;
+ UINT32 Xid;
+ EFI_STATUS Status;
+
+ PxeBc = &Private->PxeBc;
+ Mode = PxeBc->Mode;
+ Request = Private->Dhcp6Request;
+ SrcPort = PXEBC_BS_DISCOVER_PORT;
+ DestPort = PXEBC_BS_DISCOVER_PORT;
+ OpFlags = 0;
+
+ if (!UseBis && Layer != NULL) {
+ *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
+ }
+
+ if (Request == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
+ if (Discover == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Build the discover packet by the cached request packet before.
+ //
+ Xid = NET_RANDOM (NetRandomInitSeed ());
+ Discover->TransactionId = HTONL (Xid);
+ Discover->MessageType = Request->Dhcp6.Header.MessageType;
+ RequestOpt = Request->Dhcp6.Option;
+ DiscoverOpt = Discover->DhcpOptions;
+ DiscoverLen = sizeof (EFI_DHCP6_HEADER);
+ RequestLen = DiscoverLen;
+
+ while (RequestLen < Request->Length) {
+ OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
+ OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
+ if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
+ OpCode != EFI_DHCP6_IA_TYPE_TA) {
+ //
+ // Copy all the options except IA option.
+ //
+ CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
+ DiscoverOpt += (OpLen + 4);
+ DiscoverLen += (OpLen + 4);
+ }
+ RequestOpt += (OpLen + 4);
+ RequestLen += (OpLen + 4);
+ }
+
+ Status = PxeBc->UdpWrite (
+ PxeBc,
+ OpFlags,
+ &Private->ServerIp,
+ &DestPort,
+ NULL,
+ &Private->StationIp,
+ &SrcPort,
+ NULL,
+ NULL,
+ &DiscoverLen,
+ (VOID *) Discover
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Cache the right PXE reply packet here, set valid flag later.
+ // Especially for PXE discover packet, store it into mode data here.
+ //
+ if (Private->IsDoDiscover) {
+ CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);
+ Reply = &Private->PxeReply.Dhcp6.Packet.Ack;
+ } else {
+ Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
+ }
+ ReadSize = (UINTN) Reply->Size;
+
+ Status = PxeBc->UdpRead (
+ PxeBc,
+ OpFlags,
+ &Private->StationIp,
+ &SrcPort,
+ &Private->ServerIp,
+ &DestPort,
+ NULL,
+ NULL,
+ &ReadSize,
+ (VOID *) &Reply->Dhcp6
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
+
+ @param[in] Private The pointer to PxeBc private data.
+ @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL
+
+ @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
+ @retval Others Failed to finish the S.A.R.R. process.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Sarr (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP6_PROTOCOL *Dhcp6
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *PxeMode;
+ EFI_DHCP6_CONFIG_DATA Config;
+ EFI_DHCP6_MODE_DATA Mode;
+ EFI_DHCP6_RETRANSMISSION *Retransmit;
+ EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];
+ UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];
+ UINT32 OptCount;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+ PxeMode = Private->PxeBc.Mode;
+
+ //
+ // Build option list for the request packet.
+ //
+ OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer);
+ ASSERT (OptCount> 0);
+
+ Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
+ if (Retransmit == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
+ ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
+
+ Config.OptionCount = OptCount;
+ Config.OptionList = OptList;
+ Config.Dhcp6Callback = PxeBcDhcp6CallBack;
+ Config.CallbackContext = Private;
+ Config.IaInfoEvent = NULL;
+ Config.RapidCommit = FALSE;
+ Config.ReconfigureAccept = FALSE;
+ Config.IaDescriptor.IaId = 1;
+ Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;
+ Config.SolicitRetransmission = Retransmit;
+ Retransmit->Irt = 4;
+ Retransmit->Mrc = 4;
+ Retransmit->Mrt = 32;
+ Retransmit->Mrd = 60;
+
+ //
+ // Configure the DHCPv6 instance for PXE boot.
+ //
+ Status = Dhcp6->Configure (Dhcp6, &Config);
+ if (EFI_ERROR (Status)) {
+ FreePool (Retransmit);
+ return Status;
+ }
+
+ //
+ // Initialize the record fields for DHCPv6 offer in private data.
+ //
+ Private->IsProxyRecved = FALSE;
+ Private->OfferNum = 0;
+ Private->SelectIndex = 0;
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+
+ //
+ // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
+ //
+ Status = Dhcp6->Start (Dhcp6);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ICMP_ERROR) {
+ PxeMode->IcmpErrorReceived = TRUE;
+ }
+ Dhcp6->Configure (Dhcp6, NULL);
+ return Status;
+ }
+
+ //
+ // Get the acquired IPv6 address and store them.
+ //
+ Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
+ if (EFI_ERROR (Status)) {
+ Dhcp6->Stop (Dhcp6);
+ return Status;
+ }
+
+ ASSERT (Mode.Ia->State == Dhcp6Bound);
+ CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&PxeMode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
+
+ Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);
+ if (EFI_ERROR (Status)) {
+ Dhcp6->Stop (Dhcp6);
+ return Status;
+ }
+
+ Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL);
+ if (EFI_ERROR (Status)) {
+ PxeBcUnregisterIp6Address (Private);
+ Dhcp6->Stop (Dhcp6);
+ return Status;
+ }
+
+ //
+ // Check the selected offer whether BINL retry is needed.
+ //
+ Status = PxeBcHandleDhcp6Offer (Private);
+ if (EFI_ERROR (Status)) {
+ PxeBcUnregisterIp6Address (Private);
+ Dhcp6->Stop (Dhcp6);
+ return Status;
+ }
+
+ AsciiPrint ("\n Station IP address is ");
+
+ PxeBcShowIp6Addr (&Private->StationIp.v6);
+
+ return EFI_SUCCESS;
+}
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
new file mode 100644
index 0000000000..0eccacb962
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
@@ -0,0 +1,277 @@
+/** @file
+ Functions declaration related with DHCPv6 for UefiPxeBc Driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_DHCP6_H__
+#define __EFI_PXEBC_DHCP6_H__
+
+#define PXEBC_DHCP6_OPTION_MAX_NUM 16
+#define PXEBC_DHCP6_OPTION_MAX_SIZE 312
+#define PXEBC_DHCP6_PACKET_MAX_SIZE 1472
+#define PXEBC_DHCP6_MAPPING_TIMEOUT 50000000 // 5 seconds, unit is 10nanosecond.
+#define PXEBC_IP6_POLICY_MAX 0xff
+
+#define PXEBC_DHCP6_S_PORT 547
+#define PXEBC_DHCP6_C_PORT 546
+
+#define PXEBC_DHCP6_OPT_CLIENT_ID 1
+#define PXEBC_DHCP6_OPT_SERVER_ID 2
+#define PXEBC_DHCP6_OPT_IA_NA 3
+#define PXEBC_DHCP6_OPT_IA_TA 4
+#define PXEBC_DHCP6_OPT_IAADDR 5
+#define PXEBC_DHCP6_OPT_ORO 6
+#define PXEBC_DHCP6_OPT_PREFERENCE 7
+#define PXEBC_DHCP6_OPT_ELAPSED_TIME 8
+#define PXEBC_DHCP6_OPT_REPLAY_MSG 9
+#define PXEBC_DHCP6_OPT_AUTH 11
+#define PXEBC_DHCP6_OPT_UNICAST 12
+#define PXEBC_DHCP6_OPT_STATUS_CODE 13
+#define PXEBC_DHCP6_OPT_RAPID_COMMIT 14
+#define PXEBC_DHCP6_OPT_USER_CLASS 15
+#define PXEBC_DHCP6_OPT_VENDOR_CLASS 16
+#define PXEBC_DHCP6_OPT_VENDOR_OPTS 17
+#define PXEBC_DHCP6_OPT_INTERFACE_ID 18
+#define PXEBC_DHCP6_OPT_RECONFIG_MSG 19
+#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT 20
+#define PXEBC_DHCP6_OPT_BOOT_FILE_URL 59 // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM 60 // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_OPT_ARCH 61 // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_OPT_UNDI 62 // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's
+#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes.
+
+
+#define PXEBC_DHCP6_IDX_IA_NA 0
+#define PXEBC_DHCP6_IDX_BOOT_FILE_URL 1
+#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM 2
+#define PXEBC_DHCP6_IDX_VENDOR_CLASS 3
+#define PXEBC_DHCP6_IDX_MAX 4
+
+#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX "tftp://"
+#define PXEBC_TFTP_URL_SEPARATOR '/'
+#define PXEBC_ADDR_START_DELIMITER '['
+#define PXEBC_ADDR_END_DELIMITER ']'
+
+#define GET_NEXT_DHCP6_OPTION(Opt) \
+ (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \
+ sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1)
+
+#define GET_DHCP6_OPTION_SIZE(Pkt) \
+ ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER))
+
+#define IS_PROXY_OFFER(Type) \
+ ((Type) == PxeOfferTypeProxyBinl || \
+ (Type) == PxeOfferTypeProxyPxe10 || \
+ (Type) == PxeOfferTypeProxyWfm11a)
+
+
+#pragma pack(1)
+typedef struct {
+ UINT16 OpCode[256];
+} PXEBC_DHCP6_OPTION_ORO;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 MajorVer;
+ UINT8 MinorVer;
+ UINT8 Reserved;
+} PXEBC_DHCP6_OPTION_UNDI;
+
+typedef struct {
+ UINT16 Type;
+} PXEBC_DHCP6_OPTION_ARCH;
+
+typedef struct {
+ UINT8 ClassIdentifier[10];
+ UINT8 ArchitecturePrefix[5];
+ UINT8 ArchitectureType[5];
+ UINT8 Lit3[1];
+ UINT8 InterfaceName[4];
+ UINT8 Lit4[1];
+ UINT8 UndiMajor[3];
+ UINT8 UndiMinor[3];
+} PXEBC_CLASS_ID;
+
+typedef struct {
+ UINT32 Vendor;
+ UINT16 ClassLen;
+ PXEBC_CLASS_ID ClassId;
+} PXEBC_DHCP6_OPTION_VENDOR_CLASS;
+
+#pragma pack()
+
+typedef union {
+ PXEBC_DHCP6_OPTION_ORO *Oro;
+ PXEBC_DHCP6_OPTION_UNDI *Undi;
+ PXEBC_DHCP6_OPTION_ARCH *Arch;
+ PXEBC_DHCP6_OPTION_VENDOR_CLASS *VendorClass;
+} PXEBC_DHCP6_OPTION_ENTRY;
+
+typedef struct {
+ LIST_ENTRY Link;
+ EFI_DHCP6_PACKET_OPTION *Option;
+ UINT8 Precedence;
+} PXEBC_DHCP6_OPTION_NODE;
+
+typedef union {
+ EFI_DHCP6_PACKET Offer;
+ EFI_DHCP6_PACKET Ack;
+ UINT8 Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE];
+} PXEBC_DHCP6_PACKET;
+
+typedef struct {
+ PXEBC_DHCP6_PACKET Packet;
+ PXEBC_OFFER_TYPE OfferType;
+ EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX];
+} PXEBC_DHCP6_PACKET_CACHE;
+
+
+/**
+ Free all the nodes in the boot file list.
+
+ @param[in] Head The pointer to the head of the list.
+
+**/
+VOID
+PxeBcFreeBootFileOption (
+ IN LIST_ENTRY *Head
+ );
+
+
+/**
+ Parse the Boot File URL option.
+
+ @param[out] FileName The pointer to the boot file name.
+ @param[in, out] SrvAddr The pointer to the boot server address.
+ @param[in] BootFile The pointer to the boot file URL option data.
+ @param[in] Length Length of the boot file URL option data.
+
+ @retval EFI_ABORTED User canceled the operation.
+ @retval EFI_SUCCESS Selected the boot menu successfully.
+ @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileUrl (
+ OUT UINT8 **FileName,
+ IN OUT EFI_IPv6_ADDRESS *SrvAddr,
+ IN CHAR8 *BootFile,
+ IN UINT16 Length
+ );
+
+
+/**
+ Parse the Boot File Parameter option.
+
+ @param[in] BootFilePara The pointer to the boot file parameter option data.
+ @param[out] BootFileSize The pointer to the parsed boot file size.
+
+ @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.
+ @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileParam (
+ IN CHAR8 *BootFilePara,
+ OUT UINT16 *BootFileSize
+ );
+
+
+/**
+ Parse the cached DHCPv6 packet, including all the options.
+
+ @param[in] Cache6 The pointer to a cached DHCPv6 packet.
+
+ @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp6Packet (
+ IN PXEBC_DHCP6_PACKET_CACHE *Cache6
+ );
+
+
+/**
+ Register the ready address by Ip6Config protocol.
+
+ @param[in] Private The pointer to the PxeBc private data.
+ @param[in] Address The pointer to the ready address.
+
+ @retval EFI_SUCCESS Registered the address succesfully.
+ @retval Others Failed to register the address.
+
+**/
+EFI_STATUS
+PxeBcRegisterIp6Address (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_IPv6_ADDRESS *Address
+ );
+
+
+/**
+ Unregister the address by Ip6Config protocol.
+
+ @param[in] Private The pointer to the PxeBc private data.
+
+**/
+VOID
+PxeBcUnregisterIp6Address (
+ IN PXEBC_PRIVATE_DATA *Private
+ );
+
+
+/**
+ Build and send out the request packet for the bootfile, and parse the reply.
+
+ @param[in] Private The pointer to the PxeBc private data.
+ @param[in] Type PxeBc option boot item type.
+ @param[in] Layer The pointer to the option boot item layer.
+ @param[in] UseBis Use BIS or not.
+ @param[in] DestIp The pointer to the server address.
+
+ @retval EFI_SUCCESS Successfully discovered theboot file.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
+ @retval EFI_NOT_FOUND Can't get the PXE reply packet.
+ @retval Others Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Discover (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_IP_ADDRESS *DestIp
+ );
+
+
+/**
+ Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
+
+ @param[in] Private The pointer to the PxeBc private data.
+ @param[in] Dhcp6 The pointer to EFI_DHCP6_PROTOCOL.
+
+ @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
+ @retval Others Failed to finish the S.A.R.R. process.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Sarr (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP6_PROTOCOL *Dhcp6
+ );
+
+#endif
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
new file mode 100644
index 0000000000..f78525546a
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
@@ -0,0 +1,1339 @@
+/** @file
+ Driver Binding functions implementationfor for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+EFI_DRIVER_BINDING_PROTOCOL gPxeBcDriverBinding = {
+ PxeBcDriverBindingSupported,
+ PxeBcDriverBindingStart,
+ PxeBcDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+
+/**
+ Get the Nic handle using any child handle in the IPv4 stack.
+
+ @param[in] ControllerHandle Pointer to child handle over IPv4.
+
+ @return NicHandle The pointer to the Nic handle.
+
+**/
+EFI_HANDLE
+PxeBcGetNicByIp4Children (
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_HANDLE NicHandle;
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ return NULL;
+ }
+ }
+ }
+ }
+ }
+
+ return NicHandle;
+}
+
+
+/**
+ Get the Nic handle using any child handle in the IPv6 stack.
+
+ @param[in] ControllerHandle Pointer to child handle over IPv6.
+
+ @return NicHandle The pointer to the Nic handle.
+
+**/
+EFI_HANDLE
+PxeBcGetNicByIp6Children (
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_HANDLE NicHandle;
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid);
+ if (NicHandle == NULL) {
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return NicHandle;
+}
+
+
+/**
+ Destroy the opened instances based on IPv4.
+
+ @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
+ @param[in] Private Pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcDestroyIp4Children (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ ASSERT(Private != NULL);
+
+ if (Private->ArpChild != NULL) {
+ //
+ // Close Arp for PxeBc->Arp and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->ArpChild,
+ &gEfiArpProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ Private->ArpChild
+ );
+ }
+
+ if (Private->Ip4Child != NULL) {
+ //
+ // Close Ip4 for background ICMP error message and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Ip4Child,
+ &gEfiIp4ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ Private->Ip4Child
+ );
+ }
+
+ if (Private->Udp4WriteChild != NULL) {
+ //
+ // Close Udp4 for PxeBc->UdpWrite and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Udp4WriteChild,
+ &gEfiUdp4ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ Private->Udp4WriteChild
+ );
+ }
+
+ if (Private->Udp4ReadChild != NULL) {
+ //
+ // Close Udp4 for PxeBc->UdpRead and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Udp4ReadChild,
+ &gEfiUdp4ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ Private->Udp4ReadChild
+ );
+ }
+
+ if (Private->Mtftp4Child != NULL) {
+ //
+ // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Mtftp4Child,
+ &gEfiMtftp4ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ Private->Mtftp4Child
+ );
+ }
+
+ if (Private->Dhcp4Child != NULL) {
+ //
+ // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Dhcp4Child,
+ &gEfiDhcp4ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Private->Dhcp4Child
+ );
+ }
+
+ if (Private->Ip4Nic != NULL) {
+ //
+ // Close PxeBc from the parent Nic handle and destroy the virtual handle.
+ //
+ gBS->CloseProtocol (
+ Private->Controller,
+ &gEfiPxeBaseCodeProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Ip4Nic->Controller
+ );
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Private->Ip4Nic->Controller,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip4Nic->DevicePath,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip4Nic->LoadFile,
+ NULL
+ );
+ FreePool (Private->Ip4Nic);
+ }
+
+ Private->ArpChild = NULL;
+ Private->Ip4Child = NULL;
+ Private->Udp4WriteChild = NULL;
+ Private->Udp4ReadChild = NULL;
+ Private->Mtftp4Child = NULL;
+ Private->Dhcp4Child = NULL;
+ Private->Ip4Nic = NULL;
+}
+
+
+/**
+ Destroy the opened instances based on IPv6.
+
+ @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
+ @param[in] Private Pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcDestroyIp6Children (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ ASSERT(Private != NULL);
+
+ if (Private->Ip6Child != NULL) {
+ //
+ // Close Ip6 for Ip6->Ip6Config and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Ip6Child,
+ &gEfiIp6ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ Private->Ip6Child
+ );
+ }
+
+ if (Private->Udp6WriteChild != NULL) {
+ //
+ // Close Udp6 for PxeBc->UdpWrite and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Udp6WriteChild,
+ &gEfiUdp6ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ Private->Udp6WriteChild
+ );
+ }
+
+ if (Private->Udp6ReadChild != NULL) {
+ //
+ // Close Udp6 for PxeBc->UdpRead and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Udp6ReadChild,
+ &gEfiUdp6ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ Private->Udp6ReadChild
+ );
+ }
+
+ if (Private->Mtftp6Child != NULL) {
+ //
+ // Close Mtftp6 for PxeBc->Mtftp and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Mtftp6Child,
+ &gEfiMtftp6ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiMtftp6ServiceBindingProtocolGuid,
+ Private->Mtftp6Child
+ );
+ }
+
+ if (Private->Dhcp6Child != NULL) {
+ //
+ // Close Dhcp6 for PxeBc->Dhcp and destroy the instance.
+ //
+ gBS->CloseProtocol (
+ Private->Dhcp6Child,
+ &gEfiDhcp6ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ Private->Dhcp6Child
+ );
+ }
+
+ if (Private->Ip6Nic != NULL) {
+ //
+ // Close PxeBc from the parent Nic handle and destroy the virtual handle.
+ //
+ gBS->CloseProtocol (
+ Private->Controller,
+ &gEfiPxeBaseCodeProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Ip6Nic->Controller
+ );
+ gBS->UninstallMultipleProtocolInterfaces (
+ Private->Ip6Nic->Controller,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip6Nic->DevicePath,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip6Nic->LoadFile,
+ NULL
+ );
+ FreePool (Private->Ip6Nic);
+ }
+
+ Private->Ip6Child = NULL;
+ Private->Udp6WriteChild = NULL;
+ Private->Udp6ReadChild = NULL;
+ Private->Mtftp6Child = NULL;
+ Private->Dhcp6Child = NULL;
+ Private->Ip6Nic = NULL;
+ Private->Mode.Ipv6Available = FALSE;
+}
+
+
+/**
+ Create the opened instances based on IPv4.
+
+ @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL.
+ @param[in] ControllerHandle Handle of the child to destroy.
+ @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The instances based on IPv4 were all created successfully.
+ @retval Others An unexpected error occurred.
+
+**/
+EFI_STATUS
+PxeBcCreateIp4Children (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ IPv4_DEVICE_PATH Ip4Node;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_UDP4_CONFIG_DATA *Udp4CfgData;
+ EFI_IP4_CONFIG_DATA *Ip4CfgData;
+ EFI_IP4_MODE_DATA Ip4ModeData;
+
+ if (Private->Ip4Nic != NULL) {
+ //
+ // Already created before.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &Private->Dhcp4Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Dhcp4Child,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Private->Dhcp4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ &Private->Mtftp4Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Mtftp4Child,
+ &gEfiMtftp4ProtocolGuid,
+ (VOID **) &Private->Mtftp4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Private->Udp4ReadChild
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Udp4ReadChild,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Private->Udp4Read,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Private->Udp4WriteChild
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Udp4WriteChild,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Private->Udp4Write,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Arp child and open Arp protocol for PxeBc->Arp.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiArpServiceBindingProtocolGuid,
+ &Private->ArpChild
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->ArpChild,
+ &gEfiArpProtocolGuid,
+ (VOID **) &Private->Arp,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Ip4 child and open Ip4 protocol for background ICMP packets.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ &Private->Ip4Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Ip4Child,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Private->Ip4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Get max packet size from Ip4 to calculate block size for Tftp later.
+ //
+ Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize;
+
+ Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));
+ if (Private->Ip4Nic == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Private->Ip4Nic->Private = Private;
+ Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;
+
+ //
+ // Create a device path node for Ipv4 virtual nic, and append it.
+ //
+ ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH));
+ Ip4Node.Header.Type = MESSAGING_DEVICE_PATH;
+ Ip4Node.Header.SubType = MSG_IPv4_DP;
+ Ip4Node.StaticIpAddress = FALSE;
+
+ SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node));
+
+ Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header);
+
+ if (Private->Ip4Nic->DevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ CopyMem (
+ &Private->Ip4Nic->LoadFile,
+ &gLoadFileProtocolTemplate,
+ sizeof (EFI_LOAD_FILE_PROTOCOL)
+ );
+
+ //
+ // Create a new handle for IPv4 virtual nic,
+ // and install PxeBaseCode, LoadFile and DevicePath protocols.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Private->Ip4Nic->Controller,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip4Nic->DevicePath,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip4Nic->LoadFile,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Open PxeBaseCode protocol by child to setup a parent-child relationship between
+ // real NIC handle and the virtual IPv4 NIC handle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ Private->Ip4Nic->Controller,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Set default configure data for Udp4Read and Ip4 instance.
+ //
+ Mode = PxeBc->Mode;
+ Udp4CfgData = &Private->Udp4CfgData;
+ Ip4CfgData = &Private->Ip4CfgData;
+
+ Udp4CfgData->AcceptBroadcast = TRUE;
+ Udp4CfgData->AcceptAnyPort = TRUE;
+ Udp4CfgData->AllowDuplicatePort = TRUE;
+ Udp4CfgData->TypeOfService = Mode->ToS;
+ Udp4CfgData->TimeToLive = Mode->TTL;
+ Udp4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+
+ Ip4CfgData->AcceptIcmpErrors = TRUE;
+ Ip4CfgData->DefaultProtocol = EFI_IP_PROTO_ICMP;
+ Ip4CfgData->TypeOfService = Mode->ToS;
+ Ip4CfgData->TimeToLive = Mode->TTL;
+ Ip4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Ip4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ PxeBcDestroyIp4Children (This, Private);
+ return Status;
+}
+
+
+/**
+ Create the opened instances based on IPv6.
+
+ @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL.
+ @param[in] ControllerHandle Handle of the child to destroy.
+ @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The instances based on IPv6 were all created successfully.
+ @retval Others An unexpected error occurred.
+
+**/
+EFI_STATUS
+PxeBcCreateIp6Children (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN PXEBC_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ IPv6_DEVICE_PATH Ip6Node;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_UDP6_CONFIG_DATA *Udp6CfgData;
+ EFI_IP6_CONFIG_DATA *Ip6CfgData;
+ EFI_IP6_MODE_DATA Ip6ModeData;
+
+ if (Private->Ip6Nic != NULL) {
+ //
+ // Already created before.
+ //
+ return EFI_SUCCESS;
+ }
+
+ Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));
+
+ if (Private->Ip6Nic == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Private->Ip6Nic->Private = Private;
+ Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;
+
+ //
+ // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ &Private->Dhcp6Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Dhcp6Child,
+ &gEfiDhcp6ProtocolGuid,
+ (VOID **) &Private->Dhcp6,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiMtftp6ServiceBindingProtocolGuid,
+ &Private->Mtftp6Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Mtftp6Child,
+ &gEfiMtftp6ProtocolGuid,
+ (VOID **) &Private->Mtftp6,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ &Private->Udp6ReadChild
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Udp6ReadChild,
+ &gEfiUdp6ProtocolGuid,
+ (VOID **) &Private->Udp6Read,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ &Private->Udp6WriteChild
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Udp6WriteChild,
+ &gEfiUdp6ProtocolGuid,
+ (VOID **) &Private->Udp6Write,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Ip6 child and open Ip6 protocol for background ICMP6 packets.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ &Private->Ip6Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Ip6Child,
+ &gEfiIp6ProtocolGuid,
+ (VOID **) &Private->Ip6,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Get max packet size from Ip6 to calculate block size for Tftp later.
+ //
+ Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize;
+
+ //
+ // Locate Ip6->Ip6Config and store it for set IPv6 address.
+ //
+ Status = gBS->HandleProtocol (
+ ControllerHandle,
+ &gEfiIp6ConfigProtocolGuid,
+ (VOID **) &Private->Ip6Cfg
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create a device path node for Ipv6 virtual nic, and append it.
+ //
+ ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH));
+ Ip6Node.Header.Type = MESSAGING_DEVICE_PATH;
+ Ip6Node.Header.SubType = MSG_IPv6_DP;
+ Ip6Node.StaticIpAddress = FALSE;
+
+ SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node));
+
+ Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header);
+
+ if (Private->Ip6Nic->DevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ CopyMem (
+ &Private->Ip6Nic->LoadFile,
+ &gLoadFileProtocolTemplate,
+ sizeof (EFI_LOAD_FILE_PROTOCOL)
+ );
+
+ //
+ // Create a new handle for IPv6 virtual nic,
+ // and install PxeBaseCode, LoadFile and DevicePath protocols.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Private->Ip6Nic->Controller,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip6Nic->DevicePath,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip6Nic->LoadFile,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Open PxeBaseCode protocol by child to setup a parent-child relationship between
+ // real NIC handle and the virtual IPv6 NIC handle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ Private->Ip6Nic->Controller,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Set IPv6 avaiable flag and set default configure data for
+ // Udp6Read and Ip6 instance.
+ //
+ Private->Mode.Ipv6Available = TRUE;
+ Udp6CfgData = &Private->Udp6CfgData;
+ Ip6CfgData = &Private->Ip6CfgData;
+
+ Udp6CfgData->AcceptAnyPort = TRUE;
+ Udp6CfgData->AllowDuplicatePort = TRUE;
+ Udp6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT;
+ Udp6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+
+ Ip6CfgData->AcceptIcmpErrors = TRUE;
+ Ip6CfgData->DefaultProtocol = IP6_ICMP;
+ Ip6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT;
+ Ip6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Ip6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ PxeBcDestroyIp6Children (This, Private);
+ return Status;
+}
+
+
+/**
+ The entry point for UefiPxeBc driver that installs the driver
+ binding and component name protocol on its image.
+
+ @param[in] ImageHandle The Image handle of the driver.
+ @param[in] SystemTable The system table.
+
+ @return EFI_SUCCESS
+ @return Others
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gPxeBcDriverBinding,
+ ImageHandle,
+ &gPxeBcComponentName,
+ &gPxeBcComponentName2
+ );
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be tested.
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_UNSUPPORTED This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Ip4Status;
+ EFI_STATUS Ip6Status;
+
+ //
+ // Try to open the Mtftp4 and Dhcp4 protocol to test whether IPv4 stack is ready.
+ //
+ Ip4Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Ip4Status)) {
+ Ip4Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ }
+
+ //
+ // Try to open the Mtftp6 and Dhcp6 protocol to test whether IPv4 stack is ready.
+ //
+ Ip6Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Ip6Status)) {
+ Ip6Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiMtftp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ }
+
+ //
+ // It's unsupported case if both stack are not ready.
+ //
+ if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be started.
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver is installed to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_STATUS Status;
+ EFI_STATUS Ip4Status;
+ EFI_STATUS Ip6Status;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Skip the initialization if the driver has been started already.
+ //
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);
+ } else {
+ //
+ // If the driver has not been started yet, it should do initialization.
+ //
+ Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));
+ if (Private == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (
+ &Private->PxeBc,
+ &gPxeBcProtocolTemplate,
+ sizeof (EFI_PXE_BASE_CODE_PROTOCOL)
+ );
+
+ Private->Signature = PXEBC_PRIVATE_DATA_SIGNATURE;
+ Private->Controller = ControllerHandle;
+ Private->Image = This->ImageHandle;
+ Private->PxeBc.Mode = &Private->Mode;
+ Private->Mode.Ipv6Supported = TRUE;
+ Private->Mode.AutoArp = TRUE;
+ Private->Mode.TTL = DEFAULT_TTL;
+ Private->Mode.ToS = DEFAULT_ToS;
+
+ //
+ // Open device path to prepare for appending virtual NIC node.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &Private->DevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Get the NII interface if it exists, it's not required.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &Private->Nii,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Private->Nii = NULL;
+ }
+
+ //
+ // Install PxeBaseCode protocol onto the real NIC handler.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->PxeBc
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Try to create virtual NIC handle for IPv4.
+ //
+ Ip4Status = PxeBcCreateIp4Children (This, ControllerHandle, Private);
+
+ //
+ // Try to create virtual NIC handle for IPv6.
+ //
+ Ip6Status = PxeBcCreateIp6Children (This, ControllerHandle, Private);
+
+ if (EFI_ERROR (Ip4Status) && EFI_ERROR (Ip6Status)) {
+ //
+ // Failed to start PXE driver if IPv4 and IPv6 stack are both not available.
+ //
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ gBS->UninstallProtocolInterface (
+ ControllerHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ &Private->PxeBc
+ );
+ PxeBcDestroyIp4Children (This, Private);
+ PxeBcDestroyIp6Children (This, Private);
+ FreePool (Private);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver was removed ControllerHandle.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval Others This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ PXEBC_VIRTUAL_NIC *VirtualNic;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ BOOLEAN IsIpv6;
+
+ Private = NULL;
+ NicHandle = NULL;
+ VirtualNic = NULL;
+ LoadFile = NULL;
+ PxeBc = NULL;
+ IsIpv6 = FALSE;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiLoadFileProtocolGuid,
+ (VOID **) &LoadFile,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Get the Nic handle by any pass-over service child handle.
+ //
+ NicHandle = PxeBcGetNicByIp4Children (ControllerHandle);
+ if (NicHandle == NULL) {
+ NicHandle = PxeBcGetNicByIp6Children (ControllerHandle);
+ if (NicHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ } else {
+ IsIpv6 = TRUE;
+ }
+ }
+
+ //
+ // Try to retrieve the private data by PxeBc protocol.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ (VOID **) &PxeBc,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (PxeBc);
+
+ } else {
+ //
+ // It's a virtual handle with LoadFileProtocol.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiLoadFileProtocolGuid,
+ (VOID **) &LoadFile,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile);
+ Private = VirtualNic->Private;
+ NicHandle = Private->Controller;
+
+ if (Private->Ip6Nic == VirtualNic) {
+ IsIpv6 = TRUE;
+ }
+ }
+
+ if (Private->Ip4Nic != NULL && !IsIpv6) {
+ PxeBcDestroyIp4Children (This, Private);
+ }
+
+ if (Private->Ip6Nic != NULL && IsIpv6) {
+ PxeBcDestroyIp6Children (This, Private);
+ }
+
+ if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) {
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiPxeBaseCodeProtocolGuid,
+ &Private->PxeBc
+ );
+ FreePool (Private);
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
new file mode 100644
index 0000000000..8df4bae27b
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
@@ -0,0 +1,104 @@
+/** @file
+ Driver Binding functions declaration for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_DRIVER_H__
+#define __EFI_PXEBC_DRIVER_H__
+
+extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2;
+
+/**
+ Test to see if this driver supports ControllerHandle. This service
+ is called by the EFI boot service ConnectController(). In
+ order to make drivers as small as possible, there are a few calling
+ restrictions for this service. ConnectController() must
+ follow these calling restrictions. If any other agent wishes to call
+ Supported() it must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be tested.
+ @param[in] RemainingDevicePath Optional parameter use to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_UNSUPPORTED This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+
+/**
+ Start this driver on ControllerHandle. This service is called by the
+ EFI boot service ConnectController(). In order to make
+ drivers as small as possible, there are a few calling restrictions for
+ this service. ConnectController() must follow these
+ calling restrictions. If any other agent wishes to call Start() it
+ must also follow these calling restrictions.
+
+ @param[in] This The pointer to the driver binding protocol.
+ @param[in] ControllerHandle The handle of device to be started.
+ @param[in] RemainingDevicePath Optional parameter used to pick a specific child
+ device to be started.
+
+ @retval EFI_SUCCESS This driver is installed to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+
+/**
+ Stop this driver on ControllerHandle. This service is called by the
+ EFI boot service DisconnectController(). In order to
+ make drivers as small as possible, there are a few calling
+ restrictions for this service. DisconnectController()
+ must follow these calling restrictions. If any other agent wishes
+ to call Stop() it must also follow these calling restrictions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to stop driver on
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
+ children is zero stop the entire bus driver.
+ @param[in] ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed ControllerHandle
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval Others This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+#endif
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
new file mode 100644
index 0000000000..7e5b4d63fd
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
@@ -0,0 +1,2200 @@
+/** @file
+ This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+ Enables the use of the PXE Base Code Protocol functions.
+
+ This function enables the use of the PXE Base Code Protocol functions. If the
+ Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then
+ EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted
+ addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted
+ addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported
+ field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will
+ be returned. If there is not enough memory or other resources to start the PXE
+ Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the
+ PXE Base Code Protocol will be started.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] UseIpv6 Specifies the type of IP addresses that are to be
+ used during the session that is being started.
+ Set to TRUE for IPv6, and FALSE for IPv4.
+
+ @retval EFI_SUCCESS The PXE Base Code Protocol was started.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_UNSUPPORTED UseIpv6 is TRUE, but the Ipv6Supported field of the
+ EFI_PXE_BASE_CODE_MODE structure is FALSE.
+ @retval EFI_ALREADY_STARTED The PXE Base Code Protocol is already in the started state.
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+ EFI_PXE_BASE_CODE_PROTOCOL structure.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory or other resources to start the
+ PXE Base Code Protocol.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcStart (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN UseIpv6
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (Mode->Started) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Detect whether using IPv6 or not, and set it into mode data.
+ //
+ if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) {
+ Mode->UsingIpv6 = TRUE;
+ } else if (!UseIpv6 && Private->Ip4Nic != NULL) {
+ Mode->UsingIpv6 = FALSE;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Mode->UsingIpv6) {
+ AsciiPrint ("\n>>Start PXE over IPv6");
+ //
+ // Configure block size for TFTP as a default value to handle all link layers.
+ //
+ Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize -
+ PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);
+
+ //
+ // PXE over IPv6 starts here, initialize the fields and list header.
+ //
+ Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
+ Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
+ Private->DhcpAck.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
+ Private->PxeReply.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
+
+ for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
+ Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
+ }
+
+ //
+ // Create event and set status for token to capture ICMP6 error message.
+ //
+ Private->Icmp6Token.Status = EFI_NOT_READY;
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcIcmp6ErrorUpdate,
+ Private,
+ &Private->Icmp6Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ } else {
+ AsciiPrint ("\n>>Start PXE over IPv4");
+ //
+ // Configure block size for TFTP as a default value to handle all link layers.
+ //
+ Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize -
+ PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);
+
+ //
+ // PXE over IPv4 starts here, initialize the fields.
+ //
+ Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
+ Private->DhcpAck.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
+ Private->PxeReply.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
+
+ for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
+ Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
+ }
+
+ PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read);
+
+ //
+ // Create the event for Arp cache update.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ PxeBcArpCacheUpdate,
+ Private,
+ &Private->ArpUpdateEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Start a periodic timer by second to update Arp cache.
+ //
+ Status = gBS->SetTimer (
+ Private->ArpUpdateEvent,
+ TimerPeriodic,
+ TICKS_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create event and set status for token to capture ICMP error message.
+ //
+ Private->Icmp6Token.Status = EFI_NOT_READY;
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcIcmpErrorUpdate,
+ Private,
+ &Private->IcmpToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // If PcdTftpBlockSize is set to non-zero, override the default value.
+ //
+ if (PcdGet64 (PcdTftpBlockSize) != 0) {
+ Private->BlockSize = (UINTN) PcdGet64 (PcdTftpBlockSize);
+ }
+
+ //
+ // Create event for UdpRead/UdpWrite timeout since they are both blocking API.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Private->UdpTimeOutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Private->IsAddressOk = FALSE;
+ Mode->Started = TRUE;
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (Mode->UsingIpv6) {
+ if (Private->Icmp6Token.Event != NULL) {
+ gBS->CloseEvent (Private->Icmp6Token.Event);
+ Private->Icmp6Token.Event = NULL;
+ }
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+ Private->Ip6->Configure (Private->Ip6, NULL);
+ } else {
+ if (Private->ArpUpdateEvent != NULL) {
+ gBS->CloseEvent (Private->ArpUpdateEvent);
+ Private->ArpUpdateEvent = NULL;
+ }
+ if (Private->IcmpToken.Event != NULL) {
+ gBS->CloseEvent (Private->IcmpToken.Event);
+ Private->IcmpToken.Event = NULL;
+ }
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+ Private->Ip4->Configure (Private->Ip4, NULL);
+ }
+ return Status;
+}
+
+
+/**
+ Disable the use of the PXE Base Code Protocol functions.
+
+ This function stops all activity on the network device. All the resources allocated
+ in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is
+ set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE
+ structure is already FALSE, then EFI_NOT_STARTED will be returned.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+
+ @retval EFI_SUCCESS The PXE Base Code Protocol was stopped.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is already in the stopped state.
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+ EFI_PXE_BASE_CODE_PROTOCOL structure.
+ @retval Others
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcStop (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ BOOLEAN Ipv6Supported;
+ BOOLEAN Ipv6Available;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ Ipv6Supported = Mode->Ipv6Supported;
+ Ipv6Available = Mode->Ipv6Available;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->UsingIpv6) {
+ //
+ // Configure all the instances for IPv6 as NULL.
+ //
+ ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));
+ ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));
+ Private->Dhcp6->Stop (Private->Dhcp6);
+ Private->Dhcp6->Configure (Private->Dhcp6, NULL);
+ Private->Udp6Write->Configure (Private->Udp6Write, NULL);
+ Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL);
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+ Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);
+ Private->Ip6->Configure (Private->Ip6, NULL);
+ PxeBcUnregisterIp6Address (Private);
+ if (Private->Icmp6Token.Event != NULL) {
+ gBS->CloseEvent (Private->Icmp6Token.Event);
+ Private->Icmp6Token.Event = NULL;
+ }
+ if (Private->Dhcp6Request != NULL) {
+ FreePool (Private->Dhcp6Request);
+ Private->Dhcp6Request = NULL;
+ }
+ if (Private->BootFileName != NULL) {
+ FreePool (Private->BootFileName);
+ Private->BootFileName = NULL;
+ }
+ } else {
+ //
+ // Configure all the instances for IPv4 as NULL.
+ //
+ ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+ ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ Private->Dhcp4->Stop (Private->Dhcp4);
+ Private->Dhcp4->Configure (Private->Dhcp4, NULL);
+ Private->Udp4Write->Configure (Private->Udp4Write, NULL);
+ Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL);
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+ Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);
+ Private->Ip4->Configure (Private->Ip4, NULL);
+ if (Private->ArpUpdateEvent != NULL) {
+ gBS->CloseEvent (Private->ArpUpdateEvent);
+ Private->ArpUpdateEvent = NULL;
+ }
+ if (Private->IcmpToken.Event != NULL) {
+ gBS->CloseEvent (Private->IcmpToken.Event);
+ Private->IcmpToken.Event = NULL;
+ }
+ }
+
+ gBS->CloseEvent (Private->UdpTimeOutEvent);
+ Private->CurSrcPort = 0;
+ Private->BootFileSize = 0;
+
+ //
+ // Reset the mode data.
+ //
+ ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));
+ Mode->Ipv6Available = Ipv6Available;
+ Mode->Ipv6Supported = Ipv6Supported;
+ Mode->AutoArp = TRUE;
+ Mode->TTL = DEFAULT_TTL;
+ Mode->ToS = DEFAULT_ToS;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6
+ S.A.R.R (solicit / advertise / request / reply) sequence.
+
+ If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before
+ they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will
+ be tried in the order in which they are received. Please see the Preboot Execution
+ Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI)
+ Specification for additional details on the implementation of DHCP.
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then the DHCP sequence will be stopped and EFI_ABORTED will be returned.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] SortOffers TRUE if the offers received should be sorted. Set to FALSE to
+ try the offers in the order that they are received.
+
+ @retval EFI_SUCCESS Valid DHCP has completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+ EFI_PXE_BASE_CODE_PROTOCOL structure.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete the DHCP Protocol.
+ @retval EFI_ABORTED The callback function aborted the DHCP Protocol.
+ @retval EFI_TIMEOUT The DHCP Protocol timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the DHCP session.
+ @retval EFI_NO_RESPONSE Valid PXE offer was not received.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcDhcp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN SortOffers
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ Mode->IcmpErrorReceived = FALSE;
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP;
+ Private->IsOfferSorted = SortOffers;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->UsingIpv6) {
+
+ //
+ // Stop Udp6Read instance
+ //
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+
+ //
+ // Start S.A.R.R. process to get a IPv6 address and other boot information.
+ //
+ Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6);
+ } else {
+
+ //
+ // Stop Udp4Read instance
+ //
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+
+ //
+ // Start D.O.R.A. process to get a IPv4 address and other boot information.
+ //
+ Status = PxeBcDhcp4Dora (Private, Private->Dhcp4);
+ }
+
+ //
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ //
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ This->SetIpFilter (This, &IpFilter);
+
+ return Status;
+}
+
+
+/**
+ Attempts to complete the PXE Boot Server and/or boot image discovery sequence.
+
+ This function attempts to complete the PXE Boot Server and/or boot image discovery
+ sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the
+ PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the
+ EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the
+ PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure
+ will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE.
+ In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[],
+ has two uses: It is the Boot Server IP address list used for unicast discovery
+ (if the UseUCast field is TRUE), and it is the list used for Boot Server verification
+ (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure
+ is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot
+ Server reply of that type will be accepted. If the AcceptAnyResponse field is
+ FALSE, only responses from Boot Servers with matching IP addresses will be accepted.
+ This function can take at least 10 seconds to timeout and return control to the
+ caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be
+ returned. Please see the Preboot Execution Environment (PXE) Specification for
+ additional details on the implementation of the Discovery sequence.
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then the Discovery sequence is stopped and EFI_ABORTED will be returned.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] Type The type of bootstrap to perform.
+ @param[in] Layer Pointer to the boot server layer number to discover, which must be
+ PXE_BOOT_LAYER_INITIAL when a new server type is being
+ discovered.
+ @param[in] UseBis TRUE if Boot Integrity Services are to be used. FALSE otherwise.
+ @param[in] Info Pointer to a data structure that contains additional information
+ on the type of discovery operation that is to be performed.
+ It is optional.
+
+ @retval EFI_SUCCESS The Discovery sequence has been completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete Discovery.
+ @retval EFI_ABORTED The callback function aborted the Discovery sequence.
+ @retval EFI_TIMEOUT The Discovery sequence timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the PXE discovery
+ session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcDiscover (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;
+ EFI_PXE_BASE_CODE_SRVLIST *SrvList;
+ PXEBC_BOOT_SVR_ENTRY *BootSvrEntry;
+ UINT16 Index;
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ Mode->IcmpErrorReceived = FALSE;
+ BootSvrEntry = NULL;
+ SrvList = NULL;
+ Status = EFI_DEVICE_ERROR;
+ Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ //
+ // Station address should be ready before do discover.
+ //
+ if (!Private->IsAddressOk) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Mode->UsingIpv6) {
+
+ //
+ // Stop Udp6Read instance
+ //
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+ } else {
+
+ //
+ // Stop Udp4Read instance
+ //
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+ }
+
+ //
+ // There are 3 methods to get the information for discover.
+ //
+ if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) {
+ //
+ // 1. Take the previous setting as the discover info.
+ //
+ if (!Mode->PxeDiscoverValid ||
+ !Mode->PxeReplyReceived ||
+ (!Mode->PxeBisReplyReceived && UseBis)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Info = &DefaultInfo;
+ Info->IpCnt = 1;
+ Info->UseUCast = TRUE;
+ SrvList = Info->SrvList;
+ SrvList[0].Type = Type;
+ SrvList[0].AcceptAnyResponse = FALSE;
+
+ CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+
+ } else if (Info == NULL) {
+ //
+ // 2. Extract the discover information from the cached packets if unspecified.
+ //
+ Info = &DefaultInfo;
+ Status = PxeBcExtractDiscoverInfo (Private, Type, Info, &BootSvrEntry, &SrvList);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ } else {
+ //
+ // 3. Take the pass-in information as the discover info, and validate the server list.
+ //
+ SrvList = Info->SrvList;
+
+ if (!SrvList[0].AcceptAnyResponse) {
+ for (Index = 1; Index < Info->IpCnt; Index++) {
+ if (SrvList[Index].AcceptAnyResponse) {
+ break;
+ }
+ }
+ if (Index != Info->IpCnt) {
+ //
+ // It's invalid if the first server doesn't accecpt any response
+ // and meanwhile any of the rest servers accept any reponse.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ //
+ // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast.
+ //
+ if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) ||
+ (Info->MustUseList && Info->IpCnt == 0)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Private->IsDoDiscover = TRUE;
+
+ if (Info->UseUCast) {
+ //
+ // Do discover by unicast.
+ //
+ for (Index = 0; Index < Info->IpCnt; Index++) {
+ if (BootSvrEntry == NULL) {
+ CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS));
+ } else {
+ ASSERT (!Mode->UsingIpv6);
+ ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
+ }
+
+ Status = PxeBcDiscoverBootServer (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ &SrvList[Index].IpAddr,
+ 0,
+ NULL
+ );
+ }
+ } else if (Info->UseMCast) {
+ //
+ // Do discover by multicast.
+ //
+ Status = PxeBcDiscoverBootServer (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ &Info->ServerMCastIp,
+ 0,
+ NULL
+ );
+
+ } else if (Info->UseBCast) {
+ //
+ // Do discover by broadcast, but only valid for IPv4.
+ //
+ ASSERT (!Mode->UsingIpv6);
+ Status = PxeBcDiscoverBootServer (
+ Private,
+ Type,
+ Layer,
+ UseBis,
+ NULL,
+ Info->IpCnt,
+ SrvList
+ );
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Parse the cached PXE reply packet, and store it into mode data if valid.
+ //
+ if (Mode->UsingIpv6) {
+ Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6);
+ if (!EFI_ERROR (Status)) {
+ CopyMem (
+ &Mode->PxeReply.Dhcpv6,
+ &Private->PxeReply.Dhcp6.Packet.Offer,
+ Private->PxeReply.Dhcp6.Packet.Offer.Length
+ );
+ Mode->PxeReplyReceived = TRUE;
+ Mode->PxeDiscoverValid = TRUE;
+ }
+ } else {
+ Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4);
+ if (!EFI_ERROR (Status)) {
+ CopyMem (
+ &Mode->PxeReply.Dhcpv4,
+ &Private->PxeReply.Dhcp4.Packet.Offer,
+ Private->PxeReply.Dhcp4.Packet.Offer.Length
+ );
+ Mode->PxeReplyReceived = TRUE;
+ Mode->PxeDiscoverValid = TRUE;
+ }
+ }
+ }
+
+ON_EXIT:
+
+ //
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ //
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ This->SetIpFilter (This, &IpFilter);
+
+ return Status;
+}
+
+
+/**
+ Used to perform TFTP and MTFTP services.
+
+ This function is used to perform TFTP and MTFTP services. This includes the
+ TFTP operations to get the size of a file, read a directory, read a file, and
+ write a file. It also includes the MTFTP operations to get the size of a file,
+ read a directory, and read a file. The type of operation is specified by Operation.
+ If the callback function that is invoked during the TFTP/MTFTP operation does
+ not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will
+ be returned.
+ For read operations, the return data will be placed in the buffer specified by
+ BufferPtr. If BufferSize is too small to contain the entire downloaded file,
+ then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero,
+ or the size of the requested file. (NOTE: the size of the requested file is only returned
+ if the TFTP server supports TFTP options). If BufferSize is large enough for the
+ read operation, then BufferSize will be set to the size of the downloaded file,
+ and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services
+ should use the get-file-size operations to determine the size of the downloaded
+ file prior to using the read-file operations-especially when downloading large
+ (greater than 64 MB) files-instead of making two calls to the read-file operation.
+ Following this recommendation will save time if the file is larger than expected
+ and the TFTP server does not support TFTP option extensions. Without TFTP option
+ extension support, the client must download the entire file, counting and discarding
+ the received packets, to determine the file size.
+ For write operations, the data to be sent is in the buffer specified by BufferPtr.
+ BufferSize specifies the number of bytes to send. If the write operation completes
+ successfully, then EFI_SUCCESS will be returned.
+ For TFTP "get file size" operations, the size of the requested file or directory
+ is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server
+ does not support options, the file will be downloaded into a bit bucket and the
+ length of the downloaded file will be returned. For MTFTP "get file size" operations,
+ if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED
+ will be returned.
+ This function can take up to 10 seconds to timeout and return control to the caller.
+ If the TFTP sequence does not complete, EFI_TIMEOUT will be returned.
+ If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then the TFTP sequence is stopped and EFI_ABORTED will be returned.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] Operation The type of operation to perform.
+ @param[in, out] BufferPtr A pointer to the data buffer.
+ @param[in] Overwrite Only used on write file operations. TRUE if a file on a remote
+ server can be overwritten.
+ @param[in, out] BufferSize For get-file-size operations, *BufferSize returns the size of the
+ requested file.
+ @param[in] BlockSize The requested block size to be used during a TFTP transfer.
+ @param[in] ServerIp The TFTP / MTFTP server IP address.
+ @param[in] Filename A Null-terminated ASCII string that specifies a directory name
+ or a file name.
+ @param[in] Info Pointer to the MTFTP information.
+ @param[in] DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation.
+
+ @retval EFI_SUCCESS The TFTP/MTFTP operation was completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_BUFFER_TOO_SMALL The buffer is not large enough to complete the read operation.
+ @retval EFI_ABORTED The callback function aborted the TFTP/MTFTP operation.
+ @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the MTFTP session.
+ @retval EFI_TFTP_ERROR A TFTP error packet was received during the MTFTP session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcMtftp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
+ IN OUT VOID *BufferPtr OPTIONAL,
+ IN BOOLEAN Overwrite,
+ IN OUT UINT64 *BufferSize,
+ IN UINTN *BlockSize OPTIONAL,
+ IN EFI_IP_ADDRESS *ServerIp,
+ IN UINT8 *Filename,
+ IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_MTFTP4_CONFIG_DATA Mtftp4Config;
+ EFI_MTFTP6_CONFIG_DATA Mtftp6Config;
+ VOID *Config;
+ EFI_STATUS Status;
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;
+
+
+ if ((This == NULL) ||
+ (Filename == NULL) ||
+ (BufferSize == NULL) ||
+ (ServerIp == NULL) ||
+ ((BufferPtr == NULL) && DontUseBuffer) ||
+ ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) ||
+ (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Config = NULL;
+ Status = EFI_DEVICE_ERROR;
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (Mode->UsingIpv6) {
+ //
+ // Set configuration data for Mtftp6 instance.
+ //
+ ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA));
+ Config = &Mtftp6Config;
+ Mtftp6Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT;
+ Mtftp6Config.TryCount = PXEBC_MTFTP_RETRIES;
+ CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS));
+ //
+ // Stop Udp6Read instance
+ //
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+ } else {
+ //
+ // Set configuration data for Mtftp4 instance.
+ //
+ ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA));
+ Config = &Mtftp4Config;
+ Mtftp4Config.UseDefaultSetting = FALSE;
+ Mtftp4Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT;
+ Mtftp4Config.TryCount = PXEBC_MTFTP_RETRIES;
+ CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS));
+ //
+ // Stop Udp4Read instance
+ //
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+ }
+
+ Mode->TftpErrorReceived = FALSE;
+ Mode->IcmpErrorReceived = FALSE;
+
+ switch (Operation) {
+
+ case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE:
+ //
+ // Send TFTP request to get file size.
+ //
+ Status = PxeBcTftpGetFileSize (
+ Private,
+ Config,
+ Filename,
+ BlockSize,
+ BufferSize
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_TFTP_READ_FILE:
+ //
+ // Send TFTP request to read file.
+ //
+ Status = PxeBcTftpReadFile (
+ Private,
+ Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:
+ //
+ // Send TFTP request to write file.
+ //
+ Status = PxeBcTftpWriteFile (
+ Private,
+ Config,
+ Filename,
+ Overwrite,
+ BlockSize,
+ BufferPtr,
+ BufferSize
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:
+ //
+ // Send TFTP request to read directory.
+ //
+ Status = PxeBcTftpReadDirectory (
+ Private,
+ Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+
+ break;
+
+ case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE:
+ case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:
+ case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:
+ Status = EFI_UNSUPPORTED;
+
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+
+ break;
+ }
+
+ if (Status == EFI_ICMP_ERROR) {
+ Mode->IcmpErrorReceived = TRUE;
+ }
+
+ //
+ // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+ // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ //
+ ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+ IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+ This->SetIpFilter (This, &IpFilter);
+
+ return Status;
+}
+
+
+/**
+ Writes a UDP packet to the network interface.
+
+ This function writes a UDP packet specified by the (optional HeaderPtr and)
+ BufferPtr parameters to the network interface. The UDP header is automatically
+ built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp,
+ SrcIp, and SrcPort to build this header. If the packet is successfully built and
+ transmitted through the network interface, then EFI_SUCCESS will be returned.
+ If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will
+ be returned. If an ICMP error occurs during the transmission of the packet, then
+ the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and
+ EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in] DestIp The destination IP address.
+ @param[in] DestPort The destination UDP port number.
+ @param[in] GatewayIp The gateway IP address.
+ @param[in] SrcIp The source IP address.
+ @param[in, out] SrcPort The source UDP port number.
+ @param[in] HeaderSize An optional field which may be set to the length of a header
+ at HeaderPtr to be prefixed to the data at BufferPtr.
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS The UDP Write operation completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_BAD_BUFFER_SIZE The buffer is too long to be transmitted.
+ @retval EFI_ABORTED The callback function aborted the UDP Write operation.
+ @retval EFI_TIMEOUT The UDP Write operation timed out.
+ @retval EFI_ICMP_ERROR An ICMP error packet was received during the UDP write session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcUdpWrite (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort,
+ IN EFI_IP_ADDRESS *GatewayIp OPTIONAL,
+ IN EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_UDP4_SESSION_DATA Udp4Session;
+ EFI_UDP6_SESSION_DATA Udp6Session;
+ EFI_STATUS Status;
+ BOOLEAN DoNotFragment;
+
+ if (This == NULL || DestIp == NULL || DestPort == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) {
+ DoNotFragment = FALSE;
+ } else {
+ DoNotFragment = TRUE;
+ }
+
+ if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) {
+ //
+ // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (!Private->IsAddressOk && SrcIp == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Private->CurSrcPort == 0 ||
+ (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) {
+ //
+ // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed.
+ //
+ if (SrcPort != NULL) {
+ Private->CurSrcPort = *SrcPort;
+ }
+ }
+
+ if (Mode->UsingIpv6) {
+ Status = PxeBcConfigUdp6Write (
+ Private->Udp6Write,
+ &Private->StationIp.v6,
+ &Private->CurSrcPort
+ );
+ } else {
+ //
+ // Configure the UDPv4 instance with gateway information from DHCP server as default.
+ //
+ Status = PxeBcConfigUdp4Write (
+ Private->Udp4Write,
+ &Private->StationIp.v4,
+ &Private->SubnetMask.v4,
+ &Private->GatewayIp.v4,
+ &Private->CurSrcPort,
+ DoNotFragment
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ Private->CurSrcPort = 0;
+ return EFI_INVALID_PARAMETER;
+ } else if (SrcPort != NULL) {
+ *SrcPort = Private->CurSrcPort;
+ }
+
+ //
+ // Start a timer as timeout event for this blocking API.
+ //
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);
+
+ if (Mode->UsingIpv6) {
+ //
+ // Construct UDPv6 session data.
+ //
+ ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA));
+ CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS));
+ Udp6Session.DestinationPort = *DestPort;
+ if (SrcIp != NULL) {
+ CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS));
+ }
+ if (SrcPort != NULL) {
+ Udp6Session.SourcePort = *SrcPort;
+ }
+
+ Status = PxeBcUdp6Write (
+ Private->Udp6Write,
+ &Udp6Session,
+ Private->UdpTimeOutEvent,
+ HeaderSize,
+ HeaderPtr,
+ BufferSize,
+ BufferPtr
+ );
+ } else {
+ //
+ // Construct UDPv4 session data.
+ //
+ ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA));
+ CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
+ Udp4Session.DestinationPort = *DestPort;
+ if (SrcIp != NULL) {
+ CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS));
+ }
+ if (SrcPort != NULL) {
+ Udp4Session.SourcePort = *SrcPort;
+ }
+ //
+ // Override the gateway information if user specified.
+ //
+ Status = PxeBcUdp4Write (
+ Private->Udp4Write,
+ &Udp4Session,
+ Private->UdpTimeOutEvent,
+ (EFI_IPv4_ADDRESS *) GatewayIp,
+ HeaderSize,
+ HeaderPtr,
+ BufferSize,
+ BufferPtr
+ );
+ }
+
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);
+
+
+ //
+ // Reset the UdpWrite instance.
+ //
+ if (Mode->UsingIpv6) {
+ Private->Udp6Write->Configure (Private->Udp6Write, NULL);
+ } else {
+ Private->Udp4Write->Configure (Private->Udp4Write, NULL);
+ }
+
+ return Status;
+}
+
+
+/**
+ Reads a UDP packet from the network interface.
++
+ This function reads a UDP packet from a network interface. The data contents
+ are returned in (the optional HeaderPtr and) BufferPtr, and the size of the
+ buffer received is returned in BufferSize . If the input BufferSize is smaller
+ than the UDP packet received (less optional HeaderSize), it will be set to the
+ required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the
+ contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is
+ successfully received, then EFI_SUCCESS will be returned, and the information
+ from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if
+ they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort,
+ SrcIp, and SrcPort input values, different types of UDP packet receive filtering
+ will be performed. The following tables summarize these receive filter operations.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in, out] DestIp The destination IP address.
+ @param[in, out] DestPort The destination UDP port number.
+ @param[in, out] SrcIp The source IP address.
+ @param[in, out] SrcPort The source UDP port number.
+ @param[in] HeaderSize An optional field which may be set to the length of a
+ header at HeaderPtr to be prefixed to the data at BufferPtr.
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in, out] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be read.
+
+ @retval EFI_SUCCESS The UDP Read operation was completed.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_BUFFER_TOO_SMALL The packet is larger than Buffer can hold.
+ @retval EFI_ABORTED The callback function aborted the UDP Read operation.
+ @retval EFI_TIMEOUT The UDP Read operation timed out.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcUdpRead (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN OUT UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_UDP4_COMPLETION_TOKEN Udp4Token;
+ EFI_UDP6_COMPLETION_TOKEN Udp6Token;
+ EFI_UDP4_RECEIVE_DATA *Udp4Rx;
+ EFI_UDP6_RECEIVE_DATA *Udp6Rx;
+ EFI_STATUS Status;
+ BOOLEAN IsDone;
+ BOOLEAN IsMatched;
+ UINTN CopiedLen;
+
+ if (This == NULL || DestIp == NULL || DestPort == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ IsDone = FALSE;
+ IsMatched = FALSE;
+ Udp4Rx = NULL;
+ Udp6Rx = NULL;
+
+ if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0 && DestPort == NULL) ||
+ ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0 && SrcIp == NULL) ||
+ ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0 && SrcPort == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((BufferSize == NULL) || (BufferPtr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN));
+ ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN));
+
+ if (Mode->UsingIpv6) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsDone,
+ &Udp6Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsDone,
+ &Udp4Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ //
+ // Start a timer as timeout event for this blocking API.
+ //
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);
+ Mode->IcmpErrorReceived = FALSE;
+
+ //
+ // Read packet by Udp4Read/Udp6Read until matched or timeout.
+ //
+ while (!IsMatched && !EFI_ERROR (Status)) {
+ if (Mode->UsingIpv6) {
+ Status = PxeBcUdp6Read (
+ Private->Udp6Read,
+ &Udp6Token,
+ Mode,
+ Private->UdpTimeOutEvent,
+ OpFlags,
+ &IsDone,
+ &IsMatched,
+ DestIp,
+ DestPort,
+ SrcIp,
+ SrcPort
+ );
+ } else {
+ Status = PxeBcUdp4Read (
+ Private->Udp4Read,
+ &Udp4Token,
+ Mode,
+ Private->UdpTimeOutEvent,
+ OpFlags,
+ &IsDone,
+ &IsMatched,
+ DestIp,
+ DestPort,
+ SrcIp,
+ SrcPort
+ );
+ }
+ }
+
+ if (Status == EFI_ICMP_ERROR ||
+ Status == EFI_NETWORK_UNREACHABLE ||
+ Status == EFI_HOST_UNREACHABLE ||
+ Status == EFI_PROTOCOL_UNREACHABLE ||
+ Status == EFI_PORT_UNREACHABLE) {
+ //
+ // Get different return status for icmp error from Udp, refers to UEFI spec.
+ //
+ Mode->IcmpErrorReceived = TRUE;
+ }
+ gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);
+
+ if (IsMatched) {
+ //
+ // Copy the rececived packet to user if matched by filter.
+ //
+ CopiedLen = 0;
+ if (Mode->UsingIpv6) {
+ Udp6Rx = Udp6Token.Packet.RxData;
+ ASSERT (Udp6Rx != NULL);
+ //
+ // Copy the header part of received data.
+ //
+ if (HeaderSize != NULL) {
+ CopiedLen = MIN (*HeaderSize, Udp6Rx->DataLength);
+ *HeaderSize = CopiedLen;
+ CopyMem (HeaderPtr, Udp6Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);
+ }
+ //
+ // Copy the other part of received data.
+ //
+ if (Udp6Rx->DataLength - CopiedLen > *BufferSize) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+ *BufferSize = Udp6Rx->DataLength - CopiedLen;
+ CopyMem (BufferPtr, (UINT8 *) Udp6Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);
+ }
+ //
+ // Recycle the receiving buffer after copy to user.
+ //
+ gBS->SignalEvent (Udp6Rx->RecycleSignal);
+ } else {
+ Udp4Rx = Udp4Token.Packet.RxData;
+ ASSERT (Udp4Rx != NULL);
+ //
+ // Copy the header part of received data.
+ //
+ if (HeaderSize != NULL) {
+ CopiedLen = MIN (*HeaderSize, Udp4Rx->DataLength);
+ *HeaderSize = CopiedLen;
+ CopyMem (HeaderPtr, Udp4Rx->FragmentTable[0].FragmentBuffer, *HeaderSize);
+ }
+ //
+ // Copy the other part of received data.
+ //
+ if (Udp4Rx->DataLength - CopiedLen > *BufferSize) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+ *BufferSize = Udp4Rx->DataLength - CopiedLen;
+ CopyMem (BufferPtr, (UINT8 *) Udp4Rx->FragmentTable[0].FragmentBuffer + CopiedLen, *BufferSize);
+ }
+ //
+ // Recycle the receiving buffer after copy to user.
+ //
+ gBS->SignalEvent (Udp4Rx->RecycleSignal);
+ }
+ }
+
+ if (Mode->UsingIpv6) {
+ Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token);
+ gBS->CloseEvent (Udp6Token.Event);
+ } else {
+ Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token);
+ gBS->CloseEvent (Udp4Token.Event);
+ }
+
+ return Status;
+}
+
+
+/**
+ Updates the IP receive filters of a network device and enables software filtering.
+
+ The NewFilter field is used to modify the network device's current IP receive
+ filter settings and to enable a software filter. This function updates the IpFilter
+ field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter.
+ The software filter is used when the USE_FILTER in OpFlags is set to UdpRead().
+ The current hardware filter remains in effect no matter what the settings of OpFlags.
+ This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those
+ packets whose reception is enabled in hardware-physical NIC address (unicast),
+ broadcast address, logical address or addresses (multicast), or all (promiscuous).
+ UdpRead() does not modify the IP filter settings.
+ Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive
+ filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+ If an application or driver wishes to preserve the IP receive filter settings,
+ it will have to preserve the IP receive filter settings before these calls, and
+ use SetIpFilter() to restore them after the calls. If incompatible filtering is
+ requested (for example, PROMISCUOUS with anything else), or if the device does not
+ support a requested filter setting and it cannot be accommodated in software
+ (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned.
+ The IPlist field is used to enable IPs other than the StationIP. They may be
+ multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP,
+ then both the StationIP and the IPs from the IPlist will be used.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] NewFilter Pointer to the new set of IP receive filters.
+
+ @retval EFI_SUCCESS The IP receive filter settings were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetIpFilter (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter
+ )
+{
+ EFI_STATUS Status;
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_UDP4_CONFIG_DATA Udp4Cfg;
+ EFI_UDP6_CONFIG_DATA Udp6Cfg;
+ UINTN Index;
+ BOOLEAN NeedPromiscuous;
+
+ if (This == NULL || NewFilter == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ Status = EFI_SUCCESS;
+ NeedPromiscuous = FALSE;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ for (Index = 0; Index < NewFilter->IpCnt; Index++) {
+ ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);
+ if (!Mode->UsingIpv6 &&
+ IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) {
+ //
+ // IPv4 broadcast address should not be in IP filter.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&
+ (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) ||
+ NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) {
+ //
+ // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address
+ // is in IpList, promiscuous mode is needed.
+ //
+ NeedPromiscuous = TRUE;
+ }
+ }
+
+ //
+ // Clear configuration for UdpRead and leave the original group joined before.
+ //
+ if (Mode->UsingIpv6) {
+ CopyMem(&Udp6Cfg, &Private->Udp6CfgData, sizeof (EFI_UDP6_CONFIG_DATA));
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+ Udp6Cfg.AcceptPromiscuous = FALSE;
+ } else {
+ CopyMem(&Udp4Cfg, &Private->Udp4CfgData, sizeof (EFI_UDP4_CONFIG_DATA));
+ Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+ Udp4Cfg.AcceptPromiscuous = FALSE;
+ Udp4Cfg.AcceptBroadcast = FALSE;
+ }
+
+ if (NeedPromiscuous ||
+ (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 ||
+ (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) {
+ //
+ // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets.
+ //
+ Udp4Cfg.AcceptPromiscuous = TRUE;
+ Udp6Cfg.AcceptPromiscuous = TRUE;
+
+ } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) {
+ //
+ // Configure UDPv4 to receive all broadcast packets.
+ //
+ Udp4Cfg.AcceptBroadcast = TRUE;
+ }
+
+ //
+ // Configure UDPv4/UDPv6 instance with the new configuration.
+ //
+ if (Mode->UsingIpv6) {
+ Status = Private->Udp6Read->Configure (Private->Udp6Read, &Udp6Cfg);
+ } else {
+ Status = Private->Udp4Read->Configure (Private->Udp4Read, &Udp4Cfg);
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) {
+
+ for (Index = 0; Index < NewFilter->IpCnt; Index++) {
+ //
+ // Join the multicast group if needed.
+ //
+ if (Mode->UsingIpv6) {
+ if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) {
+ Status = Private->Udp6Read->Groups (
+ Private->Udp6Read,
+ TRUE,
+ &NewFilter->IpList[Index].v6
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ } else {
+ if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) {
+ Status = Private->Udp4Read->Groups (
+ Private->Udp4Read,
+ TRUE,
+ &NewFilter->IpList[Index].v4
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Save the new IP filter into mode data.
+ //
+ CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter));
+
+ON_EXIT:
+ return Status;
+}
+
+
+/**
+ Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6.
+
+ This function uses the ARP protocol to resolve a MAC address. The IP address specified
+ by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving
+ the specified address, then the ArpCacheEntries and ArpCache fields of the mode data
+ are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved
+ MAC address is placed there as well. If the PXE Base Code protocol is in the
+ stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters
+ a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is
+ returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ then EFI_ABORTED is returned.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] IpAddr Pointer to the IP address that is used to resolve a MAC address.
+ @param[in] MacAddr If not NULL, a pointer to the MAC address that was resolved with the
+ ARP protocol.
+
+ @retval EFI_SUCCESS The IP or MAC address was resolved.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval EFI_ICMP_ERROR An error occur with the ICMP packet message.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcArp (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_IP_ADDRESS *IpAddr,
+ IN EFI_MAC_ADDRESS *MacAddr OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_EVENT ResolvedEvent;
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS TempMac;
+ EFI_MAC_ADDRESS ZeroMac;
+ BOOLEAN IsResolved;
+
+ if (This == NULL || IpAddr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ ResolvedEvent = NULL;
+ Status = EFI_SUCCESS;
+ IsResolved = FALSE;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->UsingIpv6) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Station address should be ready before do arp.
+ //
+ if (!Private->IsAddressOk) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Mode->IcmpErrorReceived = FALSE;
+ ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS));
+ ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS));
+
+ if (!Mode->AutoArp) {
+ //
+ // If AutoArp is FALSE, only search in the current Arp cache.
+ //
+ PxeBcArpCacheUpdate (NULL, Private);
+ if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ } else {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsResolved,
+ &ResolvedEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // If AutoArp is TRUE, try to send Arp request on initiative.
+ //
+ Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac);
+ if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {
+ goto ON_EXIT;
+ }
+
+ while (!IsResolved) {
+ if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {
+ break;
+ }
+ }
+ if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_TIMEOUT;
+ }
+ }
+
+ //
+ // Copy the Mac address to user if needed.
+ //
+ if (MacAddr != NULL && !EFI_ERROR (Status)) {
+ CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS));
+ }
+
+ON_EXIT:
+ if (ResolvedEvent != NULL) {
+ gBS->CloseEvent (ResolvedEvent);
+ }
+ return Status;
+}
+
+
+/**
+ Updates the parameters that affect the operation of the PXE Base Code Protocol.
+
+ This function sets parameters that affect the operation of the PXE Base Code Protocol.
+ The parameter specified by NewAutoArp is used to control the generation of ARP
+ protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated
+ as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP
+ Protocol packets will be generated. In this case, the only mappings that are
+ available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure.
+ If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol
+ service, then the service will fail. This function updates the AutoArp field of
+ the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp.
+ The SetParameters() call must be invoked after a Callback Protocol is installed
+ to enable the use of callbacks.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] NewAutoArp If not NULL, a pointer to a value that specifies whether to replace the
+ current value of AutoARP.
+ @param[in] NewSendGUID If not NULL, a pointer to a value that specifies whether to replace the
+ current value of SendGUID.
+ @param[in] NewTTL If not NULL, a pointer to be used in place of the current value of TTL,
+ the "time to live" field of the IP header.
+ @param[in] NewToS If not NULL, a pointer to be used in place of the current value of ToS,
+ the "type of service" field of the IP header.
+ @param[in] NewMakeCallback If not NULL, a pointer to a value that specifies whether to replace the
+ current value of the MakeCallback field of the Mode structure.
+
+ @retval EFI_SUCCESS The new parameters values were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetParameters (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN *NewAutoArp OPTIONAL,
+ IN BOOLEAN *NewSendGUID OPTIONAL,
+ IN UINT8 *NewTTL OPTIONAL,
+ IN UINT8 *NewToS OPTIONAL,
+ IN BOOLEAN *NewMakeCallback OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_GUID SystemGuid;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (NewMakeCallback != NULL) {
+ if (*NewMakeCallback) {
+ //
+ // Update the previous PxeBcCallback protocol.
+ //
+ Status = gBS->HandleProtocol (
+ Private->Controller,
+ &gEfiPxeBaseCodeCallbackProtocolGuid,
+ (VOID **) &Private->PxeBcCallback
+ );
+
+ if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ Private->PxeBcCallback = NULL;
+ }
+ Mode->MakeCallbacks = *NewMakeCallback;
+ }
+
+ if (NewSendGUID != NULL) {
+ if (*NewSendGUID && EFI_ERROR (PxeBcGetSystemGuid (&SystemGuid))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Mode->SendGUID = *NewSendGUID;
+ }
+
+ if (NewAutoArp != NULL) {
+ Mode->AutoArp = *NewAutoArp;
+ }
+
+ if (NewTTL != NULL) {
+ Mode->TTL = *NewTTL;
+ }
+
+ if (NewToS != NULL) {
+ Mode->ToS = *NewToS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Updates the station IP address and/or subnet mask values of a network device.
+
+ This function updates the station IP address and/or subnet mask values of a network
+ device. The NewStationIp field is used to modify the network device's current IP address.
+ If NewStationIP is NULL, then the current IP address will not be modified. Otherwise,
+ this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure
+ with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet
+ mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified.
+ Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE
+ structure with NewSubnetMask.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] NewStationIp Pointer to the new IP address to be used by the network device.
+ @param[in] NewSubnetMask Pointer to the new subnet mask to be used by the network device.
+
+ @retval EFI_SUCCESS The new station IP address and/or subnet mask were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetStationIP (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN EFI_IP_ADDRESS *NewStationIp OPTIONAL,
+ IN EFI_IP_ADDRESS *NewSubnetMask OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_ARP_CONFIG_DATA ArpConfigData;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NewStationIp != NULL &&
+ (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) &&
+ !NetIp6IsValidUnicast (&NewStationIp->v6))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+ Status = EFI_SUCCESS;
+
+ if (!Mode->UsingIpv6 &&
+ NewSubnetMask != NULL &&
+ !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->UsingIpv6 && NewStationIp != NULL) {
+ //
+ // Set the IPv6 address by Ip6Config protocol.
+ //
+ Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else if (!Mode->UsingIpv6 && NewStationIp != NULL) {
+ //
+ // Configure the corresponding ARP with the IPv4 address.
+ //
+ ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));
+
+ ArpConfigData.SwAddressType = 0x0800;
+ ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);
+ ArpConfigData.StationAddress = &NewStationIp->v4;
+
+ Private->Arp->Configure (Private->Arp, NULL);
+ Private->Arp->Configure (Private->Arp, &ArpConfigData);
+
+ if (NewSubnetMask != NULL) {
+ Mode->RouteTableEntries = 1;
+ Mode->RouteTable[0].IpAddr.Addr[0] = NewStationIp->Addr[0] & NewSubnetMask->Addr[0];
+ Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0];
+ Mode->RouteTable[0].GwAddr.Addr[0] = 0;
+ }
+
+ Private->IsAddressOk = TRUE;
+ }
+
+ if (NewStationIp != NULL) {
+ CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
+ }
+
+ if (!Mode->UsingIpv6 && NewSubnetMask != NULL) {
+ CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS));
+ CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS));
+ }
+
+ Status = PxeBcFlushStaionIp (Private, NewStationIp, NewSubnetMask);
+ON_EXIT:
+ return Status;
+}
+
+
+/**
+ Updates the contents of the cached DHCP and Discover packets.
+
+ The pointers to the new packets are used to update the contents of the cached
+ packets in the EFI_PXE_BASE_CODE_MODE structure.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+ @param[in] NewDhcpDiscoverValid Pointer to a value that will replace the current
+ DhcpDiscoverValid field.
+ @param[in] NewDhcpAckReceived Pointer to a value that will replace the current
+ DhcpAckReceived field.
+ @param[in] NewProxyOfferReceived Pointer to a value that will replace the current
+ ProxyOfferReceived field.
+ @param[in] NewPxeDiscoverValid Pointer to a value that will replace the current
+ ProxyOfferReceived field.
+ @param[in] NewPxeReplyReceived Pointer to a value that will replace the current
+ PxeReplyReceived field.
+ @param[in] NewPxeBisReplyReceived Pointer to a value that will replace the current
+ PxeBisReplyReceived field.
+ @param[in] NewDhcpDiscover Pointer to the new cached DHCP Discover packet contents.
+ @param[in] NewDhcpAck Pointer to the new cached DHCP Ack packet contents.
+ @param[in] NewProxyOffer Pointer to the new cached Proxy Offer packet contents.
+ @param[in] NewPxeDiscover Pointer to the new cached PXE Discover packet contents.
+ @param[in] NewPxeReply Pointer to the new cached PXE Reply packet contents.
+ @param[in] NewPxeBisReply Pointer to the new cached PXE BIS Reply packet contents.
+
+ @retval EFI_SUCCESS The cached packet contents were updated.
+ @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state.
+ @retval EFI_INVALID_PARAMETER This is NULL or does not point to a valid
+ EFI_PXE_BASE_CODE_PROTOCOL structure.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetPackets (
+ IN EFI_PXE_BASE_CODE_PROTOCOL *This,
+ IN BOOLEAN *NewDhcpDiscoverValid OPTIONAL,
+ IN BOOLEAN *NewDhcpAckReceived OPTIONAL,
+ IN BOOLEAN *NewProxyOfferReceived OPTIONAL,
+ IN BOOLEAN *NewPxeDiscoverValid OPTIONAL,
+ IN BOOLEAN *NewPxeReplyReceived OPTIONAL,
+ IN BOOLEAN *NewPxeBisReplyReceived OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET *NewDhcpDiscover OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET *NewDhcpAck OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET *NewProxyOffer OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeDiscover OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeReply OPTIONAL,
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeBisReply OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (!Mode->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (NewDhcpDiscoverValid != NULL) {
+ Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;
+ }
+
+ if (NewDhcpAckReceived != NULL) {
+ Mode->DhcpAckReceived = *NewDhcpAckReceived;
+ }
+
+ if (NewProxyOfferReceived != NULL) {
+ Mode->ProxyOfferReceived = *NewProxyOfferReceived;
+ }
+
+ if (NewPxeDiscoverValid != NULL) {
+ Mode->PxeDiscoverValid = *NewPxeDiscoverValid;
+ }
+
+ if (NewPxeReplyReceived != NULL) {
+ Mode->PxeReplyReceived = *NewPxeReplyReceived;
+ }
+
+ if (NewPxeBisReplyReceived != NULL) {
+ Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;
+ }
+
+ if (NewDhcpDiscover != NULL) {
+ CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewDhcpAck != NULL) {
+ CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewProxyOffer != NULL) {
+ CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewPxeDiscover != NULL) {
+ CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewPxeReply != NULL) {
+ CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ if (NewPxeBisReply != NULL) {
+ CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate = {
+ EFI_PXE_BASE_CODE_PROTOCOL_REVISION,
+ EfiPxeBcStart,
+ EfiPxeBcStop,
+ EfiPxeBcDhcp,
+ EfiPxeBcDiscover,
+ EfiPxeBcMtftp,
+ EfiPxeBcUdpWrite,
+ EfiPxeBcUdpRead,
+ EfiPxeBcSetIpFilter,
+ EfiPxeBcArp,
+ EfiPxeBcSetParameters,
+ EfiPxeBcSetStationIP,
+ EfiPxeBcSetPackets,
+ NULL
+};
+
+
+/**
+ Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has
+ received, or is waiting to receive a packet.
+
+ This function is invoked when the PXE Base Code Protocol is about to transmit, has received,
+ or is waiting to receive a packet. Parameters Function and Received specify the type of event.
+ Parameters PacketLen and Packet specify the packet that generated the event. If these fields
+ are zero and NULL respectively, then this is a status update callback. If the operation specified
+ by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation
+ specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to
+ the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms.
+ The SetParameters() function must be called after a Callback Protocol is installed to enable the
+ use of callbacks.
+
+ @param[in] This Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance.
+ @param[in] Function The PXE Base Code Protocol function that is waiting for an event.
+ @param[in] Received TRUE if the callback is being invoked due to a receive event. FALSE if
+ the callback is being invoked due to a transmit event.
+ @param[in] PacketLength The length, in bytes, of Packet. This field will have a value of zero if
+ this is a wait for receive event.
+ @param[in] PacketPtr If Received is TRUE, a pointer to the packet that was just received;
+ otherwise a pointer to the packet that is about to be transmitted.
+
+ @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation.
+ @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT If Function specifies an abort operation.
+
+**/
+EFI_PXE_BASE_CODE_CALLBACK_STATUS
+EFIAPI
+EfiPxeLoadFileCallback (
+ IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *This,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function,
+ IN BOOLEAN Received,
+ IN UINT32 PacketLength,
+ IN EFI_PXE_BASE_CODE_PACKET *PacketPtr OPTIONAL
+ )
+{
+ EFI_INPUT_KEY Key;
+ EFI_STATUS Status;
+
+ //
+ // Catch Ctrl-C or ESC to abort.
+ //
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+
+ if (!EFI_ERROR (Status)) {
+
+ if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) {
+
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
+ }
+ }
+ //
+ // No print if receive packet
+ //
+ if (Received) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+ //
+ // Print only for three functions
+ //
+ switch (Function) {
+
+ case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:
+ //
+ // Print only for open MTFTP packets, not every MTFTP packets
+ //
+ if (PacketLength != 0 && PacketPtr != NULL) {
+ if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+ }
+ break;
+
+ case EFI_PXE_BASE_CODE_FUNCTION_DHCP:
+ case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:
+ break;
+
+ default:
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+ }
+
+ if (PacketLength != 0 && PacketPtr != NULL) {
+ //
+ // Print '.' when transmit a packet
+ //
+ AsciiPrint (".");
+ }
+
+ return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+}
+
+EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = {
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
+ EfiPxeLoadFileCallback
+};
+
+
+/**
+ Causes the driver to load a specified file.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] FilePath The device specific path of the file to load.
+ @param[in] BootPolicy If TRUE, indicates that the request originates from the
+ boot manager is attempting to load FilePath as a boot
+ selection. If FALSE, then FilePath must match an exact file
+ to be loaded.
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param[in] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
+ then no the size of the requested file is returned in
+ BufferSize.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy.
+ @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
+ BufferSize is NULL.
+ @retval EFI_NO_MEDIA No medium was present to load the file.
+ @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
+ @retval EFI_NO_RESPONSE The remote system did not respond.
+ @retval EFI_NOT_FOUND The file was not found.
+ @retval EFI_ABORTED The file load process was manually cancelled.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeLoadFile (
+ IN EFI_LOAD_FILE_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ PXEBC_VIRTUAL_NIC *VirtualNic;
+ EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
+ BOOLEAN UsingIpv6;
+ EFI_STATUS Status;
+ BOOLEAN MediaPresent;
+
+ VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This);
+ Private = VirtualNic->Private;
+ PxeBc = &Private->PxeBc;
+ UsingIpv6 = FALSE;
+ Status = EFI_DEVICE_ERROR;
+
+ if (This == NULL || BufferSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Only support BootPolicy
+ //
+ if (!BootPolicy) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Check media status before PXE start
+ //
+ MediaPresent = TRUE;
+ NetLibDetectMedia (Private->Controller, &MediaPresent);
+ if (!MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // Check whether the virtual nic is using IPv6 or not.
+ //
+ if (VirtualNic == Private->Ip6Nic) {
+ UsingIpv6 = TRUE;
+ }
+
+ //
+ // Start Pxe Base Code to initialize PXE boot.
+ //
+ Status = PxeBc->Start (PxeBc, UsingIpv6);
+ if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
+ Status = PxeBcLoadBootFile (Private, BufferSize, Buffer);
+ }
+
+ if (Status != EFI_SUCCESS &&
+ Status != EFI_UNSUPPORTED &&
+ Status != EFI_BUFFER_TOO_SMALL) {
+ //
+ // There are three cases, which needn't stop pxebc here.
+ // 1. success to download file.
+ // 2. success to get file size.
+ // 3. unsupported.
+ //
+ PxeBc->Stop (PxeBc);
+ }
+
+ return Status;
+}
+
+EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate = { EfiPxeLoadFile };
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
new file mode 100644
index 0000000000..7491cf8285
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
@@ -0,0 +1,207 @@
+/** @file
+ This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.
+ interfaces declaration.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_IMPL_H__
+#define __EFI_PXEBC_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Guid/SmBios.h>
+#include <IndustryStandard/SmBios.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/Arp.h>
+#include <Protocol/Ip4.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/Udp4.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Mtftp4.h>
+#include <Protocol/Mtftp6.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/PxeBaseCodeCallBack.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/DpcLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+
+typedef struct _PXEBC_PRIVATE_DATA PXEBC_PRIVATE_DATA;
+typedef struct _PXEBC_VIRTUAL_NIC PXEBC_VIRTUAL_NIC;
+
+#include "PxeBcDriver.h"
+#include "PxeBcDhcp4.h"
+#include "PxeBcDhcp6.h"
+#include "PxeBcMtftp.h"
+#include "PxeBcBoot.h"
+#include "PxeBcSupport.h"
+
+#define PXEBC_DEFAULT_HOPLIMIT 64
+#define PXEBC_DEFAULT_LIFETIME 50000 // 50 ms, unit is microsecond
+#define PXEBC_UDP_TIMEOUT 30000000 // 3 seconds, unit is 100nanosecond
+#define PXEBC_MTFTP_TIMEOUT 4
+#define PXEBC_MTFTP_RETRIES 6
+#define PXEBC_DHCP_RETRIES 4 // refers to mPxeDhcpTimeout, also by PXE2.1 spec.
+#define PXEBC_MENU_MAX_NUM 24
+#define PXEBC_OFFER_MAX_NUM 16
+
+#define PXEBC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'P')
+#define PXEBC_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'V')
+#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a) CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE)
+#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE)
+
+typedef union {
+ PXEBC_DHCP4_PACKET_CACHE Dhcp4;
+ PXEBC_DHCP6_PACKET_CACHE Dhcp6;
+} PXEBC_DHCP_PACKET_CACHE;
+
+struct _PXEBC_VIRTUAL_NIC {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+ EFI_LOAD_FILE_PROTOCOL LoadFile;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ PXEBC_PRIVATE_DATA *Private;
+};
+
+struct _PXEBC_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ PXEBC_VIRTUAL_NIC *Ip4Nic;
+ PXEBC_VIRTUAL_NIC *Ip6Nic;
+
+ EFI_HANDLE ArpChild;
+ EFI_HANDLE Ip4Child;
+ EFI_HANDLE Dhcp4Child;
+ EFI_HANDLE Mtftp4Child;
+ EFI_HANDLE Udp4ReadChild;
+ EFI_HANDLE Udp4WriteChild;
+
+ EFI_ARP_PROTOCOL *Arp;
+ EFI_IP4_PROTOCOL *Ip4;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_UDP4_PROTOCOL *Udp4Read;
+ EFI_UDP4_PROTOCOL *Udp4Write;
+
+ EFI_HANDLE Ip6Child;
+ EFI_HANDLE Dhcp6Child;
+ EFI_HANDLE Mtftp6Child;
+ EFI_HANDLE Udp6ReadChild;
+ EFI_HANDLE Udp6WriteChild;
+
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ EFI_MTFTP6_PROTOCOL *Mtftp6;
+ EFI_UDP6_PROTOCOL *Udp6Read;
+ EFI_UDP6_PROTOCOL *Udp6Write;
+
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+ EFI_PXE_BASE_CODE_PROTOCOL PxeBc;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL LoadFileCallback;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *PxeBcCallback;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ EFI_PXE_BASE_CODE_MODE Mode;
+ EFI_PXE_BASE_CODE_FUNCTION Function;
+ UINT32 Ip6Policy;
+
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;
+ EFI_UDP6_CONFIG_DATA Udp6CfgData;
+ EFI_IP4_CONFIG_DATA Ip4CfgData;
+ EFI_IP6_CONFIG_DATA Ip6CfgData;
+
+ EFI_EVENT UdpTimeOutEvent;
+ EFI_EVENT ArpUpdateEvent;
+ EFI_IP4_COMPLETION_TOKEN IcmpToken;
+ EFI_IP6_COMPLETION_TOKEN Icmp6Token;
+
+ BOOLEAN IsAddressOk;
+ BOOLEAN IsOfferSorted;
+ BOOLEAN IsProxyRecved;
+ BOOLEAN IsDoDiscover;
+
+ EFI_IP_ADDRESS StationIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS GatewayIp;
+ EFI_IP_ADDRESS ServerIp;
+ UINT16 CurSrcPort;
+
+ UINT32 Ip4MaxPacketSize;
+ UINT32 Ip6MaxPacketSize;
+ UINT8 *BootFileName;
+ UINTN BootFileSize;
+ UINTN BlockSize;
+
+ PXEBC_DHCP_PACKET_CACHE ProxyOffer;
+ PXEBC_DHCP_PACKET_CACHE DhcpAck;
+ PXEBC_DHCP_PACKET_CACHE PxeReply;
+ EFI_DHCP6_PACKET *Dhcp6Request;
+ EFI_DHCP4_PACKET SeedPacket;
+
+ //
+ // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.
+ //
+ // It supposed that
+ //
+ // OfferNum: 8
+ // OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl]
+ // (OfferBuffer is 0-based.)
+ //
+ // And assume that (DhcpPxe10 is the first priority actually.)
+ //
+ // SelectIndex: 2
+ // SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL
+ // (SelectIndex is 1-based, and 0 means no one is selected.)
+ //
+ // So it should be
+ //
+ // DhcpOnly DhcpPxe10 DhcpWfm11a DhcpBinl ProxyPxe10 ProxyWfm11a ProxyBinl Bootp
+ // OfferCount: [ 2(n), 1(n), 0(n), 1(n), 1(1), 0(1), 3(n), 1(1)]
+ //
+ // OfferIndex: {[ 2, 5, 0, 6, 3, 0, *0, 0]
+ // [ 4, 0, 0, 0, 0, 0, 1, 0]
+ // [ 0, 0, 0, 0, 0, 0, 7, 0]
+ // ... ]}
+ // (OfferIndex is 0-based.)
+ //
+ //
+ UINT32 SelectIndex;
+ UINT32 SelectProxyType;
+ PXEBC_DHCP_PACKET_CACHE OfferBuffer[PXEBC_OFFER_MAX_NUM];
+ UINT32 OfferNum;
+ UINT32 OfferCount[PxeOfferTypeMax];
+ UINT32 OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM];
+};
+
+extern EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate;
+extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate;
+extern EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate;
+
+#endif
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
new file mode 100644
index 0000000000..8adba6620f
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
@@ -0,0 +1,1105 @@
+/** @file
+ Functions implementation related with Mtftp for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = {
+ "blksize",
+ "timeout",
+ "tsize",
+ "multicast"
+};
+
+
+/**
+ This is a callback function when packets are received or transmitted in Mtftp driver.
+
+ A callback function that is provided by the caller to intercept
+ the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the
+ EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept
+ EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to
+ EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().
+
+ @param[in] This Pointer to EFI_MTFTP6_PROTOCOL.
+ @param[in] Token Pointer to EFI_MTFTP6_TOKEN.
+ @param[in] PacketLen Length of EFI_MTFTP6_PACKET.
+ @param[in] Packet Pointer to EFI_MTFTP6_PACKET to be checked.
+
+ @retval EFI_SUCCESS The current operation succeeded.
+ @retval EFI_ABORTED Abort the current transfer process.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcMtftp6CheckPacket (
+ IN EFI_MTFTP6_PROTOCOL *This,
+ IN EFI_MTFTP6_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP6_PACKET *Packet
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+ EFI_STATUS Status;
+
+ Private = (PXEBC_PRIVATE_DATA *) Token->Context;
+ Callback = Private->PxeBcCallback;
+ Status = EFI_SUCCESS;
+
+ if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) {
+ //
+ // Store the tftp error message into mode data and set the received flag.
+ //
+ Private->Mode.TftpErrorReceived = TRUE;
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+ AsciiStrnCpy (
+ Private->Mode.TftpError.ErrorString,
+ (CHAR8 *) Packet->Error.ErrorMessage,
+ PXE_MTFTP_ERROR_STRING_LENGTH
+ );
+ }
+
+ if (Callback != NULL) {
+ //
+ // Callback to user if has when received any tftp packet.
+ //
+ Status = Callback->Callback (
+ Callback,
+ Private->Function,
+ TRUE,
+ PacketLen,
+ (EFI_PXE_BASE_CODE_PACKET *) Packet
+ );
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ //
+ // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+ //
+ Status = EFI_ABORTED;
+ } else {
+ //
+ // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+ //
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to get the size of a file using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Sucessfully obtained the size of file.
+ @retval EFI_NOT_FOUND Parse the tftp ptions failed.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Has not obtained the size of the file.
+
+**/
+EFI_STATUS
+PxeBcMtftp6GetFileSize (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP6_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ EFI_MTFTP6_PROTOCOL *Mtftp6;
+ EFI_MTFTP6_OPTION ReqOpt[2];
+ EFI_MTFTP6_PACKET *Packet;
+ EFI_MTFTP6_OPTION *Option;
+ UINT32 PktLen;
+ UINT8 OptBuf[128];
+ UINT32 OptCnt;
+ EFI_STATUS Status;
+
+ *BufferSize = 0;
+ Status = EFI_DEVICE_ERROR;
+ Mtftp6 = Private->Mtftp6;
+ Packet = NULL;
+ Option = NULL;
+ PktLen = 0;
+ OptCnt = 1;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp6->Configure (Mtftp6, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Build the required options for get info.
+ //
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];
+ PxeBcUintnToAscDec (0, OptBuf);
+ ReqOpt[0].ValueStr = OptBuf;
+
+ if (BlockSize != NULL) {
+ ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);
+ OptCnt++;
+ }
+
+ Status = Mtftp6->GetInfo (
+ Mtftp6,
+ FALSE,
+ Filename,
+ NULL,
+ (UINT8) OptCnt,
+ ReqOpt,
+ &PktLen,
+ &Packet
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_TFTP_ERROR) {
+ //
+ // Store the tftp error message into mode data and set the received flag.
+ //
+ Private->Mode.TftpErrorReceived = TRUE;
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+ AsciiStrnCpy (
+ Private->Mode.TftpError.ErrorString,
+ (CHAR8 *) Packet->Error.ErrorMessage,
+ PXE_MTFTP_ERROR_STRING_LENGTH
+ );
+ }
+ goto ON_ERROR;
+ }
+
+ //
+ // Parse the options in the reply packet.
+ //
+ OptCnt = 0;
+ Status = Mtftp6->ParseOptions (
+ Mtftp6,
+ PktLen,
+ Packet,
+ (UINT32 *) &OptCnt,
+ &Option
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Parse out the value of "tsize" option.
+ //
+ Status = EFI_NOT_FOUND;
+ while (OptCnt != 0) {
+ if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {
+ *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));
+ Status = EFI_SUCCESS;
+ }
+ OptCnt--;
+ }
+ FreePool (Option);
+
+ON_ERROR:
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+ Mtftp6->Configure (Mtftp6, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is to get data of a file using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicates whether with a receive buffer.
+
+ @retval EFI_SUCCESS Successfully read the data from the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp6ReadFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP6_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_MTFTP6_PROTOCOL *Mtftp6;
+ EFI_MTFTP6_TOKEN Token;
+ EFI_MTFTP6_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp6 = Private->Mtftp6;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp6->Configure (Mtftp6, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.Context = Private;
+
+ if (DontUseBuffer) {
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ } else {
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ }
+
+ Token.CheckPacket = PxeBcMtftp6CheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp6->ReadFile (Mtftp6, &Token);
+ //
+ // Get the real size of received buffer.
+ //
+ *BufferSize = Token.BufferSize;
+
+ Mtftp6->Configure (Mtftp6, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is used to write the data of a file using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] Overwrite Indicate whether with overwrite attribute.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Successfully wrote the data into a special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp6WriteFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP6_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN BOOLEAN Overwrite,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ EFI_MTFTP6_PROTOCOL *Mtftp6;
+ EFI_MTFTP6_TOKEN Token;
+ EFI_MTFTP6_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp6 = Private->Mtftp6;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp6->Configure (Mtftp6, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ Token.CheckPacket = PxeBcMtftp6CheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp6->WriteFile (Mtftp6, &Token);
+ //
+ // Get the real size of transmitted buffer.
+ //
+ *BufferSize = Token.BufferSize;
+
+ Mtftp6->Configure (Mtftp6, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is to read the data (file) from a directory using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.
+
+ @retval EFI_SUCCESS Successfully obtained the data from the file included in directory.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp6ReadDirectory (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP6_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_MTFTP6_PROTOCOL *Mtftp6;
+ EFI_MTFTP6_TOKEN Token;
+ EFI_MTFTP6_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp6 = Private->Mtftp6;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp6->Configure (Mtftp6, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.Context = Private;
+
+ if (DontUseBuffer) {
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ } else {
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ }
+
+ Token.CheckPacket = PxeBcMtftp6CheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp6->ReadDirectory (Mtftp6, &Token);
+ //
+ // Get the real size of received buffer.
+ //
+ *BufferSize = Token.BufferSize;
+
+ Mtftp6->Configure (Mtftp6, NULL);
+
+ return Status;
+}
+
+
+/**
+ This is a callback function when packets are received or transmitted in Mtftp driver.
+
+ A callback function that is provided by the caller to intercept
+ the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the
+ EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept
+ EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to
+ EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().
+
+ @param[in] This Pointer to EFI_MTFTP4_PROTOCOL.
+ @param[in] Token Pointer to EFI_MTFTP4_TOKEN.
+ @param[in] PacketLen Length of EFI_MTFTP4_PACKET.
+ @param[in] Packet Pointer to EFI_MTFTP4_PACKET to be checked.
+
+ @retval EFI_SUCCESS The current operation succeeeded.
+ @retval EFI_ABORTED Abort the current transfer process.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcMtftp4CheckPacket (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+ EFI_STATUS Status;
+
+ Private = (PXEBC_PRIVATE_DATA *) Token->Context;
+ Callback = Private->PxeBcCallback;
+ Status = EFI_SUCCESS;
+
+ if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) {
+ //
+ // Store the tftp error message into mode data and set the received flag.
+ //
+ Private->Mode.TftpErrorReceived = TRUE;
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+ AsciiStrnCpy (
+ Private->Mode.TftpError.ErrorString,
+ (CHAR8 *) Packet->Error.ErrorMessage,
+ PXE_MTFTP_ERROR_STRING_LENGTH
+ );
+ }
+
+ if (Callback != NULL) {
+ //
+ // Callback to user if has when received any tftp packet.
+ //
+ Status = Callback->Callback (
+ Callback,
+ Private->Function,
+ TRUE,
+ PacketLen,
+ (EFI_PXE_BASE_CODE_PACKET *) Packet
+ );
+ if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+ //
+ // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+ //
+ Status = EFI_ABORTED;
+ } else {
+ //
+ // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+ //
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to get size of a file using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Successfully obtained the size of file.
+ @retval EFI_NOT_FOUND Parse the tftp options failed.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Did not obtain the size of the file.
+
+**/
+EFI_STATUS
+PxeBcMtftp4GetFileSize (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_OPTION ReqOpt[2];
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_OPTION *Option;
+ UINT32 PktLen;
+ UINT8 OptBuf[128];
+ UINT32 OptCnt;
+ EFI_STATUS Status;
+
+ *BufferSize = 0;
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ Packet = NULL;
+ Option = NULL;
+ PktLen = 0;
+ OptCnt = 1;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Build the required options for get info.
+ //
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];
+ PxeBcUintnToAscDec (0, OptBuf);
+ ReqOpt[0].ValueStr = OptBuf;
+
+ if (BlockSize != NULL) {
+ ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr);
+ OptCnt++;
+ }
+
+ Status = Mtftp4->GetInfo (
+ Mtftp4,
+ FALSE,
+ Filename,
+ NULL,
+ (UINT8) OptCnt,
+ ReqOpt,
+ &PktLen,
+ &Packet
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_TFTP_ERROR) {
+ //
+ // Store the tftp error message into mode data and set the received flag.
+ //
+ Private->Mode.TftpErrorReceived = TRUE;
+ Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+ AsciiStrnCpy (
+ Private->Mode.TftpError.ErrorString,
+ (CHAR8 *) Packet->Error.ErrorMessage,
+ PXE_MTFTP_ERROR_STRING_LENGTH
+ );
+ }
+ goto ON_ERROR;
+ }
+
+ //
+ // Parse the options in the reply packet.
+ //
+ OptCnt = 0;
+ Status = Mtftp4->ParseOptions (
+ Mtftp4,
+ PktLen,
+ Packet,
+ (UINT32 *) &OptCnt,
+ &Option
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Parse out the value of "tsize" option.
+ //
+ Status = EFI_NOT_FOUND;
+ while (OptCnt != 0) {
+ if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {
+ *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));
+ Status = EFI_SUCCESS;
+ }
+ OptCnt--;
+ }
+ FreePool (Option);
+
+ON_ERROR:
+ if (Packet != NULL) {
+ FreePool (Packet);
+ }
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is to read the data of a file using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.
+
+ @retval EFI_SUCCESS Successfully read the data from the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp4ReadFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_TOKEN Token;
+ EFI_MTFTP4_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.Context = Private;
+
+ if (DontUseBuffer) {
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ } else {
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ }
+
+ Token.CheckPacket = PxeBcMtftp4CheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp4->ReadFile (Mtftp4, &Token);
+ //
+ // Get the real size of received buffer.
+ //
+ *BufferSize = Token.BufferSize;
+
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is to write the data of a file using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] Overwrite Indicates whether to use the overwrite attribute.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Successfully write the data into the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp4WriteFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN BOOLEAN Overwrite,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_TOKEN Token;
+ EFI_MTFTP4_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ Token.CheckPacket = PxeBcMtftp4CheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp4->WriteFile (Mtftp4, &Token);
+ //
+ // Get the real size of transmitted buffer.
+ //
+ *BufferSize = Token.BufferSize;
+
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is to get data (file) from a directory using Tftp.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.
+
+ @retval EFI_SUCCES Successfully obtained the data from the file included in the directory.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp4ReadDirectory (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_MTFTP4_CONFIG_DATA *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_MTFTP4_TOKEN Token;
+ EFI_MTFTP4_OPTION ReqOpt[1];
+ UINT32 OptCnt;
+ UINT8 OptBuf[128];
+ EFI_STATUS Status;
+
+ Status = EFI_DEVICE_ERROR;
+ Mtftp4 = Private->Mtftp4;
+ OptCnt = 0;
+ Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+ Status = Mtftp4->Configure (Mtftp4, Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (BlockSize != NULL) {
+ ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+ ReqOpt[0].ValueStr = OptBuf;
+ PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr);
+ OptCnt++;
+ }
+
+ Token.Event = NULL;
+ Token.OverrideData = NULL;
+ Token.Filename = Filename;
+ Token.ModeStr = NULL;
+ Token.OptionCount = OptCnt;
+ Token.OptionList = ReqOpt;
+ Token.Context = Private;
+
+ if (DontUseBuffer) {
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ } else {
+ Token.BufferSize = *BufferSize;
+ Token.Buffer = BufferPtr;
+ }
+
+ Token.CheckPacket = PxeBcMtftp4CheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = Mtftp4->ReadDirectory (Mtftp4, &Token);
+ //
+ // Get the real size of received buffer.
+ //
+ *BufferSize = Token.BufferSize;
+
+ Mtftp4->Configure (Mtftp4, NULL);
+
+ return Status;
+}
+
+
+/**
+ This function is wrapper to get the file size using TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to configure data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Successfully obtained the size of file.
+ @retval EFI_NOT_FOUND Parse the tftp options failed.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Did not obtain the size of the file.
+
+**/
+EFI_STATUS
+PxeBcTftpGetFileSize (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ if (Private->PxeBc.Mode->UsingIpv6) {
+ return PxeBcMtftp6GetFileSize (
+ Private,
+ (EFI_MTFTP6_CONFIG_DATA *) Config,
+ Filename,
+ BlockSize,
+ BufferSize
+ );
+ } else {
+ return PxeBcMtftp4GetFileSize (
+ Private,
+ (EFI_MTFTP4_CONFIG_DATA *) Config,
+ Filename,
+ BlockSize,
+ BufferSize
+ );
+ }
+}
+
+
+/**
+ This function is a wrapper to get file using TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to config data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.
+
+ @retval EFI_SUCCESS Sucessfully read the data from the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ if (Private->PxeBc.Mode->UsingIpv6) {
+ return PxeBcMtftp6ReadFile (
+ Private,
+ (EFI_MTFTP6_CONFIG_DATA *) Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+ } else {
+ return PxeBcMtftp4ReadFile (
+ Private,
+ (EFI_MTFTP4_CONFIG_DATA *) Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+ }
+}
+
+
+/**
+ This function is a wrapper to write file using TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to config data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] Overwrite Indicate whether with overwrite attribute.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Successfully wrote the data into a special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpWriteFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN BOOLEAN Overwrite,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ if (Private->PxeBc.Mode->UsingIpv6) {
+ return PxeBcMtftp6WriteFile (
+ Private,
+ (EFI_MTFTP6_CONFIG_DATA *) Config,
+ Filename,
+ Overwrite,
+ BlockSize,
+ BufferPtr,
+ BufferSize
+ );
+ } else {
+ return PxeBcMtftp4WriteFile (
+ Private,
+ (EFI_MTFTP4_CONFIG_DATA *) Config,
+ Filename,
+ Overwrite,
+ BlockSize,
+ BufferPtr,
+ BufferSize
+ );
+ }
+}
+
+
+/**
+ This function is a wrapper to get the data (file) from a directory using TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to config data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicatse whether to use a receive buffer.
+
+ @retval EFI_SUCCES Successfully obtained the data from the file included in the directory.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadDirectory (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ )
+{
+ if (Private->PxeBc.Mode->UsingIpv6) {
+ return PxeBcMtftp6ReadDirectory (
+ Private,
+ (EFI_MTFTP6_CONFIG_DATA *) Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+ } else {
+ return PxeBcMtftp4ReadDirectory (
+ Private,
+ (EFI_MTFTP4_CONFIG_DATA *) Config,
+ Filename,
+ BlockSize,
+ BufferPtr,
+ BufferSize,
+ DontUseBuffer
+ );
+ }
+}
+
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
new file mode 100644
index 0000000000..1064195a82
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
@@ -0,0 +1,136 @@
+/** @file
+ Functions declaration related with Mtftp for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_MTFTP_H__
+#define __EFI_PXEBC_MTFTP_H__
+
+#define PXE_MTFTP_OPTION_BLKSIZE_INDEX 0
+#define PXE_MTFTP_OPTION_TIMEOUT_INDEX 1
+#define PXE_MTFTP_OPTION_TSIZE_INDEX 2
+#define PXE_MTFTP_OPTION_MULTICAST_INDEX 3
+#define PXE_MTFTP_OPTION_MAXIMUM_INDEX 4
+
+#define PXE_MTFTP_ERROR_STRING_LENGTH 127 // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR.
+#define PXE_MTFTP_DEFAULT_BLOCK_SIZE 512 // refer to rfc-1350.
+
+
+/**
+ This function is wrapper to get the file size using TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to configure data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Successfully obtained the size of file.
+ @retval EFI_NOT_FOUND Parse the tftp ptions failed.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Did not obtain the size of the file.
+
+**/
+EFI_STATUS
+PxeBcTftpGetFileSize (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN OUT UINT64 *BufferSize
+ );
+
+
+/**
+ This function is a wrapper to get a file using TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to config data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicates whether to use a receive buffer.
+
+ @retval EFI_SUCCESS Successfully read the data from the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ );
+
+
+/**
+ This function is a wrapper to put file with TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to config data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] Overwrite Indicates whether to use an overwrite attribute.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+
+ @retval EFI_SUCCESS Successfully wrote the data into the special file.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval other Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpWriteFile (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN BOOLEAN Overwrite,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize
+ );
+
+
+/**
+ This function is a wrapper to get the data (file) from a directory using TFTP.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] Config Pointer to config data.
+ @param[in] Filename Pointer to boot file name.
+ @param[in] BlockSize Pointer to required block size.
+ @param[in] BufferPtr Pointer to buffer.
+ @param[in, out] BufferSize Pointer to buffer size.
+ @param[in] DontUseBuffer Indicates whether with a receive buffer.
+
+ @retval EFI_SUCCES Successfully obtained the data from the file included in directory.
+ @retval EFI_DEVICE_ERROR The network device encountered an error during this operation.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadDirectory (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN VOID *Config,
+ IN UINT8 *Filename,
+ IN UINTN *BlockSize,
+ IN UINT8 *BufferPtr,
+ IN OUT UINT64 *BufferSize,
+ IN BOOLEAN DontUseBuffer
+ );
+#endif
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
new file mode 100644
index 0000000000..7ad070c5e3
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
@@ -0,0 +1,1588 @@
+/** @file
+ Support functions implementation for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+
+/**
+ This function returns SMBIOS string given the string number.
+
+ @param[in] Smbios The pointer to the SMBIOS structure
+ @param[in] StringNumber String number to return. 0 is used to skip all
+ strings and point to the next SMBIOS structure.
+
+ @return String The pointer to the next SMBIOS structure if
+ StringNumber == 0.
+
+**/
+CHAR8 *
+PxeBcGetSmbiosString (
+ IN SMBIOS_STRUCTURE_POINTER *Smbios,
+ IN UINT16 StringNumber
+ )
+{
+ UINT16 Index;
+ CHAR8 *String;
+
+ //
+ // Skip over formatted section.
+ //
+ String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);
+
+ //
+ // Look through unformated section.
+ //
+ for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {
+ if (StringNumber == Index) {
+ return String;
+ }
+
+ //
+ // Skip zero string.
+ //
+ while (*String != 0) {
+ String++;
+ }
+ String++;
+
+ if (*String == 0) {
+ //
+ // If double NULL then we are done.
+ // Return pointer to next structure in Smbios.
+ // if you pass in a 0 you will always get here
+ //
+ Smbios->Raw = (UINT8 *)++String;
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ This function obtains the system guid and the serial number from the smbios table.
+
+ @param[out] SystemGuid The pointer of the returned system guid.
+
+ @retval EFI_SUCCESS Successfully obtained the system guid.
+ @retval EFI_NOT_FOUND Did not find the SMBIOS table.
+
+**/
+EFI_STATUS
+PxeBcGetSystemGuid (
+ OUT EFI_GUID *SystemGuid
+ )
+{
+ EFI_STATUS Status;
+ SMBIOS_TABLE_ENTRY_POINT *SmbiosTable;
+ SMBIOS_STRUCTURE_POINTER Smbios;
+ SMBIOS_STRUCTURE_POINTER SmbiosEnd;
+ UINT16 Index;
+
+ SmbiosTable = NULL;
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);
+
+ if (EFI_ERROR (Status) || SmbiosTable == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;
+ SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);
+
+ for (Index = 0; Index < SmbiosTable->TableLength; Index++) {
+ if (Smbios.Hdr->Type == 1) {
+ if (Smbios.Hdr->Length < 0x19) {
+ //
+ // Older version did not support Guid and Serial number
+ //
+ continue;
+ }
+ //
+ // SMBIOS tables are byte packed so we need to do a byte copy to
+ // prevend alignment faults on Itanium-based platform.
+ //
+ CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));
+ PxeBcGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Make Smbios point to the next record
+ //
+ PxeBcGetSmbiosString (&Smbios, 0);
+
+ if (Smbios.Raw >= SmbiosEnd.Raw) {
+ //
+ // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.
+ // given this we must double check against the length of the structure.
+ //
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Flush the previous configration using the new station Ip address.
+
+ @param[in] Private The pointer to the PxeBc private data.
+ @param[in] StationIp The pointer to the station Ip address.
+ @param[in] SubnetMask The pointer to the subnet mask address for v4.
+
+ @retval EFI_SUCCESS Successfully flushed the previous configuration.
+ @retval Others Failed to flush using the new station Ip.
+
+**/
+EFI_STATUS
+PxeBcFlushStaionIp (
+ PXEBC_PRIVATE_DATA *Private,
+ EFI_IP_ADDRESS *StationIp,
+ EFI_IP_ADDRESS *SubnetMask OPTIONAL
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+
+ ASSERT (StationIp != NULL);
+
+ Mode = Private->PxeBc.Mode;
+ Status = EFI_SUCCESS;
+
+ if (Mode->UsingIpv6) {
+
+ CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address.
+ //
+ Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);
+ Private->Ip6->Configure (Private->Ip6, NULL);
+
+ Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ } else {
+ ASSERT (SubnetMask != NULL);
+ CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.
+ //
+ Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);
+ Private->Ip4->Configure (Private->Ip4, NULL);
+
+ Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Event The triggered event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+PxeBcCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+
+/**
+ Do arp resolution from arp cache in PxeBcMode.
+
+ @param Mode The pointer to EFI_PXE_BASE_CODE_MODE.
+ @param Ip4Addr The Ip4 address for resolution.
+ @param MacAddress The resoluted MAC address if the resolution is successful.
+ The value is undefined if the resolution fails.
+
+ @retval TRUE Found an matched entry.
+ @retval FALSE Did not find a matched entry.
+
+**/
+BOOLEAN
+PxeBcCheckArpCache (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_IPv4_ADDRESS *Ip4Addr,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ )
+{
+ UINT32 Index;
+
+ ASSERT (!Mode->UsingIpv6);
+
+ //
+ // Check whether the current Arp cache in mode data contains this information or not.
+ //
+ for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {
+ if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) {
+ CopyMem (
+ MacAddress,
+ &Mode->ArpCache[Index].MacAddr,
+ sizeof (EFI_MAC_ADDRESS)
+ );
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Update the arp cache periodically.
+
+ @param Event The pointer to EFI_PXE_BC_PROTOCOL.
+ @param Context Context of the timer event.
+
+**/
+VOID
+EFIAPI
+PxeBcArpCacheUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_ARP_FIND_DATA *ArpEntry;
+ UINT32 EntryLength;
+ UINT32 EntryCount;
+ UINT32 Index;
+ EFI_STATUS Status;
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = Private->PxeBc.Mode;
+
+ ASSERT (!Mode->UsingIpv6);
+
+ //
+ // Get the current Arp cache from Arp driver.
+ //
+ Status = Private->Arp->Find (
+ Private->Arp,
+ TRUE,
+ NULL,
+ &EntryLength,
+ &EntryCount,
+ &ArpEntry,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ //
+ // Update the Arp cache in mode data.
+ //
+ Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES);
+
+ for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {
+ CopyMem (
+ &Mode->ArpCache[Index].IpAddr,
+ ArpEntry + 1,
+ ArpEntry->SwAddressLength
+ );
+ CopyMem (
+ &Mode->ArpCache[Index].MacAddr,
+ (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength,
+ ArpEntry->HwAddressLength
+ );
+ ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength);
+ }
+}
+
+
+/**
+ Notify function to handle the received ICMP message in DPC.
+
+ @param Context The PXEBC private data.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorDpcHandle (
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_RECEIVE_DATA *RxData;
+ EFI_IP4_PROTOCOL *Ip4;
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ UINT8 Type;
+ UINTN Index;
+ UINT32 CopiedLen;
+ UINT8 *IcmpError;
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = &Private->Mode;
+ Status = Private->IcmpToken.Status;
+ RxData = Private->IcmpToken.Packet.RxData;
+ Ip4 = Private->Ip4;
+
+ ASSERT (!Mode->UsingIpv6);
+
+ if (Status == EFI_ABORTED) {
+ //
+ // It's triggered by user cancellation.
+ //
+ return;
+ }
+
+ if (RxData == NULL) {
+ goto ON_EXIT;
+ }
+
+ if (Status != EFI_ICMP_ERROR) {
+ //
+ // The return status should be recognized as EFI_ICMP_ERROR.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&
+ !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {
+ //
+ // The source address of the received packet should be a valid unicast address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {
+ //
+ // The destination address of the received packet should be equal to the host address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) {
+ //
+ // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);
+
+ if (Type != ICMP_DEST_UNREACHABLE &&
+ Type != ICMP_SOURCE_QUENCH &&
+ Type != ICMP_REDIRECT &&
+ Type != ICMP_TIME_EXCEEDED &&
+ Type != ICMP_PARAMETER_PROBLEM) {
+ //
+ // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ //
+ // Copy the right ICMP error message into mode data.
+ //
+ CopiedLen = 0;
+ IcmpError = (UINT8 *) &Mode->IcmpError;
+
+ for (Index = 0; Index < RxData->FragmentCount; Index++) {
+ CopiedLen += RxData->FragmentTable[Index].FragmentLength;
+ if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {
+ CopyMem (
+ IcmpError,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ RxData->FragmentTable[Index].FragmentLength
+ );
+ } else {
+ CopyMem (
+ IcmpError,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+ );
+ }
+ IcmpError += CopiedLen;
+ }
+
+ON_EXIT:
+ Private->IcmpToken.Status = EFI_NOT_READY;
+ Ip4->Receive (Ip4, &Private->IcmpToken);
+}
+
+
+/**
+ Callback function to update the latest ICMP6 error message.
+
+ @param Event The event signalled.
+ @param Context The context passed in using the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context);
+}
+
+
+/**
+ Notify function to handle the received ICMP6 message in DPC.
+
+ @param Context The PXEBC private data.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorDpcHandle (
+ IN VOID *Context
+ )
+{
+ PXEBC_PRIVATE_DATA *Private;
+ EFI_IP6_RECEIVE_DATA *RxData;
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT8 Type;
+ UINT32 CopiedLen;
+ UINT8 *Icmp6Error;
+
+ Private = (PXEBC_PRIVATE_DATA *) Context;
+ Mode = &Private->Mode;
+ Status = Private->Icmp6Token.Status;
+ RxData = Private->Icmp6Token.Packet.RxData;
+ Ip6 = Private->Ip6;
+
+ ASSERT (Mode->UsingIpv6);
+
+ if (Status == EFI_ABORTED) {
+ //
+ // It's triggered by user cancellation.
+ //
+ return;
+ }
+
+ if (RxData == NULL) {
+ goto ON_EXIT;
+ }
+
+ if (Status != EFI_ICMP_ERROR) {
+ //
+ // The return status should be recognized as EFI_ICMP_ERROR.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) {
+ //
+ // The source address of the received packet should be a valid unicast address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) &&
+ !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) {
+ //
+ // The destination address of the received packet should be equal to the host address.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ if (RxData->Header->NextHeader != IP6_ICMP) {
+ //
+ // The nextheader in the header of the receveid packet should be IP6_ICMP.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);
+
+ if (Type != ICMP_V6_DEST_UNREACHABLE &&
+ Type != ICMP_V6_PACKET_TOO_BIG &&
+ Type != ICMP_V6_PACKET_TOO_BIG &&
+ Type != ICMP_V6_PARAMETER_PROBLEM) {
+ //
+ // The type of the receveid packet should be an ICMP6 error message.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ goto ON_EXIT;
+ }
+
+ //
+ // Copy the right ICMP6 error message into mode data.
+ //
+ CopiedLen = 0;
+ Icmp6Error = (UINT8 *) &Mode->IcmpError;
+
+ for (Index = 0; Index < RxData->FragmentCount; Index++) {
+ CopiedLen += RxData->FragmentTable[Index].FragmentLength;
+ if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {
+ CopyMem (
+ Icmp6Error,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ RxData->FragmentTable[Index].FragmentLength
+ );
+ } else {
+ CopyMem (
+ Icmp6Error,
+ RxData->FragmentTable[Index].FragmentBuffer,
+ CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+ );
+ }
+ Icmp6Error += CopiedLen;
+ }
+
+ON_EXIT:
+ Private->Icmp6Token.Status = EFI_NOT_READY;
+ Ip6->Receive (Ip6, &Private->Icmp6Token);
+}
+
+
+/**
+ Callback function to update the latest ICMP6 error message.
+
+ @param Event The event signalled.
+ @param Context The context passed in using the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context);
+}
+
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.
+ @param[in] StationIp The pointer to the station address.
+ @param[in] SubnetMask The pointer to the subnet mask.
+ @param[in] Gateway The pointer to the gateway address.
+ @param[in, out] SrcPort The pointer to the source port.
+ @param[in] DoNotFragment If TRUE, fragment is not enabled.
+ Otherwise, fragment is enabled.
+
+ @retval EFI_SUCCESS Successfully configured this instance.
+ @retval Others Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp4Write (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_IPv4_ADDRESS *StationIp,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *Gateway,
+ IN OUT UINT16 *SrcPort,
+ IN BOOLEAN DoNotFragment
+ )
+{
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;
+ EFI_STATUS Status;
+
+ ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));
+
+ Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.TypeOfService = DEFAULT_ToS;
+ Udp4CfgData.TimeToLive = DEFAULT_TTL;
+ Udp4CfgData.AllowDuplicatePort = TRUE;
+ Udp4CfgData.DoNotFragment = DoNotFragment;
+
+ CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp));
+ CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask));
+
+ Udp4CfgData.StationPort = *SrcPort;
+
+ //
+ // Reset the UDPv4 instance.
+ //
+ Udp4->Configure (Udp4, NULL);
+
+ Status = Udp4->Configure (Udp4, &Udp4CfgData);
+ if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) {
+ //
+ // The basic configuration is OK, need to add the default route entry
+ //
+ Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway);
+ if (EFI_ERROR (Status)) {
+ Udp4->Configure (Udp4, NULL);
+ }
+ }
+
+ if (!EFI_ERROR (Status) && *SrcPort == 0) {
+ Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL);
+ *SrcPort = Udp4CfgData.StationPort;
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to configure a UDPv6 instance for UdpWrite.
+
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.
+ @param[in] StationIp The pointer to the station address.
+ @param[in, out] SrcPort The pointer to the source port.
+
+ @retval EFI_SUCCESS Successfully configured this instance.
+ @retval Others Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp6Write (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_IPv6_ADDRESS *StationIp,
+ IN OUT UINT16 *SrcPort
+ )
+{
+ EFI_UDP6_CONFIG_DATA CfgData;
+ EFI_STATUS Status;
+
+ ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA));
+
+ CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+ CfgData.HopLimit = PXEBC_DEFAULT_HOPLIMIT;
+ CfgData.AllowDuplicatePort = TRUE;
+ CfgData.StationPort = *SrcPort;
+
+ CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Reset the UDPv6 instance.
+ //
+ Udp6->Configure (Udp6, NULL);
+
+ Status = Udp6->Configure (Udp6, &CfgData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!EFI_ERROR (Status) && *SrcPort == 0) {
+ Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL);
+ *SrcPort = CfgData.StationPort;
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.
+ @param[in] Session The pointer to the UDP4 session data.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] Gateway The pointer to the gateway address.
+ @param[in] HeaderSize An optional field which may be set to the length of a header
+ at HeaderPtr to be prefixed to the data at BufferPtr.
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS Successfully send out data using Udp4Write.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Write (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_UDP4_SESSION_DATA *Session,
+ IN EFI_EVENT TimeoutEvent,
+ IN EFI_IPv4_ADDRESS *Gateway OPTIONAL,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN Token;
+ EFI_UDP4_TRANSMIT_DATA *TxData;
+ UINT32 TxLength;
+ UINT32 FragCount;
+ UINT32 DataLength;
+ BOOLEAN IsDone;
+ EFI_STATUS Status;
+
+ //
+ // Arrange one fragment buffer for data, and another fragment buffer for header if has.
+ //
+ FragCount = (HeaderSize != NULL) ? 2 : 1;
+ TxLength = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA);
+ TxData = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength);
+ if (TxData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TxData->FragmentCount = FragCount;
+ TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;
+ TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;
+ DataLength = (UINT32) *BufferSize;
+
+ if (HeaderSize != NULL) {
+ TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;
+ TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;
+ DataLength += (UINT32) *HeaderSize;
+ }
+
+ if (Gateway != NULL) {
+ TxData->GatewayAddress = Gateway;
+ }
+
+ TxData->UdpSessionData = Session;
+ TxData->DataLength = DataLength;
+ Token.Packet.TxData = TxData;
+ Token.Status = EFI_NOT_READY;
+ IsDone = FALSE;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Udp4->Transmit (Udp4, &Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!IsDone &&
+ Token.Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ Udp4->Poll (Udp4);
+ }
+
+ Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;
+
+ON_EXIT:
+ if (Token.Event != NULL) {
+ gBS->CloseEvent (Token.Event);
+ }
+ FreePool (TxData);
+
+ return Status;
+}
+
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.
+ @param[in] Session The pointer to the UDP6 session data.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] HeaderSize An optional field which may be set to the length of a header
+ at HeaderPtr to be prefixed to the data at BufferPtr.
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS Successfully sent out data using Udp6Write.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Write (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_UDP6_SESSION_DATA *Session,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ )
+{
+ EFI_UDP6_COMPLETION_TOKEN Token;
+ EFI_UDP6_TRANSMIT_DATA *TxData;
+ UINT32 TxLength;
+ UINT32 FragCount;
+ UINT32 DataLength;
+ BOOLEAN IsDone;
+ EFI_STATUS Status;
+
+ //
+ // Arrange one fragment buffer for data, and another fragment buffer for header if has.
+ //
+ FragCount = (HeaderSize != NULL) ? 2 : 1;
+ TxLength = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA);
+ TxData = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength);
+ if (TxData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TxData->FragmentCount = FragCount;
+ TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;
+ TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;
+ DataLength = (UINT32) *BufferSize;
+
+ if (HeaderSize != NULL) {
+ TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;
+ TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;
+ DataLength += (UINT32) *HeaderSize;
+ }
+
+ TxData->UdpSessionData = Session;
+ TxData->DataLength = DataLength;
+ Token.Packet.TxData = TxData;
+ Token.Status = EFI_NOT_READY;
+ IsDone = FALSE;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PxeBcCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Udp6->Transmit (Udp6, &Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!IsDone &&
+ Token.Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ Udp6->Poll (Udp6);
+ }
+
+ Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;
+
+ON_EXIT:
+ if (Token.Event != NULL) {
+ gBS->CloseEvent (Token.Event);
+ }
+ FreePool (TxData);
+
+ return Status;
+}
+
+
+/**
+ Check the received packet using the Ip filter.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the Ip filter successfully.
+ @retval FALSE Failed to pass the Ip filter.
+
+**/
+BOOLEAN
+PxeBcCheckByIpFilter (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN UINT16 OpFlags
+ )
+{
+ EFI_IP_ADDRESS DestinationIp;
+ UINTN Index;
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) {
+ return TRUE;
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) {
+ return TRUE;
+ }
+
+ //
+ // Convert the destination address in session data to host order.
+ //
+ if (Mode->UsingIpv6) {
+ CopyMem (
+ &DestinationIp,
+ &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ NTOHLLL (&DestinationIp.v6);
+ } else {
+ ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (
+ &DestinationIp,
+ &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ EFI_NTOHL (DestinationIp);
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 &&
+ (IP4_IS_MULTICAST (DestinationIp.Addr[0]) ||
+ IP6_IS_MULTICAST (&DestinationIp))) {
+ return TRUE;
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 &&
+ IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) {
+ ASSERT (!Mode->UsingIpv6);
+ return TRUE;
+ }
+
+ if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&
+ (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) ||
+ EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) {
+ //
+ // Matched if the dest address is equal to the station address.
+ //
+ return TRUE;
+ }
+
+ for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) {
+ ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);
+ if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) ||
+ EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) {
+ //
+ // Matched if the dest address is equal to any of address in the filter list.
+ //
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Filter the received packet using the destination Ip.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] DestIp The pointer to the destination Ip address.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestIp (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT EFI_IP_ADDRESS *DestIp,
+ IN UINT16 OpFlags
+ )
+{
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) {
+ //
+ // Copy the destination address from the received packet if accept any.
+ //
+ if (DestIp != NULL) {
+ if (Mode->UsingIpv6) {
+ CopyMem (
+ DestIp,
+ &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ } else {
+ ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (
+ DestIp,
+ &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ }
+ return TRUE;
+ } else if (DestIp != NULL &&
+ (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||
+ EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) {
+ //
+ // The destination address in the received packet is matched if present.
+ //
+ return TRUE;
+ } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||
+ EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) {
+ //
+ // The destination address in the received packet is equal to the host address.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Check the received packet using the destination port.
+
+ @param[in] PxeBcMode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] DestPort The pointer to the destination port.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestPort (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT UINT16 *DestPort,
+ IN UINT16 OpFlags
+ )
+{
+ UINT16 Port;
+
+ if (Mode->UsingIpv6) {
+ Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;
+ } else {
+ Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;
+ }
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) {
+ //
+ // Return the destination port in the received packet if accept any.
+ //
+ if (DestPort != NULL) {
+ *DestPort = Port;
+ }
+ return TRUE;
+ } else if (DestPort != NULL && *DestPort == Port) {
+ //
+ // The destination port in the received packet is matched if present.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Filter the received packet using the source Ip.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] SrcIp The pointer to the source Ip address.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcIp (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT EFI_IP_ADDRESS *SrcIp,
+ IN UINT16 OpFlags
+ )
+{
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) {
+ //
+ // Copy the source address from the received packet if accept any.
+ //
+ if (SrcIp != NULL) {
+ if (Mode->UsingIpv6) {
+ CopyMem (
+ SrcIp,
+ &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+ } else {
+ ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS));
+ CopyMem (
+ SrcIp,
+ &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ }
+
+ }
+ return TRUE;
+ } else if (SrcIp != NULL &&
+ (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) ||
+ EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) {
+ //
+ // The source address in the received packet is matched if present.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Filter the received packet using the source port.
+
+ @param[in] Mode The pointer to the mode data of PxeBc.
+ @param[in] Session The pointer to the current UDPv4 session.
+ @param[in, out] SrcPort The pointer to the source port.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Passed the IPv4 filter successfully.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcPort (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT UINT16 *SrcPort,
+ IN UINT16 OpFlags
+ )
+{
+ UINT16 Port;
+
+ if (Mode->UsingIpv6) {
+ Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;
+ } else {
+ Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;
+ }
+
+ if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) {
+ //
+ // Return the source port in the received packet if accept any.
+ //
+ if (SrcPort != NULL) {
+ *SrcPort = Port;
+ }
+ return TRUE;
+ } else if (SrcPort != NULL && *SrcPort == Port) {
+ //
+ // The source port in the received packet is matched if present.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ This function is to receive packet using Udp4Read.
+
+ @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL.
+ @param[in] Token The pointer to EFI_UDP4_COMPLETION_TOKEN.
+ @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in] IsDone The pointer to the IsDone flag.
+ @param[out] IsMatched The pointer to the IsMatched flag.
+ @param[in, out] DestIp The pointer to the destination address.
+ @param[in, out] DestPort The pointer to the destination port.
+ @param[in, out] SrcIp The pointer to the source address.
+ @param[in, out] SrcPort The pointer to the source port.
+
+ @retval EFI_SUCCESS Successfully read the data using Udp4.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Read (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token,
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINT16 OpFlags,
+ IN BOOLEAN *IsDone,
+ OUT BOOLEAN *IsMatched,
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL
+ )
+{
+ EFI_UDP4_RECEIVE_DATA *RxData;
+ EFI_UDP4_SESSION_DATA *Session;
+ EFI_STATUS Status;
+
+ Token->Status = EFI_NOT_READY;
+ *IsDone = FALSE;
+
+ Status = Udp4->Receive (Udp4, Token);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!(*IsDone) &&
+ Token->Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ //
+ // Poll the token utill reply/ICMPv6 error message received or timeout.
+ //
+ Udp4->Poll (Udp4);
+ if (Token->Status == EFI_ICMP_ERROR ||
+ Token->Status == EFI_NETWORK_UNREACHABLE ||
+ Token->Status == EFI_HOST_UNREACHABLE ||
+ Token->Status == EFI_PROTOCOL_UNREACHABLE ||
+ Token->Status == EFI_PORT_UNREACHABLE) {
+ break;
+ }
+ }
+
+ Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // check whether this packet matches the filters
+ //
+ RxData = Token->Packet.RxData;
+ Session = &RxData->UdpSession;
+
+ *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);
+ }
+
+ if (!(*IsMatched)) {
+ //
+ // Recycle the receiving buffer if not matched.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to receive packets using Udp6Read.
+
+ @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL.
+ @param[in] Token The pointer to EFI_UDP6_COMPLETION_TOKEN.
+ @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in] IsDone The pointer to the IsDone flag.
+ @param[out] IsMatched The pointer to the IsMatched flag.
+ @param[in, out] DestIp The pointer to the destination address.
+ @param[in, out] DestPort The pointer to the destination port.
+ @param[in, out] SrcIp The pointer to the source address.
+ @param[in, out] SrcPort The pointer to the source port.
+
+ @retval EFI_SUCCESS Successfully read data using Udp6.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Read (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token,
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINT16 OpFlags,
+ IN BOOLEAN *IsDone,
+ OUT BOOLEAN *IsMatched,
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL
+ )
+{
+ EFI_UDP6_RECEIVE_DATA *RxData;
+ EFI_UDP6_SESSION_DATA *Session;
+ EFI_STATUS Status;
+
+ Token->Status = EFI_NOT_READY;
+ *IsDone = FALSE;
+
+ Status = Udp6->Receive (Udp6, Token);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+ //
+ while (!(*IsDone) &&
+ Token->Status == EFI_NOT_READY &&
+ EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ //
+ // Poll the token utill reply/ICMPv6 error message received or timeout.
+ //
+ Udp6->Poll (Udp6);
+ if (Token->Status == EFI_ICMP_ERROR ||
+ Token->Status == EFI_NETWORK_UNREACHABLE ||
+ Token->Status == EFI_HOST_UNREACHABLE ||
+ Token->Status == EFI_PROTOCOL_UNREACHABLE ||
+ Token->Status == EFI_PORT_UNREACHABLE) {
+ break;
+ }
+ }
+
+ Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // check whether this packet matches the filters
+ //
+ RxData = Token->Packet.RxData;
+ Session = &RxData->UdpSession;
+
+ *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);
+ }
+
+ if (*IsMatched) {
+ *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);
+ }
+
+ if (!(*IsMatched)) {
+ //
+ // Recycle the receiving buffer if not matched.
+ //
+ gBS->SignalEvent (RxData->RecycleSignal);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ This function is to display the IPv4 address.
+
+ @param[in] Ip The pointer to the IPv4 address.
+
+**/
+VOID
+PxeBcShowIp4Addr (
+ IN EFI_IPv4_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < 4; Index++) {
+ AsciiPrint ("%d", Ip->Addr[Index]);
+ if (Index < 3) {
+ AsciiPrint (".");
+ }
+ }
+}
+
+
+/**
+ This function is to display the IPv6 address.
+
+ @param[in] Ip The pointer to the IPv6 address.
+
+**/
+VOID
+PxeBcShowIp6Addr (
+ IN EFI_IPv6_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < 16; Index++) {
+
+ if (Ip->Addr[Index] != 0) {
+ AsciiPrint ("%x", Ip->Addr[Index]);
+ }
+ Index++;
+ if (Index > 15) {
+ return;
+ }
+ if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {
+ AsciiPrint ("0");
+ }
+ AsciiPrint ("%x", Ip->Addr[Index]);
+ if (Index < 15) {
+ AsciiPrint (":");
+ }
+ }
+}
+
+
+/**
+ This function is to convert UINTN to ASCII string with the required formatting.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer The pointer to the buffer for ASCII string.
+ @param[in] Length The length of the required format.
+
+**/
+VOID
+PxeBcUintnToAscDecWithFormat (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN INTN Length
+ )
+{
+ UINTN Remainder;
+
+ while (Length > 0) {
+ Length--;
+ Remainder = Number % 10;
+ Number /= 10;
+ Buffer[Length] = (UINT8) ('0' + Remainder);
+ }
+}
+
+
+/**
+ This function is to convert a UINTN to a ASCII string, and return the
+ actual length of the buffer.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer The pointer to the buffer for ASCII string.
+
+ @return Length The actual length of the ASCII string.
+
+**/
+UINTN
+PxeBcUintnToAscDec (
+ IN UINTN Number,
+ IN UINT8 *Buffer
+ )
+{
+ UINTN Index;
+ UINTN Length;
+ CHAR8 TempStr[64];
+
+ Index = 63;
+ TempStr[Index] = 0;
+
+ do {
+ Index--;
+ TempStr[Index] = (CHAR8) ('0' + (Number % 10));
+ Number = (UINTN) (Number / 10);
+ } while (Number != 0);
+
+ AsciiStrCpy ((CHAR8 *) Buffer, &TempStr[Index]);
+
+ Length = AsciiStrLen ((CHAR8 *) Buffer);
+
+ return Length;
+}
+
+
+/**
+ This function is to convert unicode hex number to a UINT8.
+
+ @param[out] Digit The converted UINT8 for output.
+ @param[in] Char The unicode hex number to be converted.
+
+ @retval EFI_SUCCESS Successfully converted the unicode hex.
+ @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex.
+
+**/
+EFI_STATUS
+PxeBcUniHexToUint8 (
+ OUT UINT8 *Digit,
+ IN CHAR16 Char
+ )
+{
+ if ((Char >= L'0') && (Char <= L'9')) {
+ *Digit = (UINT8) (Char - L'0');
+ return EFI_SUCCESS;
+ }
+
+ if ((Char >= L'A') && (Char <= L'F')) {
+ *Digit = (UINT8) (Char - L'A' + 0x0A);
+ return EFI_SUCCESS;
+ }
+
+ if ((Char >= L'a') && (Char <= L'f')) {
+ *Digit = (UINT8) (Char - L'a' + 0x0A);
+ return EFI_SUCCESS;
+ }
+
+ return EFI_INVALID_PARAMETER;
+}
diff --git a/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
new file mode 100644
index 0000000000..0d782050f9
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
@@ -0,0 +1,491 @@
+/** @file
+ Support functions declaration for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_PXEBC_SUPPORT_H__
+#define __EFI_PXEBC_SUPPORT_H__
+
+
+#define ICMP_DEST_UNREACHABLE 3
+#define ICMP_SOURCE_QUENCH 4
+#define ICMP_REDIRECT 5
+#define ICMP_ECHO_REQUEST 8
+#define ICMP_TIME_EXCEEDED 11
+#define ICMP_PARAMETER_PROBLEM 12
+
+
+/**
+ This function obtain the system guid and serial number from the smbios table.
+
+ @param[out] SystemGuid The pointer of returned system guid.
+
+ @retval EFI_SUCCESS Successfully obtained the system guid.
+ @retval EFI_NOT_FOUND Did not find the SMBIOS table.
+
+**/
+EFI_STATUS
+PxeBcGetSystemGuid (
+ OUT EFI_GUID *SystemGuid
+ );
+
+
+/**
+ Flush the previous configration using the new station Ip address.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @param[in] StationIp Pointer to the station Ip address.
+ @param[in] SubnetMask Pointer to the subnet mask address for v4.
+
+ @retval EFI_SUCCESS Successfully flushed the previous config.
+ @retval Others Failed to flush using the new station Ip.
+
+**/
+EFI_STATUS
+PxeBcFlushStaionIp (
+ PXEBC_PRIVATE_DATA *Private,
+ EFI_IP_ADDRESS *StationIp,
+ EFI_IP_ADDRESS *SubnetMask OPTIONAL
+ );
+
+
+/**
+ Notify callback function when an event is triggered.
+
+ @param[in] Event The triggered event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+PxeBcCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+
+/**
+ Perform arp resolution from the arp cache in PxeBcMode.
+
+ @param Mode Pointer to EFI_PXE_BASE_CODE_MODE.
+ @param Ip4Addr The Ip4 address for resolution.
+ @param MacAddress The resoluted MAC address if the resolution is successful.
+ The value is undefined if resolution fails.
+
+ @retval TRUE Found a matched entry.
+ @retval FALSE Did not find a matched entry.
+
+**/
+BOOLEAN
+PxeBcCheckArpCache (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_IPv4_ADDRESS *Ip4Addr,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ );
+
+
+/**
+ Update arp cache periodically.
+
+ @param Event Pointer to EFI_PXE_BC_PROTOCOL.
+ @param Context Context of the timer event.
+
+**/
+VOID
+EFIAPI
+PxeBcArpCacheUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+
+/**
+ xxx
+
+ @param Event The event signaled.
+ @param Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+
+/**
+ xxx
+
+ @param Event The event signaled.
+ @param Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorUpdate (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.
+ @param[in] StationIp Pointer to the station address.
+ @param[in] SubnetMask Pointer to the subnet mask.
+ @param[in] Gateway Pointer to the gateway address.
+ @param[in, out] SrcPort Pointer to the source port.
+ @param[in] DoNotFragment The flag of DoNotFragment bit in the IPv4
+ packet.
+
+ @retval EFI_SUCCESS Successfully configured this instance.
+ @retval Others Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp4Write (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_IPv4_ADDRESS *StationIp,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *Gateway,
+ IN OUT UINT16 *SrcPort,
+ IN BOOLEAN DoNotFragment
+ );
+
+
+/**
+ This function is to configure a UDPv6 instance for UdpWrite.
+
+ @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL.
+ @param[in] StationIp Pointer to the station address.
+ @param[in, out] SrcPort Pointer to the source port.
+
+ @retval EFI_SUCCESS Successfuly configured this instance.
+ @retval Others Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp6Write (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_IPv6_ADDRESS *StationIp,
+ IN OUT UINT16 *SrcPort
+ );
+
+/**
+ This function is to configure a UDPv4 instance for UdpWrite.
+
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.
+ @param[in] Session Pointer to the UDP4 session data.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] Gateway Pointer to the gateway address.
+ @param[in] HeaderSize An optional field which may be set to the length of a header
+ at HeaderPtr to be prefixed to the data at BufferPtr.
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS Successfully sent out data with Udp4Write.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Write (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_UDP4_SESSION_DATA *Session,
+ IN EFI_EVENT TimeoutEvent,
+ IN EFI_IPv4_ADDRESS *Gateway OPTIONAL,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ );
+
+
+/**
+ This function is to configure a UDPv6 instance for UdpWrite.
+
+ @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL.
+ @param[in] Session Pointer to the UDP6 session data.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] HeaderSize An optional field which may be set to the length of a header
+ at HeaderPtr to be prefixed to the data at BufferPtr.
+ @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be
+ prefixed to the data at BufferPtr.
+ @param[in] BufferSize A pointer to the size of the data at BufferPtr.
+ @param[in] BufferPtr A pointer to the data to be written.
+
+ @retval EFI_SUCCESS Successfully to send out data with Udp6Write.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Write (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_UDP6_SESSION_DATA *Session,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINTN *HeaderSize OPTIONAL,
+ IN VOID *HeaderPtr OPTIONAL,
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ );
+
+
+/**
+ Check the received packet with the Ip filter.
+
+ @param[in] Mode Pointer to mode data of PxeBc.
+ @param[in] Session Pointer to the current UDPv4 session.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Succesfully passed the Ip filter.
+ @retval FALSE Failed to pass the Ip filter.
+
+**/
+BOOLEAN
+PxeBcCheckByIpFilter (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN UINT16 OpFlags
+ );
+
+
+/**
+ Filter the received packet with the destination Ip.
+
+ @param[in] Mode Pointer to mode data of PxeBc.
+ @param[in] Session Pointer to the current UDPv4 session.
+ @param[in, out] DestIp Pointer to the dest Ip address.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Succesfully passed the IPv4 filter.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestIp (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT EFI_IP_ADDRESS *DestIp,
+ IN UINT16 OpFlags
+ );
+
+
+/**
+ Check the received packet with the destination port.
+
+ @param[in] PxeBcMode Pointer to mode data of PxeBc.
+ @param[in] Session Pointer to the current UDPv4 session.
+ @param[in, out] DestPort Pointer to the destination port.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Succesfully passed the IPv4 filter.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestPort (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT UINT16 *DestPort,
+ IN UINT16 OpFlags
+ );
+
+
+/**
+ Filter the received packet with the source Ip.
+
+ @param[in] Mode Pointer to mode data of PxeBc.
+ @param[in] Session Pointer to the current UDPv4 session.
+ @param[in, out] SrcIp Pointer to the source Ip address.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Succesfully passed the IPv4 filter.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcIp (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT EFI_IP_ADDRESS *SrcIp,
+ IN UINT16 OpFlags
+ );
+
+
+/**
+ Filter the received packet with the source port.
+
+ @param[in] Mode Pointer to mode data of PxeBc.
+ @param[in] Session Pointer to the current UDPv4 session.
+ @param[in, out] SrcPort Pointer to the source port.
+ @param[in] OpFlags Operation flag for UdpRead/UdpWrite.
+
+ @retval TRUE Succesfully passed the IPv4 filter.
+ @retval FALSE Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcPort (
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN VOID *Session,
+ IN OUT UINT16 *SrcPort,
+ IN UINT16 OpFlags
+ );
+
+
+/**
+ This function is to receive packet with Udp4Read.
+
+ @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL.
+ @param[in] Token Pointer to EFI_UDP4_COMPLETION_TOKEN.
+ @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in] IsDone Pointer to IsDone flag.
+ @param[out] IsMatched Pointer to IsMatched flag.
+ @param[in, out] DestIp Pointer to destination address.
+ @param[in, out] DestPort Pointer to destination port.
+ @param[in, out] SrcIp Pointer to source address.
+ @param[in, out] SrcPort Pointer to source port.
+
+ @retval EFI_SUCCESS Successfully read data with Udp4.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Read (
+ IN EFI_UDP4_PROTOCOL *Udp4,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token,
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINT16 OpFlags,
+ IN BOOLEAN *IsDone,
+ OUT BOOLEAN *IsMatched,
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL
+ );
+
+
+/**
+ This function is to receive packet with Udp6Read.
+
+ @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL.
+ @param[in] Token Pointer to EFI_UDP6_COMPLETION_TOKEN.
+ @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE.
+ @param[in] TimeoutEvent The event for timeout.
+ @param[in] OpFlags The UDP operation flags.
+ @param[in] IsDone Pointer to IsDone flag.
+ @param[out] IsMatched Pointer to IsMatched flag.
+ @param[in, out] DestIp Pointer to destination address.
+ @param[in, out] DestPort Pointer to destination port.
+ @param[in, out] SrcIp Pointer to source address.
+ @param[in, out] SrcPort Pointer to source port.
+
+ @retval EFI_SUCCESS Successfully read data with Udp6.
+ @retval Others Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Read (
+ IN EFI_UDP6_PROTOCOL *Udp6,
+ IN EFI_UDP6_COMPLETION_TOKEN *Token,
+ IN EFI_PXE_BASE_CODE_MODE *Mode,
+ IN EFI_EVENT TimeoutEvent,
+ IN UINT16 OpFlags,
+ IN BOOLEAN *IsDone,
+ OUT BOOLEAN *IsMatched,
+ IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL,
+ IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL,
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL
+ );
+
+
+/**
+ This function is to display the IPv4 address.
+
+ @param[in] Ip Pointer to the IPv4 address.
+
+**/
+VOID
+PxeBcShowIp4Addr (
+ IN EFI_IPv4_ADDRESS *Ip
+ );
+
+
+/**
+ This function is to display the IPv6 address.
+
+ @param[in] Ip Pointer to the IPv6 address.
+
+**/
+VOID
+PxeBcShowIp6Addr (
+ IN EFI_IPv6_ADDRESS *Ip
+ );
+
+
+/**
+ This function is to convert UINTN to ASCII string with required format.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer Pointer to the buffer for ASCII string.
+ @param[in] Length Length of the required format.
+
+**/
+VOID
+PxeBcUintnToAscDecWithFormat (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN INTN Length
+ );
+
+
+/**
+ This function is to convert a UINTN to a ASCII string, and return the
+ actual length of the buffer.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer Pointer to the buffer for ASCII string.
+
+ @return Length The actual length of the ASCII string.
+
+**/
+UINTN
+PxeBcUintnToAscDec (
+ IN UINTN Number,
+ IN UINT8 *Buffer
+ );
+
+/**
+ This function is to convert unicode hex number to a UINT8.
+
+ @param[out] Digit The converted UINT8 for output.
+ @param[in] Char The unicode hex number to be converted.
+
+ @retval EFI_SUCCESS Successfully converted the unicode hex.
+ @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex.
+
+**/
+EFI_STATUS
+PxeBcUniHexToUint8 (
+ OUT UINT8 *Digit,
+ IN CHAR16 Char
+ );
+
+#endif
diff --git a/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf b/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
new file mode 100644
index 0000000000..1e690b882f
--- /dev/null
+++ b/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
@@ -0,0 +1,98 @@
+## @file
+# Component name for module PxeBc
+#
+# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = UefiPxeBcDxe
+ FILE_GUID = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = PxeBcDriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF
+#
+
+[Sources]
+ ComponentName.c
+ PxeBcDriver.c
+ PxeBcDriver.h
+ PxeBcImpl.c
+ PxeBcImpl.h
+ PxeBcBoot.c
+ PxeBcBoot.h
+ PxeBcDhcp6.c
+ PxeBcDhcp6.h
+ PxeBcDhcp4.c
+ PxeBcDhcp4.h
+ PxeBcMtftp.c
+ PxeBcMtftp.h
+ PxeBcSupport.c
+ PxeBcSupport.h
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ MemoryAllocationLib
+ DebugLib
+ NetLib
+ DpcLib
+ DevicePathLib
+ PcdLib
+
+
+[Guids]
+ gEfiSmbiosTableGuid
+
+
+[Protocols]
+ gEfiDevicePathProtocolGuid
+ gEfiNetworkInterfaceIdentifierProtocolGuid_31
+ gEfiArpServiceBindingProtocolGuid
+ gEfiArpProtocolGuid
+ gEfiIp4ServiceBindingProtocolGuid
+ gEfiIp4ProtocolGuid
+ gEfiIp6ServiceBindingProtocolGuid
+ gEfiIp6ProtocolGuid
+ gEfiIp6ConfigProtocolGuid
+ gEfiUdp4ServiceBindingProtocolGuid
+ gEfiUdp4ProtocolGuid
+ gEfiMtftp4ServiceBindingProtocolGuid
+ gEfiMtftp4ProtocolGuid
+ gEfiDhcp4ServiceBindingProtocolGuid
+ gEfiDhcp4ProtocolGuid
+ gEfiUdp6ServiceBindingProtocolGuid
+ gEfiUdp6ProtocolGuid
+ gEfiMtftp6ServiceBindingProtocolGuid
+ gEfiMtftp6ProtocolGuid
+ gEfiDhcp6ServiceBindingProtocolGuid
+ gEfiDhcp6ProtocolGuid
+ gEfiPxeBaseCodeCallbackProtocolGuid
+ gEfiPxeBaseCodeProtocolGuid
+ gEfiLoadFileProtocolGuid
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize ## CONSUMES