]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c
Import ArpDxe, Dhcp4Dxe, Ip4Dxe, Mtftp4Dxe, PxeBcDxe and PxeDhcp4Dxe.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Option.c
diff --git a/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c b/MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c
new file mode 100644 (file)
index 0000000..b16f469
--- /dev/null
@@ -0,0 +1,906 @@
+/** @file\r
+\r
+Copyright (c) 2006, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+Module Name:\r
+\r
+  Dhcp4Option.c\r
+\r
+Abstract:\r
+\r
+  Function to validate, parse, process the DHCP options\r
+\r
+\r
+**/\r
+\r
+#include "Dhcp4Impl.h"\r
+\r
+//\r
+// A list of the format of DHCP Options sorted by option tag\r
+// to validate a dhcp message. Refere the comments of the\r
+// DHCP_OPTION_FORMAT structure.\r
+//\r
+STATIC\r
+DHCP_OPTION_FORMAT\r
+DhcpOptionFormats [] = {\r
+  {DHCP_TAG_NETMASK,        DHCP_OPTION_IP,     1, 1  , TRUE},\r
+  {DHCP_TAG_TIME_OFFSET,    DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_ROUTER,         DHCP_OPTION_IP,     1, -1 , TRUE},\r
+  {DHCP_TAG_TIME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NAME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_DNS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_LOG_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_COOKIE_SERVER,  DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_LPR_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_IMPRESS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_RL_SERVER,      DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_HOSTNAME,       DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_BOOTFILE_LEN,   DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_DUMP,           DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_DOMAINNAME,     DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_SWAP_SERVER,    DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_ROOTPATH,       DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_EXTEND_PATH,    DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_IPFORWARD,      DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_NONLOCAL_SRR,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_POLICY_SRR,     DHCP_OPTION_IPPAIR, 1, -1 , FALSE},\r
+  {DHCP_TAG_EMTU,           DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_TTL,            DHCP_OPTION_INT8,   1, 1  , FALSE},\r
+  {DHCP_TAG_PATHMTU_AGE,    DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16,  1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_IFMTU,          DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_SUBNET_LOCAL,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_BROADCAST,      DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_DISCOVER_MASK,  DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_SUPPLY_MASK,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_STATIC_ROUTE,   DHCP_OPTION_IPPAIR, 1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_TRAILER,        DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+  {DHCP_TAG_ARPAGE,         DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_ETHER_ENCAP,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+\r
+  {DHCP_TAG_TCP_TTL,        DHCP_OPTION_INT8,   1, 1  , FALSE},\r
+  {DHCP_TAG_KEEP_INTERVAL,  DHCP_OPTION_INT32,  1, 1  , FALSE},\r
+  {DHCP_TAG_KEEP_GARBAGE,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},\r
+\r
+  {DHCP_TAG_NIS_DOMAIN,     DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_NIS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NTP_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_VENDOR,         DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_NBNS,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NBDD,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NBTYPE,         DHCP_OPTION_INT8,   1, 1  , FALSE},\r
+  {DHCP_TAG_NBSCOPE,        DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_XFONT,          DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_XDM,            DHCP_OPTION_IP,     1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_REQUEST_IP,     DHCP_OPTION_IP,     1, 1  , FALSE},\r
+  {DHCP_TAG_LEASE,          DHCP_OPTION_INT32,  1, 1  , TRUE},\r
+  {DHCP_TAG_OVERLOAD,       DHCP_OPTION_INT8,   1, 1  , TRUE},\r
+  {DHCP_TAG_TYPE,           DHCP_OPTION_INT8,   1, 1  , TRUE},\r
+  {DHCP_TAG_SERVER_ID,      DHCP_OPTION_IP,     1, 1  , TRUE},\r
+  {DHCP_TAG_PARA_LIST,      DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_MESSAGE,        DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_MAXMSG,         DHCP_OPTION_INT16,  1, 1  , FALSE},\r
+  {DHCP_TAG_T1,             DHCP_OPTION_INT32,  1, 1  , TRUE},\r
+  {DHCP_TAG_T2,             DHCP_OPTION_INT32,  1, 1  , TRUE},\r
+  {DHCP_TAG_VENDOR_CLASS,   DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_CLIENT_ID,      DHCP_OPTION_INT8,   2, -1 , FALSE},\r
+\r
+  {DHCP_TAG_NISPLUS,        DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_NISPLUS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_TFTP,           DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+  {DHCP_TAG_BOOTFILE,       DHCP_OPTION_INT8,   1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_MOBILEIP,       DHCP_OPTION_IP,     0, -1 , FALSE},\r
+  {DHCP_TAG_SMTP,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_POP3,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_NNTP,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_WWW,            DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_FINGER,         DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_IRC,            DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_STTALK,         DHCP_OPTION_IP,     1, -1 , FALSE},\r
+  {DHCP_TAG_STDA,           DHCP_OPTION_IP,     1, -1 , FALSE},\r
+\r
+  {DHCP_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8,   5, -1 , FALSE},\r
+};\r
+\r
+\r
+/**\r
+  Binary search the DhcpOptionFormats array to find the format\r
+  information about a specific option.\r
+\r
+  @param  Tag                    The option's tag.\r
+\r
+  @return The point to the option's format, NULL if not found.\r
+\r
+**/\r
+STATIC\r
+DHCP_OPTION_FORMAT *\r
+DhcpFindOptionFormat (\r
+  IN UINT8                  Tag\r
+  )\r
+{\r
+  INTN                      Left;\r
+  INTN                      Right;\r
+  INTN                      Middle;\r
+\r
+  Left  = 0;\r
+  Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;\r
+\r
+  while (Right >= Left) {\r
+    Middle = (Left + Right) / 2;\r
+\r
+    if (Tag == DhcpOptionFormats[Middle].Tag) {\r
+      return &DhcpOptionFormats[Middle];\r
+    }\r
+\r
+    if (Tag < DhcpOptionFormats[Middle].Tag) {\r
+      Right = Middle - 1;\r
+    } else {\r
+      Left = Middle + 1;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Validate whether a single DHCP option is valid according to its format.\r
+\r
+  @param  Format                 The option's format\r
+  @param  OptValue               The value of the option\r
+  @param  Len                    The length of the option value\r
+\r
+  @return TRUE is the option is valid, otherwise FALSE.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+DhcpOptionIsValid (\r
+  IN DHCP_OPTION_FORMAT     *Format,\r
+  IN UINT8                  *OptValue,\r
+  IN INTN                   Len\r
+  )\r
+{\r
+  INTN                      Unit;\r
+  INTN                      Occur;\r
+  INTN                      Index;\r
+\r
+  Unit = 0;\r
+\r
+  switch (Format->Type) {\r
+  case DHCP_OPTION_SWITCH:\r
+  case DHCP_OPTION_INT8:\r
+    Unit = 1;\r
+    break;\r
+\r
+  case DHCP_OPTION_INT16:\r
+    Unit = 2;\r
+    break;\r
+\r
+  case DHCP_OPTION_INT32:\r
+  case DHCP_OPTION_IP:\r
+    Unit = 4;\r
+    break;\r
+\r
+  case DHCP_OPTION_IPPAIR:\r
+    Unit = 8;\r
+    break;\r
+  }\r
+\r
+  ASSERT (Unit != 0);\r
+\r
+  //\r
+  // Validate that the option appears in the full units.\r
+  //\r
+  if ((Len % Unit) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Validate the occurance of the option unit is with in [MinOccur, MaxOccur]\r
+  //\r
+  Occur = Len / Unit;\r
+\r
+  if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||\r
+      ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // If the option is of type switch, only 0/1 are valid values.\r
+  //\r
+  if (Format->Type == DHCP_OPTION_SWITCH) {\r
+    for (Index = 0; Index < Occur; Index++) {\r
+      if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {\r
+        return FALSE;\r
+      }\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Extract the client interested options, all the parameters are\r
+  converted to host byte order.\r
+\r
+  @param  Tag                    The DHCP option tag\r
+  @param  Len                    The length of the option\r
+  @param  Data                   The value of the DHCP option\r
+  @param  Para                   The variable to save the interested parameter\r
+\r
+  @retval EFI_SUCCESS            The DHCP option is successfully extracted.\r
+  @retval EFI_INVALID_PARAMETER  The DHCP option is mal-formated\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpGetParameter (\r
+  IN UINT8                  Tag,\r
+  IN INTN                   Len,\r
+  IN UINT8                  *Data,\r
+  IN DHCP_PARAMETER         *Para\r
+  )\r
+{\r
+  switch (Tag) {\r
+  case DHCP_TAG_NETMASK:\r
+    Para->NetMask = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_ROUTER:\r
+    //\r
+    // Return the first router to consumer which is the preferred one\r
+    //\r
+    Para->Router = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_LEASE:\r
+    Para->Lease = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_OVERLOAD:\r
+    Para->Overload = *Data;\r
+\r
+    if ((Para->Overload < 1) || (Para->Overload > 3)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    break;\r
+\r
+  case DHCP_TAG_TYPE:\r
+    Para->DhcpType = *Data;\r
+\r
+    if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    break;\r
+\r
+  case DHCP_TAG_SERVER_ID:\r
+    Para->ServerId = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_T1:\r
+    Para->T1 = NetGetUint32 (Data);\r
+    break;\r
+\r
+  case DHCP_TAG_T2:\r
+    Para->T2 = NetGetUint32 (Data);\r
+    break;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Inspect all the options in a single buffer. DHCP options may be contained\r
+  in several buffers, such as the BOOTP options filed, boot file or server\r
+  name. Each option buffer is required to end with DHCP_TAG_EOP.\r
+\r
+  @param  Buffer                 The buffer which contains DHCP options\r
+  @param  BufLen                 The length of the buffer\r
+  @param  Check                  The callback function for each option found\r
+  @param  Context                The opaque parameter for the Check\r
+  @param  Overload               variable to save the value of DHCP_TAG_OVERLOAD\r
+                                 option.\r
+\r
+  @retval EFI_SUCCESS            All the options are valid\r
+  @retval EFI_INVALID_PARAMETER  The options are mal-formated.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpIterateBufferOptions (\r
+  IN  UINT8                 *Buffer,\r
+  IN  INTN                  BufLen,\r
+  IN  DHCP_CHECK_OPTION     Check,            OPTIONAL\r
+  IN  VOID                  *Context,\r
+  OUT UINT8                 *Overload         OPTIONAL\r
+  )\r
+{\r
+  INTN                      Cur;\r
+  UINT8                     Tag;\r
+  UINT8                     Len;\r
+\r
+  Cur = 0;\r
+\r
+  while (Cur < BufLen) {\r
+    Tag = Buffer[Cur];\r
+\r
+    if (Tag == DHCP_TAG_PAD) {\r
+      Cur++;\r
+      continue;\r
+    } else if (Tag == DHCP_TAG_EOP) {\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    Cur++;\r
+\r
+    if (Cur == BufLen) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Len = Buffer[Cur++];\r
+\r
+    if (Cur + Len > BufLen) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if ((Tag == DHCP_TAG_OVERLOAD) && (Overload != NULL)) {\r
+      if (Len != 1) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      *Overload = Buffer[Cur];\r
+    }\r
+\r
+    if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Cur += Len;\r
+  }\r
+\r
+  //\r
+  // Each option buffer is expected to end with an EOP\r
+  //\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+\r
+/**\r
+  Iterate through a DHCP message to visit each option. First inspect\r
+  all the options in the OPTION field. Then if overloaded, inspect\r
+  the options in FILENAME and SERVERNAME fields. One option may be\r
+  encoded in several places. See RFC 3396 Encoding Long Options in DHCP\r
+\r
+  @param  Packet                 The DHCP packet to check the options for\r
+  @param  Check                  The callback function to be called for each option\r
+                                 found\r
+  @param  Context                The opaque parameter for Check\r
+\r
+  @retval EFI_SUCCESS            The DHCP packet's options are well formated\r
+  @retval Others                 The DHCP packet's options are not well formated\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpIterateOptions (\r
+  IN  EFI_DHCP4_PACKET      *Packet,\r
+  IN  DHCP_CHECK_OPTION     Check,        OPTIONAL\r
+  IN  VOID                  *Context\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  UINT8                     Overload;\r
+\r
+  Overload = 0;\r
+\r
+  Status   = DhcpIterateBufferOptions (\r
+               Packet->Dhcp4.Option,\r
+               Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),\r
+               Check,\r
+               Context,\r
+               &Overload\r
+               );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {\r
+    Status = DhcpIterateBufferOptions (\r
+               Packet->Dhcp4.Header.BootFileName,\r
+               128,\r
+               Check,\r
+               Context,\r
+               NULL\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {\r
+    Status = DhcpIterateBufferOptions (\r
+               Packet->Dhcp4.Header.ServerName,\r
+               64,\r
+               Check,\r
+               Context,\r
+               NULL\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Call back function to DhcpiterateOptions to compute each option's\r
+  length. It just adds the data length of all the occurances of this\r
+  Tag. Context is an array of 256 DHCP_OPTION_COUNT.\r
+\r
+  @param  Tag                    The current option to check\r
+  @param  Len                    The length of the option data\r
+  @param  Data                   The option data\r
+  @param  Context                The context, which is a array of 256\r
+                                 DHCP_OPTION_COUNT.\r
+\r
+  @retval EFI_SUCCESS            It always returns EFI_SUCCESS.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpGetOptionLen (\r
+  IN UINT8                  Tag,\r
+  IN UINT8                  Len,\r
+  IN UINT8                  *Data,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  DHCP_OPTION_COUNT         *OpCount;\r
+\r
+  OpCount             = (DHCP_OPTION_COUNT *) Context;\r
+  OpCount[Tag].Offset = OpCount[Tag].Offset + Len;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Call back function to DhcpiterateOptions to consolidate each option's\r
+  data. There are maybe several occurance of the same option.\r
+\r
+  @param  Tag                    The option to consolidate its data\r
+  @param  Len                    The length of option data\r
+  @param  Data                   The data of the option's current occurance\r
+  @param  Context                The context, which is DHCP_OPTION_CONTEXT. This\r
+                                 array is  just a wrap to pass THREE parameters.\r
+\r
+  @retval EFI_SUCCESS            It always returns EFI_SUCCESS\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DhcpFillOption (\r
+  IN UINT8                  Tag,\r
+  IN UINT8                  Len,\r
+  IN UINT8                  *Data,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  DHCP_OPTION_CONTEXT       *OptContext;\r
+  DHCP_OPTION_COUNT         *OptCount;\r
+  DHCP_OPTION               *Options;\r
+  UINT8                     *Buf;\r
+  UINT8                     Index;\r
+\r
+  OptContext  = (DHCP_OPTION_CONTEXT *) Context;\r
+\r
+  OptCount    = OptContext->OpCount;\r
+  Index       = OptCount[Tag].Index;\r
+  Options     = OptContext->Options;\r
+  Buf         = OptContext->Buf;\r
+\r
+  if (Options[Index].Data == NULL) {\r
+    Options[Index].Tag  = Tag;\r
+    Options[Index].Data = Buf + OptCount[Tag].Offset;\r
+  }\r
+\r
+  NetCopyMem (Buf + OptCount[Tag].Offset, Data, Len);\r
+\r
+  OptCount[Tag].Offset  = OptCount[Tag].Offset + Len;\r
+  Options[Index].Len    = Options[Index].Len + Len;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Parse the options of a DHCP packet. It supports RFC 3396: Encoding\r
+  Long Options in DHCP. That is, it will combine all the option value\r
+  of all the occurances of each option.\r
+  A little bit of implemenation:\r
+  It adopts the "Key indexed counting" algorithm. First, it allocates\r
+  an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded\r
+  as a UINT8. It then iterates the DHCP packet to get data length of\r
+  each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it\r
+  knows the number of present options and their length. It allocates a\r
+  array of DHCP_OPTION and a continous buffer after the array to put\r
+  all the options' data. Each option's data is pointed to by the Data\r
+  field in DHCP_OPTION structure. At last, it call DhcpIterateOptions\r
+  with DhcpFillOption to fill each option's data to its position in the\r
+  buffer.\r
+\r
+  @param  Packet                 The DHCP packet to parse the options\r
+  @param  Count                  The number of valid dhcp options present in the\r
+                                 packet\r
+  @param  OptionPoint            The array that contains the DHCP options. Caller\r
+                                 should free it.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to parse the packet.\r
+  @retval EFI_INVALID_PARAMETER  The options are mal-formated\r
+  @retval EFI_SUCCESS            The options are parsed into OptionPoint\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpParseOption (\r
+  IN  EFI_DHCP4_PACKET      *Packet,\r
+  OUT INTN                  *Count,\r
+  OUT DHCP_OPTION           **OptionPoint\r
+  )\r
+{\r
+  DHCP_OPTION_CONTEXT       Context;\r
+  DHCP_OPTION               *Options;\r
+  DHCP_OPTION_COUNT         *OptCount;\r
+  EFI_STATUS                Status;\r
+  UINT16                    TotalLen;\r
+  INTN                      OptNum;\r
+  INTN                      Index;\r
+\r
+  ASSERT ((Count != NULL) && (OptionPoint != NULL));\r
+\r
+  //\r
+  // First compute how many options and how long each option is\r
+  // with the "Key indexed counting" algorithms.\r
+  //\r
+  OptCount = NetAllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));\r
+\r
+  if (OptCount == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Before the loop, Offset is the length of the option. After loop,\r
+  // OptCount[Index].Offset specifies the offset into the continuous\r
+  // option value buffer to put the data.\r
+  //\r
+  TotalLen  = 0;\r
+  OptNum    = 0;\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    if (OptCount[Index].Offset != 0) {\r
+      OptCount[Index].Index   = (UINT8) OptNum;\r
+\r
+      TotalLen                = TotalLen + OptCount[Index].Offset;\r
+      OptCount[Index].Offset  = TotalLen - OptCount[Index].Offset;\r
+\r
+      OptNum++;\r
+    }\r
+  }\r
+\r
+  *Count        = OptNum;\r
+  *OptionPoint  = NULL;\r
+\r
+  if (OptNum == 0) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Allocate a buffer to hold the DHCP options, and after that, a\r
+  // continuous buffer to put all the options' data.\r
+  //\r
+  Options = NetAllocateZeroPool (OptNum * sizeof (DHCP_OPTION) + TotalLen);\r
+\r
+  if (Options == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Context.OpCount = OptCount;\r
+  Context.Options = Options;\r
+  Context.Buf     = (UINT8 *) (Options + OptNum);\r
+\r
+  Status          = DhcpIterateOptions (Packet, DhcpFillOption, &Context);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetFreePool (Options);\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  *OptionPoint = Options;\r
+\r
+ON_EXIT:\r
+  NetFreePool (OptCount);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Validate the packet's options. If necessary, allocate\r
+  and fill in the interested parameters.\r
+\r
+  @param  Packet                 The packet to validate the options\r
+  @param  Para                   The variable to save the DHCP parameters.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to validate the packet.\r
+  @retval EFI_INVALID_PARAMETER  The options are mal-formated\r
+  @retval EFI_SUCCESS            The options are parsed into OptionPoint\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpValidateOptions (\r
+  IN  EFI_DHCP4_PACKET      *Packet,\r
+  OUT DHCP_PARAMETER        **Para       OPTIONAL\r
+  )\r
+{\r
+  DHCP_PARAMETER            Parameter;\r
+  DHCP_OPTION_FORMAT        *Format;\r
+  DHCP_OPTION               *AllOption;\r
+  DHCP_OPTION               *Option;\r
+  EFI_STATUS                Status;\r
+  BOOLEAN                   Updated;\r
+  INTN                      Count;\r
+  INTN                      Index;\r
+\r
+  if (Para != NULL) {\r
+    *Para = NULL;\r
+  }\r
+\r
+  AllOption = NULL;\r
+  Status    = DhcpParseOption (Packet, &Count, &AllOption);\r
+\r
+  if (EFI_ERROR (Status) || (Count == 0)) {\r
+    return Status;\r
+  }\r
+\r
+  Updated = FALSE;\r
+  NetZeroMem (&Parameter, sizeof (Parameter));\r
+\r
+  for (Index = 0; Index < Count; Index++) {\r
+    Option = &AllOption[Index];\r
+\r
+    //\r
+    // Find the format of the option then validate it.\r
+    //\r
+    Format = DhcpFindOptionFormat (Option->Tag);\r
+\r
+    if (Format == NULL) {\r
+      continue;\r
+    }\r
+\r
+    if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    //\r
+    // Get the client interested parameters\r
+    //\r
+    if (Format->Alert && (Para != NULL)) {\r
+      Updated = TRUE;\r
+      Status  = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (Updated && (Para != NULL)) {\r
+    if ((*Para = NetAllocatePool (sizeof (DHCP_PARAMETER))) == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    CopyMem (*Para, &Parameter, sizeof (DHCP_PARAMETER));\r
+  }\r
+\r
+ON_EXIT:\r
+  NetFreePool (AllOption);\r
+  return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+  Append an option to the memory, if the option is longer than\r
+  255 bytes, splits it into several options.\r
+\r
+  @param  Buf                    The buffer to append the option to\r
+  @param  Tag                    The option's tag\r
+  @param  DataLen                The length of the option's data\r
+  @param  Data                   The option's data\r
+\r
+  @return The position to append the next option\r
+\r
+**/\r
+UINT8 *\r
+DhcpAppendOption (\r
+  IN UINT8                  *Buf,\r
+  IN UINT8                  Tag,\r
+  IN UINT16                 DataLen,\r
+  IN UINT8                  *Data\r
+  )\r
+{\r
+  INTN                      Index;\r
+  INTN                      Len;\r
+\r
+  ASSERT (DataLen != 0);\r
+\r
+  for (Index = 0; Index < (DataLen + 254) / 255; Index++) {\r
+    Len      = NET_MIN (255, DataLen - Index * 255);\r
+\r
+    *(Buf++) = Tag;\r
+    *(Buf++) = (UINT8) Len;\r
+    NetCopyMem (Buf, Data + Index * 255, Len);\r
+\r
+    Buf     += Len;\r
+  }\r
+\r
+  return Buf;\r
+}\r
+\r
+\r
+/**\r
+  Build a new DHCP packet from a seed packet. Options may be deleted or\r
+  appended. The caller should free the NewPacket when finished using it.\r
+\r
+  @param  SeedPacket             The seed packet to start with\r
+  @param  DeleteCount            The number of options to delete\r
+  @param  DeleteList             The options to delete from the packet\r
+  @param  AppendCount            The number of options to append\r
+  @param  AppendList             The options to append to the packet\r
+  @param  NewPacket              The new packet, allocated and built by this\r
+                                 function.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory\r
+  @retval EFI_SUCCESS            The packet is build.\r
+\r
+**/\r
+EFI_STATUS\r
+DhcpBuild (\r
+  IN  EFI_DHCP4_PACKET        *SeedPacket,\r
+  IN  UINT32                  DeleteCount,\r
+  IN  UINT8                   *DeleteList     OPTIONAL,\r
+  IN  UINT32                  AppendCount,\r
+  IN  EFI_DHCP4_PACKET_OPTION *AppendList[]   OPTIONAL,\r
+  OUT EFI_DHCP4_PACKET        **NewPacket\r
+  )\r
+{\r
+  DHCP_OPTION               *Mark;\r
+  DHCP_OPTION               *SeedOptions;\r
+  EFI_DHCP4_PACKET          *Packet;\r
+  EFI_STATUS                Status;\r
+  INTN                      Count;\r
+  UINT32                    Index;\r
+  UINT32                    Len;\r
+  UINT8                     *Buf;\r
+\r
+  //\r
+  // Use an array of DHCP_OPTION to mark the existance\r
+  // and position of each valid options.\r
+  //\r
+  Mark = NetAllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);\r
+\r
+  if (Mark == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    Mark[Index].Tag = (UINT8) Index;\r
+    Mark[Index].Len = 0;\r
+  }\r
+\r
+  //\r
+  // Get list of the options from the seed packet, then put\r
+  // them to the mark array according to their tags.\r
+  //\r
+  SeedOptions = NULL;\r
+  Status      = DhcpParseOption (SeedPacket, &Count, &SeedOptions);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  for (Index = 0; Index < (UINT32) Count; Index++) {\r
+    Mark[SeedOptions[Index].Tag] = SeedOptions[Index];\r
+  }\r
+\r
+  //\r
+  // Mark the option's length is zero if it is in the DeleteList.\r
+  //\r
+  for (Index = 0; Index < DeleteCount; Index++) {\r
+    Mark[DeleteList[Index]].Len = 0;\r
+  }\r
+\r
+  //\r
+  // Add or replace the option if it is in the append list.\r
+  //\r
+  for (Index = 0; Index < AppendCount; Index++) {\r
+    Mark[AppendList[Index]->OpCode].Len  = AppendList[Index]->Length;\r
+    Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;\r
+  }\r
+\r
+  //\r
+  // compute the new packet length. No need to add 1 byte for\r
+  // EOP option since EFI_DHCP4_PACKET includes one extra byte\r
+  // for option. It is necessary to split the option if it is\r
+  // longer than 255 bytes.\r
+  //\r
+  Len = sizeof (EFI_DHCP4_PACKET);\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    if (Mark[Index].Len != 0) {\r
+      Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;\r
+    }\r
+  }\r
+\r
+  Status  = EFI_OUT_OF_RESOURCES;\r
+  Packet  = (EFI_DHCP4_PACKET *) NetAllocatePool (Len);\r
+\r
+  if (Packet == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Packet->Size         = Len;\r
+  Packet->Length       = 0;\r
+  CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (EFI_DHCP4_HEADER));\r
+  Packet->Dhcp4.Magik  = DHCP_OPTION_MAGIC;\r
+  Buf                  = Packet->Dhcp4.Option;\r
+\r
+  for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {\r
+    if (Mark[Index].Len != 0) {\r
+      Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);\r
+    }\r
+  }\r
+\r
+  *(Buf++)        = DHCP_TAG_EOP;\r
+  Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)\r
+                      + (UINT32) (Buf - Packet->Dhcp4.Option);\r
+\r
+  *NewPacket      = Packet;\r
+  Status          = EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+  if (SeedOptions != NULL) {\r
+    NetFreePool (SeedOptions);\r
+  }\r
+\r
+  NetFreePool (Mark);\r
+  return Status;\r
+}\r