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