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