REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the NetworkPkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
		
			
				
	
	
		
			883 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			883 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Function to validate, parse, process the DHCP options.
 | |
| 
 | |
| Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #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[] = {
 | |
|   { DHCP4_TAG_NETMASK,         DHCP_OPTION_IP,     1, 1,  TRUE  },
 | |
|   { DHCP4_TAG_TIME_OFFSET,     DHCP_OPTION_INT32,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_ROUTER,          DHCP_OPTION_IP,     1, -1, TRUE  },
 | |
|   { DHCP4_TAG_TIME_SERVER,     DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_NAME_SERVER,     DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_DNS_SERVER,      DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_LOG_SERVER,      DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_COOKIE_SERVER,   DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_LPR_SERVER,      DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_IMPRESS_SERVER,  DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_RL_SERVER,       DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_HOSTNAME,        DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_BOOTFILE_LEN,    DHCP_OPTION_INT16,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_DUMP,            DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_DOMAINNAME,      DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_SWAP_SERVER,     DHCP_OPTION_IP,     1, 1,  FALSE },
 | |
|   { DHCP4_TAG_ROOTPATH,        DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_EXTEND_PATH,     DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_IPFORWARD,       DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
|   { DHCP4_TAG_NONLOCAL_SRR,    DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
|   { DHCP4_TAG_POLICY_SRR,      DHCP_OPTION_IPPAIR, 1, -1, FALSE },
 | |
|   { DHCP4_TAG_EMTU,            DHCP_OPTION_INT16,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_TTL,             DHCP_OPTION_INT8,   1, 1,  FALSE },
 | |
|   { DHCP4_TAG_PATHMTU_AGE,     DHCP_OPTION_INT32,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_PATHMTU_PLATEAU, DHCP_OPTION_INT16,  1, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_IFMTU,           DHCP_OPTION_INT16,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_SUBNET_LOCAL,    DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
|   { DHCP4_TAG_BROADCAST,       DHCP_OPTION_IP,     1, 1,  FALSE },
 | |
|   { DHCP4_TAG_DISCOVER_MASK,   DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
|   { DHCP4_TAG_SUPPLY_MASK,     DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
|   { DHCP4_TAG_DISCOVER_ROUTE,  DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
|   { DHCP4_TAG_ROUTER_SOLICIT,  DHCP_OPTION_IP,     1, 1,  FALSE },
 | |
|   { DHCP4_TAG_STATIC_ROUTE,    DHCP_OPTION_IPPAIR, 1, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_TRAILER,         DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
|   { DHCP4_TAG_ARPAGE,          DHCP_OPTION_INT32,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_ETHER_ENCAP,     DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_TCP_TTL,         DHCP_OPTION_INT8,   1, 1,  FALSE },
 | |
|   { DHCP4_TAG_KEEP_INTERVAL,   DHCP_OPTION_INT32,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_KEEP_GARBAGE,    DHCP_OPTION_SWITCH, 1, 1,  FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_NIS_DOMAIN,      DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_NIS_SERVER,      DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_NTP_SERVER,      DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_VENDOR,          DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_NBNS,            DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_NBDD,            DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_NBTYPE,          DHCP_OPTION_INT8,   1, 1,  FALSE },
 | |
|   { DHCP4_TAG_NBSCOPE,         DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_XFONT,           DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_XDM,             DHCP_OPTION_IP,     1, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_REQUEST_IP,      DHCP_OPTION_IP,     1, 1,  FALSE },
 | |
|   { DHCP4_TAG_LEASE,           DHCP_OPTION_INT32,  1, 1,  TRUE  },
 | |
|   { DHCP4_TAG_OVERLOAD,        DHCP_OPTION_INT8,   1, 1,  TRUE  },
 | |
|   { DHCP4_TAG_MSG_TYPE,        DHCP_OPTION_INT8,   1, 1,  TRUE  },
 | |
|   { DHCP4_TAG_SERVER_ID,       DHCP_OPTION_IP,     1, 1,  TRUE  },
 | |
|   { DHCP4_TAG_PARA_LIST,       DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_MESSAGE,         DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_MAXMSG,          DHCP_OPTION_INT16,  1, 1,  FALSE },
 | |
|   { DHCP4_TAG_T1,              DHCP_OPTION_INT32,  1, 1,  TRUE  },
 | |
|   { DHCP4_TAG_T2,              DHCP_OPTION_INT32,  1, 1,  TRUE  },
 | |
|   { DHCP4_TAG_VENDOR_CLASS_ID, DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_CLIENT_ID,       DHCP_OPTION_INT8,   2, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_NISPLUS,         DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_NISPLUS_SERVER,  DHCP_OPTION_IP,     1, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_TFTP,            DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
|   { DHCP4_TAG_BOOTFILE,        DHCP_OPTION_INT8,   1, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_TAG_MOBILEIP,        DHCP_OPTION_IP,     0, -1, FALSE },
 | |
|   { DHCP4_TAG_SMTP,            DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_POP3,            DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_NNTP,            DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_WWW,             DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_FINGER,          DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_IRC,             DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_STTALK,          DHCP_OPTION_IP,     1, -1, FALSE },
 | |
|   { DHCP4_TAG_STDA,            DHCP_OPTION_IP,     1, -1, FALSE },
 | |
| 
 | |
|   { DHCP4_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 occurrence 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-formatted
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DhcpGetParameter (
 | |
|   IN  UINT8           Tag,
 | |
|   IN  INTN            Len,
 | |
|   IN  UINT8           *Data,
 | |
|   OUT DHCP_PARAMETER  *Para
 | |
|   )
 | |
| {
 | |
|   switch (Tag) {
 | |
|     case DHCP4_TAG_NETMASK:
 | |
|       Para->NetMask = NetGetUint32 (Data);
 | |
|       break;
 | |
| 
 | |
|     case DHCP4_TAG_ROUTER:
 | |
|       //
 | |
|       // Return the first router to consumer which is the preferred one
 | |
|       //
 | |
|       Para->Router = NetGetUint32 (Data);
 | |
|       break;
 | |
| 
 | |
|     case DHCP4_TAG_LEASE:
 | |
|       Para->Lease = NetGetUint32 (Data);
 | |
|       break;
 | |
| 
 | |
|     case DHCP4_TAG_OVERLOAD:
 | |
|       Para->Overload = *Data;
 | |
| 
 | |
|       if ((Para->Overload < 1) || (Para->Overload > 3)) {
 | |
|         return EFI_INVALID_PARAMETER;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case DHCP4_TAG_MSG_TYPE:
 | |
|       Para->DhcpType = *Data;
 | |
| 
 | |
|       if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
 | |
|         return EFI_INVALID_PARAMETER;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case DHCP4_TAG_SERVER_ID:
 | |
|       Para->ServerId = NetGetUint32 (Data);
 | |
|       break;
 | |
| 
 | |
|     case DHCP4_TAG_T1:
 | |
|       Para->T1 = NetGetUint32 (Data);
 | |
|       break;
 | |
| 
 | |
|     case DHCP4_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 DHCP4_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 DHCP4_TAG_OVERLOAD
 | |
|                                      option.
 | |
| 
 | |
|   @retval EFI_SUCCESS            All the options are valid
 | |
|   @retval EFI_INVALID_PARAMETER  The options are mal-formatted.
 | |
| 
 | |
| **/
 | |
| 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 == DHCP4_TAG_PAD) {
 | |
|       Cur++;
 | |
|       continue;
 | |
|     } else if (Tag == DHCP4_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 == DHCP4_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 formatted
 | |
|   @retval EFI_INVALID_PARAMETER  The DHCP packet's options are not well formatted
 | |
| 
 | |
| **/
 | |
| 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 occurrences 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 occurrence
 | |
|   @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 occurrences of each option.
 | |
|   A little bit of implementation:
 | |
|   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-formatted
 | |
|   @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-formatted
 | |
|   @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-formatted
 | |
|   @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 existence
 | |
|   // 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++)       = DHCP4_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;
 | |
| }
 |