1 /** @file 2 Mtftp6 Wrq process functions implementation. 3 4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR> 5 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php. 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 16 #include "Mtftp6Impl.h" 17 18 19 20 /** 21 Build and send a Mtftp6 data packet for upload. 22 23 @param[in] Instance The pointer to the Mtftp6 instance. 24 @param[in] BlockNum The block num to be sent. 25 26 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. 27 @retval EFI_SUCCESS The data packet was sent. 28 @retval EFI_ABORTED The user aborted this process. 29 30 **/ 31 EFI_STATUS 32 Mtftp6WrqSendBlock ( 33 IN MTFTP6_INSTANCE *Instance, 34 IN UINT16 BlockNum 35 ) 36 { 37 EFI_MTFTP6_PACKET *Packet; 38 EFI_MTFTP6_TOKEN *Token; 39 NET_BUF *UdpPacket; 40 EFI_STATUS Status; 41 UINT16 DataLen; 42 UINT8 *DataBuf; 43 UINT64 Start; 44 45 // 46 // Allocate net buffer to create data packet. 47 // 48 UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN); 49 50 if (UdpPacket == NULL) { 51 return EFI_OUT_OF_RESOURCES; 52 } 53 54 Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( 55 UdpPacket, 56 MTFTP6_DATA_HEAD_LEN, 57 FALSE 58 ); 59 ASSERT (Packet != NULL); 60 61 Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA); 62 Packet->Data.Block = HTONS (BlockNum); 63 64 // 65 // Read the block from either the buffer or PacketNeeded callback 66 // 67 Token = Instance->Token; 68 DataLen = Instance->BlkSize; 69 70 if (Token->Buffer != NULL) { 71 Start = MultU64x32 (BlockNum - 1, Instance->BlkSize); 72 73 if (Token->BufferSize < Start + Instance->BlkSize) { 74 DataLen = (UINT16) (Token->BufferSize - Start); 75 Instance->LastBlk = BlockNum; 76 Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); 77 } 78 79 if (DataLen > 0) { 80 NetbufAllocSpace (UdpPacket, DataLen, FALSE); 81 CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen); 82 } 83 84 } else { 85 // 86 // Get data from PacketNeeded 87 // 88 DataBuf = NULL; 89 Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf); 90 91 if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { 92 if (DataBuf != NULL) { 93 gBS->FreePool (DataBuf); 94 } 95 // 96 // The received packet has already been freed. 97 // 98 Mtftp6SendError ( 99 Instance, 100 EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, 101 (UINT8 *) "User aborted the transfer" 102 ); 103 104 return EFI_ABORTED; 105 } 106 107 if (DataLen < Instance->BlkSize) { 108 Instance->LastBlk = BlockNum; 109 Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); 110 } 111 112 if (DataLen > 0) { 113 NetbufAllocSpace (UdpPacket, DataLen, FALSE); 114 CopyMem (Packet->Data.Data, DataBuf, DataLen); 115 gBS->FreePool (DataBuf); 116 } 117 } 118 119 // 120 // Reset current retry count of the instance. 121 // 122 Instance->CurRetry = 0; 123 124 return Mtftp6TransmitPacket (Instance, UdpPacket); 125 } 126 127 128 /** 129 Function to handle received ACK packet. If the ACK number matches the 130 expected block number, with more data pending, send the next 131 block. Otherwise, tell the caller that we are done. 132 133 @param[in] Instance The pointer to the Mtftp6 instance. 134 @param[in] Packet The pointer to the received packet. 135 @param[in] Len The length of the packet. 136 @param[out] UdpPacket The net buf of received packet. 137 @param[out] IsCompleted If TRUE, the upload has been completed. 138 Otherwise, the upload has not been completed. 139 140 @retval EFI_SUCCESS The ACK packet successfully processed. 141 @retval EFI_TFTP_ERROR The block number loops back. 142 @retval Others Failed to transmit the next data packet. 143 144 **/ 145 EFI_STATUS 146 Mtftp6WrqHandleAck ( 147 IN MTFTP6_INSTANCE *Instance, 148 IN EFI_MTFTP6_PACKET *Packet, 149 IN UINT32 Len, 150 OUT NET_BUF **UdpPacket, 151 OUT BOOLEAN *IsCompleted 152 ) 153 { 154 UINT16 AckNum; 155 INTN Expected; 156 UINT64 TotalBlock; 157 158 *IsCompleted = FALSE; 159 AckNum = NTOHS (Packet->Ack.Block[0]); 160 Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); 161 162 ASSERT (Expected >= 0); 163 164 // 165 // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput 166 // restart receive. 167 // 168 if (Expected != AckNum) { 169 return EFI_SUCCESS; 170 } 171 172 // 173 // Remove the acked block number, if this is the last block number, 174 // tell the Mtftp6WrqInput to finish the transfer. This is the last 175 // block number if the block range are empty.. 176 // 177 Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock); 178 179 Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); 180 181 if (Expected < 0) { 182 // 183 // The block range is empty. It may either because the the last 184 // block has been ACKed, or the sequence number just looped back, 185 // that is, there is more than 0xffff blocks. 186 // 187 if (Instance->LastBlk == AckNum) { 188 ASSERT (Instance->LastBlk >= 1); 189 *IsCompleted = TRUE; 190 return EFI_SUCCESS; 191 192 } else { 193 // 194 // Free the received packet before send new packet in ReceiveNotify, 195 // since the udpio might need to be reconfigured. 196 // 197 NetbufFree (*UdpPacket); 198 *UdpPacket = NULL; 199 // 200 // Send the Mtftp6 error message if block number rolls back. 201 // 202 Mtftp6SendError ( 203 Instance, 204 EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, 205 (UINT8 *) "Block number rolls back, not supported, try blksize option" 206 ); 207 208 return EFI_TFTP_ERROR; 209 } 210 } 211 212 // 213 // Free the receive buffer before send new packet since it might need 214 // reconfigure udpio. 215 // 216 NetbufFree (*UdpPacket); 217 *UdpPacket = NULL; 218 219 return Mtftp6WrqSendBlock (Instance, (UINT16) Expected); 220 } 221 222 223 /** 224 Check whether the received OACK is valid. The OACK is valid 225 only if: 226 1. It only include options requested by us. 227 2. It can only include a smaller block size. 228 3. It can't change the proposed time out value. 229 4. Other requirements of the individal MTFTP6 options as required. 230 231 @param[in] ReplyInfo The pointer to options information in reply packet. 232 @param[in] RequestInfo The pointer to requested options information. 233 234 @retval TRUE If the option in OACK is valid. 235 @retval FALSE If the option is invalid. 236 237 **/ 238 BOOLEAN 239 Mtftp6WrqOackValid ( 240 IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, 241 IN MTFTP6_EXT_OPTION_INFO *RequestInfo 242 ) 243 { 244 // 245 // It is invalid for server to return options we don't request 246 // 247 if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { 248 return FALSE; 249 } 250 251 // 252 // Server can only specify a smaller block size to be used and 253 // return the timeout matches that requested. 254 // 255 if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || 256 (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout)) 257 ) { 258 259 return FALSE; 260 } 261 262 return TRUE; 263 } 264 265 266 /** 267 Process the OACK packet for Wrq. 268 269 @param[in] Instance The pointer to the Mtftp6 instance. 270 @param[in] Packet The pointer to the received packet. 271 @param[in] Len The length of the packet. 272 @param[out] UdpPacket The net buf of received packet. 273 @param[out] IsCompleted If TRUE, the upload has been completed. 274 Otherwise, the upload has not been completed. 275 276 @retval EFI_SUCCESS The OACK packet successfully processed. 277 @retval EFI_TFTP_ERROR An TFTP communication error happened. 278 @retval Others Failed to process the OACK packet. 279 280 **/ 281 EFI_STATUS 282 Mtftp6WrqHandleOack ( 283 IN MTFTP6_INSTANCE *Instance, 284 IN EFI_MTFTP6_PACKET *Packet, 285 IN UINT32 Len, 286 OUT NET_BUF **UdpPacket, 287 OUT BOOLEAN *IsCompleted 288 ) 289 { 290 EFI_MTFTP6_OPTION *Options; 291 UINT32 Count; 292 MTFTP6_EXT_OPTION_INFO ExtInfo; 293 EFI_MTFTP6_PACKET Dummy; 294 EFI_STATUS Status; 295 INTN Expected; 296 297 *IsCompleted = FALSE; 298 Options = NULL; 299 300 // 301 // Ignore the OACK if already started the upload 302 // 303 Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); 304 305 if (Expected != 0) { 306 return EFI_SUCCESS; 307 } 308 309 // 310 // Parse and validate the options from server 311 // 312 ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); 313 314 Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); 315 316 if (EFI_ERROR (Status)) { 317 return Status; 318 } 319 ASSERT (Options != NULL); 320 321 Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo); 322 323 if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) { 324 // 325 // Don't send a MTFTP error packet when out of resource, it can 326 // only make it worse. 327 // 328 if (Status != EFI_OUT_OF_RESOURCES) { 329 // 330 // Free the received packet before send new packet in ReceiveNotify, 331 // since the udpio might need to be reconfigured. 332 // 333 NetbufFree (*UdpPacket); 334 *UdpPacket = NULL; 335 // 336 // Send the Mtftp6 error message if invalid Oack packet received. 337 // 338 Mtftp6SendError ( 339 Instance, 340 EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, 341 (UINT8 *) "Mal-formated OACK packet" 342 ); 343 } 344 345 return EFI_TFTP_ERROR; 346 } 347 348 if (ExtInfo.BlkSize != 0) { 349 Instance->BlkSize = ExtInfo.BlkSize; 350 } 351 352 if (ExtInfo.Timeout != 0) { 353 Instance->Timeout = ExtInfo.Timeout; 354 } 355 356 // 357 // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck, 358 // which will start the transmission of the first data block. 359 // 360 Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); 361 Dummy.Ack.Block[0] = 0; 362 363 return Mtftp6WrqHandleAck ( 364 Instance, 365 &Dummy, 366 sizeof (EFI_MTFTP6_ACK_HEADER), 367 UdpPacket, 368 IsCompleted 369 ); 370 } 371 372 373 /** 374 The packet process callback for Mtftp6 upload. 375 376 @param[in] UdpPacket The pointer to the packet received. 377 @param[in] UdpEpt The pointer to the Udp6 access point. 378 @param[in] IoStatus The status from Udp6 instance. 379 @param[in] Context The pointer to the context. 380 381 **/ 382 VOID 383 EFIAPI 384 Mtftp6WrqInput ( 385 IN NET_BUF *UdpPacket, 386 IN UDP_END_POINT *UdpEpt, 387 IN EFI_STATUS IoStatus, 388 IN VOID *Context 389 ) 390 { 391 MTFTP6_INSTANCE *Instance; 392 EFI_MTFTP6_PACKET *Packet; 393 BOOLEAN IsCompleted; 394 EFI_STATUS Status; 395 UINT32 TotalNum; 396 UINT32 Len; 397 UINT16 Opcode; 398 399 Instance = (MTFTP6_INSTANCE *) Context; 400 401 NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); 402 403 IsCompleted = FALSE; 404 Packet = NULL; 405 Status = EFI_SUCCESS; 406 TotalNum = 0; 407 408 // 409 // Return error status if Udp6 instance failed to receive. 410 // 411 if (EFI_ERROR (IoStatus)) { 412 Status = IoStatus; 413 goto ON_EXIT; 414 } 415 416 ASSERT (UdpPacket != NULL); 417 418 if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { 419 goto ON_EXIT; 420 } 421 422 // 423 // Client send initial request to server's listening port. Server 424 // will select a UDP port to communicate with the client. 425 // 426 if (UdpEpt->RemotePort != Instance->ServerDataPort) { 427 if (Instance->ServerDataPort != 0) { 428 goto ON_EXIT; 429 } else { 430 Instance->ServerDataPort = UdpEpt->RemotePort; 431 } 432 } 433 434 // 435 // Copy the MTFTP packet to a continuous buffer if it isn't already so. 436 // 437 Len = UdpPacket->TotalSize; 438 TotalNum = UdpPacket->BlockOpNum; 439 440 if (TotalNum > 1) { 441 Packet = AllocateZeroPool (Len); 442 443 if (Packet == NULL) { 444 Status = EFI_OUT_OF_RESOURCES; 445 goto ON_EXIT; 446 } 447 448 NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); 449 450 } else { 451 Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); 452 ASSERT (Packet != NULL); 453 } 454 455 Opcode = NTOHS (Packet->OpCode); 456 457 // 458 // Callback to the user's CheckPacket if provided. Abort the transmission 459 // if CheckPacket returns an EFI_ERROR code. 460 // 461 if (Instance->Token->CheckPacket != NULL && 462 (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) 463 ) { 464 465 Status = Instance->Token->CheckPacket ( 466 &Instance->Mtftp6, 467 Instance->Token, 468 (UINT16) Len, 469 Packet 470 ); 471 472 if (EFI_ERROR (Status)) { 473 // 474 // Send an error message to the server to inform it 475 // 476 if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { 477 // 478 // Free the received packet before send new packet in ReceiveNotify, 479 // since the udpio might need to be reconfigured. 480 // 481 NetbufFree (UdpPacket); 482 UdpPacket = NULL; 483 // 484 // Send the Mtftp6 error message if user aborted the current session. 485 // 486 Mtftp6SendError ( 487 Instance, 488 EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, 489 (UINT8 *) "User aborted the transfer" 490 ); 491 } 492 493 Status = EFI_ABORTED; 494 goto ON_EXIT; 495 } 496 } 497 498 // 499 // Switch the process routines by the operation code. 500 // 501 switch (Opcode) { 502 case EFI_MTFTP6_OPCODE_ACK: 503 if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) { 504 goto ON_EXIT; 505 } 506 // 507 // Handle the Ack packet of Wrq. 508 // 509 Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted); 510 break; 511 512 case EFI_MTFTP6_OPCODE_OACK: 513 if (Len <= MTFTP6_OPCODE_LEN) { 514 goto ON_EXIT; 515 } 516 // 517 // Handle the Oack packet of Wrq. 518 // 519 Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted); 520 break; 521 522 default: 523 // 524 // Drop and return eror if received error message. 525 // 526 Status = EFI_TFTP_ERROR; 527 break; 528 } 529 530 ON_EXIT: 531 // 532 // Free the resources, then if !EFI_ERROR (Status) and not completed, 533 // restart the receive, otherwise end the session. 534 // 535 if (Packet != NULL && TotalNum > 1) { 536 FreePool (Packet); 537 } 538 539 if (UdpPacket != NULL) { 540 NetbufFree (UdpPacket); 541 } 542 543 if (!EFI_ERROR (Status) && !IsCompleted) { 544 Status = UdpIoRecvDatagram ( 545 Instance->UdpIo, 546 Mtftp6WrqInput, 547 Instance, 548 0 549 ); 550 } 551 // 552 // Clean up the current session if failed to continue. 553 // 554 if (EFI_ERROR (Status) || IsCompleted) { 555 Mtftp6OperationClean (Instance, Status); 556 } 557 } 558 559 560 /** 561 Start the Mtftp6 instance to upload. It will first init some states, 562 then send the WRQ request packet, and start to receive the packet. 563 564 @param[in] Instance The pointer to the Mtftp6 instance. 565 @param[in] Operation The operation code of the current packet. 566 567 @retval EFI_SUCCESS The Mtftp6 was started to upload. 568 @retval Others Failed to start to upload. 569 570 **/ 571 EFI_STATUS 572 Mtftp6WrqStart ( 573 IN MTFTP6_INSTANCE *Instance, 574 IN UINT16 Operation 575 ) 576 { 577 EFI_STATUS Status; 578 579 // 580 // The valid block number range are [0, 0xffff]. For example: 581 // the client sends an WRQ request to the server, the server 582 // ACK with an ACK0 to let client start transfer the first 583 // packet. 584 // 585 Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff); 586 587 if (EFI_ERROR (Status)) { 588 return Status; 589 } 590 591 Status = Mtftp6SendRequest (Instance, Operation); 592 593 if (EFI_ERROR (Status)) { 594 return Status; 595 } 596 597 return UdpIoRecvDatagram ( 598 Instance->UdpIo, 599 Mtftp6WrqInput, 600 Instance, 601 0 602 ); 603 } 604 605