]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
[mirror_edk2.git] / NetworkPkg / Mtftp6Dxe / Mtftp6Wrq.c
1 /** @file
2 Mtftp6 Wrq process functions implementation.
3
4 Copyright (c) 2009 - 2010, 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
299 //
300 // Ignore the OACK if already started the upload
301 //
302 Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
303
304 if (Expected != 0) {
305 return EFI_SUCCESS;
306 }
307
308 //
309 // Parse and validate the options from server
310 //
311 ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
312
313 Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);
314
315 if (EFI_ERROR (Status)) {
316 return Status;
317 }
318
319 Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);
320
321 if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) {
322 //
323 // Don't send a MTFTP error packet when out of resource, it can
324 // only make it worse.
325 //
326 if (Status != EFI_OUT_OF_RESOURCES) {
327 //
328 // Free the received packet before send new packet in ReceiveNotify,
329 // since the udpio might need to be reconfigured.
330 //
331 NetbufFree (*UdpPacket);
332 *UdpPacket = NULL;
333 //
334 // Send the Mtftp6 error message if invalid Oack packet received.
335 //
336 Mtftp6SendError (
337 Instance,
338 EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,
339 (UINT8 *) "Mal-formated OACK packet"
340 );
341 }
342
343 return EFI_TFTP_ERROR;
344 }
345
346 if (ExtInfo.BlkSize != 0) {
347 Instance->BlkSize = ExtInfo.BlkSize;
348 }
349
350 if (ExtInfo.Timeout != 0) {
351 Instance->Timeout = ExtInfo.Timeout;
352 }
353
354 //
355 // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck,
356 // which will start the transmission of the first data block.
357 //
358 Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK);
359 Dummy.Ack.Block[0] = 0;
360
361 return Mtftp6WrqHandleAck (
362 Instance,
363 &Dummy,
364 sizeof (EFI_MTFTP6_ACK_HEADER),
365 UdpPacket,
366 IsCompleted
367 );
368 }
369
370
371 /**
372 The packet process callback for Mtftp6 upload.
373
374 @param[in] UdpPacket The pointer to the packet received.
375 @param[in] UdpEpt The pointer to the Udp6 access point.
376 @param[in] IoStatus The status from Udp6 instance.
377 @param[in] Context The pointer to the context.
378
379 **/
380 VOID
381 EFIAPI
382 Mtftp6WrqInput (
383 IN NET_BUF *UdpPacket,
384 IN UDP_END_POINT *UdpEpt,
385 IN EFI_STATUS IoStatus,
386 IN VOID *Context
387 )
388 {
389 MTFTP6_INSTANCE *Instance;
390 EFI_MTFTP6_PACKET *Packet;
391 BOOLEAN IsCompleted;
392 EFI_STATUS Status;
393 UINT32 TotalNum;
394 UINT32 Len;
395 UINT16 Opcode;
396
397 Instance = (MTFTP6_INSTANCE *) Context;
398
399 NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);
400
401 IsCompleted = FALSE;
402 Packet = NULL;
403 Status = EFI_SUCCESS;
404 TotalNum = 0;
405
406 //
407 // Return error status if Udp6 instance failed to receive.
408 //
409 if (EFI_ERROR (IoStatus)) {
410 Status = IoStatus;
411 goto ON_EXIT;
412 }
413
414 ASSERT (UdpPacket != NULL);
415
416 if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {
417 goto ON_EXIT;
418 }
419
420 //
421 // Client send initial request to server's listening port. Server
422 // will select a UDP port to communicate with the client.
423 //
424 if (UdpEpt->RemotePort != Instance->ServerDataPort) {
425 if (Instance->ServerDataPort != 0) {
426 goto ON_EXIT;
427 } else {
428 Instance->ServerDataPort = UdpEpt->RemotePort;
429 }
430 }
431
432 //
433 // Copy the MTFTP packet to a continuous buffer if it isn't already so.
434 //
435 Len = UdpPacket->TotalSize;
436 TotalNum = UdpPacket->BlockOpNum;
437
438 if (TotalNum > 1) {
439 Packet = AllocateZeroPool (Len);
440
441 if (Packet == NULL) {
442 Status = EFI_OUT_OF_RESOURCES;
443 goto ON_EXIT;
444 }
445
446 NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
447
448 } else {
449 Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
450 ASSERT (Packet != NULL);
451 }
452
453 Opcode = NTOHS (Packet->OpCode);
454
455 //
456 // Callback to the user's CheckPacket if provided. Abort the transmission
457 // if CheckPacket returns an EFI_ERROR code.
458 //
459 if (Instance->Token->CheckPacket != NULL &&
460 (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)
461 ) {
462
463 Status = Instance->Token->CheckPacket (
464 &Instance->Mtftp6,
465 Instance->Token,
466 (UINT16) Len,
467 Packet
468 );
469
470 if (EFI_ERROR (Status)) {
471 //
472 // Send an error message to the server to inform it
473 //
474 if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {
475 //
476 // Free the received packet before send new packet in ReceiveNotify,
477 // since the udpio might need to be reconfigured.
478 //
479 NetbufFree (UdpPacket);
480 UdpPacket = NULL;
481 //
482 // Send the Mtftp6 error message if user aborted the current session.
483 //
484 Mtftp6SendError (
485 Instance,
486 EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
487 (UINT8 *) "User aborted the transfer"
488 );
489 }
490
491 Status = EFI_ABORTED;
492 goto ON_EXIT;
493 }
494 }
495
496 //
497 // Switch the process routines by the operation code.
498 //
499 switch (Opcode) {
500 case EFI_MTFTP6_OPCODE_ACK:
501 if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) {
502 goto ON_EXIT;
503 }
504 //
505 // Handle the Ack packet of Wrq.
506 //
507 Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted);
508 break;
509
510 case EFI_MTFTP6_OPCODE_OACK:
511 if (Len <= MTFTP6_OPCODE_LEN) {
512 goto ON_EXIT;
513 }
514 //
515 // Handle the Oack packet of Wrq.
516 //
517 Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted);
518 break;
519
520 default:
521 //
522 // Drop and return eror if received error message.
523 //
524 Status = EFI_TFTP_ERROR;
525 break;
526 }
527
528 ON_EXIT:
529 //
530 // Free the resources, then if !EFI_ERROR (Status) and not completed,
531 // restart the receive, otherwise end the session.
532 //
533 if (Packet != NULL && TotalNum > 1) {
534 FreePool (Packet);
535 }
536
537 if (UdpPacket != NULL) {
538 NetbufFree (UdpPacket);
539 }
540
541 if (!EFI_ERROR (Status) && !IsCompleted) {
542 Status = UdpIoRecvDatagram (
543 Instance->UdpIo,
544 Mtftp6WrqInput,
545 Instance,
546 0
547 );
548 }
549 //
550 // Clean up the current session if failed to continue.
551 //
552 if (EFI_ERROR (Status) || IsCompleted) {
553 Mtftp6OperationClean (Instance, Status);
554 }
555 }
556
557
558 /**
559 Start the Mtftp6 instance to upload. It will first init some states,
560 then send the WRQ request packet, and start to receive the packet.
561
562 @param[in] Instance The pointer to the Mtftp6 instance.
563 @param[in] Operation The operation code of the current packet.
564
565 @retval EFI_SUCCESS The Mtftp6 was started to upload.
566 @retval Others Failed to start to upload.
567
568 **/
569 EFI_STATUS
570 Mtftp6WrqStart (
571 IN MTFTP6_INSTANCE *Instance,
572 IN UINT16 Operation
573 )
574 {
575 EFI_STATUS Status;
576
577 //
578 // The valid block number range are [0, 0xffff]. For example:
579 // the client sends an WRQ request to the server, the server
580 // ACK with an ACK0 to let client start transfer the first
581 // packet.
582 //
583 Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff);
584
585 if (EFI_ERROR (Status)) {
586 return Status;
587 }
588
589 Status = Mtftp6SendRequest (Instance, Operation);
590
591 if (EFI_ERROR (Status)) {
592 return Status;
593 }
594
595 return UdpIoRecvDatagram (
596 Instance->UdpIo,
597 Mtftp6WrqInput,
598 Instance,
599 0
600 );
601 }
602