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