/*++ Copyright (c) 1999-2001 Microsoft Corporation Module Name: pathping.c Abstract: PathPing utility Author: Dave Thaler Revision History: Who When What -------- -------- ---------------------------------------------- rajeshsu Aug 10, 1999 Added QoS support (802.1p and RSVP) dthaler Mar 31, 2001 Added IPv6 support mjourd Feb 20, 2002 Removed QoS support. Notes: --*/ #include #include #include #define NOGDI #define NOMINMAX #include #include #include #include #include #include #include #include #include #include "ipexport.h" #include "icmpapi.h" #include "nlstxt.h" #include "pathping.h" ULONG g_ulTimeout = DEFAULT_TIMEOUT; ULONG g_ulInterval = MIN_INTERVAL; ULONG g_ulNumQueries = DEFAULT_NUM_QUERIES; HANDLE g_hIcmp = NULL; ULONG g_ulRcvBufSize = 0x2000; BOOLEAN g_bDoReverseLookup = TRUE; SOCKADDR_STORAGE g_ssMyAddr = {0}; socklen_t g_slMyAddrLen = 0; BOOLEAN g_bSetAddr = FALSE; HOP hop[MAX_HOPS]; #ifdef VXD # define FAR _far #endif // VXD char SendBuffer[DEFAULT_SEND_SIZE]; char RcvBuffer[DEFAULT_RECEIVE_SIZE]; WSADATA WsaData; struct IPErrorTable { IP_STATUS Error; // The IP Error DWORD ErrorNlsID; // The corresponding NLS string ID. } ErrorTable[] = { { IP_BUF_TOO_SMALL, PATHPING_BUF_TOO_SMALL }, { IP_DEST_NET_UNREACHABLE, PATHPING_DEST_NET_UNREACHABLE }, { IP_DEST_HOST_UNREACHABLE, PATHPING_DEST_HOST_UNREACHABLE }, { IP_DEST_PROT_UNREACHABLE, PATHPING_DEST_PROT_UNREACHABLE }, { IP_DEST_PORT_UNREACHABLE, PATHPING_DEST_PORT_UNREACHABLE }, { IP_NO_RESOURCES, PATHPING_NO_RESOURCES }, { IP_BAD_OPTION, PATHPING_BAD_OPTION }, { IP_HW_ERROR, PATHPING_HW_ERROR }, { IP_PACKET_TOO_BIG, PATHPING_PACKET_TOO_BIG }, { IP_REQ_TIMED_OUT, PATHPING_REQ_TIMED_OUT }, { IP_BAD_REQ, PATHPING_BAD_REQ }, { IP_BAD_ROUTE, PATHPING_BAD_ROUTE }, { IP_TTL_EXPIRED_TRANSIT, PATHPING_TTL_EXPIRED_TRANSIT }, { IP_TTL_EXPIRED_REASSEM, PATHPING_TTL_EXPIRED_REASSEM }, { IP_PARAM_PROBLEM, PATHPING_PARAM_PROBLEM }, { IP_SOURCE_QUENCH, PATHPING_SOURCE_QUENCH }, { IP_OPTION_TOO_BIG, PATHPING_OPTION_TOO_BIG }, { IP_BAD_DESTINATION, PATHPING_BAD_DESTINATION }, { IP_NEGOTIATING_IPSEC, PATHPING_NEGOTIATING_IPSEC }, { IP_GENERAL_FAILURE, PATHPING_GENERAL_FAILURE } }; void print_addr(SOCKADDR *sa, socklen_t salen, BOOLEAN DoReverseLookup) { char hostname[NI_MAXHOST]; int i; BOOLEAN didReverse = FALSE; if (DoReverseLookup) { i = getnameinfo(sa, salen, hostname, sizeof(hostname), NULL, 0, NI_NAMEREQD); if (i == NO_ERROR) { didReverse = TRUE; NlsPutMsg(STDOUT, PATHPING_TARGET_NAME, hostname); } } i = getnameinfo(sa, salen, hostname, sizeof(hostname), NULL, 0, NI_NUMERICHOST); if (i != NO_ERROR) { // This should never happen unless there is a memory problem, // in which case the message associated with PATHPING_NO_RESOURCES // is reasonable. NlsPutMsg(STDOUT, PATHPING_NO_RESOURCES); exit (1); } if (didReverse) { NlsPutMsg( STDOUT, PATHPING_BRKT_IP_ADDRESS, hostname ); } else { NlsPutMsg( STDOUT, PATHPING_IP_ADDRESS, hostname ); } } void print_ip_addr(IPAddr ipv4Addr, BOOLEAN DoReverseLookup) { SOCKADDR_IN sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ipv4Addr; print_addr((LPSOCKADDR)&sin, sizeof(sin), DoReverseLookup); } void print_ipv6_addr(USHORT ipv6Addr[8], BOOLEAN DoReverseLookup) { SOCKADDR_IN6 sin; memset(&sin, 0, sizeof(sin)); sin.sin6_family = AF_INET6; memcpy(&sin.sin6_addr, ipv6Addr, sizeof(sin.sin6_addr)); print_addr((LPSOCKADDR)&sin, sizeof(sin), DoReverseLookup); } void print_time(ULONG Time) { if (Time) { NlsPutMsg( STDOUT, PATHPING_TIME, Time ); // printf(" %3lu ms\n", Time); } else { NlsPutMsg( STDOUT, PATHPING_TIME_10MS ); // printf(" <10 ms\n"); } } BOOLEAN param( ULONG *parameter, char **argv, int argc, int current, ULONG min, ULONG max ) { ULONG temp; char *dummy; if (current == (argc - 1) ) { NlsPutMsg( STDOUT, PATHPING_NO_OPTION_VALUE, argv[current] ); // printf( "Value must be supplied for option %s.\n", argv[current]); return(FALSE); } temp = strtoul(argv[current+1], &dummy, 0); if (temp < min || temp > max) { NlsPutMsg( STDOUT, PATHPING_BAD_OPTION_VALUE, argv[current] ); // printf( "Bad value for option %s.\n", argv[current]); return(FALSE); } *parameter = temp; return(TRUE); } BOOLEAN ResolveTarget( IN DWORD Family, IN char *TargetString, OUT SOCKADDR_STORAGE *TargetAddress, OUT socklen_t *TargetAddressLen, OUT char *TargetName, IN DWORD TargetNameLen, IN BOOLEAN DoReverseLookup ) { int i; struct addrinfo hints, *ai; TargetName[0] = '\0'; memset(&hints, 0, sizeof(hints)); hints.ai_family = Family; hints.ai_flags = AI_NUMERICHOST; i = getaddrinfo(TargetString, NULL, &hints, &ai); if (i == NO_ERROR) { *TargetAddressLen = (socklen_t)ai->ai_addrlen; memcpy(TargetAddress, ai->ai_addr, ai->ai_addrlen); if (DoReverseLookup) { getnameinfo(ai->ai_addr, (socklen_t)ai->ai_addrlen, TargetName, TargetNameLen, NULL, 0, NI_NAMEREQD); } freeaddrinfo(ai); return(TRUE); } else { hints.ai_flags = AI_CANONNAME; if (getaddrinfo(TargetString, NULL, &hints, &ai) == 0) { *TargetAddressLen = (socklen_t)ai->ai_addrlen; memcpy(TargetAddress, ai->ai_addr, ai->ai_addrlen); strcpy(TargetName, (ai->ai_canonname)? ai->ai_canonname : TargetString); freeaddrinfo(ai); return(TRUE); } } return(FALSE); } // ResolveTarget ULONG g_ulSendsDone = 0; void SleepForTotal( DWORD dwTotal ) { DWORD dwStopAt = GetTickCount() + dwTotal; int iLeft = (int)dwTotal; while (iLeft > 0) { SleepEx(iLeft, TRUE); iLeft = dwStopAt - GetTickCount(); } } VOID EchoDone( IN PVOID pContext, IN PIO_STATUS_BLOCK Ignored1, IN ULONG Ignored2 ) { PAPC_CONTEXT pApc = (PAPC_CONTEXT)pContext; ULONG ulNumReplies; ULONG i; UNREFERENCED_PARAMETER(Ignored1); UNREFERENCED_PARAMETER(Ignored2); g_ulSendsDone++; if (g_ssMyAddr.ss_family == AF_INET) { ulNumReplies = IcmpParseReplies(pApc->pReply4, g_ulRcvBufSize); for (i=0; isinAddr.sin_addr.s_addr is pApc->pReply4[i].Address) { pApc->ulNumRcvd++; pApc->ulRTTtotal += pApc->pReply4[i].RoundTripTime; return; } } } else { ulNumReplies = Icmp6ParseReplies(pApc->pReply6, g_ulRcvBufSize); for (i=0; isin6Addr.sin6_addr, &pApc->pReply6[i].Address.sin6_addr, sizeof(struct in6_addr))) { pApc->ulNumRcvd++; pApc->ulRTTtotal += pApc->pReply6[i].RoundTripTime; return; } } } } // now that the hop[] array is filled in, ping each one every g_ulInterval // seconds void ComputeStatistics( PIP_OPTION_INFORMATION pOptions ) { ULONG h, q; ULONG ulHopCount = (ULONG)pOptions->Ttl; // Allocate memory for replies for (h=1; h<=ulHopCount; h++) hop[h].pReply = LocalAlloc(LMEM_FIXED, g_ulRcvBufSize); for (q=0; q= h hop[ulHopCount].ulHopRcvd = hop[ulHopCount].ulNumRcvd; for (h=ulHopCount-1; h>0; h--) hop[h].ulHopRcvd = MAX(hop[h].ulNumRcvd, hop[h+1].ulHopRcvd); hop[0].ulHopRcvd = g_ulNumQueries; } VOID PrintResults( ULONG ulHopCount ) { ULONG h; int sent, rcvd, lost, linklost, nodelost; // Now output results NlsPutMsg(STDOUT, PATHPING_STAT_HEADER, GetLastError()); // printf(" Source to Here This Node/Link\n"); // printf("Hop RTT Lost/Sent = Pct Lost/Sent = Pct Address\n"); // printf(" 0 "); print_addr((LPSOCKADDR)&g_ssMyAddr, g_slMyAddrLen, g_bDoReverseLookup); NlsPutMsg(STDOUT, PATHPING_CR); // printf("\n"); for (h=1; h<=ulHopCount; h++) { sent = g_ulNumQueries; rcvd = hop[h].ulNumRcvd; lost = sent - rcvd; linklost = hop[h-1].ulHopRcvd - hop[h].ulHopRcvd; nodelost = hop[h].ulHopRcvd - hop[h].ulNumRcvd; // Display previous link stats // printf( " %4d/%4d =%3.0f%% |\n", // linklost, sent, 100.0*linklost/sent); NlsPutMsg(STDOUT, PATHPING_STAT_LINK, linklost, sent, 100*linklost/sent); if (rcvd) NlsPutMsg(STDOUT, PATHPING_HOP_RTT, h, hop[h].ulRTTtotal/rcvd); else NlsPutMsg(STDOUT, PATHPING_HOP_NO_RTT, h); // printf("%3d ", h); // if (!rcvd) // printf(" --- "); #if 0 // else if (hop[h].ulRTTtotal/rcvd == 0) // printf(" <10ms "); #endif // else // printf("%4dms ", hop[h].ulRTTtotal/rcvd); // printf("%4d/%4d =%3.0f%% ", lost, sent, 100.0*lost/sent); // printf("%4d/%4d =%3.0f%% ", nodelost, sent, 100.0*nodelost/sent); NlsPutMsg(STDOUT, PATHPING_STAT_LOSS, lost, sent, 100*lost/sent); NlsPutMsg(STDOUT, PATHPING_STAT_LOSS, nodelost, sent, 100*nodelost/sent); if (!hop[h].saAddr.sa_family) { hop[h].saAddr.sa_family = g_ssMyAddr.ss_family; } print_addr(&hop[h].saAddr, g_slMyAddrLen, g_bDoReverseLookup); NlsPutMsg(STDOUT, PATHPING_CR); // printf("\n"); } } BOOLEAN SetFamily(DWORD *Family, DWORD Value, char *arg) { if ((*Family != AF_UNSPEC) && (*Family != Value)) { NlsPutMsg(STDOUT, PATHPING_FAMILY, arg, (Value==AF_INET)? "IPv4" : "IPv6"); return FALSE; } *Family = Value; return TRUE; } int __cdecl main(int argc, char **argv) { SOCKADDR_STORAGE address; socklen_t addressLen; DWORD numberOfReplies; DWORD status; PICMP_ECHO_REPLY reply4; PICMPV6_ECHO_REPLY reply6; char hostname[NI_MAXHOST], literal[INET6_ADDRSTRLEN]; char *arg; int i; ULONG maximumHops = DEFAULT_MAXIMUM_HOPS; IP_OPTION_INFORMATION options; unsigned char optionsData[MAX_OPT_SIZE]; unsigned char *optionPtr; BYTE currentIndex; IPAddr tempAddr; BYTE j; BYTE SRIndex = 0; BOOLEAN foundAddress = FALSE; BOOLEAN haveReply; int numRetries = DEFAULT_MAX_RETRIES; DWORD Family = AF_UNSPEC; if (WSAStartup( 0x0101, &WsaData)) { NlsPutMsg(STDOUT, PATHPING_WSASTARTUP_FAILED, GetLastError()); return(1); } ZeroMemory(&address, sizeof(address)); addressLen = sizeof(address); options.Ttl = 1; options.Tos = DEFAULT_TOS; options.Flags = DEFAULT_FLAGS; options.OptionsSize = 0; options.OptionsData = optionsData; if (argc < 2) { NlsPutMsg( STDOUT, PATHPING_USAGE, argv[0] ); goto error_exit; } // // process command line // for (i=1; i < argc; i++) { arg = argv[i]; if ((arg[0] == '-') || (arg[0] == '/')) { switch(arg[1]) { case '?': NlsPutMsg(STDOUT, PATHPING_USAGE, argv[0]); goto error_exit; case '4': if (!SetFamily(&Family, AF_INET, arg)) { goto error_exit; } break; case '6': if (!SetFamily(&Family, AF_INET6, arg)) { goto error_exit; } break; case 'g': // Loose source routing // Only implemented for IPv4 so far if (!SetFamily(&Family, AF_INET, arg)) { goto error_exit; } currentIndex = options.OptionsSize; if ((currentIndex + 7) > MAX_OPT_SIZE) { NlsPutMsg(STDOUT, PATHPING_TOO_MANY_OPTIONS); goto error_exit; } optionPtr = options.OptionsData; optionPtr[currentIndex] = (unsigned char) IP_OPT_LSRR; optionPtr[currentIndex+1] = 3; optionPtr[currentIndex + 2] = 4; // Set initial pointer value options.OptionsSize += 3; while ( (i < (argc - 2)) && (*argv[i+1] != '-')) { if ((currentIndex + 7) > MAX_OPT_SIZE) { NlsPutMsg(STDOUT, PATHPING_TOO_MANY_OPTIONS); goto error_exit; } arg = argv[++i]; tempAddr = inet_addr(arg); if (tempAddr == INADDR_NONE) { NlsPutMsg( STDOUT, PATHPING_BAD_ROUTE_ADDRESS, arg ); // printf("Bad route specified for loose source route"); goto error_exit; } j = optionPtr[currentIndex+1]; *(ULONG UNALIGNED *)&optionPtr[j+currentIndex] = tempAddr; optionPtr[currentIndex+1] += 4; options.OptionsSize += 4; } SRIndex = optionPtr[currentIndex+1] + currentIndex; optionPtr[currentIndex+1] += 4; // Save space for dest. addr options.OptionsSize += 4; break; case 'h': if (!param(&maximumHops, argv, argc, i, 1, 255)) { goto error_exit; } i++; break; case 'i': { char tmphostname[NI_MAXHOST]; arg = argv[++i]; if (ResolveTarget(Family, arg, &g_ssMyAddr, &g_slMyAddrLen, tmphostname, sizeof(tmphostname), FALSE)) { g_bSetAddr = TRUE; } } break; case 'n': g_bDoReverseLookup = FALSE; break; case 'p': if (!param(&g_ulInterval, argv, argc, i, 1, 0xffffffff)) { goto error_exit; } i++; break; case 'q': if (!param(&g_ulNumQueries, argv, argc, i, 1, 255)) { goto error_exit; } i++; break; case 'w': if (!param(&g_ulTimeout, argv, argc, i, 1, 0xffffffff)) { goto error_exit; } i++; break; default: NlsPutMsg(STDOUT, PATHPING_INVALID_SWITCH, argv[i]); NlsPutMsg(STDOUT, PATHPING_USAGE); goto error_exit; break; } } else { foundAddress = TRUE; if ( !ResolveTarget(Family, argv[i], &address, &addressLen, hostname, sizeof(hostname), g_bDoReverseLookup) ) { NlsPutMsg( STDOUT, PATHPING_MESSAGE_1, argv[i] ); // printf( "Unable to resolve target name %s.\n", argv[i]); goto error_exit; } } } if (!foundAddress) { NlsPutMsg(STDOUT, PATHPING_NO_ADDRESS); NlsPutMsg(STDOUT, PATHPING_USAGE); goto error_exit; } if (SRIndex != 0) { *(ULONG UNALIGNED *)&options.OptionsData[SRIndex] = ((LPSOCKADDR_IN)&address)->sin_addr.s_addr; } Family = address.ss_family; if (Family == AF_INET) { g_hIcmp = IcmpCreateFile(); } else { g_hIcmp = Icmp6CreateFile(); } if (g_hIcmp == INVALID_HANDLE_VALUE) { status = GetLastError(); NlsPutMsg( STDOUT, PATHPING_MESSAGE_2, status ); // printf( "Unable to contact IP driver. Error code %d.\n", status); goto error_exit; } getnameinfo((LPSOCKADDR)&address, addressLen, literal, sizeof(literal), NULL, 0, NI_NUMERICHOST); if (hostname[0]) { NlsPutMsg( STDOUT, PATHPING_HEADER1, hostname, literal, maximumHops ); } else { NlsPutMsg( STDOUT, PATHPING_HEADER2, literal, maximumHops ); } // Get local IP address if (!g_bSetAddr) { SOCKET s = socket(address.ss_family, SOCK_RAW, 0); DWORD BytesReturned; WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY, &address, sizeof address, &g_ssMyAddr, sizeof g_ssMyAddr, &BytesReturned, NULL, NULL); g_slMyAddrLen = BytesReturned; closesocket(s); NlsPutMsg( STDOUT, PATHPING_MESSAGE_4, 0); print_addr((LPSOCKADDR)&g_ssMyAddr, g_slMyAddrLen, g_bDoReverseLookup); NlsPutMsg(STDOUT, PATHPING_CR); } // First we need to find out the path, so we // while((options.Ttl <= maximumHops) && (options.Ttl != 0)) { NlsPutMsg( STDOUT, PATHPING_MESSAGE_4, (UINT)options.Ttl ); // printf("[%3lu] ", (UINT)SendOpts.Ttl); haveReply = FALSE; for (i=0; isin_addr.s_addr, SendBuffer, DEFAULT_SEND_SIZE, &options, RcvBuffer, DEFAULT_RECEIVE_SIZE, g_ulTimeout ); if (numberOfReplies == 0) { status = GetLastError(); reply4 = NULL; } else { reply4 = (PICMP_ECHO_REPLY) RcvBuffer; status = reply4->Status; } if (status == IP_SUCCESS) { print_ip_addr( reply4->Address, g_bDoReverseLookup ); NlsPutMsg(STDOUT, PATHPING_CR); ZeroMemory(&hop[options.Ttl], sizeof(HOP)); hop[options.Ttl].sinAddr.sin_family = AF_INET; hop[options.Ttl].sinAddr.sin_addr.s_addr = reply4->Address; goto loop_end; } if (status == IP_TTL_EXPIRED_TRANSIT) { ZeroMemory(&hop[options.Ttl], sizeof(HOP)); hop[options.Ttl].sinAddr.sin_family = AF_INET; hop[options.Ttl].sinAddr.sin_addr.s_addr = reply4->Address; break; } if (status == IP_REQ_TIMED_OUT) { NlsPutMsg(STDOUT, PATHPING_NO_REPLY_TIME); // printf("."); continue; } if (status < IP_STATUS_BASE) { NlsPutMsg( STDOUT, PATHPING_MESSAGE_7, status ); // printf("Transmit error: code %lu\n", status); continue; } // // Fatal error. // if (reply4 != NULL) { print_ip_addr( reply4->Address, g_bDoReverseLookup ); NlsPutMsg( STDOUT, PATHPING_MESSAGE_6 ); // printf(" reports: "); } } else { numberOfReplies = Icmp6SendEcho2(g_hIcmp, 0, NULL, NULL, (LPSOCKADDR_IN6)&g_ssMyAddr, (LPSOCKADDR_IN6)&address, SendBuffer, DEFAULT_SEND_SIZE, &options, RcvBuffer, DEFAULT_RECEIVE_SIZE, g_ulTimeout ); if (numberOfReplies == 0) { status = GetLastError(); reply6 = NULL; } else { reply6 = (PICMPV6_ECHO_REPLY) RcvBuffer; status = reply6->Status; } if (status == IP_SUCCESS) { print_ipv6_addr( (USHORT*)reply6->Address.sin6_addr, g_bDoReverseLookup ); NlsPutMsg(STDOUT, PATHPING_CR); ZeroMemory(&hop[options.Ttl], sizeof(HOP)); hop[options.Ttl].sin6Addr.sin6_family = AF_INET6; memcpy(&hop[options.Ttl].sin6Addr.sin6_addr.s6_words, reply6->Address.sin6_addr, sizeof(reply6->Address.sin6_addr)); hop[options.Ttl].sin6Addr.sin6_scope_id = reply6->Address.sin6_scope_id; goto loop_end; } if (status == IP_TTL_EXPIRED_TRANSIT) { ZeroMemory(&hop[options.Ttl], sizeof(HOP)); hop[options.Ttl].sin6Addr.sin6_family = AF_INET6; memcpy(&hop[options.Ttl].sin6Addr.sin6_addr.s6_words, reply6->Address.sin6_addr, sizeof(reply6->Address.sin6_addr)); hop[options.Ttl].sin6Addr.sin6_scope_id = reply6->Address.sin6_scope_id; break; } if (status == IP_REQ_TIMED_OUT) { NlsPutMsg(STDOUT, PATHPING_NO_REPLY_TIME); // printf("."); continue; } if (status < IP_STATUS_BASE) { NlsPutMsg( STDOUT, PATHPING_MESSAGE_7, status ); // printf("Transmit error: code %lu\n", status); continue; } // // Fatal error. // if (reply6 != NULL) { print_ipv6_addr( (USHORT*)reply6->Address.sin6_addr, g_bDoReverseLookup ); NlsPutMsg( STDOUT, PATHPING_MESSAGE_6 ); // printf(" reports: "); } } for (i = 0; ( ErrorTable[i].Error != status && ErrorTable[i].Error != IP_GENERAL_FAILURE ); i++ ); NlsPutMsg( STDOUT, ErrorTable[i].ErrorNlsID ); // printf("%s.\n", ErrorTable[i].ErrorString); goto loop_end; } if (i==numRetries) break; print_addr(&hop[options.Ttl].saAddr, g_slMyAddrLen, g_bDoReverseLookup ); NlsPutMsg(STDOUT, PATHPING_CR); options.Ttl++; } loop_end: NlsPutMsg(STDOUT, PATHPING_COMPUTING, options.Ttl * g_ulInterval * g_ulNumQueries / 1000); // Okay, now that we have the path, we want to go back and // compute statistics over numQueries queries sent every intvl // seconds. ComputeStatistics(&options); PrintResults((ULONG)options.Ttl); NlsPutMsg( STDOUT, PATHPING_MESSAGE_8 ); // printf("\nTrace complete.\n"); IcmpCloseHandle(g_hIcmp); WSACleanup(); return(0); error_exit: WSACleanup(); return(1); }