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