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