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