[PATCH 4/5] nsiproxy: Implement UDP stats get_all_parameters.

Huw Davies huw at codeweavers.com
Wed Aug 18 02:54:47 CDT 2021


Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/nsi/tests/nsi.c    |  36 +++++++++++
 dlls/nsiproxy.sys/udp.c | 128 ++++++++++++++++++++++++++++++++++++++++
 include/wine/nsi.h      |  11 ++++
 3 files changed, 175 insertions(+)

diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c
index ce09bbf8a22..bd7992f6916 100644
--- a/dlls/nsi/tests/nsi.c
+++ b/dlls/nsi/tests/nsi.c
@@ -938,6 +938,40 @@ static void test_tcp_tables( int family, int table_type )
     winetest_pop_context();
 }
 
+static void test_udp_stats( int family )
+{
+    DWORD err;
+    USHORT key = family;
+    struct nsi_udp_stats_dynamic dyn, dyn2;
+    MIB_UDPSTATS table;
+
+    winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" );
+
+    err = NsiGetAllParameters( 1, &NPI_MS_UDP_MODULEID, NSI_UDP_STATS_TABLE, &key, sizeof(key), NULL, 0,
+                               &dyn, sizeof(dyn), NULL, 0 );
+    ok( !err, "got %x\n", err );
+
+    err = GetUdpStatisticsEx( &table, family );
+    ok( !err, "got %d\n", err );
+
+    err = NsiGetAllParameters( 1, &NPI_MS_UDP_MODULEID, NSI_UDP_STATS_TABLE, &key, sizeof(key), NULL, 0,
+                               &dyn2, sizeof(dyn2), NULL, 0 );
+    ok( !err, "got %x\n", err );
+
+    ok( bounded( table.dwInDatagrams, dyn.in_dgrams, dyn2.in_dgrams ), "%d vs [%I64d %I64d]\n",
+        table.dwInDatagrams, dyn.in_dgrams, dyn2.in_dgrams );
+    ok( bounded( table.dwNoPorts, dyn.no_ports, dyn2.no_ports ), "%d vs [%d %d]\n",
+        table.dwNoPorts, dyn.no_ports, dyn2.no_ports);
+    ok( bounded( table.dwInErrors, dyn.in_errs, dyn2.in_errs ), "%d vs [%d %d]\n",
+        table.dwInErrors, dyn.in_errs, dyn2.in_errs );
+    ok( bounded( table.dwOutDatagrams, dyn.out_dgrams, dyn2.out_dgrams ), "%d vs [%I64d %I64d]\n",
+        table.dwOutDatagrams, dyn.out_dgrams, dyn2.out_dgrams );
+todo_wine_if(!unstable(0) && table.dwNumAddrs != dyn.num_addrs)
+    ok( unstable( table.dwNumAddrs == dyn.num_addrs ), "%d %d\n", table.dwNumAddrs, dyn.num_addrs );
+
+    winetest_pop_context();
+}
+
 static void test_udp_tables( int family )
 {
     DWORD i, err, count, size;
@@ -1030,6 +1064,8 @@ START_TEST( nsi )
     test_tcp_tables( AF_INET6, TCP_TABLE_OWNER_MODULE_CONNECTIONS );
     test_tcp_tables( AF_INET6, TCP_TABLE_OWNER_MODULE_LISTENER );
 
+    test_udp_stats( AF_INET );
+    test_udp_stats( AF_INET6 );
     test_udp_tables( AF_INET );
     test_udp_tables( AF_INET6 );
 }
diff --git a/dlls/nsiproxy.sys/udp.c b/dlls/nsiproxy.sys/udp.c
index 454aefe95aa..49acedcf214 100644
--- a/dlls/nsiproxy.sys/udp.c
+++ b/dlls/nsiproxy.sys/udp.c
@@ -73,6 +73,125 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(nsi);
 
