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