--- /dev/null
+/** @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