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