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