624 lines
18 KiB
C
624 lines
18 KiB
C
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
|
//
|
|
// Copyright (c) 1985-2000 Microsoft Corporation
|
|
//
|
|
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
|
|
// You should have received a copy of the Microsoft End-User License Agreement
|
|
// for this software along with this release; see the file "license.txt".
|
|
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
|
|
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Packet INternet Groper utility for IPv6.
|
|
//
|
|
|
|
|
|
#include <windows.h>
|
|
#include <devioctl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <wspiapi.h>
|
|
// Need ntddip6 before ws2ip6 to get CopyTDIFromSA6 and CopySAFromTDI6.
|
|
#include <ntddip6.h>
|
|
#include <ws2ip6.h>
|
|
|
|
//
|
|
// Localization library and MessageIds.
|
|
//
|
|
#include <iphlpapi.h>
|
|
#include <nls.h>
|
|
#include "localmsg.h"
|
|
|
|
#define MAX_BUFFER_SIZE (sizeof(ICMPV6_ECHO_REPLY) + 0xfff7)
|
|
#define DEFAULT_BUFFER_SIZE (0x2000 - 8)
|
|
#define DEFAULT_SEND_SIZE 32
|
|
#define DEFAULT_COUNT 4
|
|
#define DEFAULT_TIMEOUT 4000L
|
|
#define MIN_INTERVAL 1000L
|
|
|
|
struct sockaddr_in6 dstaddr, srcaddr;
|
|
|
|
PWCHAR
|
|
GetErrorString(int ErrorCode)
|
|
{
|
|
DWORD Status;
|
|
DWORD Length;
|
|
static WCHAR ErrorString[2048]; // a 2K static buffer should suffice
|
|
|
|
Length = 2048;
|
|
Status = GetIpErrorString(ErrorCode, ErrorString, &Length);
|
|
|
|
if (Status == NO_ERROR) {
|
|
return ErrorString; // success
|
|
}
|
|
|
|
return L""; // return a null string
|
|
}
|
|
|
|
void
|
|
PrintUsage(void)
|
|
{
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_0);
|
|
// printf("\nUsage: ping6 [-t] [-a] [-n count] [-l size]"
|
|
// " [-w timeout] [-s srcaddr] dest\n\n"
|
|
// "Options:\n"
|
|
// "-t Ping the specifed host until interrupted.\n"
|
|
// "-a Resolve addresses to hostnames.\n"
|
|
// "-n count Number of echo requests to send.\n"
|
|
// "-l size Send buffer size.\n"
|
|
// "-w timeout Timeout in milliseconds to wait for each reply.\n"
|
|
// "-s srcaddr Source address to use.\n"
|
|
// "-r Use routing header to test reverse route also.\n");
|
|
|
|
}
|
|
|
|
//
|
|
// Can only be called once, because
|
|
// a) does not call freeaddrinfo
|
|
// b) uses a static buffer for some results
|
|
//
|
|
int
|
|
get_pingee(char *ahstr, int dnsreq, struct sockaddr_in6 *address, char **hstr)
|
|
{
|
|
struct addrinfo hints;
|
|
struct addrinfo *result;
|
|
char *name = NULL;
|
|
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
hints.ai_family = PF_INET6;
|
|
|
|
if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
|
|
//
|
|
// Not a numeric address.
|
|
// Try again with DNS name resolution.
|
|
//
|
|
hints.ai_flags = AI_CANONNAME;
|
|
if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
|
|
//
|
|
// Failure - we can not resolve the name.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
name = result->ai_canonname;
|
|
}
|
|
else {
|
|
//
|
|
// Should we do a reverse-lookup to get a name?
|
|
//
|
|
if (dnsreq) {
|
|
static char namebuf[NI_MAXHOST];
|
|
|
|
if (getnameinfo(result->ai_addr, result->ai_addrlen,
|
|
namebuf, sizeof namebuf,
|
|
NULL, 0,
|
|
NI_NAMEREQD) == 0) {
|
|
//
|
|
// Reverse lookup succeeded.
|
|
//
|
|
name = namebuf;
|
|
}
|
|
}
|
|
}
|
|
|
|
*address = * (struct sockaddr_in6 *) result->ai_addr;
|
|
*hstr = name;
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
get_source(char *astr, struct sockaddr_in6 *address)
|
|
{
|
|
struct addrinfo hints;
|
|
struct addrinfo *result;
|
|
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_flags = AI_PASSIVE;
|
|
hints.ai_family = PF_INET6;
|
|
|
|
if (getaddrinfo(astr, NULL, &hints, &result) != 0)
|
|
return FALSE;
|
|
|
|
*address = * (struct sockaddr_in6 *) result->ai_addr;
|
|
return TRUE;
|
|
}
|
|
|
|
char *
|
|
format_addr(struct sockaddr_in6 *address)
|
|
{
|
|
static char buffer[128];
|
|
|
|
if (getnameinfo((struct sockaddr *)address, sizeof *address,
|
|
buffer, sizeof buffer, NULL, 0, NI_NUMERICHOST) != 0)
|
|
strcpy(buffer, "<invalid>");
|
|
|
|
return buffer;
|
|
}
|
|
|
|
u_long
|
|
param(char **argv, int argc, int current, u_long min, u_long max)
|
|
{
|
|
u_long temp;
|
|
char *dummy;
|
|
|
|
if (current == (argc - 1) ) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_1, argv[current]);
|
|
// printf("Value must be supplied for option %s.\n", argv[current]);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
temp = strtoul(argv[current+1], &dummy, 0);
|
|
if (temp < min || temp > max) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_2, argv[current]);
|
|
// printf("Bad value for option %s.\n", argv[current]);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
return temp;
|
|
}
|
|
|
|
u_int num_send=0, num_recv=0,
|
|
time_min=(u_int)-1, time_max=0, time_total=0;
|
|
|
|
void
|
|
print_statistics( )
|
|
{
|
|
if (num_send > 0) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_3, format_addr(&dstaddr));
|
|
// printf("\nPing statistics for %s:\n", format_addr(&dstaddr));
|
|
|
|
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_4,
|
|
num_send, num_recv, num_send - num_recv,
|
|
100 * (num_send - num_recv) / num_send);
|
|
// printf(" Packets: Sent = %u, Received = %u, Lost = %u (%u%% loss),\n",
|
|
// num_send, num_recv, num_send - num_recv,
|
|
// 100 * (num_send - num_recv) / num_send);
|
|
|
|
|
|
if (num_recv > 0) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_5);
|
|
// printf("Approximate round trip times in milli-seconds:\n");
|
|
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_6,
|
|
time_min, time_max, time_total / num_recv);
|
|
// printf(" Minimum = %ums, Maximum = %ums, Average = %ums\n",
|
|
// time_min, time_max, time_total / num_recv);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// Press C-c to print and abort.
|
|
// Press C-break to print and continue.
|
|
|
|
BOOL
|
|
ConsoleControlHandler(DWORD dwCtrlType)
|
|
{
|
|
print_statistics();
|
|
switch ( dwCtrlType ) {
|
|
case CTRL_BREAK_EVENT:
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_7);
|
|
// printf("Control-Break\n");
|
|
|
|
return TRUE;
|
|
case CTRL_C_EVENT:
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_8);
|
|
// printf("Control-C\n");
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
int __cdecl
|
|
main(int argc, char **argv)
|
|
{
|
|
char *arg;
|
|
u_int i;
|
|
u_int j;
|
|
int found_addr = FALSE;
|
|
int dnsreq = FALSE;
|
|
char *hostname = NULL;
|
|
u_int Count = DEFAULT_COUNT;
|
|
u_long Timeout = DEFAULT_TIMEOUT;
|
|
DWORD errorCode;
|
|
HANDLE Handle;
|
|
int err;
|
|
BOOL result;
|
|
PICMPV6_ECHO_REQUEST request;
|
|
PICMPV6_ECHO_REPLY reply;
|
|
char *SendBuffer, *RcvBuffer;
|
|
u_int RcvSize;
|
|
u_int SendSize = DEFAULT_SEND_SIZE;
|
|
u_int ReplySize;
|
|
DWORD bytesReturned;
|
|
WSADATA WsaData;
|
|
int Reverse = FALSE;
|
|
|
|
PWCHAR Message;
|
|
|
|
memset(&srcaddr, 0, sizeof srcaddr);
|
|
memset(&dstaddr, 0, sizeof dstaddr);
|
|
|
|
err = WSAStartup(MAKEWORD(2, 0), &WsaData);
|
|
if (err) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_9, GetLastError());
|
|
// printf("Unable to initialize Windows Sockets interface, error code %d\n", GetLastError());
|
|
|
|
exit(1);
|
|
}
|
|
|
|
if (argc < 2) {
|
|
PrintUsage();
|
|
exit(1);
|
|
} else {
|
|
i = 1;
|
|
while (i < (u_int) argc) {
|
|
arg = argv[i];
|
|
if (arg[0] == '-' || arg[0] == '/') { // Have an option
|
|
switch (arg[1]) {
|
|
case '?':
|
|
PrintUsage();
|
|
exit(0);
|
|
|
|
case 'l':
|
|
// Avoid jumbo-grams, we don't support them yet.
|
|
// Need to allow 8 bytes for the Echo Request header.
|
|
SendSize = (u_int)param(argv, argc, i++, 0, 0xffff - 8);
|
|
break;
|
|
|
|
case 't':
|
|
Count = (u_int)-1;
|
|
break;
|
|
|
|
case 'n':
|
|
Count = (u_int)param(argv, argc, i++, 1, 0xffffffff);
|
|
break;
|
|
|
|
case 'w':
|
|
Timeout = param(argv, argc, i++, 0, 0xffffffff);
|
|
break;
|
|
|
|
case 'a':
|
|
dnsreq = TRUE;
|
|
break;
|
|
|
|
case 'r':
|
|
Reverse = TRUE;
|
|
break;
|
|
|
|
case 's':
|
|
if (!get_source(argv[++i], &srcaddr)) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_10, argv[i]);
|
|
// printf("Bad IPv6 address %s.\n", argv[i]);
|
|
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_11, arg);
|
|
// printf("Bad option %s.\n\n", arg);
|
|
|
|
PrintUsage();
|
|
exit(1);
|
|
break;
|
|
}
|
|
i++;
|
|
} else { // Not an option, must be an IPv6 address.
|
|
if (found_addr) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_12, arg);
|
|
// printf("Bad parameter %s.\n", arg);
|
|
|
|
exit(1);
|
|
}
|
|
if (get_pingee(arg, dnsreq, &dstaddr, &hostname)) {
|
|
found_addr = TRUE;
|
|
i++;
|
|
} else {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_10, arg);
|
|
// printf("Bad IPv6 address %s.\n", arg);
|
|
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found_addr) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_13);
|
|
// printf("IPv6 address must be specified.\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
Handle = CreateFileW(WIN_IPV6_DEVICE_NAME,
|
|
0, // desired access
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, // security attributes
|
|
OPEN_EXISTING,
|
|
0, // flags & attributes
|
|
NULL); // template file
|
|
if (Handle == INVALID_HANDLE_VALUE) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_14,
|
|
GetLastError() );
|
|
// printf("Unable to contact IPv6 driver, error code %d.\n",
|
|
// GetLastError() );
|
|
|
|
exit(1);
|
|
}
|
|
|
|
if (srcaddr.sin6_family == 0) {
|
|
SOCKET s;
|
|
DWORD BytesReturned;
|
|
|
|
//
|
|
// A source address was not specified.
|
|
// Get the preferred source address for this destination.
|
|
//
|
|
// If you want each individual echo request
|
|
// to select a source address, use "-s ::".
|
|
//
|
|
|
|
s = socket(AF_INET6, 0, 0);
|
|
if (s == INVALID_SOCKET) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_15,
|
|
WSAGetLastError());
|
|
// printf("Unable to create IPv6 socket, error code %d.\n",
|
|
// WSAGetLastError());
|
|
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// This ioctl will fail if ping6 is run on a system
|
|
// without the proper support in AFD and the stack.
|
|
// In that case, srcaddr will be unchanged.
|
|
//
|
|
(void) WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY,
|
|
&dstaddr, sizeof dstaddr,
|
|
&srcaddr, sizeof srcaddr,
|
|
&BytesReturned, NULL, NULL);
|
|
|
|
closesocket(s);
|
|
}
|
|
|
|
request = LocalAlloc(LMEM_FIXED, sizeof *request + SendSize);
|
|
if (request == NULL) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_16);
|
|
// printf("Unable to allocate required memory.\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
SendBuffer = (char *)(request + 1);
|
|
|
|
//
|
|
// Calculate receive buffer size and try to allocate it.
|
|
//
|
|
if (SendSize <= DEFAULT_SEND_SIZE) {
|
|
RcvSize = DEFAULT_BUFFER_SIZE;
|
|
}
|
|
else {
|
|
RcvSize = MAX_BUFFER_SIZE;
|
|
}
|
|
|
|
reply = LocalAlloc(LMEM_FIXED, sizeof *reply + RcvSize);
|
|
if (reply == NULL) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_16);
|
|
// printf("Unable to allocate required memory.\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
RcvBuffer = (char *)(reply + 1);
|
|
|
|
//
|
|
// Initialize the request buffer.
|
|
//
|
|
CopyTDIFromSA6(&request->DstAddress, &dstaddr);
|
|
CopyTDIFromSA6(&request->SrcAddress, &srcaddr);
|
|
request->Flags = 0;
|
|
if (Reverse)
|
|
request->Flags |= ICMPV6_ECHO_REQUEST_FLAG_REVERSE;
|
|
request->Timeout = Timeout;
|
|
request->TTL = 0; // default TTL
|
|
|
|
//
|
|
// Initialize the request data pattern.
|
|
//
|
|
for (i = 0; i < SendSize; i++)
|
|
SendBuffer[i] = 'a' + (i % 23);
|
|
|
|
if (hostname)
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_17, hostname, format_addr(&dstaddr));
|
|
// printf("\nPinging %s [%s]", hostname, format_addr(&dstaddr));
|
|
|
|
else
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_18, format_addr(&dstaddr));
|
|
// printf("\nPinging %s", format_addr(&dstaddr));
|
|
|
|
if (srcaddr.sin6_family != 0)
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_19, format_addr(&srcaddr));
|
|
// printf("\nfrom %s", format_addr(&srcaddr));
|
|
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_20, SendSize);
|
|
// printf(" with %u bytes of data:\n\n", SendSize);
|
|
|
|
|
|
SetConsoleCtrlHandler(&ConsoleControlHandler, TRUE);
|
|
|
|
for (i = 0; i < Count; i++) {
|
|
|
|
if (! DeviceIoControl(Handle,
|
|
IOCTL_ICMPV6_ECHO_REQUEST,
|
|
request,
|
|
sizeof *request + SendSize,
|
|
reply, sizeof *reply + RcvSize,
|
|
&bytesReturned,
|
|
NULL) ||
|
|
(bytesReturned < sizeof *reply)) {
|
|
|
|
errorCode = GetLastError();
|
|
|
|
if (errorCode < IP_STATUS_BASE) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_21, errorCode);
|
|
// printf("PING: transmit failed, error code %u\n", errorCode);
|
|
|
|
}
|
|
else {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_22,
|
|
GetErrorString(errorCode));
|
|
// printf("PING: %s\n", GetErrorString(errorCode));
|
|
|
|
}
|
|
}
|
|
else {
|
|
struct sockaddr_in6 ReplyAddress;
|
|
|
|
ReplySize = bytesReturned - sizeof *reply;
|
|
|
|
CopySAFromTDI6(&ReplyAddress, &reply->Address);
|
|
|
|
num_send++;
|
|
|
|
if (! IN6_IS_ADDR_UNSPECIFIED(&ReplyAddress.sin6_addr))
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_23, format_addr(&ReplyAddress));
|
|
// printf("Reply from %s: ", format_addr(&ReplyAddress));
|
|
|
|
|
|
if (reply->Status == IP_SUCCESS) {
|
|
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_24, ReplySize);
|
|
// printf("bytes=%u ", ReplySize);
|
|
|
|
|
|
if (ReplySize != SendSize) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_25, SendSize);
|
|
// printf("(sent %u) ", SendSize);
|
|
|
|
}
|
|
else {
|
|
char *sendptr, *recvptr;
|
|
|
|
sendptr = SendBuffer;
|
|
recvptr = RcvBuffer;
|
|
|
|
for (j = 0; j < SendSize; j++)
|
|
if (*sendptr++ != *recvptr++) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_26, j);
|
|
// printf("- MISCOMPARE at offset %u - ", j);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (reply->RoundTripTime) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_27, reply->RoundTripTime);
|
|
// printf("time=%ums ", reply->RoundTripTime);
|
|
|
|
}
|
|
else {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_28);
|
|
// printf("time<1ms ");
|
|
|
|
}
|
|
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_29);
|
|
// printf("\n");
|
|
|
|
|
|
//
|
|
// Collect statistics.
|
|
//
|
|
num_recv++;
|
|
time_total += reply->RoundTripTime;
|
|
if (reply->RoundTripTime < time_min)
|
|
time_min = reply->RoundTripTime;
|
|
if (reply->RoundTripTime > time_max)
|
|
time_max = reply->RoundTripTime;
|
|
}
|
|
else {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_30,
|
|
GetErrorString(reply->Status));
|
|
// printf("%s\n", GetErrorString(reply->Status));
|
|
|
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&ReplyAddress.sin6_addr)) {
|
|
//
|
|
// Diagnose some common errors.
|
|
//
|
|
if (reply->Status == IP_DEST_NO_ROUTE) {
|
|
if (IN6_IS_ADDR_LINKLOCAL(&dstaddr.sin6_addr) ||
|
|
IN6_IS_ADDR_SITELOCAL(&dstaddr.sin6_addr) ||
|
|
IN6_IS_ADDR_MULTICAST(&dstaddr.sin6_addr))
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_31);
|
|
// printf(" Specify correct scope-id or "
|
|
// "use -s to specify source address.\n");
|
|
|
|
}
|
|
else if (reply->Status == IP_PARAMETER_PROBLEM) {
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&dstaddr.sin6_addr))
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_32);
|
|
// printf(" Illegal destination address.\n");
|
|
|
|
else if (dstaddr.sin6_scope_id != 0)
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_33);
|
|
// printf(" Invalid scope-id specified.\n");
|
|
|
|
else if (IN6_IS_ADDR_LINKLOCAL(&dstaddr.sin6_addr) ||
|
|
IN6_IS_ADDR_SITELOCAL(&dstaddr.sin6_addr) ||
|
|
IN6_IS_ADDR_MULTICAST(&dstaddr.sin6_addr))
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_31);
|
|
// printf(" Specify correct scope-id or "
|
|
// "use -s to specify source address.\n");
|
|
|
|
}
|
|
else if (reply->Status == IP_BAD_ROUTE) {
|
|
NlsPutMsg(STDOUT, PING6_MESSAGE_34);
|
|
// printf(" Problem with source address or scope-id.\n");
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i < (Count - 1)) {
|
|
|
|
if (reply->RoundTripTime < MIN_INTERVAL) {
|
|
Sleep(MIN_INTERVAL - reply->RoundTripTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
print_statistics();
|
|
return num_recv == 0;
|
|
}
|