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