Signed-off-by: Huw Davies <huw(a)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];
--
2.23.0