]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c
90207a2a9d66cdf3b85a8e316cf95a0b13b6b0e4
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Mtftp4Dxe / Mtftp4Support.c
1 /** @file
2 Support routines for Mtftp.
3
4 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php<BR>
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Mtftp4Impl.h"
16
17
18 /**
19 Allocate a MTFTP4 block range, then init it to the range of [Start, End]
20
21 @param Start The start block number
22 @param End The last block number in the range
23
24 @return Pointer to the created block range, NULL if failed to allocate memory.
25
26 **/
27 MTFTP4_BLOCK_RANGE *
28 Mtftp4AllocateRange (
29 IN UINT16 Start,
30 IN UINT16 End
31 )
32 {
33 MTFTP4_BLOCK_RANGE *Range;
34
35 Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));
36
37 if (Range == NULL) {
38 return NULL;
39 }
40
41 InitializeListHead (&Range->Link);
42 Range->Start = Start;
43 Range->End = End;
44 Range->Bound = End;
45
46 return Range;
47 }
48
49
50 /**
51 Initialize the block range for either RRQ or WRQ.
52
53 RRQ and WRQ have different requirements for Start and End.
54 For example, during start up, WRQ initializes its whole valid block range
55 to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us
56 to start the upload. When the client received ACK0, it will remove 0 from the
57 range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
58 without option negotiation, the server will directly send us the BLOCK1 in
59 response to the client's RRQ. When received BLOCK1, the client will remove
60 it from the block range and send an ACK. It also works if there is option
61 negotiation.
62
63 @param Head The block range head to initialize
64 @param Start The Start block number.
65 @param End The last block number.
66
67 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
68 @retval EFI_SUCCESS The initial block range is created.
69
70 **/
71 EFI_STATUS
72 Mtftp4InitBlockRange (
73 IN LIST_ENTRY *Head,
74 IN UINT16 Start,
75 IN UINT16 End
76 )
77 {
78 MTFTP4_BLOCK_RANGE *Range;
79
80 Range = Mtftp4AllocateRange (Start, End);
81
82 if (Range == NULL) {
83 return EFI_OUT_OF_RESOURCES;
84 }
85
86 InsertTailList (Head, &Range->Link);
87 return EFI_SUCCESS;
88 }
89
90
91 /**
92 Get the first valid block number on the range list.
93
94 @param Head The block range head
95
96 @return The first valid block number, -1 if the block range is empty.
97
98 **/
99 INTN
100 Mtftp4GetNextBlockNum (
101 IN LIST_ENTRY *Head
102 )
103 {
104 MTFTP4_BLOCK_RANGE *Range;
105
106 if (IsListEmpty (Head)) {
107 return -1;
108 }
109
110 Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);
111 return Range->Start;
112 }
113
114
115 /**
116 Set the last block number of the block range list.
117
118 It will remove all the blocks after the Last. MTFTP initialize the block range
119 to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
120 last block number, it will call this function to set the last block number.
121
122 @param Head The block range list
123 @param Last The last block number
124
125 **/
126 VOID
127 Mtftp4SetLastBlockNum (
128 IN LIST_ENTRY *Head,
129 IN UINT16 Last
130 )
131 {
132 MTFTP4_BLOCK_RANGE *Range;
133
134 //
135 // Iterate from the tail to head to remove the block number
136 // after the last.
137 //
138 while (!IsListEmpty (Head)) {
139 Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);
140
141 if (Range->Start > Last) {
142 RemoveEntryList (&Range->Link);
143 FreePool (Range);
144 continue;
145 }
146
147 if (Range->End > Last) {
148 Range->End = Last;
149 }
150
151 return ;
152 }
153 }
154
155
156 /**
157 Remove the block number from the block range list.
158
159 @param Head The block range list to remove from
160 @param Num The block number to remove
161 @param Completed Whether Num is the last block number
162 @param TotalBlock The continuous block number in all
163
164 @retval EFI_NOT_FOUND The block number isn't in the block range list
165 @retval EFI_SUCCESS The block number has been removed from the list
166 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
167
168 **/
169 EFI_STATUS
170 Mtftp4RemoveBlockNum (
171 IN LIST_ENTRY *Head,
172 IN UINT16 Num,
173 IN BOOLEAN Completed,
174 OUT UINT64 *TotalBlock
175 )
176 {
177 MTFTP4_BLOCK_RANGE *Range;
178 MTFTP4_BLOCK_RANGE *NewRange;
179 LIST_ENTRY *Entry;
180
181 NET_LIST_FOR_EACH (Entry, Head) {
182
183 //
184 // Each block represents a hole [Start, End] in the file,
185 // skip to the first range with End >= Num
186 //
187 Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
188
189 if (Range->End < Num) {
190 continue;
191 }
192
193 //
194 // There are three different cases for Start
195 // 1. (Start > Num) && (End >= Num):
196 // because all the holes before this one has the condition of
197 // End < Num, so this block number has been removed.
198 //
199 // 2. (Start == Num) && (End >= Num):
200 // Need to increase the Start by one, and if End == Num, this
201 // hole has been removed completely, remove it.
202 //
203 // 3. (Start < Num) && (End >= Num):
204 // if End == Num, only need to decrease the End by one because
205 // we have (Start < Num) && (Num == End), so (Start <= End - 1).
206 // if (End > Num), the hold is splited into two holes, with
207 // [Start, Num - 1] and [Num + 1, End].
208 //
209 if (Range->Start > Num) {
210 return EFI_NOT_FOUND;
211
212 } else if (Range->Start == Num) {
213 Range->Start++;
214
215 //
216 // Note that: RFC 1350 does not mention block counter roll-over,
217 // but several TFTP hosts implement the roll-over be able to accept
218 // transfers of unlimited size. There is no consensus, however, whether
219 // the counter should wrap around to zero or to one. Many implementations
220 // wrap to zero, because this is the simplest to implement. Here we choose
221 // this solution.
222 //
223 *TotalBlock = Num;
224
225 if (Range->Round > 0) {
226 *TotalBlock += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;
227 }
228
229 if (Range->Start > Range->Bound) {
230 Range->Start = 0;
231 Range->Round ++;
232 }
233
234 if ((Range->Start > Range->End) || Completed) {
235 RemoveEntryList (&Range->Link);
236 FreePool (Range);
237 }
238
239 return EFI_SUCCESS;
240
241 } else {
242 if (Range->End == Num) {
243 Range->End--;
244 } else {
245 NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
246
247 if (NewRange == NULL) {
248 return EFI_OUT_OF_RESOURCES;
249 }
250
251 Range->End = Num - 1;
252 NetListInsertAfter (&Range->Link, &NewRange->Link);
253 }
254
255 return EFI_SUCCESS;
256 }
257 }
258
259 return EFI_NOT_FOUND;
260 }
261
262
263 /**
264 Build then transmit the request packet for the MTFTP session.
265
266 @param Instance The Mtftp session
267
268 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
269 @retval EFI_SUCCESS The request is built and sent
270 @retval Others Failed to transmit the packet.
271
272 **/
273 EFI_STATUS
274 Mtftp4SendRequest (
275 IN MTFTP4_PROTOCOL *Instance
276 )
277 {
278 EFI_MTFTP4_PACKET *Packet;
279 EFI_MTFTP4_OPTION *Options;
280 EFI_MTFTP4_TOKEN *Token;
281 NET_BUF *Nbuf;
282 UINT8 *Mode;
283 UINT8 *Cur;
284 UINT32 Len;
285 UINTN Index;
286 UINT32 Len1;
287 UINT32 Len2;
288
289 Token = Instance->Token;
290 Options = Token->OptionList;
291 Mode = Instance->Token->ModeStr;
292
293 if (Mode == NULL) {
294 Mode = (UINT8 *) "octet";
295 }
296
297 //
298 // Compute the packet length
299 //
300 Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename);
301 Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode);
302 Len = (Len1 + Len2 + 4);
303
304 for (Index = 0; Index < Token->OptionCount; Index++) {
305 Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
306 Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
307 Len += Len1 + Len2 + 2;
308 }
309
310 //
311 // Allocate a packet then copy the data over
312 //
313 if ((Nbuf = NetbufAlloc (Len)) == NULL) {
314 return EFI_OUT_OF_RESOURCES;
315 }
316
317 Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);
318 ASSERT (Packet != NULL);
319
320 Packet->OpCode = HTONS (Instance->Operation);
321 Cur = Packet->Rrq.Filename;
322 Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename);
323 Cur += AsciiStrLen ((CHAR8 *) Token->Filename) + 1;
324 Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode);
325 Cur += AsciiStrLen ((CHAR8 *) Mode) + 1;
326
327 for (Index = 0; Index < Token->OptionCount; ++Index) {
328 Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr);
329 Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1;
330
331 Cur = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr);
332 Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1;
333 }
334
335 return Mtftp4SendPacket (Instance, Nbuf);
336 }
337
338
339 /**
340 Build then send an error message.
341
342 @param Instance The MTFTP session
343 @param ErrCode The error code
344 @param ErrInfo The error message
345
346 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
347 @retval EFI_SUCCESS The error packet is transmitted.
348 @retval Others Failed to transmit the packet.
349
350 **/
351 EFI_STATUS
352 Mtftp4SendError (
353 IN MTFTP4_PROTOCOL *Instance,
354 IN UINT16 ErrCode,
355 IN UINT8 *ErrInfo
356 )
357 {
358 NET_BUF *Packet;
359 EFI_MTFTP4_PACKET *TftpError;
360 UINT32 Len;
361
362 Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));
363 Packet = NetbufAlloc (Len);
364 if (Packet == NULL) {
365 return EFI_OUT_OF_RESOURCES;
366 }
367
368 TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);
369 ASSERT (TftpError != NULL);
370
371 TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);
372 TftpError->Error.ErrorCode = HTONS (ErrCode);
373
374 AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo);
375
376 return Mtftp4SendPacket (Instance, Packet);
377 }
378
379
380 /**
381 The callback function called when the packet is transmitted.
382
383 It simply frees the packet.
384
385 @param Packet The transmitted (or failed to) packet
386 @param EndPoint The local and remote UDP access point
387 @param IoStatus The result of the transmission
388 @param Context Opaque parameter to the callback
389
390 **/
391 VOID
392 EFIAPI
393 Mtftp4OnPacketSent (
394 IN NET_BUF *Packet,
395 IN UDP_END_POINT *EndPoint,
396 IN EFI_STATUS IoStatus,
397 IN VOID *Context
398 )
399 {
400 NetbufFree (Packet);
401 }
402
403
404 /**
405 Set the timeout for the instance. User a longer time for passive instances.
406
407 @param Instance The Mtftp session to set time out
408
409 **/
410 VOID
411 Mtftp4SetTimeout (
412 IN OUT MTFTP4_PROTOCOL *Instance
413 )
414 {
415 if (Instance->Master) {
416 Instance->PacketToLive = Instance->Timeout;
417 } else {
418 Instance->PacketToLive = Instance->Timeout * 2;
419 }
420 }
421
422
423 /**
424 Send the packet for the instance.
425
426 It will first save a reference to the packet for later retransmission.
427 Then determine the destination port, listen port for requests, and connected
428 port for others. At last, send the packet out.
429
430 @param Instance The Mtftp instance
431 @param Packet The packet to send
432
433 @retval EFI_SUCCESS The packet is sent out
434 @retval Others Failed to transmit the packet.
435
436 **/
437 EFI_STATUS
438 Mtftp4SendPacket (
439 IN OUT MTFTP4_PROTOCOL *Instance,
440 IN OUT NET_BUF *Packet
441 )
442 {
443 UDP_END_POINT UdpPoint;
444 EFI_STATUS Status;
445 UINT16 OpCode;
446 UINT16 Value;
447
448 //
449 // Save the packet for retransmission
450 //
451 if (Instance->LastPacket != NULL) {
452 NetbufFree (Instance->LastPacket);
453 }
454
455 Instance->LastPacket = Packet;
456
457 Instance->CurRetry = 0;
458 Mtftp4SetTimeout (Instance);
459
460 ZeroMem (&UdpPoint, sizeof (UdpPoint));
461 UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
462
463 //
464 // Send the requests to the listening port, other packets
465 // to the connected port
466 //
467 Value = *((UINT16 *) NetbufGetByte (Packet, 0, NULL));
468 ASSERT (Value != NULL);
469 OpCode = NTOHS (Value);
470
471 if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||
472 (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
473 (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
474 UdpPoint.RemotePort = Instance->ListeningPort;
475 } else {
476 UdpPoint.RemotePort = Instance->ConnectedPort;
477 }
478
479 NET_GET_REF (Packet);
480
481 Status = UdpIoSendDatagram (
482 Instance->UnicastPort,
483 Packet,
484 &UdpPoint,
485 NULL,
486 Mtftp4OnPacketSent,
487 Instance
488 );
489
490 if (EFI_ERROR (Status)) {
491 NET_PUT_REF (Packet);
492 }
493
494 return Status;
495 }
496
497
498 /**
499 Retransmit the last packet for the instance.
500
501 @param Instance The Mtftp instance
502
503 @retval EFI_SUCCESS The last packet is retransmitted.
504 @retval Others Failed to retransmit.
505
506 **/
507 EFI_STATUS
508 Mtftp4Retransmit (
509 IN MTFTP4_PROTOCOL *Instance
510 )
511 {
512 UDP_END_POINT UdpPoint;
513 EFI_STATUS Status;
514 UINT16 OpCode;
515 UINT16 Value;
516
517 ASSERT (Instance->LastPacket != NULL);
518
519 ZeroMem (&UdpPoint, sizeof (UdpPoint));
520 UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
521
522 //
523 // Set the requests to the listening port, other packets to the connected port
524 //
525 Value = *(UINT16 *) NetbufGetByte (Instance->LastPacket, 0, NULL);
526 ASSERT (Value != NULL);
527 OpCode = NTOHS (Value);
528
529 if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
530 (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
531 UdpPoint.RemotePort = Instance->ListeningPort;
532 } else {
533 UdpPoint.RemotePort = Instance->ConnectedPort;
534 }
535
536 NET_GET_REF (Instance->LastPacket);
537
538 Status = UdpIoSendDatagram (
539 Instance->UnicastPort,
540 Instance->LastPacket,
541 &UdpPoint,
542 NULL,
543 Mtftp4OnPacketSent,
544 Instance
545 );
546
547 if (EFI_ERROR (Status)) {
548 NET_PUT_REF (Instance->LastPacket);
549 }
550
551 return Status;
552 }
553
554
555 /**
556 The timer ticking function for the Mtftp service instance.
557
558 @param Event The ticking event
559 @param Context The Mtftp service instance
560
561 **/
562 VOID
563 EFIAPI
564 Mtftp4OnTimerTick (
565 IN EFI_EVENT Event,
566 IN VOID *Context
567 )
568 {
569 MTFTP4_SERVICE *MtftpSb;
570 LIST_ENTRY *Entry;
571 LIST_ENTRY *Next;
572 MTFTP4_PROTOCOL *Instance;
573 EFI_MTFTP4_TOKEN *Token;
574
575 MtftpSb = (MTFTP4_SERVICE *) Context;
576
577 //
578 // Iterate through all the children of the Mtftp service instance. Time
579 // out the packet. If maximum retries reached, clean the session up.
580 //
581 NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
582 Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
583
584 if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {
585 continue;
586 }
587
588 //
589 // Call the user's time out handler
590 //
591 Token = Instance->Token;
592
593 if ((Token->TimeoutCallback != NULL) &&
594 EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {
595
596 Mtftp4SendError (
597 Instance,
598 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
599 (UINT8 *) "User aborted the transfer in time out"
600 );
601
602 Mtftp4CleanOperation (Instance, EFI_ABORTED);
603 continue;
604 }
605
606 //
607 // Retransmit the packet if haven't reach the maxmium retry count,
608 // otherwise exit the transfer.
609 //
610 if (++Instance->CurRetry < Instance->MaxRetry) {
611 Mtftp4Retransmit (Instance);
612 Mtftp4SetTimeout (Instance);
613 } else {
614 Mtftp4CleanOperation (Instance, EFI_TIMEOUT);
615 continue;
616 }
617 }
618 }