2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS ping utility
4 * FILE: base/applications/network/ping/ping.c
5 * PURPOSE: Network test utility
18 /* General ICMP constants */
19 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
20 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
22 /* ICMP message types */
23 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
24 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
28 /* IPv4 header structure */
29 typedef struct _IPv4_HEADER
32 unsigned char Version
:4;
34 unsigned short Length
;
36 unsigned short FragFlags
;
38 unsigned char Protocol
;
39 unsigned short Checksum
;
40 unsigned int SrcAddress
;
41 unsigned int DstAddress
;
42 } IPv4_HEADER
, *PIPv4_HEADER
;
44 /* ICMP echo request/reply header structure */
45 typedef struct _ICMP_HEADER
49 unsigned short Checksum
;
51 unsigned short SeqNum
;
52 } ICMP_HEADER
, *PICMP_HEADER
;
54 typedef struct _ICMP_ECHO_PACKET
57 } ICMP_ECHO_PACKET
, *PICMP_ECHO_PACKET
;
63 BOOL ResolveAddresses
;
65 UINT DataSize
; /* ICMP echo request data size */
80 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
84 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
85 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
86 LARGE_INTEGER SentTime
;
87 BOOL UsePerformanceCounter
;
90 /* Display the contents of a buffer */
91 static VOID
DisplayBuffer(
98 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
101 for (i
= 0; i
< Size
; i
++)
105 printf("%02X ", (p
[i
]) & 0xFF);
110 /* Display usage information on screen */
111 static VOID
Usage(VOID
)
113 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
114 printf("Options:\n");
115 printf(" -t Ping the specified host until stopped.\n");
116 printf(" To stop - type Control-C.\n");
117 printf(" -n count Number of echo requests to send.\n");
118 printf(" -l size Send buffer size.\n");
119 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
122 /* Reset configuration to default values */
123 static VOID
Reset(VOID
)
125 LARGE_INTEGER PerformanceCounterFrequency
;
128 ResolveAddresses
= FALSE
;
131 DontFragment
= FALSE
;
135 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
137 if (UsePerformanceCounter
)
139 /* Performance counters may return incorrect results on some multiprocessor
140 platforms so we restrict execution on the first processor. This may fail
141 on Windows NT so we fall back to GetCurrentTick() for timing */
142 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
143 UsePerformanceCounter
= FALSE
;
145 /* Convert frequency to ticks per millisecond */
146 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
147 /* And to ticks per microsecond */
148 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
150 if (!UsePerformanceCounter
)
152 /* 1 tick per millisecond for GetCurrentTick() */
153 TicksPerMs
.QuadPart
= 1;
154 /* GetCurrentTick() cannot handle microseconds */
155 TicksPerUs
.QuadPart
= 1;
159 /* Return ULONG in a string */
160 static ULONG
GetULONG(LPSTR String
)
166 Length
= (UINT
)_tcslen(String
);
167 while ((i
< Length
) && ((String
[i
] < '0') || (String
[i
] > '9'))) i
++;
168 if ((i
>= Length
) || ((String
[i
] < '0') || (String
[i
] > '9')))
170 InvalidOption
= TRUE
;
173 Value
= strtoul(&String
[i
], &StopString
, 10);
178 /* Return ULONG in a string. Try next paramter if not successful */
179 static ULONG
GetULONG2(LPSTR String1
, LPSTR String2
, PINT i
)
183 Value
= GetULONG(String1
);
186 InvalidOption
= FALSE
;
187 if (String2
[0] != '-')
189 Value
= GetULONG(String2
);
198 /* Parse command line parameters */
199 static BOOL
ParseCmdline(int argc
, char* argv
[])
209 InvalidOption
= FALSE
;
211 for (i
= 1; i
< argc
; i
++)
213 if (argv
[i
][0] == '-')
217 case 't': NeverStop
= TRUE
; break;
218 case 'a': ResolveAddresses
= TRUE
; break;
219 case 'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
221 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
222 if (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
) - sizeof(IPv4_HEADER
))
224 printf("Bad value for option -l, valid range is from 0 to %d.\n",
225 ICMP_MAXSIZE
- (int)sizeof(ICMP_ECHO_PACKET
) - (int)sizeof(IPv4_HEADER
));
229 case 'f': DontFragment
= TRUE
; break;
230 case 'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
231 case 'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
232 case 'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
234 printf("Bad option %s.\n", argv
[i
]);
240 printf("Bad option format %s.\n", argv
[i
]);
248 printf("Bad parameter %s.\n", argv
[i
]);
253 lstrcpy(TargetName
, argv
[i
]);
259 if ((!ShowUsage
) && (!FoundTarget
))
261 printf("Name or IP address of destination host must be specified.\n");
273 /* Calculate checksum of data */
274 static WORD
Checksum(PUSHORT data
, UINT size
)
281 size
-= sizeof(USHORT
);
285 sum
+= *(UCHAR
*)data
;
287 sum
= (sum
>> 16) + (sum
& 0xFFFF);
290 return (USHORT
)(~sum
);
293 /* Prepare to ping target */
294 static BOOL
Setup(VOID
)
296 WORD wVersionRequested
;
302 wVersionRequested
= MAKEWORD(2, 2);
304 Status
= WSAStartup(wVersionRequested
, &WsaData
);
307 printf("Could not initialize winsock dll.\n");
311 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
312 if (IcmpSock
== INVALID_SOCKET
)
314 printf("Could not create socket (#%d).\n", WSAGetLastError());
318 if (setsockopt(IcmpSock
,
321 (const char *)&DontFragment
,
322 sizeof(DontFragment
)) == SOCKET_ERROR
)
324 printf("setsockopt failed (%d).\n", WSAGetLastError());
328 if (setsockopt(IcmpSock
,
331 (const char *)&TTLValue
,
332 sizeof(TTLValue
)) == SOCKET_ERROR
)
334 printf("setsockopt failed (%d).\n", WSAGetLastError());
339 ZeroMemory(&Target
, sizeof(Target
));
341 Addr
= inet_addr(TargetName
);
342 if (Addr
== INADDR_NONE
)
344 phe
= gethostbyname(TargetName
);
347 printf("Unknown host %s.\n", TargetName
);
353 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
355 Target
.sin_addr
.s_addr
= Addr
;
358 Target
.sin_family
= phe
->h_addrtype
;
360 Target
.sin_family
= AF_INET
;
362 TargetIP
= inet_ntoa(Target
.sin_addr
);
374 static VOID
Cleanup(VOID
)
376 if (IcmpSock
!= INVALID_SOCKET
)
377 closesocket(IcmpSock
);
382 static VOID
QueryTime(PLARGE_INTEGER Time
)
384 if (UsePerformanceCounter
)
386 if (QueryPerformanceCounter(Time
) == 0)
388 /* This should not happen, but we fall
389 back to GetCurrentTick() if it does */
390 Time
->u
.LowPart
= (ULONG
)GetTickCount();
391 Time
->u
.HighPart
= 0;
393 /* 1 tick per millisecond for GetCurrentTick() */
394 TicksPerMs
.QuadPart
= 1;
395 /* GetCurrentTick() cannot handle microseconds */
396 TicksPerUs
.QuadPart
= 1;
398 UsePerformanceCounter
= FALSE
;
403 Time
->u
.LowPart
= (ULONG
)GetTickCount();
404 Time
->u
.HighPart
= 0;
408 static VOID
TimeToMsString(LPSTR String
, LARGE_INTEGER Time
)
411 LARGE_INTEGER LargeTime
;
413 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
415 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
416 strcpy(String
, Convstr
);
417 strcat(String
, "ms");
420 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
422 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
424 PIPv4_HEADER IpHeader
;
425 PICMP_ECHO_PACKET Icmp
;
428 LARGE_INTEGER RelativeTime
;
429 LARGE_INTEGER LargeTime
;
432 IpHeader
= (PIPv4_HEADER
)buffer
;
434 IphLength
= IpHeader
->IHL
* 4;
436 if (size
< IphLength
+ ICMP_MINSIZE
)
439 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
444 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
446 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
449 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
454 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
457 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
462 if (from
->sin_addr
.s_addr
!= Target
.sin_addr
.s_addr
)
465 printf("Bad source address (%s should be %s)\n", inet_ntoa(from
->sin_addr
), inet_ntoa(Target
.sin_addr
));
470 QueryTime(&LargeTime
);
472 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- SentTime
.QuadPart
);
474 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
482 TimeToMsString(Time
, RelativeTime
);
486 printf("Reply from %s: bytes=%d time%s%s TTL=%d\n", inet_ntoa(from
->sin_addr
),
487 size
- IphLength
- (int)sizeof(ICMP_ECHO_PACKET
), Sign
, Time
, IpHeader
->TTL
);
488 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
)
490 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
493 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
494 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
496 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
501 /* Send and receive one ping */
502 static BOOL
Ping(VOID
)
509 PICMP_ECHO_PACKET Packet
;
511 /* Account for extra space for IP header when packet is received */
512 Size
= DataSize
+ 128;
513 Buffer
= GlobalAlloc(0, Size
);
516 printf("Not enough free resources available.\n");
520 ZeroMemory(Buffer
, Size
);
521 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
523 /* Assemble ICMP echo request packet */
524 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
525 Packet
->Icmp
.Code
= 0;
526 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
527 Packet
->Icmp
.SeqNum
= htons((USHORT
)CurrentSeqNum
);
528 Packet
->Icmp
.Checksum
= 0;
530 /* Calculate checksum for ICMP header and data area */
531 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
535 /* Send ICMP echo request */
538 FD_SET(IcmpSock
, &Fds
);
539 Timeval
.tv_sec
= Timeout
/ 1000;
540 Timeval
.tv_usec
= Timeout
% 1000;
541 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
542 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
546 printf("Sending packet\n");
547 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
551 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
552 0, (SOCKADDR
*)&Target
, sizeof(Target
));
553 QueryTime(&SentTime
);
556 if (Status
== SOCKET_ERROR
)
558 if (WSAGetLastError() == WSAEHOSTUNREACH
)
559 printf("Destination host unreachable.\n");
561 printf("Could not transmit data (%d).\n", WSAGetLastError());
566 /* Expect to receive ICMP echo reply */
568 FD_SET(IcmpSock
, &Fds
);
569 Timeval
.tv_sec
= Timeout
/ 1000;
570 Timeval
.tv_usec
= Timeout
% 1000;
573 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
574 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
576 Length
= sizeof(From
);
577 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
580 printf("Received packet\n");
581 DisplayBuffer(Buffer
, Status
);
587 if (Status
== SOCKET_ERROR
)
589 if (WSAGetLastError() != WSAETIMEDOUT
)
591 printf("Could not receive data (%d).\n", WSAGetLastError());
600 printf("Request timed out.\n");
605 } while (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
));
612 /* Program entry point */
613 int main(int argc
, char* argv
[])
622 if ((ParseCmdline(argc
, argv
)) && (Setup()))
625 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
626 TargetName
, TargetIP
, DataSize
);
629 while ((NeverStop
) || (Count
< PingCount
))
633 if((NeverStop
) || (Count
< PingCount
))
639 /* Calculate avarage round trip time */
640 if ((SentCount
- LostCount
) > 0)
641 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
645 /* Calculate loss percent */
646 Count
= SentCount
? (LostCount
* 100) / SentCount
: 0;
651 TimeToMsString(MinTime
, MinRTT
);
652 TimeToMsString(MaxTime
, MaxRTT
);
653 TimeToMsString(AvgTime
, AvgRTT
);
655 /* Print statistics */
656 printf("\nPing statistics for %s:\n", TargetIP
);
657 printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n",
658 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
659 /* Print approximate times or NO approximate times if 100% loss */
660 if ((SentCount
- LostCount
) > 0)
662 printf("Approximate round trip times in milli-seconds:\n");
663 printf(" Minimum = %s, Maximum = %s, Average = %s\n",
664 MinTime
, MaxTime
, AvgTime
);