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