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