git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10880 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			897 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			897 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Function to validate, parse, process the DHCP options.
 | |
|   
 | |
| Copyright (c) 2006 - 2009, 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 "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.
 | |
| ///
 | |
| 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[in]  Tag                    The option's tag.
 | |
| 
 | |
|   @return The point to the option's format, NULL if not found.
 | |
| 
 | |
| **/
 | |
| 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[in]  Format                 The option's format
 | |
|   @param[in]  OptValue               The value of the option
 | |
|   @param[in]  Len                    The length of the option value
 | |
| 
 | |
|   @retval TRUE     The option is valid.
 | |
|   @retval FALSE    Otherwise.
 | |
| 
 | |
| **/
 | |
| 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[in]  Tag                    The DHCP option tag
 | |
|   @param[in]  Len                    The length of the option
 | |
|   @param[in]  Data                   The value of the DHCP option
 | |
|   @param[out] 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
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DhcpGetParameter (
 | |
|   IN  UINT8                  Tag,
 | |
|   IN  INTN                   Len,
 | |
|   IN  UINT8                  *Data,
 | |
|   OUT 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[in]  Buffer                 The buffer which contains DHCP options
 | |
|   @param[in]  BufLen                 The length of the buffer
 | |
|   @param[in]  Check                  The callback function for each option found
 | |
|   @param[in]  Context                The opaque parameter for the Check
 | |
|   @param[out] 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.
 | |
| 
 | |
| **/
 | |
| 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[in]  Packet                 The DHCP packet to check the options for
 | |
|   @param[in]  Check                  The callback function to be called for each option
 | |
|                                      found
 | |
|   @param[in]  Context                The opaque parameter for Check
 | |
| 
 | |
|   @retval EFI_SUCCESS            The DHCP packet's options are well formated
 | |
|   @retval EFI_INVALID_PARAMETER  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 (
 | |
|                (UINT8 *) 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 (
 | |
|                (UINT8 *) 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[in]  Tag                    The current option to check
 | |
|   @param[in]  Len                    The length of the option data
 | |
|   @param[in]  Data                   The option data
 | |
|   @param[in]  Context                The context, which is a array of 256
 | |
|                                      DHCP_OPTION_COUNT.
 | |
| 
 | |
|   @retval EFI_SUCCESS            It always returns EFI_SUCCESS.
 | |
| 
 | |
| **/
 | |
| 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 = (UINT16) (OpCount[Tag].Offset + Len);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Call back function to DhcpIterateOptions to consolidate each option's
 | |
|   data. There are maybe several occurrence of the same option.
 | |
| 
 | |
|   @param[in]  Tag                    The option to consolidate its data
 | |
|   @param[in]  Len                    The length of option data
 | |
|   @param[in]  Data                   The data of the option's current occurance
 | |
|   @param[in]  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
 | |
| 
 | |
| **/
 | |
| 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;
 | |
|   }
 | |
| 
 | |
|   CopyMem (Buf + OptCount[Tag].Offset, Data, Len);
 | |
| 
 | |
|   OptCount[Tag].Offset  = (UINT16) (OptCount[Tag].Offset + Len);
 | |
|   Options[Index].Len    = (UINT16) (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 continuous 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[in]  Packet                 The DHCP packet to parse the options
 | |
|   @param[out] Count                  The number of valid dhcp options present in the
 | |
|                                      packet
 | |
|   @param[out] OptionPoint            The array that contains the DHCP options. Caller
 | |
|                                      should free it.
 | |
| 
 | |
|   @retval EFI_NOT_FOUND          Cannot find any option.
 | |
|   @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 = AllocateZeroPool (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                = (UINT16) (TotalLen + OptCount[Index].Offset);
 | |
|       OptCount[Index].Offset  = (UINT16) (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 = AllocateZeroPool ((UINTN) (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)) {
 | |
|     FreePool (Options);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   *OptionPoint = Options;
 | |
| 
 | |
| ON_EXIT:
 | |
|   FreePool (OptCount);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Validate the packet's options. If necessary, allocate
 | |
|   and fill in the interested parameters.
 | |
| 
 | |
|   @param[in]  Packet                 The packet to validate the options
 | |
|   @param[out] 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;
 | |
|   }
 | |
|   ASSERT (AllOption != NULL);
 | |
| 
 | |
|   Updated = FALSE;
 | |
|   ZeroMem (&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)) {
 | |
|     *Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), &Parameter);
 | |
|     if (*Para == NULL) {
 | |
|       Status = EFI_OUT_OF_RESOURCES;
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   FreePool (AllOption);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Append an option to the memory, if the option is longer than
 | |
|   255 bytes, splits it into several options.
 | |
| 
 | |
|   @param[out] Buf                    The buffer to append the option to
 | |
|   @param[in]  Tag                    The option's tag
 | |
|   @param[in]  DataLen                The length of the option's data
 | |
|   @param[in]  Data                   The option's data
 | |
| 
 | |
|   @return The position to append the next option
 | |
| 
 | |
| **/
 | |
| UINT8 *
 | |
| DhcpAppendOption (
 | |
|   OUT 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      = MIN (255, DataLen - Index * 255);
 | |
| 
 | |
|     *(Buf++) = Tag;
 | |
|     *(Buf++) = (UINT8) Len;
 | |
|     CopyMem (Buf, Data + Index * 255, (UINTN) 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[in]  SeedPacket             The seed packet to start with
 | |
|   @param[in]  DeleteCount            The number of options to delete
 | |
|   @param[in]  DeleteList             The options to delete from the packet
 | |
|   @param[in]  AppendCount            The number of options to append
 | |
|   @param[in]  AppendList             The options to append to the packet
 | |
|   @param[out] NewPacket              The new packet, allocated and built by this
 | |
|                                      function.
 | |
| 
 | |
|   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory
 | |
|   @retval EFI_INVALID_PARAMETER  The options in SeekPacket are mal-formated
 | |
|   @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 = AllocatePool (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;
 | |
|   }
 | |
| 
 | |
|   if (SeedOptions != NULL) {
 | |
|     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 *) AllocatePool (Len);
 | |
| 
 | |
|   if (Packet == NULL) {
 | |
|     goto ON_ERROR;
 | |
|   }
 | |
| 
 | |
|   Packet->Size         = Len;
 | |
|   Packet->Length       = 0;
 | |
|   CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->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) {
 | |
|     FreePool (SeedOptions);
 | |
|   }
 | |
| 
 | |
|   FreePool (Mark);
 | |
|   return Status;
 | |
| }
 |