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