Signed-off-by: Huw Davies huw@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;