diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index d1cf1961463..368159a8072 100644 --- a/dlls/iphlpapi/iphlpapi.spec +++ b/dlls/iphlpapi/iphlpapi.spec @@ -83,7 +83,7 @@ @ stdcall GetExtendedTcpTable( ptr ptr long long long long ) @ stdcall GetExtendedUdpTable( ptr ptr long long long long ) @ stdcall GetFriendlyIfIndex( long ) -#@ stub GetIcmpStatisticsEx +@ stdcall GetIcmpStatisticsEx( ptr long ) @ stdcall GetIcmpStatistics( ptr ) @ stub GetIcmpStatsFromStack @ stdcall GetIfEntry( ptr ) diff --git a/dlls/iphlpapi/ipstats.c b/dlls/iphlpapi/ipstats.c index f314745e9c1..79dac58a2e9 100644 --- a/dlls/iphlpapi/ipstats.c +++ b/dlls/iphlpapi/ipstats.c @@ -551,6 +551,178 @@ DWORD WINAPI GetIcmpStatistics(PMIB_ICMP stats) return ret; } +/****************************************************************** + * GetIcmpStatisticsEx (IPHLPAPI.@) + * + * Get the IPv4 and IPv6 ICMP statistics for the local computer. + * + * PARAMS + * stats [Out] buffer for ICMP statistics + * family [In] specifies wether IPv4 or IPv6 statistics are returned + * + * RETURNS + * Success: NO_ERROR + * Failure: error code from winerror.h + */ +DWORD WINAPI GetIcmpStatisticsEx(PMIB_ICMP_EX stats, DWORD family) +{ + DWORD ret = ERROR_NOT_SUPPORTED; + MIB_ICMP ipv4stats; + + if (!stats) return ERROR_INVALID_PARAMETER; + if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER; + memset( stats, 0, sizeof(MIB_ICMP_EX) ); + + if (family == WS_AF_INET6) + { +#ifdef __linux__ + { + FILE *fp; + + if ((fp = fopen("/proc/net/snmp6", "r"))) + { + struct icmpstatstruct{ + const char *name; + DWORD pos; + }; + static const struct icmpstatstruct icmpinstatlist[] = { + { "Icmp6InDestUnreachs", ICMP6_DST_UNREACH }, + { "Icmp6InPktTooBigs", ICMP6_PACKET_TOO_BIG }, + { "Icmp6InTimeExcds", ICMP6_TIME_EXCEEDED }, + { "Icmp6InParmProblems", ICMP6_PARAM_PROB }, + { "Icmp6InEchos", ICMP6_ECHO_REQUEST }, + { "Icmp6InEchoReplies", ICMP6_ECHO_REPLY }, + { "Icmp6InGroupMembQueries", ICMP6_MEMBERSHIP_QUERY }, + { "Icmp6InGroupMembResponses", ICMP6_MEMBERSHIP_REPORT }, + { "Icmp6InGroupMembReductions", ICMP6_MEMBERSHIP_REDUCTION }, + { "Icmp6InRouterSolicits", ND_ROUTER_SOLICIT }, + { "Icmp6InRouterAdvertisements", ND_ROUTER_ADVERT }, + { "Icmp6InNeighborSolicits", ND_NEIGHBOR_SOLICIT }, + { "Icmp6InNeighborAdvertisements", ND_NEIGHBOR_ADVERT }, + { "Icmp6InRedirects", ND_REDIRECT }, + { "Icmp6InMLDv2Reports", ICMP6_V2_MEMBERSHIP_REPORT }, + }; + static const struct icmpstatstruct icmpoutstatlist[] = { + { "Icmp6OutDestUnreachs", ICMP6_DST_UNREACH }, + { "Icmp6OutPktTooBigs", ICMP6_PACKET_TOO_BIG }, + { "Icmp6OutTimeExcds", ICMP6_TIME_EXCEEDED }, + { "Icmp6OutParmProblems", ICMP6_PARAM_PROB }, + { "Icmp6OutEchos", ICMP6_ECHO_REQUEST }, + { "Icmp6OutEchoReplies", ICMP6_ECHO_REPLY }, + { "Icmp6OutGroupMembQueries", ICMP6_MEMBERSHIP_QUERY }, + { "Icmp6OutGroupMembResponses", ICMP6_MEMBERSHIP_REPORT }, + { "Icmp6OutGroupMembReductions", ICMP6_MEMBERSHIP_REDUCTION }, + { "Icmp6OutRouterSolicits", ND_ROUTER_SOLICIT }, + { "Icmp6OutRouterAdvertisements", ND_ROUTER_ADVERT }, + { "Icmp6OutNeighborSolicits", ND_NEIGHBOR_SOLICIT }, + { "Icmp6OutNeighborAdvertisements", ND_NEIGHBOR_ADVERT }, + { "Icmp6OutRedirects", ND_REDIRECT }, + { "Icmp6OutMLDv2Reports", ICMP6_V2_MEMBERSHIP_REPORT }, + }; + char buf[512], *ptr, *value; + DWORD res, i; + + while ((ptr = fgets(buf, sizeof(buf), fp))) + { + if (!(value = strchr(buf, ' '))) + continue; + + /* terminate the valuename */ + ptr = value - 1; + *(ptr + 1) = '\0'; + + /* and strip leading spaces from value */ + value += 1; + while (*value==' ') value++; + if ((ptr = strchr(value, '\n'))) + *ptr='\0'; + + if (!strcasecmp(buf, "Icmp6InMsgs")) + { + if (sscanf(value, "%d", &res)) stats->icmpInStats.dwMsgs = res; + continue; + } + + if (!strcasecmp(buf, "Icmp6InErrors")) + { + if (sscanf(value, "%d", &res)) stats->icmpInStats.dwErrors = res; + continue; + } + + for (i = 0; i < sizeof(icmpinstatlist)/sizeof(icmpinstatlist[0]); i++) + { + if (!strcasecmp(buf, icmpinstatlist[i].name)) + { + if (sscanf(value, "%d", &res)) + stats->icmpInStats.rgdwTypeCount[icmpinstatlist[i].pos] = res; + break; + } + } + + if (!strcasecmp(buf, "Icmp6OutMsgs")) + { + if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwMsgs = res; + continue; + } + + if (!strcasecmp(buf, "Icmp6OutErrors")) + { + if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwErrors = res; + continue; + } + + for (i = 0; i < sizeof(icmpoutstatlist)/sizeof(icmpoutstatlist[0]); i++) + { + if (!strcasecmp(buf, icmpoutstatlist[i].name)) + { + if (sscanf(value, "%d", &res)) + stats->icmpOutStats.rgdwTypeCount[icmpoutstatlist[i].pos] = res; + break; + } + } + + } + fclose(fp); + ret = NO_ERROR; + } + } +#else + FIXME( "unimplemented for IPv6\n" ); +#endif + return ret; + } + + ret = GetIcmpStatistics(&ipv4stats); + if SUCCEEDED(ret) + { + stats->icmpInStats.dwMsgs = ipv4stats.stats.icmpInStats.dwMsgs; + stats->icmpInStats.dwErrors = ipv4stats.stats.icmpInStats.dwErrors; + stats->icmpInStats.rgdwTypeCount[ICMP4_DST_UNREACH] = ipv4stats.stats.icmpInStats.dwDestUnreachs; + stats->icmpInStats.rgdwTypeCount[ICMP4_SOURCE_QUENCH] = ipv4stats.stats.icmpInStats.dwSrcQuenchs; + stats->icmpInStats.rgdwTypeCount[ICMP4_REDIRECT] = ipv4stats.stats.icmpInStats.dwRedirects; + stats->icmpInStats.rgdwTypeCount[ICMP4_ECHO_REQUEST] = ipv4stats.stats.icmpInStats.dwEchos; + stats->icmpInStats.rgdwTypeCount[ICMP4_TIME_EXCEEDED] = ipv4stats.stats.icmpInStats.dwTimeExcds; + stats->icmpInStats.rgdwTypeCount[ICMP4_PARAM_PROB] = ipv4stats.stats.icmpInStats.dwParmProbs; + stats->icmpInStats.rgdwTypeCount[ICMP4_TIMESTAMP_REQUEST] = ipv4stats.stats.icmpInStats.dwTimestamps; + stats->icmpInStats.rgdwTypeCount[ICMP4_TIMESTAMP_REPLY] = ipv4stats.stats.icmpInStats.dwTimestampReps; + stats->icmpInStats.rgdwTypeCount[ICMP4_MASK_REQUEST] = ipv4stats.stats.icmpInStats.dwAddrMasks; + stats->icmpInStats.rgdwTypeCount[ICMP4_MASK_REPLY] = ipv4stats.stats.icmpInStats.dwAddrMaskReps; + + stats->icmpOutStats.dwMsgs = ipv4stats.stats.icmpOutStats.dwMsgs; + stats->icmpOutStats.dwErrors = ipv4stats.stats.icmpOutStats.dwErrors; + stats->icmpOutStats.rgdwTypeCount[ICMP4_DST_UNREACH] = ipv4stats.stats.icmpOutStats.dwDestUnreachs; + stats->icmpOutStats.rgdwTypeCount[ICMP4_SOURCE_QUENCH] = ipv4stats.stats.icmpOutStats.dwSrcQuenchs; + stats->icmpOutStats.rgdwTypeCount[ICMP4_REDIRECT] = ipv4stats.stats.icmpOutStats.dwRedirects; + stats->icmpOutStats.rgdwTypeCount[ICMP4_ECHO_REQUEST] = ipv4stats.stats.icmpOutStats.dwEchos; + stats->icmpOutStats.rgdwTypeCount[ICMP4_TIME_EXCEEDED] = ipv4stats.stats.icmpOutStats.dwTimeExcds; + stats->icmpOutStats.rgdwTypeCount[ICMP4_PARAM_PROB] = ipv4stats.stats.icmpOutStats.dwParmProbs; + stats->icmpOutStats.rgdwTypeCount[ICMP4_TIMESTAMP_REQUEST] = ipv4stats.stats.icmpOutStats.dwTimestamps; + stats->icmpOutStats.rgdwTypeCount[ICMP4_TIMESTAMP_REPLY] = ipv4stats.stats.icmpOutStats.dwTimestampReps; + stats->icmpOutStats.rgdwTypeCount[ICMP4_MASK_REQUEST] = ipv4stats.stats.icmpOutStats.dwAddrMasks; + stats->icmpOutStats.rgdwTypeCount[ICMP4_MASK_REPLY] = ipv4stats.stats.icmpOutStats.dwAddrMaskReps; + } + return ret; +} /****************************************************************** * GetIpStatisticsEx (IPHLPAPI.@) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 02a299f9284..eb961891a12 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -530,7 +530,7 @@ static void testGetIcmpStatisticsEx(void) if (!pGetIcmpStatisticsEx) { - skip( "GetIcmpStatisticsEx not available\n" ); + win_skip( "GetIcmpStatisticsEx not available\n" ); return; } @@ -541,6 +541,10 @@ static void testGetIcmpStatisticsEx(void) "GetIcmpStatisticsEx(NULL, AF_INET) returned %d, expected ERROR_INVALID_PARAMETER\n", apiReturn); } + apiReturn = pGetIcmpStatisticsEx(&stats, AF_BAN); + ok(apiReturn == ERROR_INVALID_PARAMETER, + "GetIcmpStatisticsEx(&stats, AF_BAN) returned %d, expected ERROR_INVALID_PARAMETER\n", apiReturn); + apiReturn = pGetIcmpStatisticsEx(&stats, AF_INET); ok(apiReturn == NO_ERROR, "GetIcmpStatisticsEx returned %d, expected NO_ERROR\n", apiReturn); if (apiReturn == NO_ERROR && winetest_debug > 1) diff --git a/include/ipmib.h b/include/ipmib.h index bc42c903a3f..65576be064d 100644 --- a/include/ipmib.h +++ b/include/ipmib.h @@ -192,6 +192,42 @@ typedef struct _MIB_ICMP MIBICMPINFO stats; } MIB_ICMP, *PMIB_ICMP; +typedef enum +{ + ICMP4_ECHO_REPLY = 0, + ICMP4_DST_UNREACH = 3, + ICMP4_SOURCE_QUENCH = 4, + ICMP4_REDIRECT = 5, + ICMP4_ECHO_REQUEST = 8, + ICMP4_ROUTER_ADVERT = 9, + ICMP4_ROUTER_SOLICIT = 10, + ICMP4_TIME_EXCEEDED = 11, + ICMP4_PARAM_PROB = 12, + ICMP4_TIMESTAMP_REQUEST = 13, + ICMP4_TIMESTAMP_REPLY = 14, + ICMP4_MASK_REQUEST = 17, + ICMP4_MASK_REPLY = 18, +} ICMP4_TYPE, *PICMP4_TYPE; + +typedef enum +{ + ICMP6_DST_UNREACH = 1, + ICMP6_PACKET_TOO_BIG = 2, + ICMP6_TIME_EXCEEDED = 3, + ICMP6_PARAM_PROB = 4, + ICMP6_ECHO_REQUEST = 128, + ICMP6_ECHO_REPLY = 129, + ICMP6_MEMBERSHIP_QUERY = 130, + ICMP6_MEMBERSHIP_REPORT = 131, + ICMP6_MEMBERSHIP_REDUCTION = 132, + ND_ROUTER_SOLICIT = 133, + ND_ROUTER_ADVERT = 134, + ND_NEIGHBOR_SOLICIT = 135, + ND_NEIGHBOR_ADVERT = 136, + ND_REDIRECT = 137, + ICMP6_V2_MEMBERSHIP_REPORT = 143, +} ICMP6_TYPE, *PICMP6_TYPE; + typedef struct _MIBICMPSTATS_EX { DWORD dwMsgs;