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
Mtftp6WrqSendBlock(IN MTFTP6_INSTANCE * Instance,IN UINT16 BlockNum)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
Mtftp6WrqHandleAck(IN MTFTP6_INSTANCE * Instance,IN EFI_MTFTP6_PACKET * Packet,IN UINT32 Len,OUT NET_BUF ** UdpPacket,OUT BOOLEAN * IsCompleted)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
Mtftp6WrqOackValid(IN MTFTP6_EXT_OPTION_INFO * ReplyInfo,IN MTFTP6_EXT_OPTION_INFO * RequestInfo)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
Mtftp6WrqHandleOack(IN MTFTP6_INSTANCE * Instance,IN EFI_MTFTP6_PACKET * Packet,IN UINT32 Len,OUT NET_BUF ** UdpPacket,OUT BOOLEAN * IsCompleted)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
Mtftp6WrqInput(IN NET_BUF * UdpPacket,IN UDP_END_POINT * UdpEpt,IN EFI_STATUS IoStatus,IN VOID * Context)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
Mtftp6WrqStart(IN MTFTP6_INSTANCE * Instance,IN UINT16 Operation)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