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