]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c
MdeModulePke/Mtftp4Dxe: Support windowsize in read request operation.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Mtftp4Dxe / Mtftp4Wrq.c
CommitLineData
772db4bb 1/** @file\r
dab714aa 2 Routines to process Wrq (upload).\r
d1102dba 3\r
36c19ee6 4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
e5eed7d3 5This program and the accompanying materials\r
772db4bb 6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
dab714aa 8http://opensource.org/licenses/bsd-license.php<BR>\r
772db4bb 9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
772db4bb 13**/\r
14\r
15#include "Mtftp4Impl.h"\r
16\r
772db4bb 17\r
18\r
19/**\r
20 Build then send a MTFTP data packet for the MTFTP upload session.\r
21\r
dab714aa 22 @param Instance The MTFTP upload session.\r
23 @param BlockNum The block number to send.\r
772db4bb 24\r
dab714aa 25 @retval EFI_OUT_OF_RESOURCES Failed to build the packet.\r
772db4bb 26 @retval EFI_ABORTED The consumer of this child directs to abort the\r
dab714aa 27 transmission by return an error through PacketNeeded.\r
772db4bb 28 @retval EFI_SUCCESS The data is sent.\r
29\r
30**/\r
31EFI_STATUS\r
32Mtftp4WrqSendBlock (\r
dab714aa 33 IN OUT MTFTP4_PROTOCOL *Instance,\r
34 IN UINT16 BlockNum\r
772db4bb 35 )\r
36{\r
37 EFI_MTFTP4_PACKET *Packet;\r
38 EFI_MTFTP4_TOKEN *Token;\r
39 NET_BUF *UdpPacket;\r
40 EFI_STATUS Status;\r
41 UINT16 DataLen;\r
42 UINT8 *DataBuf;\r
43 UINT64 Start;\r
44\r
45 //\r
46 // Allocate a buffer to hold the user data\r
47 //\r
48 UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);\r
49\r
50 if (UdpPacket == NULL) {\r
51 return EFI_OUT_OF_RESOURCES;\r
52 }\r
53\r
dab714aa 54 Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);\r
894d038a 55 ASSERT (Packet != NULL);\r
772db4bb 56\r
57 Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);\r
58 Packet->Data.Block = HTONS (BlockNum);\r
59\r
60 //\r
61 // Read the block from either the buffer or PacketNeeded callback\r
62 //\r
63 Token = Instance->Token;\r
64 DataLen = Instance->BlkSize;\r
65\r
66 if (Token->Buffer != NULL) {\r
67 Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);\r
68\r
69 if (Token->BufferSize < Start + Instance->BlkSize) {\r
70 DataLen = (UINT16) (Token->BufferSize - Start);\r
71 Instance->LastBlock = BlockNum;\r
72 Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
73 }\r
74\r
75 if (DataLen > 0) {\r
76 NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
e48e37fc 77 CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);\r
772db4bb 78 }\r
79\r
80 } else {\r
81 //\r
82 // Get data from PacketNeeded\r
83 //\r
84 DataBuf = NULL;\r
dab714aa 85 Status = Token->PacketNeeded (\r
86 &Instance->Mtftp4,\r
87 Token,\r
88 &DataLen,\r
89 (VOID **) &DataBuf\r
90 );\r
772db4bb 91\r
92 if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {\r
93 if (DataBuf != NULL) {\r
766c7483 94 FreePool (DataBuf);\r
772db4bb 95 }\r
96\r
36c19ee6
WF
97 if (UdpPacket != NULL) {\r
98 NetbufFree (UdpPacket);\r
99 }\r
100\r
772db4bb 101 Mtftp4SendError (\r
102 Instance,\r
103 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
67a58d0f 104 (UINT8 *) "User aborted the transfer"\r
772db4bb 105 );\r
106\r
107 return EFI_ABORTED;\r
108 }\r
109\r
110 if (DataLen < Instance->BlkSize) {\r
111 Instance->LastBlock = BlockNum;\r
112 Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
113 }\r
114\r
115 if (DataLen > 0) {\r
116 NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
e48e37fc 117 CopyMem (Packet->Data.Data, DataBuf, DataLen);\r
766c7483 118 FreePool (DataBuf);\r
772db4bb 119 }\r
120 }\r
121\r
122 return Mtftp4SendPacket (Instance, UdpPacket);\r
123}\r
124\r
125\r
126/**\r
d1102dba
LG
127 Function to handle received ACK packet.\r
128\r
129 If the ACK number matches the expected block number, and there are more\r
dab714aa 130 data pending, send the next block. Otherwise tell the caller that we are done.\r
772db4bb 131\r
132 @param Instance The MTFTP upload session\r
133 @param Packet The MTFTP packet received\r
134 @param Len The packet length\r
135 @param Completed Return whether the upload has finished.\r
136\r
137 @retval EFI_SUCCESS The ACK is successfully processed.\r
138 @retval EFI_TFTP_ERROR The block number loops back.\r
139 @retval Others Failed to transmit the next data packet.\r
140\r
141**/\r
142EFI_STATUS\r
143Mtftp4WrqHandleAck (\r
dab714aa 144 IN MTFTP4_PROTOCOL *Instance,\r
145 IN EFI_MTFTP4_PACKET *Packet,\r
146 IN UINT32 Len,\r
147 OUT BOOLEAN *Completed\r
772db4bb 148 )\r
149{\r
150 UINT16 AckNum;\r
151 INTN Expected;\r
f1f11ea2 152 UINT64 TotalBlock;\r
d1102dba 153\r
772db4bb 154 *Completed = FALSE;\r
155 AckNum = NTOHS (Packet->Ack.Block[0]);\r
156 Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
157\r
158 ASSERT (Expected >= 0);\r
159\r
160 //\r
161 // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput\r
162 // restart receive.\r
163 //\r
164 if (Expected != AckNum) {\r
165 return EFI_SUCCESS;\r
166 }\r
167\r
168 //\r
169 // Remove the acked block number, if this is the last block number,\r
170 // tell the Mtftp4WrqInput to finish the transfer. This is the last\r
171 // block number if the block range are empty..\r
172 //\r
49fd66cb 173 Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed,&TotalBlock);\r
772db4bb 174\r
175 Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
176\r
177 if (Expected < 0) {\r
d1102dba 178\r
772db4bb 179 //\r
180 // The block range is empty. It may either because the the last\r
181 // block has been ACKed, or the sequence number just looped back,\r
182 // that is, there is more than 0xffff blocks.\r
183 //\r
184 if (Instance->LastBlock == AckNum) {\r
185 ASSERT (Instance->LastBlock >= 1);\r
186 *Completed = TRUE;\r
187 return EFI_SUCCESS;\r
188\r
189 } else {\r
190 Mtftp4SendError (\r
191 Instance,\r
192 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
67a58d0f 193 (UINT8 *) "Block number rolls back, not supported, try blksize option"\r
772db4bb 194 );\r
195\r
196 return EFI_TFTP_ERROR;\r
197 }\r
198 }\r
199\r
200 return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);\r
201}\r
202\r
203\r
204/**\r
d1102dba
LG
205 Check whether the received OACK is valid.\r
206\r
dab714aa 207 The OACK is valid only if:\r
772db4bb 208 1. It only include options requested by us\r
209 2. It can only include a smaller block size\r
210 3. It can't change the proposed time out value.\r
dab714aa 211 4. Other requirements of the individal MTFTP options as required.\r
772db4bb 212\r
213 @param Reply The options included in the OACK\r
214 @param Request The options we requested\r
215\r
dab714aa 216 @retval TRUE The options included in OACK is valid.\r
217 @retval FALSE The options included in OACK is invalid.\r
772db4bb 218\r
219**/\r
220BOOLEAN\r
221Mtftp4WrqOackValid (\r
222 IN MTFTP4_OPTION *Reply,\r
223 IN MTFTP4_OPTION *Request\r
224 )\r
225{\r
226 //\r
227 // It is invalid for server to return options we don't request\r
228 //\r
dab714aa 229 if ((Reply->Exist & ~Request->Exist) != 0) {\r
772db4bb 230 return FALSE;\r
231 }\r
232\r
233 //\r
234 // Server can only specify a smaller block size to be used and\r
235 // return the timeout matches that requested.\r
236 //\r
dab714aa 237 if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||\r
238 (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {\r
772db4bb 239 return FALSE;\r
240 }\r
241\r
242 return TRUE;\r
243}\r
244\r
245\r
246/**\r
d1102dba
LG
247 Function to handle the MTFTP OACK packet.\r
248\r
dab714aa 249 It parses the packet's options, and update the internal states of the session.\r
772db4bb 250\r
251 @param Instance The MTFTP session\r
252 @param Packet The received OACK packet\r
253 @param Len The length of the packet\r
254 @param Completed Whether the transmisson has completed. NOT used by\r
255 this function.\r
256\r
257 @retval EFI_SUCCESS The OACK process is OK\r
258 @retval EFI_TFTP_ERROR Some error occured, and the session reset.\r
259\r
260**/\r
261EFI_STATUS\r
262Mtftp4WrqHandleOack (\r
dab714aa 263 IN OUT MTFTP4_PROTOCOL *Instance,\r
264 IN EFI_MTFTP4_PACKET *Packet,\r
265 IN UINT32 Len,\r
266 OUT BOOLEAN *Completed\r
772db4bb 267 )\r
268{\r
269 MTFTP4_OPTION Reply;\r
270 EFI_MTFTP4_PACKET Bogus;\r
271 EFI_STATUS Status;\r
272 INTN Expected;\r
273\r
274 *Completed = FALSE;\r
275\r
276 //\r
277 // Ignore the OACK if already started the upload\r
278 //\r
279 Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
280\r
281 if (Expected != 0) {\r
282 return EFI_SUCCESS;\r
283 }\r
284\r
285 //\r
286 // Parse and validate the options from server\r
287 //\r
e48e37fc 288 ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
6c047cfa 289 Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);\r
772db4bb 290\r
291 if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {\r
292 //\r
293 // Don't send a MTFTP error packet when out of resource, it can\r
294 // only make it worse.\r
295 //\r
296 if (Status != EFI_OUT_OF_RESOURCES) {\r
297 Mtftp4SendError (\r
298 Instance,\r
299 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,\r
67a58d0f 300 (UINT8 *) "Mal-formated OACK packet"\r
772db4bb 301 );\r
302 }\r
303\r
304 return EFI_TFTP_ERROR;\r
305 }\r
306\r
307 if (Reply.BlkSize != 0) {\r
308 Instance->BlkSize = Reply.BlkSize;\r
309 }\r
310\r
311 if (Reply.Timeout != 0) {\r
312 Instance->Timeout = Reply.Timeout;\r
313 }\r
314\r
315 //\r
316 // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,\r
317 // which will start the transmission of the first data block.\r
318 //\r
319 Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
320 Bogus.Ack.Block[0] = 0;\r
321\r
dab714aa 322 Status = Mtftp4WrqHandleAck (\r
323 Instance,\r
324 &Bogus,\r
325 sizeof (EFI_MTFTP4_ACK_HEADER),\r
326 Completed\r
327 );\r
328\r
329 return Status;\r
772db4bb 330}\r
331\r
332\r
333/**\r
334 The input process routine for MTFTP upload.\r
335\r
336 @param UdpPacket The received MTFTP packet.\r
b45b45b2 337 @param EndPoint The local/remote access point\r
772db4bb 338 @param IoStatus The result of the packet receiving\r
339 @param Context Opaque parameter for the callback, which is the\r
340 MTFTP session.\r
772db4bb 341**/\r
342VOID\r
e798cd87 343EFIAPI\r
772db4bb 344Mtftp4WrqInput (\r
345 IN NET_BUF *UdpPacket,\r
b45b45b2 346 IN UDP_END_POINT *EndPoint,\r
772db4bb 347 IN EFI_STATUS IoStatus,\r
348 IN VOID *Context\r
349 )\r
350{\r
351 MTFTP4_PROTOCOL *Instance;\r
352 EFI_MTFTP4_PACKET *Packet;\r
353 BOOLEAN Completed;\r
354 EFI_STATUS Status;\r
355 UINT32 Len;\r
356 UINT16 Opcode;\r
357\r
358 Instance = (MTFTP4_PROTOCOL *) Context;\r
359 NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);\r
360\r
361 Completed = FALSE;\r
362 Packet = NULL;\r
363 Status = EFI_SUCCESS;\r
364\r
365 if (EFI_ERROR (IoStatus)) {\r
366 Status = IoStatus;\r
367 goto ON_EXIT;\r
368 }\r
369\r
370 ASSERT (UdpPacket != NULL);\r
371\r
372 if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {\r
373 goto ON_EXIT;\r
374 }\r
375\r
376 //\r
377 // Client send initial request to server's listening port. Server\r
378 // will select a UDP port to communicate with the client.\r
379 //\r
b45b45b2 380 if (EndPoint->RemotePort != Instance->ConnectedPort) {\r
772db4bb 381 if (Instance->ConnectedPort != 0) {\r
382 goto ON_EXIT;\r
383 } else {\r
b45b45b2 384 Instance->ConnectedPort = EndPoint->RemotePort;\r
772db4bb 385 }\r
386 }\r
387\r
388 //\r
389 // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
390 //\r
391 Len = UdpPacket->TotalSize;\r
392\r
393 if (UdpPacket->BlockOpNum > 1) {\r
e48e37fc 394 Packet = AllocatePool (Len);\r
772db4bb 395\r
396 if (Packet == NULL) {\r
397 Status = EFI_OUT_OF_RESOURCES;\r
398 goto ON_EXIT;\r
399 }\r
400\r
401 NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
402\r
403 } else {\r
404 Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
523f48e7 405 ASSERT (Packet != NULL);\r
772db4bb 406 }\r
407\r
408 Opcode = NTOHS (Packet->OpCode);\r
409\r
410 //\r
411 // Call the user's CheckPacket if provided. Abort the transmission\r
412 // if CheckPacket returns an EFI_ERROR code.\r
413 //\r
414 if ((Instance->Token->CheckPacket != NULL) &&\r
415 ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {\r
416\r
417 Status = Instance->Token->CheckPacket (\r
418 &Instance->Mtftp4,\r
419 Instance->Token,\r
420 (UINT16) Len,\r
421 Packet\r
422 );\r
423\r
424 if (EFI_ERROR (Status)) {\r
425 //\r
426 // Send an error message to the server to inform it\r
427 //\r
428 if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {\r
429 Mtftp4SendError (\r
430 Instance,\r
431 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
67a58d0f 432 (UINT8 *) "User aborted the transfer"\r
772db4bb 433 );\r
434 }\r
435\r
436 Status = EFI_ABORTED;\r
437 goto ON_EXIT;\r
438 }\r
439 }\r
440\r
441 switch (Opcode) {\r
442 case EFI_MTFTP4_OPCODE_ACK:\r
443 if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {\r
444 goto ON_EXIT;\r
445 }\r
446\r
447 Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);\r
448 break;\r
449\r
450 case EFI_MTFTP4_OPCODE_OACK:\r
451 if (Len <= MTFTP4_OPCODE_LEN) {\r
452 goto ON_EXIT;\r
453 }\r
454\r
455 Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);\r
456 break;\r
457\r
458 case EFI_MTFTP4_OPCODE_ERROR:\r
459 Status = EFI_TFTP_ERROR;\r
460 break;\r
d1102dba 461\r
dab714aa 462 default:\r
463 break;\r
772db4bb 464 }\r
465\r
466ON_EXIT:\r
467 //\r
468 // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
469 // restart the receive, otherwise end the session.\r
470 //\r
471 if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {\r
766c7483 472 FreePool (Packet);\r
772db4bb 473 }\r
474\r
475 if (UdpPacket != NULL) {\r
476 NetbufFree (UdpPacket);\r
477 }\r
478\r
479 if (!EFI_ERROR (Status) && !Completed) {\r
480 Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
481 }\r
482\r
483 //\r
484 // Status may have been updated by UdpIoRecvDatagram\r
485 //\r
486 if (EFI_ERROR (Status) || Completed) {\r
487 Mtftp4CleanOperation (Instance, Status);\r
488 }\r
489}\r
dab714aa 490\r
491\r
492\r
493/**\r
494 Start the MTFTP session for upload.\r
d1102dba
LG
495\r
496 It will first init some states, then send the WRQ request packet,\r
dab714aa 497 and start receiving the packet.\r
498\r
499 @param Instance The MTFTP session\r
500 @param Operation Redundant parameter, which is always\r
501 EFI_MTFTP4_OPCODE_WRQ here.\r
502\r
503 @retval EFI_SUCCESS The upload process has been started.\r
504 @retval Others Failed to start the upload.\r
505\r
506**/\r
507EFI_STATUS\r
508Mtftp4WrqStart (\r
509 IN MTFTP4_PROTOCOL *Instance,\r
510 IN UINT16 Operation\r
511 )\r
512{\r
513 EFI_STATUS Status;\r
514\r
515 //\r
516 // The valid block number range are [0, 0xffff]. For example:\r
517 // the client sends an WRQ request to the server, the server\r
518 // ACK with an ACK0 to let client start transfer the first\r
519 // packet.\r
520 //\r
521 Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);\r
522\r
523 if (EFI_ERROR (Status)) {\r
524 return Status;\r
525 }\r
526\r
527 Status = Mtftp4SendRequest (Instance);\r
528\r
529 if (EFI_ERROR (Status)) {\r
530 return Status;\r
531 }\r
532\r
533 return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
534}\r
535\r