iphlpapi: Implement GetTcpStatisticsEx() on top of nsi.

Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Huw Davies 2021-08-13 08:35:50 +01:00 committed by Alexandre Julliard
parent c9680e8991
commit 4716138d07
4 changed files with 64 additions and 166 deletions

View File

@ -2807,6 +2807,68 @@ BOOL WINAPI GetRTTAndHopCount(IPAddr DestIpAddress, PULONG HopCount, ULONG MaxHo
return FALSE; return FALSE;
} }
/******************************************************************
* GetTcpStatistics (IPHLPAPI.@)
*
* Get the TCP statistics for the local computer.
*
* PARAMS
* stats [Out] buffer for TCP statistics
*
* RETURNS
* Success: NO_ERROR
* Failure: error code from winerror.h
*/
DWORD WINAPI GetTcpStatistics( MIB_TCPSTATS *stats )
{
return GetTcpStatisticsEx( stats, WS_AF_INET );
}
/******************************************************************
* GetTcpStatisticsEx (IPHLPAPI.@)
*
* Get the IPv4 and IPv6 TCP statistics for the local computer.
*
* PARAMS
* stats [Out] buffer for TCP statistics
* family [In] specifies whether IPv4 or IPv6 statistics are returned
*
* RETURNS
* Success: NO_ERROR
* Failure: error code from winerror.h
*/
DWORD WINAPI GetTcpStatisticsEx( MIB_TCPSTATS *stats, DWORD family )
{
struct nsi_tcp_stats_dynamic dyn;
struct nsi_tcp_stats_static stat;
USHORT key = (USHORT)family;
DWORD err;
if (!stats || !ip_module_id( family )) return ERROR_INVALID_PARAMETER;
memset( stats, 0, sizeof(*stats) );
err = NsiGetAllParameters( 1, &NPI_MS_TCP_MODULEID, NSI_TCP_STATS_TABLE, &key, sizeof(key), NULL, 0,
&dyn, sizeof(dyn), &stat, sizeof(stat) );
if (err) return err;
stats->u.RtoAlgorithm = stat.rto_algo;
stats->dwRtoMin = stat.rto_min;
stats->dwRtoMax = stat.rto_max;
stats->dwMaxConn = stat.max_conns;
stats->dwActiveOpens = dyn.active_opens;
stats->dwPassiveOpens = dyn.passive_opens;
stats->dwAttemptFails = dyn.attempt_fails;
stats->dwEstabResets = dyn.est_rsts;
stats->dwCurrEstab = dyn.cur_est;
stats->dwInSegs = (DWORD)dyn.in_segs;
stats->dwOutSegs = (DWORD)dyn.out_segs;
stats->dwRetransSegs = dyn.retrans_segs;
stats->dwInErrs = dyn.in_errs;
stats->dwOutRsts = dyn.out_rsts;
stats->dwNumConns = dyn.num_conns;
return err;
}
/****************************************************************** /******************************************************************
* GetTcpTable (IPHLPAPI.@) * GetTcpTable (IPHLPAPI.@)

View File

@ -276,167 +276,6 @@ static void *read_mib_entry( int fd, int level, int name, int *len )
} }
#endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */ #endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */
/******************************************************************
* GetTcpStatisticsEx (IPHLPAPI.@)
*
* Get the IPv4 and IPv6 TCP statistics for the local computer.
*
* PARAMS
* stats [Out] buffer for TCP statistics
* family [In] specifies whether IPv4 or IPv6 statistics are returned
*
* RETURNS
* Success: NO_ERROR
* Failure: error code from winerror.h
*/
DWORD WINAPI GetTcpStatisticsEx(PMIB_TCPSTATS stats, DWORD family)
{
DWORD ret = ERROR_NOT_SUPPORTED;
if (!stats) return ERROR_INVALID_PARAMETER;
if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
memset( stats, 0, sizeof(*stats) );
if (family == WS_AF_INET6)
{
FIXME( "unimplemented for IPv6\n" );
return ret;
}
#ifdef __linux__
{
FILE *fp;
if ((fp = fopen("/proc/net/snmp", "r")))
{
static const char hdr[] = "Tcp:";
MIB_TCPTABLE *tcp_table;
char buf[512], *ptr;
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",
&stats->u.dwRtoAlgorithm,
&stats->dwRtoMin,
&stats->dwRtoMax,
&stats->dwMaxConn,
&stats->dwActiveOpens,
&stats->dwPassiveOpens,
&stats->dwAttemptFails,
&stats->dwEstabResets,
&stats->dwCurrEstab,
&stats->dwInSegs,
&stats->dwOutSegs,
&stats->dwRetransSegs,
&stats->dwInErrs,
&stats->dwOutRsts );
break;
}
}
if (!AllocateAndGetTcpTableFromStack( &tcp_table, FALSE, GetProcessHeap(), 0 ))
{
stats->dwNumConns = tcp_table->dwNumEntries;
HeapFree( GetProcessHeap(), 0, tcp_table );
}
fclose(fp);
ret = NO_ERROR;
}
}
#elif defined(HAVE_LIBKSTAT)
{
static char tcp[] = "tcp";
kstat_ctl_t *kc;
kstat_t *ksp;
if ((kc = kstat_open()) &&
(ksp = kstat_lookup( kc, tcp, 0, tcp )) &&
kstat_read( kc, ksp, NULL ) != -1 &&
ksp->ks_type == KSTAT_TYPE_NAMED)
{
stats->u.dwRtoAlgorithm = kstat_get_ui32( ksp, "rtoAlgorithm" );
stats->dwRtoMin = kstat_get_ui32( ksp, "rtoMin" );
stats->dwRtoMax = kstat_get_ui32( ksp, "rtoMax" );
stats->dwMaxConn = kstat_get_ui32( ksp, "maxConn" );
stats->dwActiveOpens = kstat_get_ui32( ksp, "activeOpens" );
stats->dwPassiveOpens = kstat_get_ui32( ksp, "passiveOpens" );
stats->dwAttemptFails = kstat_get_ui32( ksp, "attemptFails" );
stats->dwEstabResets = kstat_get_ui32( ksp, "estabResets" );
stats->dwCurrEstab = kstat_get_ui32( ksp, "currEstab" );
stats->dwInSegs = kstat_get_ui32( ksp, "inSegs" );
stats->dwOutSegs = kstat_get_ui32( ksp, "outSegs" );
stats->dwRetransSegs = kstat_get_ui32( ksp, "retransSegs" );
stats->dwInErrs = kstat_get_ui32( ksp, "inErrs" );
stats->dwOutRsts = kstat_get_ui32( ksp, "outRsts" );
stats->dwNumConns = kstat_get_ui32( ksp, "connTableSize" );
ret = NO_ERROR;
}
if (kc) kstat_close( kc );
}
#elif defined(HAVE_SYS_SYSCTL_H) && defined(TCPCTL_STATS) && (defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT) || defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT))
{
#ifndef TCPTV_MIN /* got removed in Mac OS X for some reason */
#define TCPTV_MIN 2
#define TCPTV_REXMTMAX 128
#endif
int mib[] = {CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS};
#define hz 1000
#if defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT)
struct tcpstat tcp_stat;
#elif defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT)
struct tcp_stats tcp_stat;
#endif
size_t needed = sizeof(tcp_stat);
if(sysctl(mib, ARRAY_SIZE(mib), &tcp_stat, &needed, NULL, 0) != -1)
{
stats->u.RtoAlgorithm = MIB_TCP_RTO_VANJ;
stats->dwRtoMin = TCPTV_MIN;
stats->dwRtoMax = TCPTV_REXMTMAX;
stats->dwMaxConn = -1;
stats->dwActiveOpens = tcp_stat.tcps_connattempt;
stats->dwPassiveOpens = tcp_stat.tcps_accepts;
stats->dwAttemptFails = tcp_stat.tcps_conndrops;
stats->dwEstabResets = tcp_stat.tcps_drops;
stats->dwCurrEstab = 0;
stats->dwInSegs = tcp_stat.tcps_rcvtotal;
stats->dwOutSegs = tcp_stat.tcps_sndtotal - tcp_stat.tcps_sndrexmitpack;
stats->dwRetransSegs = tcp_stat.tcps_sndrexmitpack;
stats->dwInErrs = tcp_stat.tcps_rcvbadsum + tcp_stat.tcps_rcvbadoff + tcp_stat.tcps_rcvmemdrop + tcp_stat.tcps_rcvshort;
stats->dwOutRsts = tcp_stat.tcps_sndctrl - tcp_stat.tcps_closed;
stats->dwNumConns = tcp_stat.tcps_connects;
ret = NO_ERROR;
}
else ERR ("failed to get tcpstat\n");
}
#else
FIXME( "unimplemented\n" );
#endif
return ret;
}
/******************************************************************
* GetTcpStatistics (IPHLPAPI.@)
*
* Get the TCP statistics for the local computer.
*
* PARAMS
* stats [Out] buffer for TCP statistics
*
* RETURNS
* Success: NO_ERROR
* Failure: error code from winerror.h
*/
DWORD WINAPI GetTcpStatistics(PMIB_TCPSTATS stats)
{
return GetTcpStatisticsEx(stats, WS_AF_INET);
}
/****************************************************************** /******************************************************************
* GetUdpStatistics (IPHLPAPI.@) * GetUdpStatistics (IPHLPAPI.@)
* *

View File

@ -715,8 +715,8 @@ static void testGetTcpStatisticsEx(void)
} }
apiReturn = GetTcpStatisticsEx(&stats, AF_INET6); apiReturn = GetTcpStatisticsEx(&stats, AF_INET6);
todo_wine ok(apiReturn == NO_ERROR || broken(apiReturn == ERROR_NOT_SUPPORTED), ok(apiReturn == NO_ERROR || broken(apiReturn == ERROR_NOT_SUPPORTED),
"GetTcpStatisticsEx returned %d, expected NO_ERROR\n", apiReturn); "GetTcpStatisticsEx returned %d, expected NO_ERROR\n", apiReturn);
if (apiReturn == NO_ERROR && winetest_debug > 1) if (apiReturn == NO_ERROR && winetest_debug > 1)
{ {
trace( "TCP IPv6 Ex stats:\n" ); trace( "TCP IPv6 Ex stats:\n" );

View File

@ -815,9 +815,7 @@ static void test_tcp_stats( int family )
ok( !err, "got %x\n", err ); ok( !err, "got %x\n", err );
err = GetTcpStatisticsEx( &table, family ); err = GetTcpStatisticsEx( &table, family );
todo_wine_if(family == AF_INET6)
ok( !err, "got %d\n", err ); ok( !err, "got %d\n", err );
if (err) goto err;
err = NsiGetAllParameters( 1, &NPI_MS_TCP_MODULEID, NSI_TCP_STATS_TABLE, &key, sizeof(key), NULL, 0, err = NsiGetAllParameters( 1, &NPI_MS_TCP_MODULEID, NSI_TCP_STATS_TABLE, &key, sizeof(key), NULL, 0,
&dyn2, sizeof(dyn), NULL, 0 ); &dyn2, sizeof(dyn), NULL, 0 );
@ -847,7 +845,6 @@ todo_wine_if(family == AF_INET6)
table.dwOutRsts, dyn.out_rsts, dyn2.out_rsts ); table.dwOutRsts, dyn.out_rsts, dyn2.out_rsts );
ok( unstable( table.dwNumConns == dyn.num_conns ), "%d vs %d\n", table.dwNumConns, dyn.num_conns ); ok( unstable( table.dwNumConns == dyn.num_conns ), "%d vs %d\n", table.dwNumConns, dyn.num_conns );
err:
winetest_pop_context(); winetest_pop_context();
} }