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