+static DWORD udp_num_addrs( USHORT family )
+{
+    DWORD endpoint_count = 0;
+
+    nsi_enumerate_all( 1, 0, &NPI_MS_UDP_MODULEID, NSI_UDP_ENDPOINT_TABLE,
+                       NULL, 0, NULL, 0, NULL, 0, NULL, 0, &endpoint_count );
+    /* FIXME: actually retrieve the keys and only count endpoints which match family */
+    return endpoint_count;
+}
+
+static NTSTATUS udp_stats_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_udp_stats_dynamic dyn;
+    const USHORT *family = key;
+
+    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 );
+
+    if (*family != WS_AF_INET && *family != WS_AF_INET6) return STATUS_NOT_SUPPORTED;
+
+    memset( &dyn, 0, sizeof(dyn) );
+
+    dyn.num_addrs = udp_num_addrs( *family );
+
+#ifdef __linux__
+    if (*family == WS_AF_INET)
+    {
+        NTSTATUS status = STATUS_NOT_SUPPORTED;
+        static const char hdr[] = "Udp:";
+        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))
+            {
+                unsigned int in_dgrams, out_dgrams;
+                ptr += sizeof(hdr);
+                sscanf( ptr, "%u %u %u %u %u",
+                        &in_dgrams, &dyn.no_ports, &dyn.in_errs, &out_dgrams, &dyn.num_addrs );
+                dyn.in_dgrams = in_dgrams;
+                dyn.out_dgrams = out_dgrams;
+                if (dynamic_data) *(struct nsi_udp_stats_dynamic *)dynamic_data = dyn;
+                status = STATUS_SUCCESS;
+                break;
+            }
+        }
+        fclose( fp );
+        return status;
+    }
+    else
+    {
+        unsigned int in_dgrams = 0, out_dgrams = 0;
+        struct
+        {
+            const char *name;
+            DWORD *elem;
+        } udp_stat_list[] =
+        {
+            { "Udp6InDatagrams",  &in_dgrams },
+            { "Udp6NoPorts",      &dyn.no_ports },
+            { "Udp6InErrors",     &dyn.in_errs },
+            { "Udp6OutDatagrams", &out_dgrams },
+        };
+        char buf[512], *ptr, *value;
+        DWORD res, i;
+        FILE *fp;
+
+        if (!(fp = fopen( "/proc/net/snmp6", "r" ))) return STATUS_NOT_SUPPORTED;
+
+        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';
+
+            for (i = 0; i < ARRAY_SIZE(udp_stat_list); i++)
+                if (!_strnicmp( buf, udp_stat_list[i].name, -1 ) && sscanf( value, "%d", &res ))
+                    *udp_stat_list[i].elem = res;
+        }
+        dyn.in_dgrams = in_dgrams;
+        dyn.out_dgrams = out_dgrams;
+        if (dynamic_data) *(struct nsi_udp_stats_dynamic *)dynamic_data = dyn;
+        fclose( fp );
+        return STATUS_SUCCESS;
+    }
+#elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS) && defined(HAVE_STRUCT_UDPSTAT_UDPS_IPACKETS)
+    {
+        int mib[] = { CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS };
+        struct udpstat udp_stat;
+        size_t needed = sizeof(udp_stat);
+
+        if (sysctl( mib, ARRAY_SIZE(mib), &udp_stat, &needed, NULL, 0 ) == -1) return STATUS_NOT_SUPPORTED;
+
+        dyn.in_dgrams = udp_stat.udps_ipackets;
+        dyn.out_dgrams = udp_stat.udps_opackets;
+        dyn.no_ports = udp_stat.udps_noport;
+        dyn.in_errs = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen;
+        if (dynamic_data) *(struct nsi_udp_stats_dynamic *)dynamic_data = dyn;
+        return STATUS_SUCCESS;
+    }
+#endif
+    FIXME( "Not implemented\n" );
+    return STATUS_NOT_SUPPORTED;
+}
+
 static NTSTATUS udp_endpoint_enumerate_all( void *key_data, DWORD key_size, void *rw_data, DWORD rw_size,
                                             void *dynamic_data, DWORD dynamic_size,
                                             void *static_data, DWORD static_size, DWORD_PTR *count )
@@ -271,6 +390,15 @@ static NTSTATUS udp_endpoint_enumerate_all( void *key_data, DWORD key_size, void
 
 static struct module_table udp_tables[] =
 {
+    {
+        NSI_UDP_STATS_TABLE,
+        {
+            sizeof(USHORT), 0,
+            sizeof(struct nsi_udp_stats_dynamic), 0
+        },
+        NULL,
+        udp_stats_get_all_parameters,
+    },
     {
         NSI_UDP_ENDPOINT_TABLE,
         {
diff --git a/include/wine/nsi.h b/include/wine/nsi.h
index 8bf3bc1d5d0..7b8cb29e9d5 100644
--- a/include/wine/nsi.h
+++ b/include/wine/nsi.h
@@ -347,8 +347,19 @@ struct nsi_tcp_conn_static
 };
 
 /* Undocumented NSI UDP tables */
+#define NSI_UDP_STATS_TABLE                0
 #define NSI_UDP_ENDPOINT_TABLE             1
 
+struct nsi_udp_stats_dynamic
+{
+    ULONGLONG in_dgrams;
+    DWORD no_ports;
+    DWORD in_errs;
+    ULONGLONG out_dgrams;
+    DWORD num_addrs;
+    DWORD unk[5];
+};
+
 struct nsi_udp_endpoint_key
 {
     SOCKADDR_INET local;
-- 
2.23.0




More information about the wine-devel mailing list