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