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