Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/nsi/tests/nsi.c | 44 +++++++++++++ dlls/nsiproxy.sys/ip.c | 141 +++++++++++++++++++++++++++++++++++++++++ include/wine/nsi.h | 11 ++++ 3 files changed, 196 insertions(+)
diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 7d9d8e12bac..3b1e757fa23 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -446,6 +446,48 @@ err: winetest_pop_context(); }
+static void test_ip_icmpstats( int family ) +{ + const NPI_MODULEID *mod = (family == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID; + struct nsi_ip_icmpstats_dynamic nsi_stats, nsi_stats2; + MIB_ICMP_EX table; + DWORD err, i; + + winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" ); + + err = NsiGetAllParameters( 1, mod, NSI_IP_ICMPSTATS_TABLE, NULL, 0, NULL, 0, &nsi_stats, sizeof(nsi_stats), NULL, 0 ); +todo_wine_if( family == AF_INET6) + ok( !err, "got %d\n", err ); + if (err) goto err; + + err = GetIcmpStatisticsEx( &table, family ); + ok( !err, "got %d\n", err ); + if (err) goto err; + + err = NsiGetAllParameters( 1, mod, NSI_IP_ICMPSTATS_TABLE, NULL, 0, NULL, 0, &nsi_stats2, sizeof(nsi_stats2), NULL, 0 ); + ok( !err, "got %d\n", err ); + + ok( bounded( table.icmpInStats.dwMsgs, nsi_stats.in_msgs, nsi_stats2.in_msgs ), + "%d vs [%d %d]\n", table.icmpInStats.dwMsgs, nsi_stats.in_msgs, nsi_stats2.in_msgs ); + ok( bounded( table.icmpInStats.dwErrors, nsi_stats.in_errors, nsi_stats2.in_errors ), + "%d vs [%d %d]\n", table.icmpInStats.dwErrors, nsi_stats.in_errors, nsi_stats2.in_errors ); + ok( bounded( table.icmpOutStats.dwMsgs, nsi_stats.out_msgs, nsi_stats2.out_msgs ), + "%d vs [%d %d]\n", table.icmpOutStats.dwMsgs, nsi_stats.out_msgs, nsi_stats2.out_msgs ); + ok( bounded( table.icmpOutStats.dwErrors, nsi_stats.out_errors, nsi_stats2.out_errors ), + "%d vs [%d %d]\n", table.icmpOutStats.dwErrors, nsi_stats.out_errors, nsi_stats2.out_errors ); + for (i = 0; i < ARRAY_SIZE(nsi_stats.in_type_counts); i++) + { + winetest_push_context( "%d", i ); + ok( bounded( table.icmpInStats.rgdwTypeCount[i], nsi_stats.in_type_counts[i], nsi_stats2.in_type_counts[i] ), + "%d vs [%d %d]\n", table.icmpInStats.rgdwTypeCount[i], nsi_stats.in_type_counts[i], nsi_stats2.in_type_counts[i] ); + ok( bounded( table.icmpOutStats.rgdwTypeCount[i], nsi_stats.out_type_counts[i], nsi_stats2.out_type_counts[i] ), + "%d vs [%d %d]\n", table.icmpOutStats.rgdwTypeCount[i], nsi_stats.out_type_counts[i], nsi_stats2.out_type_counts[i] ); + winetest_pop_context(); + } +err: + winetest_pop_context(); +} +
static void test_ip_ipstats( int family ) { @@ -763,6 +805,8 @@ START_TEST( nsi )
test_ip_cmpt( AF_INET ); test_ip_cmpt( AF_INET6 ); + test_ip_icmpstats( AF_INET ); + test_ip_icmpstats( AF_INET6 ); test_ip_ipstats( AF_INET ); test_ip_ipstats( AF_INET6 ); test_ip_unicast( AF_INET ); diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index 19272861cef..8b7b41a291c 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -41,10 +41,26 @@ #include <netinet/in.h> #endif
+#ifdef HAVE_NETINET_IP_H +#include <netinet/ip.h> +#endif + +#ifdef HAVE_NETINET_IN_SYSTM_H +#include <netinet/in_systm.h> +#endif + +#ifdef HAVE_NETINET_IP_ICMP_H +#include <netinet/ip_icmp.h> +#endif + #ifdef HAVE_NETINET_IP_VAR_H #include <netinet/ip_var.h> #endif
+#ifdef HAVE_NETINET_ICMP_VAR_H +#include <netinet/icmp_var.h> +#endif + #ifdef HAVE_NETINET_IF_ETHER_H #include <netinet/if_ether.h> #endif @@ -76,6 +92,7 @@ #include "ws2ipdef.h" #include "nldef.h" #include "ifdef.h" +#include "ipmib.h" #include "netiodef.h" #include "wine/heap.h" #include "wine/nsi.h" @@ -223,6 +240,121 @@ static NTSTATUS ipv6_cmpt_get_all_parameters( const void *key, DWORD key_size, v dynamic_data, dynamic_size, static_data, static_size ); }
+static NTSTATUS ipv4_icmpstats_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size, + void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size ) +{ + struct nsi_ip_icmpstats_dynamic dyn; + + TRACE( "%p %d %p %d %p %d %p %d\n", key, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size ); + + memset( &dyn, 0, sizeof(dyn) ); + +#ifdef __linux__ + { + NTSTATUS status = STATUS_NOT_SUPPORTED; + static const char hdr[] = "Icmp:"; + char buf[512], *ptr; + FILE *fp; + + if (!(fp = fopen( "/proc/net/snmp", "r" ))) return STATUS_NOT_SUPPORTED; + + while ((ptr = fgets( buf, sizeof(buf), fp ))) + { + if (_strnicmp( buf, hdr, sizeof(hdr) - 1 )) continue; + /* last line was a header, get another */ + if (!(ptr = fgets( buf, sizeof(buf), fp ))) break; + if (!_strnicmp( buf, hdr, sizeof(hdr) - 1 )) + { + ptr += sizeof(hdr); + sscanf( ptr, "%u %u %*u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u", + &dyn.in_msgs, + &dyn.in_errors, + &dyn.in_type_counts[ICMP4_DST_UNREACH], + &dyn.in_type_counts[ICMP4_TIME_EXCEEDED], + &dyn.in_type_counts[ICMP4_PARAM_PROB], + &dyn.in_type_counts[ICMP4_SOURCE_QUENCH], + &dyn.in_type_counts[ICMP4_REDIRECT], + &dyn.in_type_counts[ICMP4_ECHO_REQUEST], + &dyn.in_type_counts[ICMP4_ECHO_REPLY], + &dyn.in_type_counts[ICMP4_TIMESTAMP_REQUEST], + &dyn.in_type_counts[ICMP4_TIMESTAMP_REPLY], + &dyn.in_type_counts[ICMP4_MASK_REQUEST], + &dyn.in_type_counts[ICMP4_MASK_REPLY], + &dyn.out_msgs, + &dyn.out_errors, + &dyn.out_type_counts[ICMP4_DST_UNREACH], + &dyn.out_type_counts[ICMP4_TIME_EXCEEDED], + &dyn.out_type_counts[ICMP4_PARAM_PROB], + &dyn.out_type_counts[ICMP4_SOURCE_QUENCH], + &dyn.out_type_counts[ICMP4_REDIRECT], + &dyn.out_type_counts[ICMP4_ECHO_REQUEST], + &dyn.out_type_counts[ICMP4_ECHO_REPLY], + &dyn.out_type_counts[ICMP4_TIMESTAMP_REQUEST], + &dyn.out_type_counts[ICMP4_TIMESTAMP_REPLY], + &dyn.out_type_counts[ICMP4_MASK_REQUEST], + &dyn.out_type_counts[ICMP4_MASK_REPLY] ); + status = STATUS_SUCCESS; + if (dynamic_data) *(struct nsi_ip_icmpstats_dynamic *)dynamic_data = dyn; + break; + } + } + fclose( fp ); + return status; + } +#elif defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS) + { + int mib[] = { CTL_NET, PF_INET, IPPROTO_ICMP, ICMPCTL_STATS }; + struct icmpstat icmp_stat; + size_t needed = sizeof(icmp_stat); + int i; + + if (sysctl( mib, ARRAY_SIZE(mib), &icmp_stat, &needed, NULL, 0 ) == -1) return STATUS_NOT_SUPPORTED; + + dyn.in_msgs = icmp_stat.icps_badcode + icmp_stat.icps_checksum + icmp_stat.icps_tooshort + icmp_stat.icps_badlen; + for (i = 0; i <= ICMP_MAXTYPE; i++) + dyn.in_msgs += icmp_stat.icps_inhist[i]; + + dyn.in_errors = icmp_stat.icps_badcode + icmp_stat.icps_tooshort + icmp_stat.icps_checksum + icmp_stat.icps_badlen; + + dyn.in_type_counts[ICMP4_DST_UNREACH] = icmp_stat.icps_inhist[ICMP_UNREACH]; + dyn.in_type_counts[ICMP4_TIME_EXCEEDED] = icmp_stat.icps_inhist[ICMP_TIMXCEED]; + dyn.in_type_counts[ICMP4_PARAM_PROB] = icmp_stat.icps_inhist[ICMP_PARAMPROB]; + dyn.in_type_counts[ICMP4_SOURCE_QUENCH] = icmp_stat.icps_inhist[ICMP_SOURCEQUENCH]; + dyn.in_type_counts[ICMP4_REDIRECT] = icmp_stat.icps_inhist[ICMP_REDIRECT]; + dyn.in_type_counts[ICMP4_ECHO_REQUEST] = icmp_stat.icps_inhist[ICMP_ECHO]; + dyn.in_type_counts[ICMP4_ECHO_REPLY] = icmp_stat.icps_inhist[ICMP_ECHOREPLY]; + dyn.in_type_counts[ICMP4_TIMESTAMP_REQUEST] = icmp_stat.icps_inhist[ICMP_TSTAMP]; + dyn.in_type_counts[ICMP4_TIMESTAMP_REPLY] = icmp_stat.icps_inhist[ICMP_TSTAMPREPLY]; + dyn.in_type_counts[ICMP4_MASK_REQUEST] = icmp_stat.icps_inhist[ICMP_MASKREQ]; + dyn.in_type_counts[ICMP4_MASK_REPLY] = icmp_stat.icps_inhist[ICMP_MASKREPLY]; + + dyn.out_msgs = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp; + for (i = 0; i <= ICMP_MAXTYPE; i++) + dyn.out_msgs += icmp_stat.icps_outhist[i]; + + dyn.out_errors = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp; + + dyn.out_type_counts[ICMP4_DST_UNREACH] = icmp_stat.icps_outhist[ICMP_UNREACH]; + dyn.out_type_counts[ICMP4_TIME_EXCEEDED] = icmp_stat.icps_outhist[ICMP_TIMXCEED]; + dyn.out_type_counts[ICMP4_PARAM_PROB] = icmp_stat.icps_outhist[ICMP_PARAMPROB]; + dyn.out_type_counts[ICMP4_SOURCE_QUENCH] = icmp_stat.icps_outhist[ICMP_SOURCEQUENCH]; + dyn.out_type_counts[ICMP4_REDIRECT] = icmp_stat.icps_outhist[ICMP_REDIRECT]; + dyn.out_type_counts[ICMP4_ECHO_REQUEST] = icmp_stat.icps_outhist[ICMP_ECHO]; + dyn.out_type_counts[ICMP4_ECHO_REPLY] = icmp_stat.icps_outhist[ICMP_ECHOREPLY]; + dyn.out_type_counts[ICMP4_TIMESTAMP_REQUEST] = icmp_stat.icps_outhist[ICMP_TSTAMP]; + dyn.out_type_counts[ICMP4_TIMESTAMP_REPLY] = icmp_stat.icps_outhist[ICMP_TSTAMPREPLY]; + dyn.out_type_counts[ICMP4_MASK_REQUEST] = icmp_stat.icps_outhist[ICMP_MASKREQ]; + dyn.out_type_counts[ICMP4_MASK_REPLY] = icmp_stat.icps_outhist[ICMP_MASKREPLY]; + if (dynamic_data) *(struct nsi_ip_icmpstats_dynamic *)dynamic_data = dyn; + return STATUS_SUCCESS; + } +#else + FIXME( "not implemented\n" ); + return STATUS_NOT_IMPLEMENTED; +#endif +} + static NTSTATUS ipv4_ipstats_get_all_parameters( const void *key, DWORD key_size, void *rw_data, DWORD rw_size, void *dynamic_data, DWORD dynamic_size, void *static_data, DWORD static_size ) { @@ -981,6 +1113,15 @@ static struct module_table ipv4_tables[] = NULL, ipv4_cmpt_get_all_parameters, }, + { + NSI_IP_ICMPSTATS_TABLE, + { + 0, 0, + sizeof(struct nsi_ip_icmpstats_dynamic), 0 + }, + NULL, + ipv4_icmpstats_get_all_parameters, + }, { NSI_IP_IPSTATS_TABLE, { diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 6a90f5f1203..2c72c16aedc 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -98,6 +98,7 @@ struct nsi_ndis_ifinfo_static
/* Undocumented NSI IP tables */ #define NSI_IP_COMPARTMENT_TABLE 2 +#define NSI_IP_ICMPSTATS_TABLE 3 #define NSI_IP_IPSTATS_TABLE 6 #define NSI_IP_UNICAST_TABLE 10 #define NSI_IP_NEIGHBOUR_TABLE 11 @@ -119,6 +120,16 @@ struct nsi_ip_cmpt_dynamic DWORD num_addrs; };
+struct nsi_ip_icmpstats_dynamic +{ + DWORD in_msgs; + DWORD in_errors; + DWORD in_type_counts[256]; + DWORD out_msgs; + DWORD out_errors; + DWORD out_type_counts[256]; +}; + struct nsi_ip_ipstats_dynamic { DWORD unk[4];