1 /** @file 2 Function to validate, parse, process the DHCP options. 3 4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> 5 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 {DHCP4_TAG_NETMASK, DHCP_OPTION_IP, 1, 1 , TRUE}, 24 {DHCP4_TAG_TIME_OFFSET, DHCP_OPTION_INT32, 1, 1 , FALSE}, 25 {DHCP4_TAG_ROUTER, DHCP_OPTION_IP, 1, -1 , TRUE}, 26 {DHCP4_TAG_TIME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 27 {DHCP4_TAG_NAME_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 28 {DHCP4_TAG_DNS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 29 {DHCP4_TAG_LOG_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 30 {DHCP4_TAG_COOKIE_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 31 {DHCP4_TAG_LPR_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 32 {DHCP4_TAG_IMPRESS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 33 {DHCP4_TAG_RL_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 34 {DHCP4_TAG_HOSTNAME, DHCP_OPTION_INT8, 1, -1 , FALSE}, 35 {DHCP4_TAG_BOOTFILE_LEN, DHCP_OPTION_INT16, 1, 1 , FALSE}, 36 {DHCP4_TAG_DUMP, DHCP_OPTION_INT8, 1, -1 , FALSE}, 37 {DHCP4_TAG_DOMAINNAME, DHCP_OPTION_INT8, 1, -1 , FALSE}, 38 {DHCP4_TAG_SWAP_SERVER, DHCP_OPTION_IP, 1, 1 , FALSE}, 39 {DHCP4_TAG_ROOTPATH, DHCP_OPTION_INT8, 1, -1 , FALSE}, 40 {DHCP4_TAG_EXTEND_PATH, DHCP_OPTION_INT8, 1, -1 , FALSE}, 41 42 {DHCP4_TAG_IPFORWARD, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 43 {DHCP4_TAG_NONLOCAL_SRR, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 44 {DHCP4_TAG_POLICY_SRR, DHCP_OPTION_IPPAIR, 1, -1 , FALSE}, 45 {DHCP4_TAG_EMTU, DHCP_OPTION_INT16, 1, 1 , FALSE}, 46 {DHCP4_TAG_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE}, 47 {DHCP4_TAG_PATHMTU_AGE, DHCP_OPTION_INT32, 1, 1 , FALSE}, 48 {DHCP4_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16, 1, -1 , FALSE}, 49 50 {DHCP4_TAG_IFMTU, DHCP_OPTION_INT16, 1, 1 , FALSE}, 51 {DHCP4_TAG_SUBNET_LOCAL, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 52 {DHCP4_TAG_BROADCAST, DHCP_OPTION_IP, 1, 1 , FALSE}, 53 {DHCP4_TAG_DISCOVER_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 54 {DHCP4_TAG_SUPPLY_MASK, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 55 {DHCP4_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 56 {DHCP4_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP, 1, 1 , FALSE}, 57 {DHCP4_TAG_STATIC_ROUTE, DHCP_OPTION_IPPAIR, 1, -1 , FALSE}, 58 59 {DHCP4_TAG_TRAILER, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 60 {DHCP4_TAG_ARPAGE, DHCP_OPTION_INT32, 1, 1 , FALSE}, 61 {DHCP4_TAG_ETHER_ENCAP, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 62 63 {DHCP4_TAG_TCP_TTL, DHCP_OPTION_INT8, 1, 1 , FALSE}, 64 {DHCP4_TAG_KEEP_INTERVAL, DHCP_OPTION_INT32, 1, 1 , FALSE}, 65 {DHCP4_TAG_KEEP_GARBAGE, DHCP_OPTION_SWITCH, 1, 1 , FALSE}, 66 67 {DHCP4_TAG_NIS_DOMAIN, DHCP_OPTION_INT8, 1, -1 , FALSE}, 68 {DHCP4_TAG_NIS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 69 {DHCP4_TAG_NTP_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 70 {DHCP4_TAG_VENDOR, DHCP_OPTION_INT8, 1, -1 , FALSE}, 71 {DHCP4_TAG_NBNS, DHCP_OPTION_IP, 1, -1 , FALSE}, 72 {DHCP4_TAG_NBDD, DHCP_OPTION_IP, 1, -1 , FALSE}, 73 {DHCP4_TAG_NBTYPE, DHCP_OPTION_INT8, 1, 1 , FALSE}, 74 {DHCP4_TAG_NBSCOPE, DHCP_OPTION_INT8, 1, -1 , FALSE}, 75 {DHCP4_TAG_XFONT, DHCP_OPTION_IP, 1, -1 , FALSE}, 76 {DHCP4_TAG_XDM, DHCP_OPTION_IP, 1, -1 , FALSE}, 77 78 {DHCP4_TAG_REQUEST_IP, DHCP_OPTION_IP, 1, 1 , FALSE}, 79 {DHCP4_TAG_LEASE, DHCP_OPTION_INT32, 1, 1 , TRUE}, 80 {DHCP4_TAG_OVERLOAD, DHCP_OPTION_INT8, 1, 1 , TRUE}, 81 {DHCP4_TAG_MSG_TYPE, DHCP_OPTION_INT8, 1, 1 , TRUE}, 82 {DHCP4_TAG_SERVER_ID, DHCP_OPTION_IP, 1, 1 , TRUE}, 83 {DHCP4_TAG_PARA_LIST, DHCP_OPTION_INT8, 1, -1 , FALSE}, 84 {DHCP4_TAG_MESSAGE, DHCP_OPTION_INT8, 1, -1 , FALSE}, 85 {DHCP4_TAG_MAXMSG, DHCP_OPTION_INT16, 1, 1 , FALSE}, 86 {DHCP4_TAG_T1, DHCP_OPTION_INT32, 1, 1 , TRUE}, 87 {DHCP4_TAG_T2, DHCP_OPTION_INT32, 1, 1 , TRUE}, 88 {DHCP4_TAG_VENDOR_CLASS_ID,DHCP_OPTION_INT8, 1, -1 , FALSE}, 89 {DHCP4_TAG_CLIENT_ID, DHCP_OPTION_INT8, 2, -1 , FALSE}, 90 91 {DHCP4_TAG_NISPLUS, DHCP_OPTION_INT8, 1, -1 , FALSE}, 92 {DHCP4_TAG_NISPLUS_SERVER, DHCP_OPTION_IP, 1, -1 , FALSE}, 93 94 {DHCP4_TAG_TFTP, DHCP_OPTION_INT8, 1, -1 , FALSE}, 95 {DHCP4_TAG_BOOTFILE, DHCP_OPTION_INT8, 1, -1 , FALSE}, 96 97 {DHCP4_TAG_MOBILEIP, DHCP_OPTION_IP, 0, -1 , FALSE}, 98 {DHCP4_TAG_SMTP, DHCP_OPTION_IP, 1, -1 , FALSE}, 99 {DHCP4_TAG_POP3, DHCP_OPTION_IP, 1, -1 , FALSE}, 100 {DHCP4_TAG_NNTP, DHCP_OPTION_IP, 1, -1 , FALSE}, 101 {DHCP4_TAG_WWW, DHCP_OPTION_IP, 1, -1 , FALSE}, 102 {DHCP4_TAG_FINGER, DHCP_OPTION_IP, 1, -1 , FALSE}, 103 {DHCP4_TAG_IRC, DHCP_OPTION_IP, 1, -1 , FALSE}, 104 {DHCP4_TAG_STTALK, DHCP_OPTION_IP, 1, -1 , FALSE}, 105 {DHCP4_TAG_STDA, DHCP_OPTION_IP, 1, -1 , FALSE}, 106 107 {DHCP4_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 DHCP4_TAG_NETMASK: 252 Para->NetMask = NetGetUint32 (Data); 253 break; 254 255 case DHCP4_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 DHCP4_TAG_LEASE: 263 Para->Lease = NetGetUint32 (Data); 264 break; 265 266 case DHCP4_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 DHCP4_TAG_MSG_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 DHCP4_TAG_SERVER_ID: 283 Para->ServerId = NetGetUint32 (Data); 284 break; 285 286 case DHCP4_TAG_T1: 287 Para->T1 = NetGetUint32 (Data); 288 break; 289 290 case DHCP4_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 DHCP4_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 DHCP4_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 == DHCP4_TAG_PAD) { 334 Cur++; 335 continue; 336 } else if (Tag == DHCP4_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 == DHCP4_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_NOT_FOUND Cannot find any option. 546 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to parse the packet. 547 @retval EFI_INVALID_PARAMETER The options are mal-formated 548 @retval EFI_SUCCESS The options are parsed into OptionPoint 549 550 **/ 551 EFI_STATUS 552 DhcpParseOption ( 553 IN EFI_DHCP4_PACKET *Packet, 554 OUT INTN *Count, 555 OUT DHCP_OPTION **OptionPoint 556 ) 557 { 558 DHCP_OPTION_CONTEXT Context; 559 DHCP_OPTION *Options; 560 DHCP_OPTION_COUNT *OptCount; 561 EFI_STATUS Status; 562 UINT16 TotalLen; 563 INTN OptNum; 564 INTN Index; 565 566 ASSERT ((Count != NULL) && (OptionPoint != NULL)); 567 568 // 569 // First compute how many options and how long each option is 570 // with the "Key indexed counting" algorithms. 571 // 572 OptCount = AllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT)); 573 574 if (OptCount == NULL) { 575 return EFI_OUT_OF_RESOURCES; 576 } 577 578 Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount); 579 580 if (EFI_ERROR (Status)) { 581 goto ON_EXIT; 582 } 583 584 // 585 // Before the loop, Offset is the length of the option. After loop, 586 // OptCount[Index].Offset specifies the offset into the continuous 587 // option value buffer to put the data. 588 // 589 TotalLen = 0; 590 OptNum = 0; 591 592 for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { 593 if (OptCount[Index].Offset != 0) { 594 OptCount[Index].Index = (UINT8) OptNum; 595 596 TotalLen = (UINT16) (TotalLen + OptCount[Index].Offset); 597 OptCount[Index].Offset = (UINT16) (TotalLen - OptCount[Index].Offset); 598 599 OptNum++; 600 } 601 } 602 603 *Count = OptNum; 604 *OptionPoint = NULL; 605 606 if (OptNum == 0) { 607 goto ON_EXIT; 608 } 609 610 // 611 // Allocate a buffer to hold the DHCP options, and after that, a 612 // continuous buffer to put all the options' data. 613 // 614 Options = AllocateZeroPool ((UINTN) (OptNum * sizeof (DHCP_OPTION)) + TotalLen); 615 616 if (Options == NULL) { 617 Status = EFI_OUT_OF_RESOURCES; 618 goto ON_EXIT; 619 } 620 621 Context.OpCount = OptCount; 622 Context.Options = Options; 623 Context.Buf = (UINT8 *) (Options + OptNum); 624 625 Status = DhcpIterateOptions (Packet, DhcpFillOption, &Context); 626 627 if (EFI_ERROR (Status)) { 628 FreePool (Options); 629 goto ON_EXIT; 630 } 631 632 *OptionPoint = Options; 633 634 ON_EXIT: 635 FreePool (OptCount); 636 return Status; 637 } 638 639 640 /** 641 Validate the packet's options. If necessary, allocate 642 and fill in the interested parameters. 643 644 @param[in] Packet The packet to validate the options 645 @param[out] Para The variable to save the DHCP parameters. 646 647 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to validate the packet. 648 @retval EFI_INVALID_PARAMETER The options are mal-formated 649 @retval EFI_SUCCESS The options are parsed into OptionPoint 650 651 **/ 652 EFI_STATUS 653 DhcpValidateOptions ( 654 IN EFI_DHCP4_PACKET *Packet, 655 OUT DHCP_PARAMETER **Para OPTIONAL 656 ) 657 { 658 DHCP_PARAMETER Parameter; 659 DHCP_OPTION_FORMAT *Format; 660 DHCP_OPTION *AllOption; 661 DHCP_OPTION *Option; 662 EFI_STATUS Status; 663 BOOLEAN Updated; 664 INTN Count; 665 INTN Index; 666 667 if (Para != NULL) { 668 *Para = NULL; 669 } 670 671 AllOption = NULL; 672 673 Status = DhcpParseOption (Packet, &Count, &AllOption); 674 if (EFI_ERROR (Status) || (Count == 0)) { 675 return Status; 676 } 677 ASSERT (AllOption != NULL); 678 679 Updated = FALSE; 680 ZeroMem (&Parameter, sizeof (Parameter)); 681 682 for (Index = 0; Index < Count; Index++) { 683 Option = &AllOption[Index]; 684 685 // 686 // Find the format of the option then validate it. 687 // 688 Format = DhcpFindOptionFormat (Option->Tag); 689 690 if (Format == NULL) { 691 continue; 692 } 693 694 if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) { 695 Status = EFI_INVALID_PARAMETER; 696 goto ON_EXIT; 697 } 698 699 // 700 // Get the client interested parameters 701 // 702 if (Format->Alert && (Para != NULL)) { 703 Updated = TRUE; 704 Status = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter); 705 706 if (EFI_ERROR (Status)) { 707 goto ON_EXIT; 708 } 709 } 710 } 711 712 if (Updated && (Para != NULL)) { 713 *Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), &Parameter); 714 if (*Para == NULL) { 715 Status = EFI_OUT_OF_RESOURCES; 716 goto ON_EXIT; 717 } 718 } 719 720 ON_EXIT: 721 FreePool (AllOption); 722 return Status; 723 } 724 725 726 727 /** 728 Append an option to the memory, if the option is longer than 729 255 bytes, splits it into several options. 730 731 @param[out] Buf The buffer to append the option to 732 @param[in] Tag The option's tag 733 @param[in] DataLen The length of the option's data 734 @param[in] Data The option's data 735 736 @return The position to append the next option 737 738 **/ 739 UINT8 * 740 DhcpAppendOption ( 741 OUT UINT8 *Buf, 742 IN UINT8 Tag, 743 IN UINT16 DataLen, 744 IN UINT8 *Data 745 ) 746 { 747 INTN Index; 748 INTN Len; 749 750 ASSERT (DataLen != 0); 751 752 for (Index = 0; Index < (DataLen + 254) / 255; Index++) { 753 Len = MIN (255, DataLen - Index * 255); 754 755 *(Buf++) = Tag; 756 *(Buf++) = (UINT8) Len; 757 CopyMem (Buf, Data + Index * 255, (UINTN) Len); 758 759 Buf += Len; 760 } 761 762 return Buf; 763 } 764 765 766 /** 767 Build a new DHCP packet from a seed packet. Options may be deleted or 768 appended. The caller should free the NewPacket when finished using it. 769 770 @param[in] SeedPacket The seed packet to start with 771 @param[in] DeleteCount The number of options to delete 772 @param[in] DeleteList The options to delete from the packet 773 @param[in] AppendCount The number of options to append 774 @param[in] AppendList The options to append to the packet 775 @param[out] NewPacket The new packet, allocated and built by this 776 function. 777 778 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory 779 @retval EFI_INVALID_PARAMETER The options in SeekPacket are mal-formated 780 @retval EFI_SUCCESS The packet is build. 781 782 **/ 783 EFI_STATUS 784 DhcpBuild ( 785 IN EFI_DHCP4_PACKET *SeedPacket, 786 IN UINT32 DeleteCount, 787 IN UINT8 *DeleteList OPTIONAL, 788 IN UINT32 AppendCount, 789 IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, 790 OUT EFI_DHCP4_PACKET **NewPacket 791 ) 792 { 793 DHCP_OPTION *Mark; 794 DHCP_OPTION *SeedOptions; 795 EFI_DHCP4_PACKET *Packet; 796 EFI_STATUS Status; 797 INTN Count; 798 UINT32 Index; 799 UINT32 Len; 800 UINT8 *Buf; 801 802 // 803 // Use an array of DHCP_OPTION to mark the existance 804 // and position of each valid options. 805 // 806 Mark = AllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS); 807 808 if (Mark == NULL) { 809 return EFI_OUT_OF_RESOURCES; 810 } 811 812 for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { 813 Mark[Index].Tag = (UINT8) Index; 814 Mark[Index].Len = 0; 815 } 816 817 // 818 // Get list of the options from the seed packet, then put 819 // them to the mark array according to their tags. 820 // 821 SeedOptions = NULL; 822 Status = DhcpParseOption (SeedPacket, &Count, &SeedOptions); 823 824 if (EFI_ERROR (Status)) { 825 goto ON_ERROR; 826 } 827 828 if (SeedOptions != NULL) { 829 for (Index = 0; Index < (UINT32) Count; Index++) { 830 Mark[SeedOptions[Index].Tag] = SeedOptions[Index]; 831 } 832 } 833 834 // 835 // Mark the option's length is zero if it is in the DeleteList. 836 // 837 for (Index = 0; Index < DeleteCount; Index++) { 838 Mark[DeleteList[Index]].Len = 0; 839 } 840 841 // 842 // Add or replace the option if it is in the append list. 843 // 844 for (Index = 0; Index < AppendCount; Index++) { 845 Mark[AppendList[Index]->OpCode].Len = AppendList[Index]->Length; 846 Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data; 847 } 848 849 // 850 // compute the new packet length. No need to add 1 byte for 851 // EOP option since EFI_DHCP4_PACKET includes one extra byte 852 // for option. It is necessary to split the option if it is 853 // longer than 255 bytes. 854 // 855 Len = sizeof (EFI_DHCP4_PACKET); 856 857 for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { 858 if (Mark[Index].Len != 0) { 859 Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len; 860 } 861 } 862 863 Status = EFI_OUT_OF_RESOURCES; 864 Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len); 865 866 if (Packet == NULL) { 867 goto ON_ERROR; 868 } 869 870 Packet->Size = Len; 871 Packet->Length = 0; 872 CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->Dhcp4.Header)); 873 Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC; 874 Buf = Packet->Dhcp4.Option; 875 876 for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) { 877 if (Mark[Index].Len != 0) { 878 Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data); 879 } 880 } 881 882 *(Buf++) = DHCP4_TAG_EOP; 883 Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32) 884 + (UINT32) (Buf - Packet->Dhcp4.Option); 885 886 *NewPacket = Packet; 887 Status = EFI_SUCCESS; 888 889 ON_ERROR: 890 if (SeedOptions != NULL) { 891 FreePool (SeedOptions); 892 } 893 894 FreePool (Mark); 895 return Status; 896 } 897