]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellNetwork1CommandsLib/Ping.c
ShellPkg/Dp: Add null pointer check
[mirror_edk2.git] / ShellPkg / Library / UefiShellNetwork1CommandsLib / Ping.c
1 /** @file
2 The implementation for Ping shell command.
3
4 (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
6 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
7
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php.
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include "UefiShellNetwork1CommandsLib.h"
19
20 #define PING_IP4_COPY_ADDRESS(Dest, Src) (CopyMem ((Dest), (Src), sizeof (EFI_IPv4_ADDRESS)))
21
22 UINT64 mCurrentTick = 0;
23
24 //
25 // Function templates to match the IPv4 and IPv6 commands that we use.
26 //
27 typedef
28 EFI_STATUS
29 (EFIAPI *PING_IPX_POLL)(
30 IN VOID *This
31 );
32
33 typedef
34 EFI_STATUS
35 (EFIAPI *PING_IPX_TRANSMIT)(
36 IN VOID *This,
37 IN VOID *Token
38 );
39
40 typedef
41 EFI_STATUS
42 (EFIAPI *PING_IPX_RECEIVE)(
43 IN VOID *This,
44 IN VOID *Token
45 );
46
47 typedef
48 EFI_STATUS
49 (EFIAPI *PING_IPX_CANCEL)(
50 IN VOID *This,
51 IN VOID *Token OPTIONAL
52 );
53
54 ///
55 /// A set of pointers to either IPv6 or IPv4 functions.
56 /// Unknown which one to the ping command.
57 ///
58 typedef struct {
59 PING_IPX_TRANSMIT Transmit;
60 PING_IPX_RECEIVE Receive;
61 PING_IPX_CANCEL Cancel;
62 PING_IPX_POLL Poll;
63 }PING_IPX_PROTOCOL;
64
65
66 typedef union {
67 VOID *RxData;
68 VOID *TxData;
69 } PING_PACKET;
70
71 //
72 // PING_IPX_COMPLETION_TOKEN
73 // structures are used for both transmit and receive operations.
74 // This version is IP-unaware.
75 //
76 typedef struct {
77 EFI_EVENT Event;
78 EFI_STATUS Status;
79 PING_PACKET Packet;
80 } PING_IPX_COMPLETION_TOKEN;
81
82 #pragma pack(1)
83 typedef struct _ICMPX_ECHO_REQUEST_REPLY {
84 UINT8 Type;
85 UINT8 Code;
86 UINT16 Checksum;
87 UINT16 Identifier;
88 UINT16 SequenceNum;
89 UINT32 TimeStamp;
90 UINT8 Data[1];
91 } ICMPX_ECHO_REQUEST_REPLY;
92 #pragma pack()
93
94 typedef struct _PING_ICMP_TX_INFO {
95 LIST_ENTRY Link;
96 UINT16 SequenceNum;
97 UINT32 TimeStamp;
98 PING_IPX_COMPLETION_TOKEN *Token;
99 } PING_ICMPX_TX_INFO;
100
101 #define DEFAULT_TIMEOUT 5000
102 #define MAX_SEND_NUMBER 10000
103 #define MAX_BUFFER_SIZE 32768
104 #define DEFAULT_TIMER_PERIOD 358049
105 #define ONE_SECOND 10000000
106 #define PING_IP_CHOICE_IP4 1
107 #define PING_IP_CHOICE_IP6 2
108 #define DEFAULT_SEND_COUNT 10
109 #define DEFAULT_BUFFER_SIZE 16
110 #define ICMP_V4_ECHO_REQUEST 0x8
111 #define ICMP_V4_ECHO_REPLY 0x0
112 #define STALL_1_MILLI_SECOND 1000
113
114 #define PING_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'i', 'n', 'g')
115 typedef struct _PING_PRIVATE_DATA {
116 UINT32 Signature;
117 EFI_HANDLE NicHandle;
118 EFI_HANDLE IpChildHandle;
119 EFI_EVENT Timer;
120
121 UINT32 TimerPeriod;
122 UINT32 RttTimerTick;
123 EFI_EVENT RttTimer;
124
125 EFI_STATUS Status;
126 LIST_ENTRY TxList;
127 UINT16 RxCount;
128 UINT16 TxCount;
129 UINT64 RttSum;
130 UINT64 RttMin;
131 UINT64 RttMax;
132 UINT32 SequenceNum;
133
134 UINT32 SendNum;
135 UINT32 BufferSize;
136 UINT32 IpChoice;
137
138 PING_IPX_PROTOCOL ProtocolPointers;
139 VOID *IpProtocol;
140 UINT8 SrcAddress[MAX(sizeof(EFI_IPv6_ADDRESS) , sizeof(EFI_IPv4_ADDRESS) )];
141 UINT8 DstAddress[MAX(sizeof(EFI_IPv6_ADDRESS) , sizeof(EFI_IPv4_ADDRESS) )];
142 PING_IPX_COMPLETION_TOKEN RxToken;
143 UINT16 FailedCount;
144 } PING_PRIVATE_DATA;
145
146 /**
147 Calculate the internet checksum (see RFC 1071).
148
149 @param[in] Packet Buffer which contains the data to be checksummed.
150 @param[in] Length Length to be checksummed.
151
152 @retval Checksum Returns the 16 bit ones complement of
153 ones complement sum of 16 bit words
154 **/
155 UINT16
156 NetChecksum (
157 IN UINT8 *Buffer,
158 IN UINT32 Length
159 )
160 {
161 UINT32 Sum;
162 UINT8 Odd;
163 UINT16 *Packet;
164
165 Packet = (UINT16 *) Buffer;
166
167 Sum = 0;
168 Odd = (UINT8) (Length & 1);
169 Length >>= 1;
170 while ((Length--) != 0) {
171 Sum += *Packet++;
172 }
173
174 if (Odd != 0) {
175 Sum += *(UINT8 *) Packet;
176 }
177
178 Sum = (Sum & 0xffff) + (Sum >> 16);
179
180 //
181 // in case above carried
182 //
183 Sum += Sum >> 16;
184
185 return (UINT16) Sum;
186 }
187
188 /**
189 Reads and returns the current value of register.
190 In IA64, the register is the Interval Timer Vector (ITV).
191 In X86(IA32/X64), the register is the Time Stamp Counter (TSC)
192
193 @return The current value of the register.
194
195 **/
196
197 STATIC CONST SHELL_PARAM_ITEM PingParamList[] = {
198 {
199 L"-l",
200 TypeValue
201 },
202 {
203 L"-n",
204 TypeValue
205 },
206 {
207 L"-s",
208 TypeValue
209 },
210 {
211 L"-_s",
212 TypeValue
213 },
214 {
215 L"-_ip6",
216 TypeFlag
217 },
218 {
219 NULL,
220 TypeMax
221 },
222 };
223
224 //
225 // Global Variables in Ping command.
226 //
227 STATIC CONST CHAR16 *mDstString;
228 STATIC CONST CHAR16 *mSrcString;
229
230 /**
231 RTT timer tick routine.
232
233 @param[in] Event A EFI_EVENT type event.
234 @param[in] Context The pointer to Context.
235
236 **/
237 VOID
238 EFIAPI
239 RttTimerTickRoutine (
240 IN EFI_EVENT Event,
241 IN VOID *Context
242 )
243 {
244 UINT32 *RttTimerTick;
245
246 RttTimerTick = (UINT32*) Context;
247 (*RttTimerTick)++;
248 }
249
250 /**
251 Get the timer period of the system.
252
253 This function tries to get the system timer period by creating
254 an 1ms period timer.
255
256 @return System timer period in MS, or 0 if operation failed.
257
258 **/
259 UINT32
260 GetTimerPeriod(
261 VOID
262 )
263 {
264 EFI_STATUS Status;
265 UINT32 RttTimerTick;
266 EFI_EVENT TimerEvent;
267 UINT32 StallCounter;
268 EFI_TPL OldTpl;
269
270 RttTimerTick = 0;
271 StallCounter = 0;
272
273 Status = gBS->CreateEvent (
274 EVT_TIMER | EVT_NOTIFY_SIGNAL,
275 TPL_NOTIFY,
276 RttTimerTickRoutine,
277 &RttTimerTick,
278 &TimerEvent
279 );
280 if (EFI_ERROR (Status)) {
281 return 0;
282 }
283
284 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
285 Status = gBS->SetTimer (
286 TimerEvent,
287 TimerPeriodic,
288 TICKS_PER_MS
289 );
290 if (EFI_ERROR (Status)) {
291 gBS->CloseEvent (TimerEvent);
292 return 0;
293 }
294
295 while (RttTimerTick < 10) {
296 gBS->Stall (STALL_1_MILLI_SECOND);
297 ++StallCounter;
298 }
299
300 gBS->RestoreTPL (OldTpl);
301
302 gBS->SetTimer (TimerEvent, TimerCancel, 0);
303 gBS->CloseEvent (TimerEvent);
304
305 return StallCounter / RttTimerTick;
306 }
307
308 /**
309 Initialize the timer event for RTT (round trip time).
310
311 @param[in] Private The pointer to PING_PRIVATE_DATA.
312
313 @retval EFI_SUCCESS RTT timer is started.
314 @retval Others Failed to start the RTT timer.
315
316 **/
317 EFI_STATUS
318 PingInitRttTimer (
319 PING_PRIVATE_DATA *Private
320 )
321 {
322 EFI_STATUS Status;
323
324 Private->TimerPeriod = GetTimerPeriod ();
325 if (Private->TimerPeriod == 0) {
326 return EFI_ABORTED;
327 }
328
329 Private->RttTimerTick = 0;
330 Status = gBS->CreateEvent (
331 EVT_TIMER | EVT_NOTIFY_SIGNAL,
332 TPL_NOTIFY,
333 RttTimerTickRoutine,
334 &Private->RttTimerTick,
335 &Private->RttTimer
336 );
337 if (EFI_ERROR (Status)) {
338 return Status;
339 }
340
341 Status = gBS->SetTimer (
342 Private->RttTimer,
343 TimerPeriodic,
344 TICKS_PER_MS
345 );
346 if (EFI_ERROR (Status)) {
347 gBS->CloseEvent (Private->RttTimer);
348 return Status;
349 }
350
351 return EFI_SUCCESS;
352 }
353
354 /**
355 Free RTT timer event resource.
356
357 @param[in] Private The pointer to PING_PRIVATE_DATA.
358
359 **/
360 VOID
361 PingFreeRttTimer (
362 PING_PRIVATE_DATA *Private
363 )
364 {
365 if (Private->RttTimer != NULL) {
366 gBS->SetTimer (Private->RttTimer, TimerCancel, 0);
367 gBS->CloseEvent (Private->RttTimer);
368 }
369 }
370
371 /**
372 Read the current time.
373
374 @param[in] Private The pointer to PING_PRIVATE_DATA.
375
376 @retval the current tick value.
377 **/
378 UINT32
379 ReadTime (
380 PING_PRIVATE_DATA *Private
381 )
382 {
383 return Private->RttTimerTick;
384 }
385
386 /**
387 Calculate a duration in ms.
388
389 @param[in] Private The pointer to PING_PRIVATE_DATA.
390 @param[in] Begin The start point of time.
391 @param[in] End The end point of time.
392
393 @return The duration in ms.
394 @retval 0 The parameters were not valid.
395 **/
396 UINT32
397 CalculateTick (
398 PING_PRIVATE_DATA *Private,
399 IN UINT32 Begin,
400 IN UINT32 End
401 )
402 {
403 if (End < Begin) {
404 return (0);
405 }
406
407 return (End - Begin) * Private->TimerPeriod;
408 }
409
410 /**
411 Destroy PING_ICMPX_TX_INFO, and recollect the memory.
412
413 @param[in] TxInfo The pointer to PING_ICMPX_TX_INFO.
414 @param[in] IpChoice Whether the token is IPv4 or IPv6
415 **/
416 VOID
417 PingDestroyTxInfo (
418 IN PING_ICMPX_TX_INFO *TxInfo,
419 IN UINT32 IpChoice
420 )
421 {
422 EFI_IP6_TRANSMIT_DATA *Ip6TxData;
423 EFI_IP4_TRANSMIT_DATA *Ip4TxData;
424 EFI_IP6_FRAGMENT_DATA *FragData;
425 UINTN Index;
426
427 if (TxInfo == NULL) {
428 return;
429 }
430
431 if (TxInfo->Token != NULL) {
432
433 if (TxInfo->Token->Event != NULL) {
434 gBS->CloseEvent (TxInfo->Token->Event);
435 }
436
437 if (TxInfo->Token->Packet.TxData != NULL) {
438 if (IpChoice == PING_IP_CHOICE_IP6) {
439 Ip6TxData = TxInfo->Token->Packet.TxData;
440
441 if (Ip6TxData->OverrideData != NULL) {
442 FreePool (Ip6TxData->OverrideData);
443 }
444
445 if (Ip6TxData->ExtHdrs != NULL) {
446 FreePool (Ip6TxData->ExtHdrs);
447 }
448
449 for (Index = 0; Index < Ip6TxData->FragmentCount; Index++) {
450 FragData = Ip6TxData->FragmentTable[Index].FragmentBuffer;
451 if (FragData != NULL) {
452 FreePool (FragData);
453 }
454 }
455 } else {
456 Ip4TxData = TxInfo->Token->Packet.TxData;
457
458 if (Ip4TxData->OverrideData != NULL) {
459 FreePool (Ip4TxData->OverrideData);
460 }
461
462 for (Index = 0; Index < Ip4TxData->FragmentCount; Index++) {
463 FragData = Ip4TxData->FragmentTable[Index].FragmentBuffer;
464 if (FragData != NULL) {
465 FreePool (FragData);
466 }
467 }
468 }
469 }
470
471 FreePool (TxInfo->Token);
472 }
473
474 FreePool (TxInfo);
475 }
476
477 /**
478 Match the request, and reply with SequenceNum/TimeStamp.
479
480 @param[in] Private The pointer to PING_PRIVATE_DATA.
481 @param[in] Packet The pointer to ICMPX_ECHO_REQUEST_REPLY.
482
483 @retval EFI_SUCCESS The match is successful.
484 @retval EFI_NOT_FOUND The reply can't be matched with any request.
485
486 **/
487 EFI_STATUS
488 Ping6MatchEchoReply (
489 IN PING_PRIVATE_DATA *Private,
490 IN ICMPX_ECHO_REQUEST_REPLY *Packet
491 )
492 {
493 PING_ICMPX_TX_INFO *TxInfo;
494 LIST_ENTRY *Entry;
495 LIST_ENTRY *NextEntry;
496
497 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
498 TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
499
500 if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
501 Private->RxCount++;
502 RemoveEntryList (&TxInfo->Link);
503 PingDestroyTxInfo (TxInfo, Private->IpChoice);
504 return EFI_SUCCESS;
505 }
506 }
507
508 return EFI_NOT_FOUND;
509 }
510
511 /**
512 The original intention is to send a request.
513 Currently, the application retransmits an icmp6 echo request packet
514 per second in sendnumber times that is specified by the user.
515 Because nothing can be done here, all things move to the timer rountine.
516
517 @param[in] Event A EFI_EVENT type event.
518 @param[in] Context The pointer to Context.
519
520 **/
521 VOID
522 EFIAPI
523 Ping6OnEchoRequestSent (
524 IN EFI_EVENT Event,
525 IN VOID *Context
526 )
527 {
528 }
529
530 /**
531 receive reply, match and print reply infomation.
532
533 @param[in] Event A EFI_EVENT type event.
534 @param[in] Context The pointer to context.
535
536 **/
537 VOID
538 EFIAPI
539 Ping6OnEchoReplyReceived (
540 IN EFI_EVENT Event,
541 IN VOID *Context
542 )
543 {
544 EFI_STATUS Status;
545 PING_PRIVATE_DATA *Private;
546 ICMPX_ECHO_REQUEST_REPLY *Reply;
547 UINT32 PayLoad;
548 UINT32 Rtt;
549
550 Private = (PING_PRIVATE_DATA *) Context;
551
552 if (Private == NULL || Private->Status == EFI_ABORTED || Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
553 return;
554 }
555
556 if (Private->RxToken.Packet.RxData == NULL) {
557 return;
558 }
559
560 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
561 Reply = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
562 PayLoad = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
563 if (((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->NextHeader != IP6_ICMP) {
564 goto ON_EXIT;
565 }
566 if (!IP6_IS_MULTICAST ((EFI_IPv6_ADDRESS*)&Private->DstAddress) &&
567 !EFI_IP6_EQUAL (&((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv6_ADDRESS*)&Private->DstAddress)) {
568 goto ON_EXIT;
569 }
570
571 if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
572 goto ON_EXIT;
573 }
574 } else {
575 Reply = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
576 PayLoad = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
577 if (!IP4_IS_MULTICAST (EFI_IP4(*(EFI_IPv4_ADDRESS*)Private->DstAddress)) &&
578 !EFI_IP4_EQUAL (&((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv4_ADDRESS*)&Private->DstAddress)) {
579 goto ON_EXIT;
580 }
581
582 if ((Reply->Type != ICMP_V4_ECHO_REPLY) || (Reply->Code != 0)) {
583 goto ON_EXIT;
584 }
585 }
586
587
588 if (PayLoad != Private->BufferSize) {
589 goto ON_EXIT;
590 }
591 //
592 // Check whether the reply matches the sent request before.
593 //
594 Status = Ping6MatchEchoReply (Private, Reply);
595 if (EFI_ERROR(Status)) {
596 goto ON_EXIT;
597 }
598 //
599 // Display statistics on this icmp6 echo reply packet.
600 //
601 Rtt = CalculateTick (Private, Reply->TimeStamp, ReadTime (Private));
602
603 Private->RttSum += Rtt;
604 Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin;
605 Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax;
606
607 ShellPrintHiiEx (
608 -1,
609 -1,
610 NULL,
611 STRING_TOKEN (STR_PING_REPLY_INFO),
612 gShellNetwork1HiiHandle,
613 PayLoad,
614 mDstString,
615 Reply->SequenceNum,
616 Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->HopLimit:0,
617 Rtt,
618 Rtt + Private->TimerPeriod
619 );
620
621 ON_EXIT:
622
623 if (Private->RxCount < Private->SendNum) {
624 //
625 // Continue to receive icmp echo reply packets.
626 //
627 Private->RxToken.Status = EFI_ABORTED;
628
629 Status = Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken);
630
631 if (EFI_ERROR (Status)) {
632 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_RECEIVE), gShellNetwork1HiiHandle, Status);
633 Private->Status = EFI_ABORTED;
634 }
635 } else {
636 //
637 // All reply have already been received from the dest host.
638 //
639 Private->Status = EFI_SUCCESS;
640 }
641 //
642 // Singal to recycle the each rxdata here, not at the end of process.
643 //
644 gBS->SignalEvent (Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal:((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal);
645 }
646
647 /**
648 Create a PING_IPX_COMPLETION_TOKEN.
649
650 @param[in] Private The pointer of PING_PRIVATE_DATA.
651 @param[in] TimeStamp The TimeStamp of request.
652 @param[in] SequenceNum The SequenceNum of request.
653
654 @return The pointer of PING_IPX_COMPLETION_TOKEN.
655
656 **/
657 PING_IPX_COMPLETION_TOKEN *
658 PingGenerateToken (
659 IN PING_PRIVATE_DATA *Private,
660 IN UINT32 TimeStamp,
661 IN UINT16 SequenceNum
662 )
663 {
664 EFI_STATUS Status;
665 PING_IPX_COMPLETION_TOKEN *Token;
666 VOID *TxData;
667 ICMPX_ECHO_REQUEST_REPLY *Request;
668 UINT16 HeadSum;
669 UINT16 TempChecksum;
670
671 Request = AllocateZeroPool (Private->BufferSize);
672 if (Request == NULL) {
673 return NULL;
674 }
675 TxData = AllocateZeroPool (Private->IpChoice==PING_IP_CHOICE_IP6?sizeof (EFI_IP6_TRANSMIT_DATA):sizeof (EFI_IP4_TRANSMIT_DATA));
676 if (TxData == NULL) {
677 FreePool (Request);
678 return NULL;
679 }
680 Token = AllocateZeroPool (sizeof (PING_IPX_COMPLETION_TOKEN));
681 if (Token == NULL) {
682 FreePool (Request);
683 FreePool (TxData);
684 return NULL;
685 }
686
687 //
688 // Assembly echo request packet.
689 //
690 Request->Type = (UINT8)(Private->IpChoice==PING_IP_CHOICE_IP6?ICMP_V6_ECHO_REQUEST:ICMP_V4_ECHO_REQUEST);
691 Request->Code = 0;
692 Request->SequenceNum = SequenceNum;
693 Request->Identifier = 0;
694 Request->Checksum = 0;
695
696 //
697 // Assembly token for transmit.
698 //
699 if (Private->IpChoice==PING_IP_CHOICE_IP6) {
700 Request->TimeStamp = TimeStamp;
701 ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrsLength = 0;
702 ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrs = NULL;
703 ((EFI_IP6_TRANSMIT_DATA*)TxData)->OverrideData = 0;
704 ((EFI_IP6_TRANSMIT_DATA*)TxData)->DataLength = Private->BufferSize;
705 ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentCount = 1;
706 ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
707 ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
708 } else {
709 ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsLength = 0;
710 ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsBuffer = NULL;
711 ((EFI_IP4_TRANSMIT_DATA*)TxData)->OverrideData = 0;
712 ((EFI_IP4_TRANSMIT_DATA*)TxData)->TotalDataLength = Private->BufferSize;
713 ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentCount = 1;
714 ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
715 ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
716 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[0] = Private->DstAddress[0];
717 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[1] = Private->DstAddress[1];
718 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[2] = Private->DstAddress[2];
719 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[3] = Private->DstAddress[3];
720
721 HeadSum = NetChecksum ((UINT8 *) Request, Private->BufferSize);
722 Request->TimeStamp = TimeStamp;
723 TempChecksum = NetChecksum ((UINT8 *) &Request->TimeStamp, sizeof (UINT64));
724 Request->Checksum = (UINT16)(~NetAddChecksum (HeadSum, TempChecksum));
725 }
726
727
728 Token->Status = EFI_ABORTED;
729 Token->Packet.TxData = TxData;
730
731 Status = gBS->CreateEvent (
732 EVT_NOTIFY_SIGNAL,
733 TPL_CALLBACK,
734 Ping6OnEchoRequestSent,
735 Private,
736 &Token->Event
737 );
738
739 if (EFI_ERROR (Status)) {
740 FreePool (Request);
741 FreePool (TxData);
742 FreePool (Token);
743 return NULL;
744 }
745
746 return Token;
747 }
748
749 /**
750 Transmit the PING_IPX_COMPLETION_TOKEN.
751
752 @param[in] Private The pointer of PING_PRIVATE_DATA.
753
754 @retval EFI_SUCCESS Transmitted successfully.
755 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
756 @retval others Transmitted unsuccessfully.
757
758 **/
759 EFI_STATUS
760 PingSendEchoRequest (
761 IN PING_PRIVATE_DATA *Private
762 )
763 {
764 EFI_STATUS Status;
765 PING_ICMPX_TX_INFO *TxInfo;
766
767 TxInfo = AllocateZeroPool (sizeof (PING_ICMPX_TX_INFO));
768
769 if (TxInfo == NULL) {
770 return EFI_OUT_OF_RESOURCES;
771 }
772
773 TxInfo->TimeStamp = ReadTime (Private);
774 TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
775 TxInfo->Token = PingGenerateToken (
776 Private,
777 TxInfo->TimeStamp,
778 TxInfo->SequenceNum
779 );
780
781 if (TxInfo->Token == NULL) {
782 PingDestroyTxInfo (TxInfo, Private->IpChoice);
783 return EFI_OUT_OF_RESOURCES;
784 }
785
786 ASSERT(Private->ProtocolPointers.Transmit != NULL);
787 Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token);
788
789 if (EFI_ERROR (Status)) {
790 PingDestroyTxInfo (TxInfo, Private->IpChoice);
791 return Status;
792 }
793
794 InsertTailList (&Private->TxList, &TxInfo->Link);
795 Private->TxCount++;
796
797 return EFI_SUCCESS;
798 }
799
800 /**
801 Place a completion token into the receive packet queue to receive the echo reply.
802
803 @param[in] Private The pointer of PING_PRIVATE_DATA.
804
805 @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
806 @retval others Put the token into the receive packet queue unsuccessfully.
807
808 **/
809 EFI_STATUS
810 Ping6ReceiveEchoReply (
811 IN PING_PRIVATE_DATA *Private
812 )
813 {
814 EFI_STATUS Status;
815
816 ZeroMem (&Private->RxToken, sizeof (PING_IPX_COMPLETION_TOKEN));
817
818 Status = gBS->CreateEvent (
819 EVT_NOTIFY_SIGNAL,
820 TPL_CALLBACK,
821 Ping6OnEchoReplyReceived,
822 Private,
823 &Private->RxToken.Event
824 );
825
826 if (EFI_ERROR (Status)) {
827 return Status;
828 }
829
830 Private->RxToken.Status = EFI_NOT_READY;
831
832 Status = Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken);
833 if (EFI_ERROR (Status)) {
834 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_RECEIVE), gShellNetwork1HiiHandle, Status);
835 }
836 return Status;
837 }
838
839 /**
840 Remove the timeout request from the list.
841
842 @param[in] Event A EFI_EVENT type event.
843 @param[in] Context The pointer to Context.
844
845 **/
846 VOID
847 EFIAPI
848 Ping6OnTimerRoutine (
849 IN EFI_EVENT Event,
850 IN VOID *Context
851 )
852 {
853 EFI_STATUS Status;
854 PING_PRIVATE_DATA *Private;
855 PING_ICMPX_TX_INFO *TxInfo;
856 LIST_ENTRY *Entry;
857 LIST_ENTRY *NextEntry;
858 UINT64 Time;
859
860 Private = (PING_PRIVATE_DATA *) Context;
861 if (Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
862 Private->Status = EFI_NOT_FOUND;
863 return;
864 }
865
866 //
867 // Retransmit icmp6 echo request packets per second in sendnumber times.
868 //
869 if (Private->TxCount < Private->SendNum) {
870
871 Status = PingSendEchoRequest (Private);
872 if (Private->TxCount != 0){
873 if (EFI_ERROR (Status)) {
874 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_SEND_REQUEST), gShellNetwork1HiiHandle, Private->TxCount + 1);
875 }
876 }
877 }
878 //
879 // Check whether any icmp6 echo request in the list timeout.
880 //
881 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
882 TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
883 Time = CalculateTick (Private, TxInfo->TimeStamp, ReadTime (Private));
884
885 //
886 // Remove the timeout echo request from txlist.
887 //
888 if (Time > DEFAULT_TIMEOUT) {
889
890 if (EFI_ERROR (TxInfo->Token->Status)) {
891 Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
892 }
893 //
894 // Remove the timeout icmp6 echo request from list.
895 //
896 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_TIMEOUT), gShellNetwork1HiiHandle, TxInfo->SequenceNum);
897
898 RemoveEntryList (&TxInfo->Link);
899 PingDestroyTxInfo (TxInfo, Private->IpChoice);
900
901 Private->RxCount++;
902 Private->FailedCount++;
903
904 if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
905 //
906 // All the left icmp6 echo request in the list timeout.
907 //
908 Private->Status = EFI_TIMEOUT;
909 }
910 }
911 }
912 }
913
914 /**
915 Determine if a IP4 address is Link Local.
916
917 169.254.1.0 through 169.254.254.255 is link local.
918
919 @param[in] Address The address to test.
920
921 @retval TRUE It is.
922 @retval FALSE It is not.
923 **/
924 BOOLEAN
925 PingNetIp4IsLinkLocalAddr (
926 IN CONST EFI_IPv4_ADDRESS *Address
927 )
928 {
929 return ((BOOLEAN)(Address->Addr[0] == 169 && Address->Addr[1] == 254 && Address->Addr[2] >= 1 && Address->Addr[2] <= 254));
930 }
931
932 /**
933 Determine if a IP4 address is unspecified.
934
935 @param[in] Address The address to test.
936
937 @retval TRUE It is.
938 @retval FALSE It is not.
939 **/
940 BOOLEAN
941 PingNetIp4IsUnspecifiedAddr (
942 IN CONST EFI_IPv4_ADDRESS *Address
943 )
944 {
945 return ((BOOLEAN)((ReadUnaligned32 ((UINT32*)&Address->Addr[0])) == 0x00000000));
946 }
947
948 /**
949 Create a valid IP instance.
950
951 @param[in] Private The pointer of PING_PRIVATE_DATA.
952
953 @retval EFI_SUCCESS Create a valid IPx instance successfully.
954 @retval EFI_ABORTED Locate handle with ipx service binding protocol unsuccessfully.
955 @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link-local address.
956 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
957 @retval EFI_NOT_FOUND The source address is not found.
958 **/
959 EFI_STATUS
960 PingCreateIpInstance (
961 IN PING_PRIVATE_DATA *Private
962 )
963 {
964 EFI_STATUS Status;
965 UINTN HandleIndex;
966 UINTN HandleNum;
967 EFI_HANDLE *HandleBuffer;
968 BOOLEAN UnspecifiedSrc;
969 EFI_STATUS MediaStatus;
970 EFI_SERVICE_BINDING_PROTOCOL *EfiSb;
971 VOID *IpXCfg;
972 EFI_IP6_CONFIG_DATA Ip6Config;
973 EFI_IP4_CONFIG_DATA Ip4Config;
974 VOID *IpXInterfaceInfo;
975 UINTN IfInfoSize;
976 EFI_IPv6_ADDRESS *Addr;
977 UINTN AddrIndex;
978
979 HandleBuffer = NULL;
980 UnspecifiedSrc = FALSE;
981 MediaStatus = EFI_SUCCESS;
982 EfiSb = NULL;
983 IpXInterfaceInfo = NULL;
984 IfInfoSize = 0;
985
986 //
987 // Locate all the handles with ip6 service binding protocol.
988 //
989 Status = gBS->LocateHandleBuffer (
990 ByProtocol,
991 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
992 NULL,
993 &HandleNum,
994 &HandleBuffer
995 );
996 if (EFI_ERROR (Status) || (HandleNum == 0) || (HandleBuffer == NULL)) {
997 return EFI_ABORTED;
998 }
999
1000 if (Private->IpChoice == PING_IP_CHOICE_IP6 ? NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress) : \
1001 PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress)) {
1002 //
1003 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
1004 //
1005 UnspecifiedSrc = TRUE;
1006 }
1007
1008 //
1009 // Source address is required when pinging a link-local address.
1010 //
1011 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1012 if (NetIp6IsLinkLocalAddr ((EFI_IPv6_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) {
1013 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle);
1014 Status = EFI_INVALID_PARAMETER;
1015 goto ON_ERROR;
1016 }
1017 } else {
1018 ASSERT(Private->IpChoice == PING_IP_CHOICE_IP4);
1019 if (PingNetIp4IsLinkLocalAddr ((EFI_IPv4_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) {
1020 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle);
1021 Status = EFI_INVALID_PARAMETER;
1022 goto ON_ERROR;
1023 }
1024 }
1025
1026 //
1027 // For each ip6 protocol, check interface addresses list.
1028 //
1029 for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
1030 EfiSb = NULL;
1031 IpXInterfaceInfo = NULL;
1032 IfInfoSize = 0;
1033
1034 if (UnspecifiedSrc) {
1035 //
1036 // Check media.
1037 //
1038 NetLibDetectMediaWaitTimeout (HandleBuffer[HandleIndex], 0, &MediaStatus);
1039 if (MediaStatus != EFI_SUCCESS) {
1040 //
1041 // Skip this one.
1042 //
1043 continue;
1044 }
1045 }
1046
1047 Status = gBS->HandleProtocol (
1048 HandleBuffer[HandleIndex],
1049 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
1050 (VOID **) &EfiSb
1051 );
1052 if (EFI_ERROR (Status)) {
1053 goto ON_ERROR;
1054 }
1055
1056 //
1057 // Ip6config protocol and ip6 service binding protocol are installed
1058 // on the same handle.
1059 //
1060 Status = gBS->HandleProtocol (
1061 HandleBuffer[HandleIndex],
1062 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ConfigProtocolGuid:&gEfiIp4Config2ProtocolGuid,
1063 (VOID **) &IpXCfg
1064 );
1065
1066 if (EFI_ERROR (Status)) {
1067 goto ON_ERROR;
1068 }
1069 //
1070 // Get the interface information size.
1071 //
1072 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1073 Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
1074 IpXCfg,
1075 Ip6ConfigDataTypeInterfaceInfo,
1076 &IfInfoSize,
1077 NULL
1078 );
1079 } else {
1080 Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
1081 IpXCfg,
1082 Ip4Config2DataTypeInterfaceInfo,
1083 &IfInfoSize,
1084 NULL
1085 );
1086 }
1087
1088 //
1089 // Skip the ones not in current use.
1090 //
1091 if (Status == EFI_NOT_STARTED) {
1092 continue;
1093 }
1094
1095 if (Status != EFI_BUFFER_TOO_SMALL) {
1096 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
1097 goto ON_ERROR;
1098 }
1099
1100 IpXInterfaceInfo = AllocateZeroPool (IfInfoSize);
1101
1102 if (IpXInterfaceInfo == NULL) {
1103 Status = EFI_OUT_OF_RESOURCES;
1104 goto ON_ERROR;
1105 }
1106 //
1107 // Get the interface info.
1108 //
1109 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1110 Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
1111 IpXCfg,
1112 Ip6ConfigDataTypeInterfaceInfo,
1113 &IfInfoSize,
1114 IpXInterfaceInfo
1115 );
1116 } else {
1117 Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
1118 IpXCfg,
1119 Ip4Config2DataTypeInterfaceInfo,
1120 &IfInfoSize,
1121 IpXInterfaceInfo
1122 );
1123 }
1124
1125 if (EFI_ERROR (Status)) {
1126 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
1127 goto ON_ERROR;
1128 }
1129 //
1130 // Check whether the source address is one of the interface addresses.
1131 //
1132 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1133 for (AddrIndex = 0; AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount; AddrIndex++) {
1134 Addr = &(((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfo[AddrIndex].Address);
1135
1136 if (UnspecifiedSrc) {
1137 if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
1138 //
1139 // Select the interface automatically.
1140 //
1141 CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress));
1142 break;
1143 }
1144 } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
1145 //
1146 // Match a certain interface address.
1147 //
1148 break;
1149 }
1150 }
1151
1152 if (AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount) {
1153 //
1154 // Found a nic handle with right interface address.
1155 //
1156 break;
1157 }
1158 } else {
1159 if (UnspecifiedSrc) {
1160 if (!PingNetIp4IsUnspecifiedAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress) &&
1161 !PingNetIp4IsLinkLocalAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) {
1162 //
1163 // Select the interface automatically.
1164 //
1165 break;
1166 }
1167 } else if (EFI_IP4_EQUAL (&Private->SrcAddress, &((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) {
1168 //
1169 // Match a certain interface address.
1170 //
1171 break;
1172 }
1173 }
1174
1175 FreePool (IpXInterfaceInfo);
1176 IpXInterfaceInfo = NULL;
1177 }
1178 //
1179 // No exact interface address matched.
1180 //
1181
1182 if (HandleIndex == HandleNum) {
1183 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIGD_NIC_NF), gShellNetwork1HiiHandle, L"ping");
1184 Status = EFI_NOT_FOUND;
1185 goto ON_ERROR;
1186 }
1187
1188 Private->NicHandle = HandleBuffer[HandleIndex];
1189
1190 ASSERT (EfiSb != NULL);
1191 Status = EfiSb->CreateChild (EfiSb, &Private->IpChildHandle);
1192
1193 if (EFI_ERROR (Status)) {
1194 goto ON_ERROR;
1195 }
1196 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1197 Status = gBS->OpenProtocol (
1198 Private->IpChildHandle,
1199 &gEfiIp6ProtocolGuid,
1200 &Private->IpProtocol,
1201 gImageHandle,
1202 Private->IpChildHandle,
1203 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1204 );
1205 if (EFI_ERROR (Status)) {
1206 goto ON_ERROR;
1207 }
1208
1209
1210 ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
1211
1212 //
1213 // Configure the ip6 instance for icmp6 packet exchange.
1214 //
1215 Ip6Config.DefaultProtocol = 58;
1216 Ip6Config.AcceptAnyProtocol = FALSE;
1217 Ip6Config.AcceptIcmpErrors = TRUE;
1218 Ip6Config.AcceptPromiscuous = FALSE;
1219 Ip6Config.TrafficClass = 0;
1220 Ip6Config.HopLimit = 128;
1221 Ip6Config.FlowLabel = 0;
1222 Ip6Config.ReceiveTimeout = 0;
1223 Ip6Config.TransmitTimeout = 0;
1224
1225 IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
1226 IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
1227
1228 Status = ((EFI_IP6_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip6Config);
1229
1230 if (EFI_ERROR (Status)) {
1231 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1232 goto ON_ERROR;
1233 }
1234
1235 Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Transmit;
1236 Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Receive;
1237 Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Cancel;
1238 Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Poll;
1239 } else {
1240 Status = gBS->OpenProtocol (
1241 Private->IpChildHandle,
1242 &gEfiIp4ProtocolGuid,
1243 &Private->IpProtocol,
1244 gImageHandle,
1245 Private->IpChildHandle,
1246 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1247 );
1248 if (EFI_ERROR (Status)) {
1249 goto ON_ERROR;
1250 }
1251
1252
1253 ZeroMem (&Ip4Config, sizeof (EFI_IP4_CONFIG_DATA));
1254
1255 //
1256 // Configure the ip4 instance for icmp4 packet exchange.
1257 //
1258 Ip4Config.DefaultProtocol = 1;
1259 Ip4Config.AcceptAnyProtocol = FALSE;
1260 Ip4Config.AcceptBroadcast = FALSE;
1261 Ip4Config.AcceptIcmpErrors = TRUE;
1262 Ip4Config.AcceptPromiscuous = FALSE;
1263 Ip4Config.DoNotFragment = FALSE;
1264 Ip4Config.RawData = FALSE;
1265 Ip4Config.ReceiveTimeout = 0;
1266 Ip4Config.TransmitTimeout = 0;
1267 Ip4Config.UseDefaultAddress = TRUE;
1268 Ip4Config.TimeToLive = 128;
1269 Ip4Config.TypeOfService = 0;
1270
1271 Status = ((EFI_IP4_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip4Config);
1272
1273 if (EFI_ERROR (Status)) {
1274 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1275 goto ON_ERROR;
1276 }
1277
1278 Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Transmit;
1279 Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Receive;
1280 Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Cancel;
1281 Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Poll;
1282 }
1283
1284 if (HandleBuffer != NULL) {
1285 FreePool (HandleBuffer);
1286 }
1287
1288 return EFI_SUCCESS;
1289
1290 ON_ERROR:
1291 if (HandleBuffer != NULL) {
1292 FreePool (HandleBuffer);
1293 }
1294
1295 if (IpXInterfaceInfo != NULL) {
1296 FreePool (IpXInterfaceInfo);
1297 }
1298
1299 if ((EfiSb != NULL) && (Private->IpChildHandle != NULL)) {
1300 EfiSb->DestroyChild (EfiSb, Private->IpChildHandle);
1301 }
1302
1303 return Status;
1304 }
1305
1306 /**
1307 Destroy the IP instance.
1308
1309 @param[in] Private The pointer of PING_PRIVATE_DATA.
1310
1311 **/
1312 VOID
1313 Ping6DestroyIp6Instance (
1314 IN PING_PRIVATE_DATA *Private
1315 )
1316 {
1317 EFI_STATUS Status;
1318 EFI_SERVICE_BINDING_PROTOCOL *IpSb;
1319
1320 gBS->CloseProtocol (
1321 Private->IpChildHandle,
1322 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ProtocolGuid:&gEfiIp4ProtocolGuid,
1323 gImageHandle,
1324 Private->IpChildHandle
1325 );
1326
1327 Status = gBS->HandleProtocol (
1328 Private->NicHandle,
1329 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
1330 (VOID **) &IpSb
1331 );
1332
1333 if (!EFI_ERROR(Status)) {
1334 IpSb->DestroyChild (IpSb, Private->IpChildHandle);
1335 }
1336 }
1337
1338
1339 /**
1340 The Ping Process.
1341
1342 @param[in] SendNumber The send request count.
1343 @param[in] BufferSize The send buffer size.
1344 @param[in] SrcAddress The source address.
1345 @param[in] DstAddress The destination address.
1346 @param[in] IpChoice The choice between IPv4 and IPv6.
1347
1348 @retval SHELL_SUCCESS The ping processed successfullly.
1349 @retval others The ping processed unsuccessfully.
1350 **/
1351 SHELL_STATUS
1352 ShellPing (
1353 IN UINT32 SendNumber,
1354 IN UINT32 BufferSize,
1355 IN EFI_IPv6_ADDRESS *SrcAddress,
1356 IN EFI_IPv6_ADDRESS *DstAddress,
1357 IN UINT32 IpChoice
1358 )
1359 {
1360 EFI_STATUS Status;
1361 PING_PRIVATE_DATA *Private;
1362 PING_ICMPX_TX_INFO *TxInfo;
1363 LIST_ENTRY *Entry;
1364 LIST_ENTRY *NextEntry;
1365 SHELL_STATUS ShellStatus;
1366
1367 ShellStatus = SHELL_SUCCESS;
1368 Private = AllocateZeroPool (sizeof (PING_PRIVATE_DATA));
1369
1370 if (Private == NULL) {
1371 return (SHELL_OUT_OF_RESOURCES);
1372 }
1373
1374 Private->IpChoice = IpChoice;
1375 Private->Signature = PING_PRIVATE_DATA_SIGNATURE;
1376 Private->SendNum = SendNumber;
1377 Private->BufferSize = BufferSize;
1378 Private->RttMin = ~((UINT64 )(0x0));
1379 Private->Status = EFI_NOT_READY;
1380
1381 CopyMem(&Private->SrcAddress, SrcAddress, sizeof(Private->SrcAddress));
1382 CopyMem(&Private->DstAddress, DstAddress, sizeof(Private->DstAddress));
1383
1384 InitializeListHead (&Private->TxList);
1385
1386 //
1387 // Open and configure a ip instance for us.
1388 //
1389 Status = PingCreateIpInstance (Private);
1390
1391 if (EFI_ERROR (Status)) {
1392 ShellStatus = SHELL_ACCESS_DENIED;
1393 goto ON_EXIT;
1394 }
1395 //
1396 // Print the command line itself.
1397 //
1398 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_START), gShellNetwork1HiiHandle, mDstString, Private->BufferSize);
1399 //
1400 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1401 //
1402 Status = Ping6ReceiveEchoReply (Private);
1403
1404 if (EFI_ERROR (Status)) {
1405 ShellStatus = SHELL_ACCESS_DENIED;
1406 goto ON_EXIT;
1407 }
1408 //
1409 // Create and start timer to send icmp6 echo request packet per second.
1410 //
1411 Status = gBS->CreateEvent (
1412 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1413 TPL_CALLBACK,
1414 Ping6OnTimerRoutine,
1415 Private,
1416 &Private->Timer
1417 );
1418
1419 if (EFI_ERROR (Status)) {
1420 ShellStatus = SHELL_ACCESS_DENIED;
1421 goto ON_EXIT;
1422 }
1423
1424 //
1425 // Start a timer to calculate the RTT.
1426 //
1427 Status = PingInitRttTimer (Private);
1428 if (EFI_ERROR (Status)) {
1429 ShellStatus = SHELL_ACCESS_DENIED;
1430 goto ON_EXIT;
1431 }
1432
1433 //
1434 // Create a ipv6 token to send the first icmp6 echo request packet.
1435 //
1436 Status = PingSendEchoRequest (Private);
1437 //
1438 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1439 //
1440 if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
1441 ShellStatus = SHELL_ACCESS_DENIED;
1442 if(Status == EFI_NOT_FOUND) {
1443 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOSOURCE_INDO), gShellNetwork1HiiHandle, mDstString);
1444 } else if (Status == RETURN_NO_MAPPING) {
1445 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOROUTE_FOUND), gShellNetwork1HiiHandle, mDstString, mSrcString);
1446 } else {
1447 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NETWORK_ERROR), gShellNetwork1HiiHandle, L"ping", Status);
1448 }
1449
1450 goto ON_EXIT;
1451 }
1452
1453 Status = gBS->SetTimer (
1454 Private->Timer,
1455 TimerPeriodic,
1456 ONE_SECOND
1457 );
1458
1459 if (EFI_ERROR (Status)) {
1460 ShellStatus = SHELL_ACCESS_DENIED;
1461 goto ON_EXIT;
1462 }
1463 //
1464 // Control the ping6 process by two factors:
1465 // 1. Hot key
1466 // 2. Private->Status
1467 // 2.1. success means all icmp6 echo request packets get reply packets.
1468 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1469 // 2.3. noready means ping6 process is on-the-go.
1470 //
1471 while (Private->Status == EFI_NOT_READY) {
1472 Status = Private->ProtocolPointers.Poll (Private->IpProtocol);
1473 if (ShellGetExecutionBreakFlag()) {
1474 Private->Status = EFI_ABORTED;
1475 goto ON_STAT;
1476 }
1477 }
1478
1479 ON_STAT:
1480 //
1481 // Display the statistics in all.
1482 //
1483 gBS->SetTimer (Private->Timer, TimerCancel, 0);
1484
1485 if (Private->TxCount != 0) {
1486 ShellPrintHiiEx (
1487 -1,
1488 -1,
1489 NULL,
1490 STRING_TOKEN (STR_PING_STAT),
1491 gShellNetwork1HiiHandle,
1492 Private->TxCount,
1493 (Private->RxCount - Private->FailedCount),
1494 (100 - ((100 * (Private->RxCount - Private->FailedCount)) / Private->TxCount)),
1495 Private->RttSum
1496 );
1497 }
1498
1499 if (Private->RxCount > Private->FailedCount) {
1500 ShellPrintHiiEx (
1501 -1,
1502 -1,
1503 NULL,
1504 STRING_TOKEN (STR_PING_RTT),
1505 gShellNetwork1HiiHandle,
1506 Private->RttMin,
1507 Private->RttMin + Private->TimerPeriod,
1508 Private->RttMax,
1509 Private->RttMax + Private->TimerPeriod,
1510 DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL),
1511 DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL) + Private->TimerPeriod
1512 );
1513 }
1514
1515 ON_EXIT:
1516
1517 if (Private != NULL) {
1518
1519 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1520 TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
1521
1522 if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1523 Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
1524 }
1525
1526 RemoveEntryList (&TxInfo->Link);
1527 PingDestroyTxInfo (TxInfo, Private->IpChoice);
1528 }
1529
1530 PingFreeRttTimer (Private);
1531
1532 if (Private->Timer != NULL) {
1533 gBS->CloseEvent (Private->Timer);
1534 }
1535
1536 if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1537 Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, &Private->RxToken);
1538 }
1539
1540 if (Private->RxToken.Event != NULL) {
1541 gBS->CloseEvent (Private->RxToken.Event);
1542 }
1543
1544 if (Private->IpChildHandle != NULL) {
1545 Ping6DestroyIp6Instance (Private);
1546 }
1547
1548 FreePool (Private);
1549 }
1550
1551 return ShellStatus;
1552 }
1553
1554 /**
1555 Function for 'ping' command.
1556
1557 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1558 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1559
1560 @retval SHELL_SUCCESS The ping processed successfullly.
1561 @retval others The ping processed unsuccessfully.
1562
1563 **/
1564 SHELL_STATUS
1565 EFIAPI
1566 ShellCommandRunPing (
1567 IN EFI_HANDLE ImageHandle,
1568 IN EFI_SYSTEM_TABLE *SystemTable
1569 )
1570 {
1571 EFI_STATUS Status;
1572 SHELL_STATUS ShellStatus;
1573 EFI_IPv6_ADDRESS DstAddress;
1574 EFI_IPv6_ADDRESS SrcAddress;
1575 UINT64 BufferSize;
1576 UINTN SendNumber;
1577 LIST_ENTRY *ParamPackage;
1578 CONST CHAR16 *ValueStr;
1579 UINTN NonOptionCount;
1580 UINT32 IpChoice;
1581 CHAR16 *ProblemParam;
1582
1583 //
1584 // we use IPv6 buffers to hold items...
1585 // make sure this is enough space!
1586 //
1587 ASSERT(sizeof(EFI_IPv4_ADDRESS ) <= sizeof(EFI_IPv6_ADDRESS ));
1588 ASSERT(sizeof(EFI_IP4_COMPLETION_TOKEN) <= sizeof(EFI_IP6_COMPLETION_TOKEN ));
1589
1590 IpChoice = PING_IP_CHOICE_IP4;
1591
1592 ShellStatus = SHELL_SUCCESS;
1593 ProblemParam = NULL;
1594
1595 Status = ShellCommandLineParseEx (PingParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
1596 if (EFI_ERROR(Status)) {
1597 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ProblemParam);
1598 ShellStatus = SHELL_INVALID_PARAMETER;
1599 goto ON_EXIT;
1600 }
1601
1602 if (ShellCommandLineGetFlag (ParamPackage, L"-_ip6")) {
1603 IpChoice = PING_IP_CHOICE_IP6;
1604 }
1605
1606 //
1607 // Parse the parameter of count number.
1608 //
1609 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1610 if (ValueStr != NULL) {
1611 SendNumber = ShellStrToUintn (ValueStr);
1612
1613 //
1614 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1615 //
1616 if ((SendNumber == 0) || (SendNumber > MAX_SEND_NUMBER)) {
1617 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1618 ShellStatus = SHELL_INVALID_PARAMETER;
1619 goto ON_EXIT;
1620 }
1621 } else {
1622 SendNumber = DEFAULT_SEND_COUNT;
1623 }
1624 //
1625 // Parse the parameter of buffer size.
1626 //
1627 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1628 if (ValueStr != NULL) {
1629 BufferSize = ShellStrToUintn (ValueStr);
1630
1631 //
1632 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1633 //
1634 if ((BufferSize < 16) || (BufferSize > MAX_BUFFER_SIZE)) {
1635 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1636 ShellStatus = SHELL_INVALID_PARAMETER;
1637 goto ON_EXIT;
1638 }
1639 } else {
1640 BufferSize = DEFAULT_BUFFER_SIZE;
1641 }
1642
1643 ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1644 ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1645
1646 //
1647 // Parse the parameter of source ip address.
1648 //
1649 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
1650 if (ValueStr == NULL) {
1651 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-_s");
1652 }
1653
1654 if (ValueStr != NULL) {
1655 mSrcString = ValueStr;
1656 if (IpChoice == PING_IP_CHOICE_IP6) {
1657 Status = NetLibStrToIp6 (ValueStr, &SrcAddress);
1658 } else {
1659 Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&SrcAddress);
1660 }
1661 if (EFI_ERROR (Status)) {
1662 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1663 ShellStatus = SHELL_INVALID_PARAMETER;
1664 goto ON_EXIT;
1665 }
1666 }
1667 //
1668 // Parse the parameter of destination ip address.
1669 //
1670 NonOptionCount = ShellCommandLineGetCount(ParamPackage);
1671 if (NonOptionCount < 2) {
1672 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellNetwork1HiiHandle, L"ping");
1673 ShellStatus = SHELL_INVALID_PARAMETER;
1674 goto ON_EXIT;
1675 }
1676 if (NonOptionCount > 2) {
1677 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellNetwork1HiiHandle, L"ping");
1678 ShellStatus = SHELL_INVALID_PARAMETER;
1679 goto ON_EXIT;
1680 }
1681 ValueStr = ShellCommandLineGetRawValue (ParamPackage, 1);
1682 if (ValueStr != NULL) {
1683 mDstString = ValueStr;
1684 if (IpChoice == PING_IP_CHOICE_IP6) {
1685 Status = NetLibStrToIp6 (ValueStr, &DstAddress);
1686 } else {
1687 Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&DstAddress);
1688 }
1689 if (EFI_ERROR (Status)) {
1690 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1691 ShellStatus = SHELL_INVALID_PARAMETER;
1692 goto ON_EXIT;
1693 }
1694 }
1695
1696 //
1697 // Enter into ping process.
1698 //
1699 ShellStatus = ShellPing (
1700 (UINT32)SendNumber,
1701 (UINT32)BufferSize,
1702 &SrcAddress,
1703 &DstAddress,
1704 IpChoice
1705 );
1706
1707 ON_EXIT:
1708 ShellCommandLineFreeVarList (ParamPackage);
1709 return ShellStatus;
1710 }