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