From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsiproxy.sys/ip.c | 195 +++++++++++++++++++++++++++++++ dlls/nsiproxy.sys/unix_private.h | 23 ++++ include/wine/nsi.h | 42 +++++++ 3 files changed, 260 insertions(+)
diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index 1576ce1c2a1..65784cb5aa1 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -810,6 +810,183 @@ static NTSTATUS ipv6_ipstats_get_all_parameters( const void *key, UINT key_size, #endif }
+static NTSTATUS ip_interface_fill( UINT fam, const char *unix_name, void *key_data, UINT key_size, + void *rw_data, UINT rw_size, void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + BOOL want_data = key_size || rw_size || dynamic_size || static_size; + int base_reachable_time, dad_transmits, site_prefix_len; + struct nsi_ip_interface_dynamic *dyn = dynamic_data; + struct nsi_ip_interface_static *stat = static_data; + struct nsi_ndis_ifinfo_dynamic iface_dynamic; + struct nsi_ip_interface_key *key = key_data; + struct nsi_ndis_ifinfo_static iface_static; + struct ipv6_addr_scope *addr_scopes = NULL; + unsigned int addr_scopes_size = 0; + struct nsi_ip_interface_rw *rw = rw_data; + struct ifaddrs *addrs, *entry, *entry2; + UINT num = 0, scope_id = 0xffffffff; + char path[256]; + NET_LUID luid; + + if (getifaddrs( &addrs )) return STATUS_NO_MORE_ENTRIES; + + if (fam == AF_INET6) addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); + + rw = rw_data; + for (entry = addrs; entry; entry = entry->ifa_next) + { + if (!entry->ifa_addr || entry->ifa_addr->sa_family != fam) continue; + if (unix_name && strcmp( entry->ifa_name, unix_name )) continue; + if (fam == AF_INET6) + { + scope_id = find_ipv6_addr_scope( (IN6_ADDR*)&((struct sockaddr_in6 *)entry->ifa_addr)->sin6_addr, addr_scopes, + addr_scopes_size ); + /* Info in the IP interface table entry corresponds to link local IPv6 address, while reported info for + * loopback is different on Windows. */ + if (scope_id != 0xffffffff && scope_id != 0x1000 /* loopback */ && scope_id != 0x2000 /* link_local */) + continue; + } + if (!unix_name) + { + /* getifaddrs may return multipe entries for the same interface having different IP addresses. + * IP interface table being returned has only one entry per network interface. */ + for (entry2 = addrs; entry2 && entry2 != entry; entry2 = entry2->ifa_next) + { + if (!entry2->ifa_addr || entry2->ifa_addr->sa_family != fam) continue; + if (fam == AF_INET6) + { + scope_id = find_ipv6_addr_scope( (IN6_ADDR*)&((struct sockaddr_in6 *)entry2->ifa_addr)->sin6_addr, + addr_scopes, addr_scopes_size ); + if (scope_id != 0xffffffff && scope_id != 0x1000 /* loopback */ + && scope_id != 0x2000 /* link_local */) continue; + } + if (!strcmp( entry2->ifa_name, entry->ifa_name )) + break; + } + if (entry2 != entry) continue; + } + if (!convert_unix_name_to_luid( entry->ifa_name, &luid )) continue; + if (!nsi_get_all_parameters( &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &luid, sizeof(luid), + NULL, 0, &iface_dynamic, sizeof(iface_dynamic), + &iface_static, sizeof(iface_static) )) + { + ERR( "Could not get iface parameters.\n" ); + continue; + } + + if (!count || num < *count) + { + if (key && count) memcpy( key, &luid, sizeof(luid) ); + if (stat) memset( stat, 0, sizeof(*stat) ); + + base_reachable_time = 0; + if (iface_static.type == MIB_IF_TYPE_LOOPBACK) dad_transmits = 0; + else dad_transmits = (fam == AF_INET6) ? 1 : 3; +#if __linux__ + if (rw || dyn) + { + sprintf( path, "/proc/sys/net/%s/neigh/%s/base_reachable_time_ms", + (fam == AF_INET) ? "ipv4" : "ipv6", entry->ifa_name); + read_sysctl_int( path, &base_reachable_time ); + } + if (rw) + { + if (fam == AF_INET6 && iface_static.type != MIB_IF_TYPE_LOOPBACK) + { + sprintf( path, "/proc/sys/net/ipv6/conf/%s/dad_transmits", entry->ifa_name); + read_sysctl_int( path, &dad_transmits ); + } + } +#endif + if (rw) + { + site_prefix_len = 64; + if (fam == AF_INET6 && iface_static.type != MIB_IF_TYPE_LOOPBACK) + { + /* For some reason prefix length reported on ipv4 is 64 for ipv4 addresses on Windows and + * prefix len is 64 for loopback device. */ + site_prefix_len = mask_v6_to_prefix( &((struct sockaddr_in6 *)entry->ifa_netmask)->sin6_addr ); + } + memset( rw, 0, sizeof(*rw) ); + rw->mtu = iface_dynamic.mtu; + rw->site_prefix_len = site_prefix_len; + rw->base_reachable_time = base_reachable_time; + rw->dad_transmits = dad_transmits; + rw->retransmit_time = 1000; + rw->path_mtu_discovery_timeout = 600000; + rw->link_local_address_behavior = (fam == AF_INET6) ? LinkLocalAlwaysOn : LinkLocalDelayed; + rw->link_local_address_timeout = (fam == AF_INET6) ? 0 : 6500; + } + if (dyn) + { + memset( dyn, 0, sizeof(*dyn) ); + dyn->if_index = iface_static.if_index; + dyn->connected = (iface_dynamic.media_conn_state == MediaConnectStateConnected); + dyn->reachable_time = base_reachable_time; + } + } + if (!count) + { + freeifaddrs( addrs ); + free( addr_scopes ); + return STATUS_SUCCESS; + } + ++num; + if (key) ++key; + if (rw) ++rw; + if (dyn) ++dyn; + if (stat) ++stat; + } + freeifaddrs( addrs ); + free( addr_scopes ); + + if (!count) return STATUS_NOT_FOUND; + if (want_data && num > *count) return STATUS_BUFFER_OVERFLOW; + *count = num; + return STATUS_SUCCESS; +} + +static NTSTATUS ipv4_interface_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + return ip_interface_fill( AF_INET, NULL, key_data, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, count ); +} + +static NTSTATUS ipv4_interface_get_all_parameters( const void *key, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_ip_interface_key *ip_key = (void *)key; + const char *unix_name; + + if (!convert_luid_to_unix_name( &ip_key->luid, &unix_name )) return STATUS_NOT_FOUND; + + return ip_interface_fill( AF_INET, unix_name, ip_key, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, NULL ); +} + +static NTSTATUS ipv6_interface_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + return ip_interface_fill( AF_INET6, NULL, key_data, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, count ); +} + +static NTSTATUS ipv6_interface_get_all_parameters( const void *key, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_ip_interface_key *ip_key = (void *)key; + const char *unix_name; + + if (!convert_luid_to_unix_name( &ip_key->luid, &unix_name )) return STATUS_NOT_FOUND; + + return ip_interface_fill( AF_INET6, unix_name, ip_key, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, NULL ); +} + 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 ) { @@ -1660,6 +1837,15 @@ static struct module_table ipv4_tables[] = NULL, ipv4_ipstats_get_all_parameters, }, + { + NSI_IP_INTERFACE_TABLE, + { + sizeof(struct nsi_ip_interface_key), sizeof(struct nsi_ip_interface_rw), + sizeof(struct nsi_ip_interface_dynamic), sizeof(struct nsi_ip_interface_static) + }, + ipv4_interface_enumerate_all, + ipv4_interface_get_all_parameters, + }, { NSI_IP_UNICAST_TABLE, { @@ -1725,6 +1911,15 @@ static struct module_table ipv6_tables[] = NULL, ipv6_ipstats_get_all_parameters, }, + { + NSI_IP_INTERFACE_TABLE, + { + sizeof(struct nsi_ip_interface_key), sizeof(struct nsi_ip_interface_rw), + sizeof(struct nsi_ip_interface_dynamic), sizeof(struct nsi_ip_interface_static) + }, + ipv6_interface_enumerate_all, + ipv6_interface_get_all_parameters, + }, { NSI_IP_UNICAST_TABLE, { diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h index b7c56710e22..5487de5111d 100644 --- a/dlls/nsiproxy.sys/unix_private.h +++ b/dlls/nsiproxy.sys/unix_private.h @@ -98,6 +98,29 @@ static inline BOOL convert_index_to_luid( UINT index, NET_LUID *luid ) return !nsi_get_parameter_ex( ¶ms ); }
+static inline BOOL nsi_get_all_parameters( const NPI_MODULEID *module, UINT table, + void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_get_all_parameters_ex params; + + params.unknown[0] = 0; + params.unknown[1] = 0; + params.module = module; + params.table = table; + params.first_arg = 1; + params.unknown2 = 0; + params.key = key_data; + params.key_size = key_size; + params.rw_data = rw_data; + params.rw_size = rw_size; + params.dynamic_data = dynamic_data; + params.dynamic_size = dynamic_size; + params.static_data = static_data; + params.static_size = static_size; + return !nsi_get_all_parameters_ex( ¶ms ); +} + struct ipv6_addr_scope { IN6_ADDR addr; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 6bf3ce5bd5d..0df313db624 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -24,6 +24,7 @@ #include "ws2def.h" #include "ws2ipdef.h" #include "winioctl.h" +#include "nldef.h"
/* Undocumented NSI NDIS tables */ #define NSI_NDIS_IFINFO_TABLE 0 @@ -101,6 +102,7 @@ struct nsi_ndis_ifinfo_static #define NSI_IP_COMPARTMENT_TABLE 2 #define NSI_IP_ICMPSTATS_TABLE 3 #define NSI_IP_IPSTATS_TABLE 6 +#define NSI_IP_INTERFACE_TABLE 7 #define NSI_IP_UNICAST_TABLE 10 #define NSI_IP_NEIGHBOUR_TABLE 11 #define NSI_IP_FORWARD_TABLE 16 @@ -293,6 +295,46 @@ struct nsi_ip_forward_static UINT if_index; };
+struct nsi_ip_interface_key +{ + NET_LUID luid; +}; + +struct nsi_ip_interface_rw +{ + UINT32 unk1[5]; + UINT router_discovery_behaviour; + UINT32 unk2; + ULONG metric; + ULONG base_reachable_time; + ULONG retransmit_time; + ULONG path_mtu_discovery_timeout; + ULONG dad_transmits; + UINT link_local_address_behavior; + ULONG link_local_address_timeout; + ULONG zone_indices[ScopeLevelCount]; + ULONG mtu; + ULONG site_prefix_len; + UINT32 unk3[28]; +}; + +struct nsi_ip_interface_dynamic +{ + UINT if_index; + UINT unk1; + UINT supports_wakeup_patterns; + ULONG reachable_time; + UINT connected; + UINT unk2[3]; + NL_INTERFACE_OFFLOAD_ROD transmit_offload; + UINT32 unk3[13]; +}; + +struct nsi_ip_interface_static +{ + UINT32 unk[8]; +}; + /* Undocumented NSI TCP tables */ #define NSI_TCP_STATS_TABLE 0 #define NSI_TCP_ALL_TABLE 3