[PATCH 1/4] nsiproxy: Implement IPv4 ipstats get_all_parameters.
Huw Davies
huw at codeweavers.com
Wed Aug 11 05:01:28 CDT 2021
Signed-off-by: Huw Davies <huw at codeweavers.com>
---
dlls/nsi/tests/nsi.c | 50 ++++++++++++++++++
dlls/nsiproxy.sys/ip.c | 115 +++++++++++++++++++++++++++++++++++++++++
include/wine/nsi.h | 35 +++++++++++++
3 files changed, 200 insertions(+)
diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c
index f2a247d3333..dd1a552cce6 100644
--- a/dlls/nsi/tests/nsi.c
+++ b/dlls/nsi/tests/nsi.c
@@ -411,6 +411,54 @@ static void test_ndis_index_luid( void )
ok( err == ERROR_FILE_NOT_FOUND, "got %d\n", err );
}
+static void test_ip_ipstats( int family )
+{
+ const NPI_MODULEID *mod = (family == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID;
+ struct nsi_ip_ipstats_dynamic dyn, dyn2;
+ struct nsi_ip_ipstats_static stat;
+ MIB_IPSTATS table;
+ DWORD err;
+
+ winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" );
+
+ /* The table appears to consist of a single object without a key. The rw data does exist but
+ isn't part of GetIpStatisticsEx() and isn't yet tested */
+ err = NsiGetAllParameters( 1, mod, NSI_IP_IPSTATS_TABLE, NULL, 0, NULL, 0, &dyn, sizeof(dyn), &stat, sizeof(stat) );
+todo_wine_if( family == AF_INET6 )
+ ok( !err, "got %x\n", err );
+ if (err) goto err;
+
+ err = GetIpStatisticsEx( &table, family );
+ ok( !err, "got %d\n", err );
+
+ err = NsiGetAllParameters( 1, mod, NSI_IP_IPSTATS_TABLE, NULL, 0, NULL, 0, &dyn2, sizeof(dyn2), NULL, 0 );
+ ok( !err, "got %x\n", err );
+
+ /* dwForwarding and dwDefaultTTL come from the compartment table */
+ ok( bounded( table.dwInReceives, dyn.in_recv, dyn2.in_recv ), "mismatch\n" );
+ ok( bounded( table.dwInHdrErrors, dyn.in_hdr_errs, dyn2.in_hdr_errs ), "mismatch\n" );
+ ok( bounded( table.dwInAddrErrors, dyn.in_addr_errs, dyn2.in_addr_errs ), "mismatch\n" );
+ ok( bounded( table.dwForwDatagrams, dyn.fwd_dgrams, dyn2.fwd_dgrams ), "mismatch\n" );
+ ok( bounded( table.dwInUnknownProtos, dyn.in_unk_protos, dyn2.in_unk_protos ), "mismatch\n" );
+ ok( bounded( table.dwInDiscards, dyn.in_discards, dyn2.in_discards ), "mismatch\n" );
+ ok( bounded( table.dwInDelivers, dyn.in_delivers, dyn2.in_delivers ), "mismatch\n" );
+ ok( bounded( table.dwOutRequests, dyn.out_reqs, dyn2.out_reqs ), "mismatch\n" );
+ ok( bounded( table.dwRoutingDiscards, dyn.routing_discards, dyn2.routing_discards ), "mismatch\n" );
+ ok( bounded( table.dwOutDiscards, dyn.out_discards, dyn2.out_discards ), "mismatch\n" );
+ ok( bounded( table.dwOutNoRoutes, dyn.out_no_routes, dyn2.out_no_routes ), "mismatch\n" );
+ ok( table.dwReasmTimeout == stat.reasm_timeout, "mismatch\n" );
+ ok( bounded( table.dwReasmReqds, dyn.reasm_reqds, dyn2.reasm_reqds ), "mismatch\n" );
+ ok( bounded( table.dwReasmOks, dyn.reasm_oks, dyn2.reasm_oks ), "mismatch\n" );
+ ok( bounded( table.dwReasmFails, dyn.reasm_fails, dyn2.reasm_fails ), "mismatch\n" );
+ ok( bounded( table.dwFragOks, dyn.frag_oks, dyn2.frag_oks ), "mismatch\n" );
+ ok( bounded( table.dwFragFails, dyn.frag_fails, dyn2.frag_fails ), "mismatch\n" );
+ ok( bounded( table.dwFragCreates, dyn.frag_creates, dyn2.frag_creates ), "mismatch\n" );
+ /* dwNumIf, dwNumAddr and dwNumRoutes come from the compartment table */
+
+err:
+ winetest_pop_context();
+}
+
static void test_ip_unicast( int family )
{
DWORD rw_sizes[] = { FIELD_OFFSET(struct nsi_ip_unicast_rw, unk[0]), FIELD_OFFSET(struct nsi_ip_unicast_rw, unk[1]),
@@ -678,6 +726,8 @@ START_TEST( nsi )
test_ndis_ifinfo();
test_ndis_index_luid();
+ test_ip_ipstats( AF_INET );
+ test_ip_ipstats( AF_INET6 );
test_ip_unicast( AF_INET );
test_ip_unicast( AF_INET6 );
test_ip_neighbour( AF_INET );
diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c
index 2e727e5f369..438ae219174 100644
--- a/dlls/nsiproxy.sys/ip.c
+++ b/dlls/nsiproxy.sys/ip.c
@@ -120,6 +120,112 @@ static ULONG64 get_boot_time( void )
return ti.BootTime.QuadPart;
}
+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 )
+{
+ struct nsi_ip_ipstats_dynamic dyn;
+ struct nsi_ip_ipstats_static stat;
+
+ 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) );
+ memset( &stat, 0, sizeof(stat) );
+
+#ifdef __linux__
+ {
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
+ static const char hdr[] = "Ip:";
+ 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 ))
+ {
+ DWORD in_recv, in_hdr_errs, fwd_dgrams, in_delivers, out_reqs;
+ ptr += sizeof(hdr);
+ sscanf( ptr, "%*u %*u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
+ &in_recv,
+ &in_hdr_errs,
+ &dyn.in_addr_errs,
+ &fwd_dgrams,
+ &dyn.in_unk_protos,
+ &dyn.in_discards,
+ &in_delivers,
+ &out_reqs,
+ &dyn.out_discards,
+ &dyn.out_no_routes,
+ &stat.reasm_timeout,
+ &dyn.reasm_reqds,
+ &dyn.reasm_oks,
+ &dyn.reasm_fails,
+ &dyn.frag_oks,
+ &dyn.frag_fails,
+ &dyn.frag_creates );
+ /* no routingDiscards */
+ dyn.in_recv = in_recv;
+ dyn.in_hdr_errs = in_hdr_errs;
+ dyn.fwd_dgrams = fwd_dgrams;
+ dyn.in_delivers = in_delivers;
+ dyn.out_reqs = out_reqs;
+ if (dynamic_data) *(struct nsi_ip_ipstats_dynamic *)dynamic_data = dyn;
+ if (static_data) *(struct nsi_ip_ipstats_static *)static_data = stat;
+ status = STATUS_SUCCESS;
+ break;
+ }
+ }
+ fclose( fp );
+ return status;
+ }
+#elif defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS) && (defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL) || defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL))
+ {
+ int mib[] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS };
+#if defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL)
+ struct ipstat ip_stat;
+#elif defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL)
+ struct ip_stats ip_stat;
+#endif
+ size_t needed;
+
+ needed = sizeof(ip_stat);
+ if (sysctl( mib, ARRAY_SIZE(mib), &ip_stat, &needed, NULL, 0 ) == -1) return STATUS_NOT_SUPPORTED;
+
+ dyn.in_recv = ip_stat.ips_total;
+ dyn.in_hdr_errs = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen +
+ ip_stat.ips_badvers + ip_stat.ips_badoptions;
+ /* ips_badaddr also includes outgoing packets with a bad address, but we can't account for that right now */
+ dyn.in_addr_errs = ip_stat.ips_cantforward + ip_stat.ips_badaddr + ip_stat.ips_notmember;
+ dyn.fwd_dgrams = ip_stat.ips_forward;
+ dyn.in_unk_protos = ip_stat.ips_noproto;
+ dyn.in_discards = ip_stat.ips_fragdropped;
+ dyn.in_delivers = ip_stat.ips_delivered;
+ dyn.out_reqs = ip_stat.ips_localout;
+ dyn.out_discards = ip_stat.ips_odropped;
+ dyn.out_no_routes = ip_stat.ips_noroute;
+ stat.reasm_timeout = ip_stat.ips_fragtimeout;
+ dyn.reasm_reqds = ip_stat.ips_fragments;
+ dyn.reasm_oks = ip_stat.ips_reassembled;
+ dyn.reasm_fails = ip_stat.ips_fragments - ip_stat.ips_reassembled;
+ dyn.frag_oks = ip_stat.ips_fragmented;
+ dyn.frag_fails = ip_stat.ips_cantfrag;
+ dyn.frag_creates = ip_stat.ips_ofragments;
+
+ if (dynamic_data) *(struct nsi_ip_ipstats_dynamic *)dynamic_data = dyn;
+ if (static_data) *(struct nsi_ip_ipstats_static *)static_data = stat;
+ return STATUS_SUCCESS;
+ }
+#else
+ FIXME( "not implemented\n" );
+ return STATUS_NOT_IMPLEMENTED;
+#endif
+}
+
static void unicast_fill_entry( struct ifaddrs *entry, void *key, struct nsi_ip_unicast_rw *rw,
struct nsi_ip_unicast_dynamic *dyn, struct nsi_ip_unicast_static *stat )
{
@@ -687,6 +793,15 @@ static NTSTATUS ipv6_forward_enumerate_all( void *key_data, DWORD key_size, void
static struct module_table ipv4_tables[] =
{
+ {
+ NSI_IP_IPSTATS_TABLE,
+ {
+ 0, 0,
+ sizeof(struct nsi_ip_ipstats_dynamic), sizeof(struct nsi_ip_ipstats_static)
+ },
+ NULL,
+ ipv4_ipstats_get_all_parameters,
+ },
{
NSI_IP_UNICAST_TABLE,
{
diff --git a/include/wine/nsi.h b/include/wine/nsi.h
index 437582ad692..f3edef9f3fd 100644
--- a/include/wine/nsi.h
+++ b/include/wine/nsi.h
@@ -97,10 +97,45 @@ struct nsi_ndis_ifinfo_static
};
/* Undocumented NSI IP tables */
+#define NSI_IP_IPSTATS_TABLE 6
#define NSI_IP_UNICAST_TABLE 10
#define NSI_IP_NEIGHBOUR_TABLE 11
#define NSI_IP_FORWARD_TABLE 16
+struct nsi_ip_ipstats_dynamic
+{
+ DWORD unk[4];
+ ULONGLONG in_recv;
+ ULONGLONG in_octets;
+ ULONGLONG fwd_dgrams;
+ ULONGLONG in_delivers;
+ ULONGLONG out_reqs;
+ ULONGLONG unk2;
+ ULONGLONG unk3;
+ ULONGLONG out_octets;
+ ULONGLONG unk4[6];
+ ULONGLONG in_hdr_errs;
+ DWORD in_addr_errs;
+ DWORD in_unk_protos;
+ DWORD unk5;
+ DWORD reasm_reqds;
+ DWORD reasm_oks;
+ DWORD reasm_fails;
+ DWORD in_discards;
+ DWORD out_no_routes;
+ DWORD out_discards;
+ DWORD routing_discards;
+ DWORD frag_oks;
+ DWORD frag_fails;
+ DWORD frag_creates;
+ DWORD unk6[7];
+};
+
+struct nsi_ip_ipstats_static
+{
+ DWORD reasm_timeout;
+};
+
struct nsi_ipv4_unicast_key
{
NET_LUID luid;
--
2.23.0
More information about the wine-devel
mailing list