Home | History | Annotate | Download | only in Dhcp4Dxe
      1 /** @file
      2   Function to validate, parse, process the DHCP options.
      3 
      4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "Dhcp4Impl.h"
     16 
     17 ///
     18 /// A list of the format of DHCP Options sorted by option tag
     19 /// to validate a dhcp message. Refere the comments of the
     20 /// DHCP_OPTION_FORMAT structure.
     21 ///
     22 DHCP_OPTION_FORMAT DhcpOptionFormats[] = {
     23   {DHCP4_TAG_NETMASK,        DHCP_OPTION_IP,     1, 1  , TRUE},
     24   {DHCP4_TAG_TIME_OFFSET,    DHCP_OPTION_INT32,  1, 1  , FALSE},
     25   {DHCP4_TAG_ROUTER,         DHCP_OPTION_IP,     1, -1 , TRUE},
     26   {DHCP4_TAG_TIME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},
     27   {DHCP4_TAG_NAME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},
     28   {DHCP4_TAG_DNS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
     29   {DHCP4_TAG_LOG_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
     30   {DHCP4_TAG_COOKIE_SERVER,  DHCP_OPTION_IP,     1, -1 , FALSE},
     31   {DHCP4_TAG_LPR_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
     32   {DHCP4_TAG_IMPRESS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},
     33   {DHCP4_TAG_RL_SERVER,      DHCP_OPTION_IP,     1, -1 , FALSE},
     34   {DHCP4_TAG_HOSTNAME,       DHCP_OPTION_INT8,   1, -1 , FALSE},
     35   {DHCP4_TAG_BOOTFILE_LEN,   DHCP_OPTION_INT16,  1, 1  , FALSE},
     36   {DHCP4_TAG_DUMP,           DHCP_OPTION_INT8,   1, -1 , FALSE},
     37   {DHCP4_TAG_DOMAINNAME,     DHCP_OPTION_INT8,   1, -1 , FALSE},
     38   {DHCP4_TAG_SWAP_SERVER,    DHCP_OPTION_IP,     1, 1  , FALSE},
     39   {DHCP4_TAG_ROOTPATH,       DHCP_OPTION_INT8,   1, -1 , FALSE},
     40   {DHCP4_TAG_EXTEND_PATH,    DHCP_OPTION_INT8,   1, -1 , FALSE},
     41 
     42   {DHCP4_TAG_IPFORWARD,      DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     43   {DHCP4_TAG_NONLOCAL_SRR,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     44   {DHCP4_TAG_POLICY_SRR,     DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
     45   {DHCP4_TAG_EMTU,           DHCP_OPTION_INT16,  1, 1  , FALSE},
     46   {DHCP4_TAG_TTL,            DHCP_OPTION_INT8,   1, 1  , FALSE},
     47   {DHCP4_TAG_PATHMTU_AGE,    DHCP_OPTION_INT32,  1, 1  , FALSE},
     48   {DHCP4_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16,  1, -1 , FALSE},
     49 
     50   {DHCP4_TAG_IFMTU,          DHCP_OPTION_INT16,  1, 1  , FALSE},
     51   {DHCP4_TAG_SUBNET_LOCAL,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     52   {DHCP4_TAG_BROADCAST,      DHCP_OPTION_IP,     1, 1  , FALSE},
     53   {DHCP4_TAG_DISCOVER_MASK,  DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     54   {DHCP4_TAG_SUPPLY_MASK,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     55   {DHCP4_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     56   {DHCP4_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP,     1, 1  , FALSE},
     57   {DHCP4_TAG_STATIC_ROUTE,   DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
     58 
     59   {DHCP4_TAG_TRAILER,        DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     60   {DHCP4_TAG_ARPAGE,         DHCP_OPTION_INT32,  1, 1  , FALSE},
     61   {DHCP4_TAG_ETHER_ENCAP,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     62 
     63   {DHCP4_TAG_TCP_TTL,        DHCP_OPTION_INT8,   1, 1  , FALSE},
     64   {DHCP4_TAG_KEEP_INTERVAL,  DHCP_OPTION_INT32,  1, 1  , FALSE},
     65   {DHCP4_TAG_KEEP_GARBAGE,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},
     66 
     67   {DHCP4_TAG_NIS_DOMAIN,     DHCP_OPTION_INT8,   1, -1 , FALSE},
     68   {DHCP4_TAG_NIS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
     69   {DHCP4_TAG_NTP_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
     70   {DHCP4_TAG_VENDOR,         DHCP_OPTION_INT8,   1, -1 , FALSE},
     71   {DHCP4_TAG_NBNS,           DHCP_OPTION_IP,     1, -1 , FALSE},
     72   {DHCP4_TAG_NBDD,           DHCP_OPTION_IP,     1, -1 , FALSE},
     73   {DHCP4_TAG_NBTYPE,         DHCP_OPTION_INT8,   1, 1  , FALSE},
     74   {DHCP4_TAG_NBSCOPE,        DHCP_OPTION_INT8,   1, -1 , FALSE},
     75   {DHCP4_TAG_XFONT,          DHCP_OPTION_IP,     1, -1 , FALSE},
     76   {DHCP4_TAG_XDM,            DHCP_OPTION_IP,     1, -1 , FALSE},
     77 
     78   {DHCP4_TAG_REQUEST_IP,     DHCP_OPTION_IP,     1, 1  , FALSE},
     79   {DHCP4_TAG_LEASE,          DHCP_OPTION_INT32,  1, 1  , TRUE},
     80   {DHCP4_TAG_OVERLOAD,       DHCP_OPTION_INT8,   1, 1  , TRUE},
     81   {DHCP4_TAG_MSG_TYPE,       DHCP_OPTION_INT8,   1, 1  , TRUE},
     82   {DHCP4_TAG_SERVER_ID,      DHCP_OPTION_IP,     1, 1  , TRUE},
     83   {DHCP4_TAG_PARA_LIST,      DHCP_OPTION_INT8,   1, -1 , FALSE},
     84   {DHCP4_TAG_MESSAGE,        DHCP_OPTION_INT8,   1, -1 , FALSE},
     85   {DHCP4_TAG_MAXMSG,         DHCP_OPTION_INT16,  1, 1  , FALSE},
     86   {DHCP4_TAG_T1,             DHCP_OPTION_INT32,  1, 1  , TRUE},
     87   {DHCP4_TAG_T2,             DHCP_OPTION_INT32,  1, 1  , TRUE},
     88   {DHCP4_TAG_VENDOR_CLASS_ID,DHCP_OPTION_INT8,   1, -1 , FALSE},
     89   {DHCP4_TAG_CLIENT_ID,      DHCP_OPTION_INT8,   2, -1 , FALSE},
     90 
     91   {DHCP4_TAG_NISPLUS,        DHCP_OPTION_INT8,   1, -1 , FALSE},
     92   {DHCP4_TAG_NISPLUS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},
     93 
     94   {DHCP4_TAG_TFTP,           DHCP_OPTION_INT8,   1, -1 , FALSE},
     95   {DHCP4_TAG_BOOTFILE,       DHCP_OPTION_INT8,   1, -1 , FALSE},
     96 
     97   {DHCP4_TAG_MOBILEIP,       DHCP_OPTION_IP,     0, -1 , FALSE},
     98   {DHCP4_TAG_SMTP,           DHCP_OPTION_IP,     1, -1 , FALSE},
     99   {DHCP4_TAG_POP3,           DHCP_OPTION_IP,     1, -1 , FALSE},
    100   {DHCP4_TAG_NNTP,           DHCP_OPTION_IP,     1, -1 , FALSE},
    101   {DHCP4_TAG_WWW,            DHCP_OPTION_IP,     1, -1 , FALSE},
    102   {DHCP4_TAG_FINGER,         DHCP_OPTION_IP,     1, -1 , FALSE},
    103   {DHCP4_TAG_IRC,            DHCP_OPTION_IP,     1, -1 , FALSE},
    104   {DHCP4_TAG_STTALK,         DHCP_OPTION_IP,     1, -1 , FALSE},
    105   {DHCP4_TAG_STDA,           DHCP_OPTION_IP,     1, -1 , FALSE},
    106 
    107   {DHCP4_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8,   5, -1 , FALSE},
    108 };
    109 
    110 
    111 /**
    112   Binary search the DhcpOptionFormats array to find the format
    113   information about a specific option.
    114 
    115   @param[in]  Tag                    The option's tag.
    116 
    117   @return The point to the option's format, NULL if not found.
    118 
    119 **/
    120 DHCP_OPTION_FORMAT *
    121 DhcpFindOptionFormat (
    122   IN UINT8                  Tag
    123   )
    124 {
    125   INTN                      Left;
    126   INTN                      Right;
    127   INTN                      Middle;
    128 
    129   Left  = 0;
    130   Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;
    131 
    132   while (Right >= Left) {
    133     Middle = (Left + Right) / 2;
    134 
    135     if (Tag == DhcpOptionFormats[Middle].Tag) {
    136       return &DhcpOptionFormats[Middle];
    137     }
    138 
    139     if (Tag < DhcpOptionFormats[Middle].Tag) {
    140       Right = Middle - 1;
    141     } else {
    142       Left  = Middle + 1;
    143     }
    144   }
    145 
    146   return NULL;
    147 }
    148 
    149 
    150 /**
    151   Validate whether a single DHCP option is valid according to its format.
    152 
    153   @param[in]  Format                 The option's format
    154   @param[in]  OptValue               The value of the option
    155   @param[in]  Len                    The length of the option value
    156 
    157   @retval TRUE     The option is valid.
    158   @retval FALSE    Otherwise.
    159 
    160 **/
    161 BOOLEAN
    162 DhcpOptionIsValid (
    163   IN DHCP_OPTION_FORMAT     *Format,
    164   IN UINT8                  *OptValue,
    165   IN INTN                   Len
    166   )
    167 {
    168   INTN                      Unit;
    169   INTN                      Occur;
    170   INTN                      Index;
    171 
    172   Unit = 0;
    173 
    174   switch (Format->Type) {
    175   case DHCP_OPTION_SWITCH:
    176   case DHCP_OPTION_INT8:
    177     Unit = 1;
    178     break;
    179 
    180   case DHCP_OPTION_INT16:
    181     Unit = 2;
    182     break;
    183 
    184   case DHCP_OPTION_INT32:
    185   case DHCP_OPTION_IP:
    186     Unit = 4;
    187     break;
    188 
    189   case DHCP_OPTION_IPPAIR:
    190     Unit = 8;
    191     break;
    192   }
    193 
    194   ASSERT (Unit != 0);
    195 
    196   //
    197   // Validate that the option appears in the full units.
    198   //
    199   if ((Len % Unit) != 0) {
    200     return FALSE;
    201   }
    202 
    203   //
    204   // Validate the occurance of the option unit is with in [MinOccur, MaxOccur]
    205   //
    206   Occur = Len / Unit;
    207 
    208   if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||
    209       ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))
    210       ) {
    211     return FALSE;
    212   }
    213 
    214   //
    215   // If the option is of type switch, only 0/1 are valid values.
    216   //
    217   if (Format->Type == DHCP_OPTION_SWITCH) {
    218     for (Index = 0; Index < Occur; Index++) {
    219       if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {
    220         return FALSE;
    221       }
    222     }
    223   }
    224 
    225   return TRUE;
    226 }
    227 
    228 
    229 /**
    230   Extract the client interested options, all the parameters are
    231   converted to host byte order.
    232 
    233   @param[in]  Tag                    The DHCP option tag
    234   @param[in]  Len                    The length of the option
    235   @param[in]  Data                   The value of the DHCP option
    236   @param[out] Para                   The variable to save the interested parameter
    237 
    238   @retval EFI_SUCCESS            The DHCP option is successfully extracted.
    239   @retval EFI_INVALID_PARAMETER  The DHCP option is mal-formated
    240 
    241 **/
    242 EFI_STATUS
    243 DhcpGetParameter (
    244   IN  UINT8                  Tag,
    245   IN  INTN                   Len,
    246   IN  UINT8                  *Data,
    247   OUT DHCP_PARAMETER         *Para
    248   )
    249 {
    250   switch (Tag) {
    251   case DHCP4_TAG_NETMASK:
    252     Para->NetMask = NetGetUint32 (Data);
    253     break;
    254 
    255   case DHCP4_TAG_ROUTER:
    256     //
    257     // Return the first router to consumer which is the preferred one
    258     //
    259     Para->Router = NetGetUint32 (Data);
    260     break;
    261 
    262   case DHCP4_TAG_LEASE:
    263     Para->Lease = NetGetUint32 (Data);
    264     break;
    265 
    266   case DHCP4_TAG_OVERLOAD:
    267     Para->Overload = *Data;
    268 
    269     if ((Para->Overload < 1) || (Para->Overload > 3)) {
    270       return EFI_INVALID_PARAMETER;
    271     }
    272     break;
    273 
    274   case DHCP4_TAG_MSG_TYPE:
    275     Para->DhcpType = *Data;
    276 
    277     if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
    278       return EFI_INVALID_PARAMETER;
    279     }
    280     break;
    281 
    282   case DHCP4_TAG_SERVER_ID:
    283     Para->ServerId = NetGetUint32 (Data);
    284     break;
    285 
    286   case DHCP4_TAG_T1:
    287     Para->T1 = NetGetUint32 (Data);
    288     break;
    289 
    290   case DHCP4_TAG_T2:
    291     Para->T2 = NetGetUint32 (Data);
    292     break;
    293   }
    294 
    295   return EFI_SUCCESS;
    296 }
    297 
    298 
    299 /**
    300   Inspect all the options in a single buffer. DHCP options may be contained
    301   in several buffers, such as the BOOTP options filed, boot file or server
    302   name. Each option buffer is required to end with DHCP4_TAG_EOP.
    303 
    304   @param[in]  Buffer                 The buffer which contains DHCP options
    305   @param[in]  BufLen                 The length of the buffer
    306   @param[in]  Check                  The callback function for each option found
    307   @param[in]  Context                The opaque parameter for the Check
    308   @param[out] Overload               Variable to save the value of DHCP4_TAG_OVERLOAD
    309                                      option.
    310 
    311   @retval EFI_SUCCESS            All the options are valid
    312   @retval EFI_INVALID_PARAMETER  The options are mal-formated.
    313 
    314 **/
    315 EFI_STATUS
    316 DhcpIterateBufferOptions (
    317   IN  UINT8                 *Buffer,
    318   IN  INTN                  BufLen,
    319   IN  DHCP_CHECK_OPTION     Check             OPTIONAL,
    320   IN  VOID                  *Context,
    321   OUT UINT8                 *Overload         OPTIONAL
    322   )
    323 {
    324   INTN                      Cur;
    325   UINT8                     Tag;
    326   UINT8                     Len;
    327 
    328   Cur = 0;
    329 
    330   while (Cur < BufLen) {
    331     Tag = Buffer[Cur];
    332 
    333     if (Tag == DHCP4_TAG_PAD) {
    334       Cur++;
    335       continue;
    336     } else if (Tag == DHCP4_TAG_EOP) {
    337       return EFI_SUCCESS;
    338     }
    339 
    340     Cur++;
    341 
    342     if (Cur == BufLen) {
    343       return EFI_INVALID_PARAMETER;
    344     }
    345 
    346     Len = Buffer[Cur++];
    347 
    348     if (Cur + Len > BufLen) {
    349       return EFI_INVALID_PARAMETER;
    350     }
    351 
    352     if ((Tag == DHCP4_TAG_OVERLOAD) && (Overload != NULL)) {
    353       if (Len != 1) {
    354         return EFI_INVALID_PARAMETER;
    355       }
    356 
    357       *Overload = Buffer[Cur];
    358     }
    359 
    360     if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {
    361       return EFI_INVALID_PARAMETER;
    362     }
    363 
    364     Cur += Len;
    365   }
    366 
    367   //
    368   // Each option buffer is expected to end with an EOP
    369   //
    370   return EFI_INVALID_PARAMETER;
    371 }
    372 
    373 
    374 /**
    375   Iterate through a DHCP message to visit each option. First inspect
    376   all the options in the OPTION field. Then if overloaded, inspect
    377   the options in FILENAME and SERVERNAME fields. One option may be
    378   encoded in several places. See RFC 3396 Encoding Long Options in DHCP
    379 
    380   @param[in]  Packet                 The DHCP packet to check the options for
    381   @param[in]  Check                  The callback function to be called for each option
    382                                      found
    383   @param[in]  Context                The opaque parameter for Check
    384 
    385   @retval EFI_SUCCESS            The DHCP packet's options are well formated
    386   @retval EFI_INVALID_PARAMETER  The DHCP packet's options are not well formated
    387 
    388 **/
    389 EFI_STATUS
    390 DhcpIterateOptions (
    391   IN  EFI_DHCP4_PACKET      *Packet,
    392   IN  DHCP_CHECK_OPTION     Check         OPTIONAL,
    393   IN  VOID                  *Context
    394   )
    395 {
    396   EFI_STATUS                Status;
    397   UINT8                     Overload;
    398 
    399   Overload = 0;
    400 
    401   Status   = DhcpIterateBufferOptions (
    402                Packet->Dhcp4.Option,
    403                Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),
    404                Check,
    405                Context,
    406                &Overload
    407                );
    408 
    409   if (EFI_ERROR (Status)) {
    410     return Status;
    411   }
    412 
    413   if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
    414     Status = DhcpIterateBufferOptions (
    415                (UINT8 *) Packet->Dhcp4.Header.BootFileName,
    416                128,
    417                Check,
    418                Context,
    419                NULL
    420                );
    421 
    422     if (EFI_ERROR (Status)) {
    423       return Status;
    424     }
    425   }
    426 
    427   if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
    428     Status = DhcpIterateBufferOptions (
    429                (UINT8 *) Packet->Dhcp4.Header.ServerName,
    430                64,
    431                Check,
    432                Context,
    433                NULL
    434                );
    435 
    436     if (EFI_ERROR (Status)) {
    437       return Status;
    438     }
    439   }
    440 
    441   return EFI_SUCCESS;
    442 }
    443 
    444 
    445 /**
    446   Call back function to DhcpIterateOptions to compute each option's
    447   length. It just adds the data length of all the occurances of this
    448   Tag. Context is an array of 256 DHCP_OPTION_COUNT.
    449 
    450   @param[in]  Tag                    The current option to check
    451   @param[in]  Len                    The length of the option data
    452   @param[in]  Data                   The option data
    453   @param[in]  Context                The context, which is a array of 256
    454                                      DHCP_OPTION_COUNT.
    455 
    456   @retval EFI_SUCCESS            It always returns EFI_SUCCESS.
    457 
    458 **/
    459 EFI_STATUS
    460 DhcpGetOptionLen (
    461   IN UINT8                  Tag,
    462   IN UINT8                  Len,
    463   IN UINT8                  *Data,
    464   IN VOID                   *Context
    465   )
    466 {
    467   DHCP_OPTION_COUNT         *OpCount;
    468 
    469   OpCount             = (DHCP_OPTION_COUNT *) Context;
    470   OpCount[Tag].Offset = (UINT16) (OpCount[Tag].Offset + Len);
    471 
    472   return EFI_SUCCESS;
    473 }
    474 
    475 
    476 /**
    477   Call back function to DhcpIterateOptions to consolidate each option's
    478   data. There are maybe several occurrence of the same option.
    479 
    480   @param[in]  Tag                    The option to consolidate its data
    481   @param[in]  Len                    The length of option data
    482   @param[in]  Data                   The data of the option's current occurance
    483   @param[in]  Context                The context, which is DHCP_OPTION_CONTEXT. This
    484                                      array is  just a wrap to pass THREE parameters.
    485 
    486   @retval EFI_SUCCESS            It always returns EFI_SUCCESS
    487 
    488 **/
    489 EFI_STATUS
    490 DhcpFillOption (
    491   IN UINT8                  Tag,
    492   IN UINT8                  Len,
    493   IN UINT8                  *Data,
    494   IN VOID                   *Context
    495   )
    496 {
    497   DHCP_OPTION_CONTEXT       *OptContext;
    498   DHCP_OPTION_COUNT         *OptCount;
    499   DHCP_OPTION               *Options;
    500   UINT8                     *Buf;
    501   UINT8                     Index;
    502 
    503   OptContext  = (DHCP_OPTION_CONTEXT *) Context;
    504 
    505   OptCount    = OptContext->OpCount;
    506   Index       = OptCount[Tag].Index;
    507   Options     = OptContext->Options;
    508   Buf         = OptContext->Buf;
    509 
    510   if (Options[Index].Data == NULL) {
    511     Options[Index].Tag  = Tag;
    512     Options[Index].Data = Buf + OptCount[Tag].Offset;
    513   }
    514 
    515   CopyMem (Buf + OptCount[Tag].Offset, Data, Len);
    516 
    517   OptCount[Tag].Offset  = (UINT16) (OptCount[Tag].Offset + Len);
    518   Options[Index].Len    = (UINT16) (Options[Index].Len + Len);
    519   return EFI_SUCCESS;
    520 }
    521 
    522 
    523 /**
    524   Parse the options of a DHCP packet. It supports RFC 3396: Encoding
    525   Long Options in DHCP. That is, it will combine all the option value
    526   of all the occurances of each option.
    527   A little bit of implemenation:
    528   It adopts the "Key indexed counting" algorithm. First, it allocates
    529   an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
    530   as a UINT8. It then iterates the DHCP packet to get data length of
    531   each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
    532   knows the number of present options and their length. It allocates a
    533   array of DHCP_OPTION and a continuous buffer after the array to put
    534   all the options' data. Each option's data is pointed to by the Data
    535   field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
    536   with DhcpFillOption to fill each option's data to its position in the
    537   buffer.
    538 
    539   @param[in]  Packet                 The DHCP packet to parse the options
    540   @param[out] Count                  The number of valid dhcp options present in the
    541                                      packet
    542   @param[out] OptionPoint            The array that contains the DHCP options. Caller
    543                                      should free it.
    544 
    545   @retval EFI_NOT_FOUND          Cannot find any option.
    546   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to parse the packet.
    547   @retval EFI_INVALID_PARAMETER  The options are mal-formated
    548   @retval EFI_SUCCESS            The options are parsed into OptionPoint
    549 
    550 **/
    551 EFI_STATUS
    552 DhcpParseOption (
    553   IN  EFI_DHCP4_PACKET      *Packet,
    554   OUT INTN                  *Count,
    555   OUT DHCP_OPTION           **OptionPoint
    556   )
    557 {
    558   DHCP_OPTION_CONTEXT       Context;
    559   DHCP_OPTION               *Options;
    560   DHCP_OPTION_COUNT         *OptCount;
    561   EFI_STATUS                Status;
    562   UINT16                    TotalLen;
    563   INTN                      OptNum;
    564   INTN                      Index;
    565 
    566   ASSERT ((Count != NULL) && (OptionPoint != NULL));
    567 
    568   //
    569   // First compute how many options and how long each option is
    570   // with the "Key indexed counting" algorithms.
    571   //
    572   OptCount = AllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));
    573 
    574   if (OptCount == NULL) {
    575     return EFI_OUT_OF_RESOURCES;
    576   }
    577 
    578   Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);
    579 
    580   if (EFI_ERROR (Status)) {
    581     goto ON_EXIT;
    582   }
    583 
    584   //
    585   // Before the loop, Offset is the length of the option. After loop,
    586   // OptCount[Index].Offset specifies the offset into the continuous
    587   // option value buffer to put the data.
    588   //
    589   TotalLen  = 0;
    590   OptNum    = 0;
    591 
    592   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
    593     if (OptCount[Index].Offset != 0) {
    594       OptCount[Index].Index   = (UINT8) OptNum;
    595 
    596       TotalLen                = (UINT16) (TotalLen + OptCount[Index].Offset);
    597       OptCount[Index].Offset  = (UINT16) (TotalLen - OptCount[Index].Offset);
    598 
    599       OptNum++;
    600     }
    601   }
    602 
    603   *Count        = OptNum;
    604   *OptionPoint  = NULL;
    605 
    606   if (OptNum == 0) {
    607     goto ON_EXIT;
    608   }
    609 
    610   //
    611   // Allocate a buffer to hold the DHCP options, and after that, a
    612   // continuous buffer to put all the options' data.
    613   //
    614   Options = AllocateZeroPool ((UINTN) (OptNum * sizeof (DHCP_OPTION)) + TotalLen);
    615 
    616   if (Options == NULL) {
    617     Status = EFI_OUT_OF_RESOURCES;
    618     goto ON_EXIT;
    619   }
    620 
    621   Context.OpCount = OptCount;
    622   Context.Options = Options;
    623   Context.Buf     = (UINT8 *) (Options + OptNum);
    624 
    625   Status          = DhcpIterateOptions (Packet, DhcpFillOption, &Context);
    626 
    627   if (EFI_ERROR (Status)) {
    628     FreePool (Options);
    629     goto ON_EXIT;
    630   }
    631 
    632   *OptionPoint = Options;
    633 
    634 ON_EXIT:
    635   FreePool (OptCount);
    636   return Status;
    637 }
    638 
    639 
    640 /**
    641   Validate the packet's options. If necessary, allocate
    642   and fill in the interested parameters.
    643 
    644   @param[in]  Packet                 The packet to validate the options
    645   @param[out] Para                   The variable to save the DHCP parameters.
    646 
    647   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to validate the packet.
    648   @retval EFI_INVALID_PARAMETER  The options are mal-formated
    649   @retval EFI_SUCCESS            The options are parsed into OptionPoint
    650 
    651 **/
    652 EFI_STATUS
    653 DhcpValidateOptions (
    654   IN  EFI_DHCP4_PACKET      *Packet,
    655   OUT DHCP_PARAMETER        **Para       OPTIONAL
    656   )
    657 {
    658   DHCP_PARAMETER            Parameter;
    659   DHCP_OPTION_FORMAT        *Format;
    660   DHCP_OPTION               *AllOption;
    661   DHCP_OPTION               *Option;
    662   EFI_STATUS                Status;
    663   BOOLEAN                   Updated;
    664   INTN                      Count;
    665   INTN                      Index;
    666 
    667   if (Para != NULL) {
    668     *Para = NULL;
    669   }
    670 
    671   AllOption = NULL;
    672 
    673   Status = DhcpParseOption (Packet, &Count, &AllOption);
    674   if (EFI_ERROR (Status) || (Count == 0)) {
    675     return Status;
    676   }
    677   ASSERT (AllOption != NULL);
    678 
    679   Updated = FALSE;
    680   ZeroMem (&Parameter, sizeof (Parameter));
    681 
    682   for (Index = 0; Index < Count; Index++) {
    683     Option = &AllOption[Index];
    684 
    685     //
    686     // Find the format of the option then validate it.
    687     //
    688     Format = DhcpFindOptionFormat (Option->Tag);
    689 
    690     if (Format == NULL) {
    691       continue;
    692     }
    693 
    694     if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {
    695       Status = EFI_INVALID_PARAMETER;
    696       goto ON_EXIT;
    697     }
    698 
    699     //
    700     // Get the client interested parameters
    701     //
    702     if (Format->Alert && (Para != NULL)) {
    703       Updated = TRUE;
    704       Status  = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);
    705 
    706       if (EFI_ERROR (Status)) {
    707         goto ON_EXIT;
    708       }
    709     }
    710   }
    711 
    712   if (Updated && (Para != NULL)) {
    713     *Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), &Parameter);
    714     if (*Para == NULL) {
    715       Status = EFI_OUT_OF_RESOURCES;
    716       goto ON_EXIT;
    717     }
    718   }
    719 
    720 ON_EXIT:
    721   FreePool (AllOption);
    722   return Status;
    723 }
    724 
    725 
    726 
    727 /**
    728   Append an option to the memory, if the option is longer than
    729   255 bytes, splits it into several options.
    730 
    731   @param[out] Buf                    The buffer to append the option to
    732   @param[in]  Tag                    The option's tag
    733   @param[in]  DataLen                The length of the option's data
    734   @param[in]  Data                   The option's data
    735 
    736   @return The position to append the next option
    737 
    738 **/
    739 UINT8 *
    740 DhcpAppendOption (
    741   OUT UINT8                  *Buf,
    742   IN  UINT8                  Tag,
    743   IN  UINT16                 DataLen,
    744   IN  UINT8                  *Data
    745   )
    746 {
    747   INTN                      Index;
    748   INTN                      Len;
    749 
    750   ASSERT (DataLen != 0);
    751 
    752   for (Index = 0; Index < (DataLen + 254) / 255; Index++) {
    753     Len      = MIN (255, DataLen - Index * 255);
    754 
    755     *(Buf++) = Tag;
    756     *(Buf++) = (UINT8) Len;
    757     CopyMem (Buf, Data + Index * 255, (UINTN) Len);
    758 
    759     Buf     += Len;
    760   }
    761 
    762   return Buf;
    763 }
    764 
    765 
    766 /**
    767   Build a new DHCP packet from a seed packet. Options may be deleted or
    768   appended. The caller should free the NewPacket when finished using it.
    769 
    770   @param[in]  SeedPacket             The seed packet to start with
    771   @param[in]  DeleteCount            The number of options to delete
    772   @param[in]  DeleteList             The options to delete from the packet
    773   @param[in]  AppendCount            The number of options to append
    774   @param[in]  AppendList             The options to append to the packet
    775   @param[out] NewPacket              The new packet, allocated and built by this
    776                                      function.
    777 
    778   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory
    779   @retval EFI_INVALID_PARAMETER  The options in SeekPacket are mal-formated
    780   @retval EFI_SUCCESS            The packet is build.
    781 
    782 **/
    783 EFI_STATUS
    784 DhcpBuild (
    785   IN  EFI_DHCP4_PACKET        *SeedPacket,
    786   IN  UINT32                  DeleteCount,
    787   IN  UINT8                   *DeleteList     OPTIONAL,
    788   IN  UINT32                  AppendCount,
    789   IN  EFI_DHCP4_PACKET_OPTION *AppendList[]   OPTIONAL,
    790   OUT EFI_DHCP4_PACKET        **NewPacket
    791   )
    792 {
    793   DHCP_OPTION               *Mark;
    794   DHCP_OPTION               *SeedOptions;
    795   EFI_DHCP4_PACKET          *Packet;
    796   EFI_STATUS                Status;
    797   INTN                      Count;
    798   UINT32                    Index;
    799   UINT32                    Len;
    800   UINT8                     *Buf;
    801 
    802   //
    803   // Use an array of DHCP_OPTION to mark the existance
    804   // and position of each valid options.
    805   //
    806   Mark = AllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);
    807 
    808   if (Mark == NULL) {
    809     return EFI_OUT_OF_RESOURCES;
    810   }
    811 
    812   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
    813     Mark[Index].Tag = (UINT8) Index;
    814     Mark[Index].Len = 0;
    815   }
    816 
    817   //
    818   // Get list of the options from the seed packet, then put
    819   // them to the mark array according to their tags.
    820   //
    821   SeedOptions = NULL;
    822   Status      = DhcpParseOption (SeedPacket, &Count, &SeedOptions);
    823 
    824   if (EFI_ERROR (Status)) {
    825     goto ON_ERROR;
    826   }
    827 
    828   if (SeedOptions != NULL) {
    829     for (Index = 0; Index < (UINT32) Count; Index++) {
    830       Mark[SeedOptions[Index].Tag] = SeedOptions[Index];
    831     }
    832   }
    833 
    834   //
    835   // Mark the option's length is zero if it is in the DeleteList.
    836   //
    837   for (Index = 0; Index < DeleteCount; Index++) {
    838     Mark[DeleteList[Index]].Len = 0;
    839   }
    840 
    841   //
    842   // Add or replace the option if it is in the append list.
    843   //
    844   for (Index = 0; Index < AppendCount; Index++) {
    845     Mark[AppendList[Index]->OpCode].Len  = AppendList[Index]->Length;
    846     Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;
    847   }
    848 
    849   //
    850   // compute the new packet length. No need to add 1 byte for
    851   // EOP option since EFI_DHCP4_PACKET includes one extra byte
    852   // for option. It is necessary to split the option if it is
    853   // longer than 255 bytes.
    854   //
    855   Len = sizeof (EFI_DHCP4_PACKET);
    856 
    857   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
    858     if (Mark[Index].Len != 0) {
    859       Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;
    860     }
    861   }
    862 
    863   Status  = EFI_OUT_OF_RESOURCES;
    864   Packet  = (EFI_DHCP4_PACKET *) AllocatePool (Len);
    865 
    866   if (Packet == NULL) {
    867     goto ON_ERROR;
    868   }
    869 
    870   Packet->Size         = Len;
    871   Packet->Length       = 0;
    872   CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->Dhcp4.Header));
    873   Packet->Dhcp4.Magik  = DHCP_OPTION_MAGIC;
    874   Buf                  = Packet->Dhcp4.Option;
    875 
    876   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
    877     if (Mark[Index].Len != 0) {
    878       Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);
    879     }
    880   }
    881 
    882   *(Buf++)        = DHCP4_TAG_EOP;
    883   Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)
    884                       + (UINT32) (Buf - Packet->Dhcp4.Option);
    885 
    886   *NewPacket      = Packet;
    887   Status          = EFI_SUCCESS;
    888 
    889 ON_ERROR:
    890   if (SeedOptions != NULL) {
    891     FreePool (SeedOptions);
    892   }
    893 
    894   FreePool (Mark);
    895   return Status;
    896 }
    897