[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