]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Option.c
Patch to remove STATIC modifier. This is on longer recommended by EFI Framework codin...
[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
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
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
772db4bb 168BOOLEAN\r
169DhcpOptionIsValid (\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
772db4bb 248EFI_STATUS\r
249DhcpGetParameter (\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
772db4bb 321EFI_STATUS\r
322DhcpIterateBufferOptions (\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
395EFI_STATUS\r
396DhcpIterateOptions (\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
687a2e5f 421 (UINT8 *) Packet->Dhcp4.Header.BootFileName,\r
772db4bb 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
687a2e5f 435 (UINT8 *) Packet->Dhcp4.Header.ServerName,\r
772db4bb 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
772db4bb 465EFI_STATUS\r
466DhcpGetOptionLen (\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
687a2e5f 476 OpCount[Tag].Offset = (UINT16) (OpCount[Tag].Offset + Len);\r
772db4bb 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
772db4bb 495EFI_STATUS\r
496DhcpFillOption (\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
e48e37fc 521 CopyMem (Buf + OptCount[Tag].Offset, Data, Len);\r
772db4bb 522\r
687a2e5f 523 OptCount[Tag].Offset = (UINT16) (OptCount[Tag].Offset + Len);\r
524 Options[Index].Len = (UINT16) (Options[Index].Len + Len);\r
772db4bb 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
556EFI_STATUS\r
557DhcpParseOption (\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
e48e37fc 577 OptCount = AllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));\r
772db4bb 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
687a2e5f 601 TotalLen = (UINT16) (TotalLen + OptCount[Index].Offset);\r
602 OptCount[Index].Offset = (UINT16) (TotalLen - OptCount[Index].Offset);\r
772db4bb 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
e48e37fc 619 Options = AllocateZeroPool (OptNum * sizeof (DHCP_OPTION) + TotalLen);\r
772db4bb 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
e48e37fc 633 gBS->FreePool (Options);\r
772db4bb 634 goto ON_EXIT;\r
635 }\r
636\r
637 *OptionPoint = Options;\r
638\r
639ON_EXIT:\r
e48e37fc 640 gBS->FreePool (OptCount);\r
772db4bb 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
657EFI_STATUS\r
658DhcpValidateOptions (\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
e48e37fc 684 ZeroMem (&Parameter, sizeof (Parameter));\r
772db4bb 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
e48e37fc 717 if ((*Para = AllocatePool (sizeof (DHCP_PARAMETER))) == NULL) {\r
772db4bb 718 Status = EFI_OUT_OF_RESOURCES;\r
719 goto ON_EXIT;\r
720 }\r
721\r
687a2e5f 722 CopyMem (*Para, &Parameter, sizeof (**Para));\r
772db4bb 723 }\r
724\r
725ON_EXIT:\r
e48e37fc 726 gBS->FreePool (AllOption);\r
772db4bb 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
744UINT8 *\r
745DhcpAppendOption (\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
36ee91ca 758 Len = MIN (255, DataLen - Index * 255);\r
772db4bb 759\r
760 *(Buf++) = Tag;\r
761 *(Buf++) = (UINT8) Len;\r
e48e37fc 762 CopyMem (Buf, Data + Index * 255, Len);\r
772db4bb 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
787EFI_STATUS\r
788DhcpBuild (\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
e48e37fc 810 Mark = AllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);\r
772db4bb 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
e48e37fc 866 Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);\r
772db4bb 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
687a2e5f 874 CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->Dhcp4.Header));\r
772db4bb 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
891ON_ERROR:\r
892 if (SeedOptions != NULL) {\r
e48e37fc 893 gBS->FreePool (SeedOptions);\r
772db4bb 894 }\r
895\r
e48e37fc 896 gBS->FreePool (Mark);\r
772db4bb 897 return Status;\r
898}\r