Home | History | Annotate | Download | only in Mtftp6Dxe
      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