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
17 /* General ICMP constants */
18 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
19 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
21 /* ICMP message types */
22 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
23 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
27 /* IPv4 header structure */
28 typedef struct _IPv4_HEADER
31 unsigned char Version
:4;
33 unsigned short Length
;
35 unsigned short FragFlags
;
37 unsigned char Protocol
;
38 unsigned short Checksum
;
39 unsigned int SrcAddress
;
40 unsigned int DstAddress
;
41 } IPv4_HEADER
, *PIPv4_HEADER
;
43 /* ICMP echo request/reply header structure */
44 typedef struct _ICMP_HEADER
48 unsigned short Checksum
;
50 unsigned short SeqNum
;
51 } ICMP_HEADER
, *PICMP_HEADER
;
53 typedef struct _ICMP_ECHO_PACKET
56 LARGE_INTEGER Timestamp
;
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 BOOL UsePerformanceCounter
;
89 /* Display the contents of a buffer */
90 static VOID
DisplayBuffer(
97 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
100 for (i
= 0; i
< Size
; i
++)
104 printf("%02X ", (p
[i
]) & 0xFF);
109 /* Display usage information on screen */
110 static VOID
Usage(VOID
)
112 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
113 printf("Options:\n");
114 printf(" -t Ping the specified host until stopped.\n");
115 printf(" To stop - type Control-C.\n");
116 printf(" -n count Number of echo requests to send.\n");
117 printf(" -l size Send buffer size.\n");
118 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
121 /* Reset configuration to default values */
122 static VOID
Reset(VOID
)
124 LARGE_INTEGER PerformanceCounterFrequency
;
127 ResolveAddresses
= FALSE
;
130 DontFragment
= FALSE
;
134 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
136 if (UsePerformanceCounter
)
138 /* Performance counters may return incorrect results on some multiprocessor
139 platforms so we restrict execution on the first processor. This may fail
140 on Windows NT so we fall back to GetCurrentTick() for timing */
141 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
142 UsePerformanceCounter
= FALSE
;
144 /* Convert frequency to ticks per millisecond */
145 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
146 /* And to ticks per microsecond */
147 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
149 if (!UsePerformanceCounter
)
151 /* 1 tick per millisecond for GetCurrentTick() */
152 TicksPerMs
.QuadPart
= 1;
153 /* GetCurrentTick() cannot handle microseconds */
154 TicksPerUs
.QuadPart
= 1;
158 /* Return ULONG in a string */
159 static ULONG
GetULONG(LPSTR String
)
165 Length
= (UINT
)_tcslen(String
);
166 while ((i
< Length
) && ((String
[i
] < '0') || (String
[i
] > '9'))) i
++;
167 if ((i
>= Length
) || ((String
[i
] < '0') || (String
[i
] > '9')))
169 InvalidOption
= TRUE
;
172 Value
= strtoul(&String
[i
], &StopString
, 10);
177 /* Return ULONG in a string. Try next paramter if not successful */
178 static ULONG
GetULONG2(LPSTR String1
, LPSTR String2
, PINT i
)
182 Value
= GetULONG(String1
);
185 InvalidOption
= FALSE
;
186 if (String2
[0] != '-')
188 Value
= GetULONG(String2
);
197 /* Parse command line parameters */
198 static BOOL
ParseCmdline(int argc
, char* argv
[])
208 InvalidOption
= FALSE
;
210 for (i
= 1; i
< argc
; i
++)
212 if (argv
[i
][0] == '-')
216 case 't': NeverStop
= TRUE
; break;
217 case 'a': ResolveAddresses
= TRUE
; break;
218 case 'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
220 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
221 if (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
))
223 printf("Bad value for option -l, valid range is from 0 to %d.\n",
224 ICMP_MAXSIZE
- (int)sizeof(ICMP_ECHO_PACKET
));
228 case 'f': DontFragment
= TRUE
; break;
229 case 'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
230 case 'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
231 case 'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
233 printf("Bad option %s.\n", argv
[i
]);
239 printf("Bad option format %s.\n", argv
[i
]);
247 printf("Bad parameter %s.\n", argv
[i
]);
252 lstrcpy(TargetName
, argv
[i
]);
258 if ((!ShowUsage
) && (!FoundTarget
))
260 printf("Name or IP address of destination host must be specified.\n");
272 /* Calculate checksum of data */
273 static WORD
Checksum(PUSHORT data
, UINT size
)
280 size
-= sizeof(USHORT
);
284 sum
+= *(UCHAR
*)data
;
286 sum
= (sum
>> 16) + (sum
& 0xFFFF);
289 return (USHORT
)(~sum
);
292 /* Prepare to ping target */
293 static BOOL
Setup(VOID
)
295 WORD wVersionRequested
;
301 wVersionRequested
= MAKEWORD(2, 2);
303 Status
= WSAStartup(wVersionRequested
, &WsaData
);
306 printf("Could not initialize winsock dll.\n");
310 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
311 if (IcmpSock
== INVALID_SOCKET
)
313 printf("Could not create socket (#%d).\n", WSAGetLastError());
317 ZeroMemory(&Target
, sizeof(Target
));
319 Addr
= inet_addr(TargetName
);
320 if (Addr
== INADDR_NONE
)
322 phe
= gethostbyname(TargetName
);
325 printf("Unknown host %s.\n", TargetName
);
331 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
333 Target
.sin_addr
.s_addr
= Addr
;
336 Target
.sin_family
= phe
->h_addrtype
;
338 Target
.sin_family
= AF_INET
;
340 TargetIP
= inet_ntoa(Target
.sin_addr
);
352 static VOID
Cleanup(VOID
)
354 if (IcmpSock
!= INVALID_SOCKET
)
355 closesocket(IcmpSock
);
360 static VOID
QueryTime(PLARGE_INTEGER Time
)
362 if (UsePerformanceCounter
)
364 if (QueryPerformanceCounter(Time
) == 0)
366 /* This should not happen, but we fall
367 back to GetCurrentTick() if it does */
368 Time
->u
.LowPart
= (ULONG
)GetTickCount();
369 Time
->u
.HighPart
= 0;
371 /* 1 tick per millisecond for GetCurrentTick() */
372 TicksPerMs
.QuadPart
= 1;
373 /* GetCurrentTick() cannot handle microseconds */
374 TicksPerUs
.QuadPart
= 1;
376 UsePerformanceCounter
= FALSE
;
381 Time
->u
.LowPart
= (ULONG
)GetTickCount();
382 Time
->u
.HighPart
= 0;
386 static VOID
TimeToMsString(LPSTR String
, LARGE_INTEGER Time
)
389 LARGE_INTEGER LargeTime
;
391 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
393 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
394 strcpy(String
, Convstr
);
395 strcat(String
, "ms");
398 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
400 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
402 PIPv4_HEADER IpHeader
;
403 PICMP_ECHO_PACKET Icmp
;
406 LARGE_INTEGER RelativeTime
;
407 LARGE_INTEGER LargeTime
;
410 IpHeader
= (PIPv4_HEADER
)buffer
;
412 IphLength
= IpHeader
->IHL
* 4;
414 if (size
< IphLength
+ ICMP_MINSIZE
)
417 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
422 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
424 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
427 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
432 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
435 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
440 if (from
->sin_addr
.s_addr
!= Target
.sin_addr
.s_addr
)
443 printf("Bad source address (%s should be %s)\n", inet_ntoa(from
->sin_addr
), inet_ntoa(Target
.sin_addr
));
448 QueryTime(&LargeTime
);
450 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- Icmp
->Timestamp
.QuadPart
);
452 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
460 TimeToMsString(Time
, RelativeTime
);
464 printf("Reply from %s: bytes=%d time%s%s TTL=%d\n", inet_ntoa(from
->sin_addr
),
465 size
- IphLength
- (int)sizeof(ICMP_ECHO_PACKET
), Sign
, Time
, IpHeader
->TTL
);
466 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
)
468 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
471 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
472 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
474 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
479 /* Send and receive one ping */
480 static BOOL
Ping(VOID
)
487 PICMP_ECHO_PACKET Packet
;
489 /* Account for extra space for IP header when packet is received */
490 Size
= DataSize
+ 128;
491 Buffer
= GlobalAlloc(0, Size
);
494 printf("Not enough free resources available.\n");
498 ZeroMemory(Buffer
, Size
);
499 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
501 /* Assemble ICMP echo request packet */
502 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
503 Packet
->Icmp
.Code
= 0;
504 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
505 Packet
->Icmp
.SeqNum
= htons((USHORT
)CurrentSeqNum
);
506 Packet
->Icmp
.Checksum
= 0;
508 /* Timestamp is part of data area */
509 QueryTime(&Packet
->Timestamp
);
511 CopyMemory(Buffer
, &Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
512 /* Calculate checksum for ICMP header and data area */
513 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
517 /* Send ICMP echo request */
520 FD_SET(IcmpSock
, &Fds
);
521 Timeval
.tv_sec
= Timeout
/ 1000;
522 Timeval
.tv_usec
= Timeout
% 1000;
523 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
524 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
528 printf("Sending packet\n");
529 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
533 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
534 0, (SOCKADDR
*)&Target
, sizeof(Target
));
537 if (Status
== SOCKET_ERROR
)
540 if (WSAGetLastError() == WSAEHOSTUNREACH
)
541 printf("Destination host unreachable.\n");
543 printf("Could not transmit data (%d).\n", WSAGetLastError());
548 /* Expect to receive ICMP echo reply */
550 FD_SET(IcmpSock
, &Fds
);
551 Timeval
.tv_sec
= Timeout
/ 1000;
552 Timeval
.tv_usec
= Timeout
% 1000;
555 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
556 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
558 Length
= sizeof(From
);
559 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
562 printf("Received packet\n");
563 DisplayBuffer(Buffer
, Status
);
569 if (Status
== SOCKET_ERROR
)
571 if (WSAGetLastError() != WSAETIMEDOUT
)
573 printf("Could not receive data (%d).\n", WSAGetLastError());
582 printf("Request timed out.\n");
587 } while (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
));
594 /* Program entry point */
595 int main(int argc
, char* argv
[])
604 if ((ParseCmdline(argc
, argv
)) && (Setup()))
607 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
608 TargetName
, TargetIP
, DataSize
);
611 while ((NeverStop
) || (Count
< PingCount
))
620 /* Calculate avarage round trip time */
621 if ((SentCount
- LostCount
) > 0)
622 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
626 /* Calculate loss percent */
627 Count
= SentCount
? (LostCount
* 100) / SentCount
: 0;
632 TimeToMsString(MinTime
, MinRTT
);
633 TimeToMsString(MaxTime
, MaxRTT
);
634 TimeToMsString(AvgTime
, AvgRTT
);
636 /* Print statistics */
637 printf("\nPing statistics for %s:\n", TargetIP
);
638 printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n",
639 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
640 /* Print approximate times or NO approximate times if 100% loss */
641 if ((SentCount
- LostCount
) > 0)
643 printf("Approximate round trip times in milli-seconds:\n");
644 printf(" Minimum = %s, Maximum = %s, Average = %s\n",
645 MinTime
, MaxTime
, AvgTime
);