Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/nsi/tests/nsi.c | 72 +++++++++ dlls/nsiproxy.sys/Makefile.in | 1 + dlls/nsiproxy.sys/ip.c | 223 +++++++++++++++++++++++++++ dlls/nsiproxy.sys/ndis.c | 21 +++ dlls/nsiproxy.sys/nsi.c | 2 + dlls/nsiproxy.sys/nsiproxy_private.h | 4 + include/netioapi.h | 1 + include/wine/nsi.h | 39 +++++ 8 files changed, 363 insertions(+) create mode 100644 dlls/nsiproxy.sys/ip.c
diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index cefd42983d4..3408dfae58f 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -411,10 +411,82 @@ static void test_ndis_index_luid( void ) ok( err == ERROR_FILE_NOT_FOUND, "got %d\n", err ); }
+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]), + sizeof(struct nsi_ip_unicast_rw) }; + struct nsi_ipv4_unicast_key *key_tbl, *key4; + struct nsi_ipv6_unicast_key *key6; + struct nsi_ip_unicast_rw *rw_tbl, *rw; + struct nsi_ip_unicast_dynamic *dyn_tbl, *dyn; + struct nsi_ip_unicast_static *stat_tbl, *stat; + MIB_UNICASTIPADDRESS_TABLE *table; + const NPI_MODULEID *mod = (family == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID; + DWORD err, count, i, rw_size, key_size = (family == AF_INET) ? sizeof(*key4) : sizeof(*key6); + + winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" ); + + for (i = 0; i < ARRAY_SIZE(rw_sizes); i++) + { + err = NsiAllocateAndGetTable( 1, mod, NSI_IP_UNICAST_TABLE, (void **)&key_tbl, key_size, + (void **)&rw_tbl, rw_sizes[i], (void **)&dyn_tbl, sizeof(*dyn_tbl), + (void **)&stat_tbl, sizeof(*stat_tbl), &count, 0 ); + if (!err) break; + } + ok( !err, "got %d\n", err ); + rw_size = rw_sizes[i]; + + err = GetUnicastIpAddressTable( family, &table ); + ok( !err, "got %d\n", err ); + ok( table->NumEntries == count, "table entries %d count %d\n", table->NumEntries, count ); + + for (i = 0; i < count; i++) + { + MIB_UNICASTIPADDRESS_ROW *row = table->Table + i; + rw = (struct nsi_ip_unicast_rw *)((BYTE *)rw_tbl + i * rw_size); + dyn = dyn_tbl + i; + stat = stat_tbl + i; + winetest_push_context( "%d", i ); + + ok( row->Address.si_family == family, "mismatch\n" ); + + if (family == AF_INET) + { + key4 = key_tbl + i; + ok( !memcmp( &row->Address.Ipv4.sin_addr, &key4->addr, sizeof(struct in_addr) ), "mismatch\n" ); + ok( row->InterfaceLuid.Value == key4->luid.Value, "mismatch\n" ); + } + else + { + key6 = (struct nsi_ipv6_unicast_key *)key_tbl + i; + ok( !memcmp( &row->Address.Ipv6.sin6_addr, &key6->addr, sizeof(struct in6_addr) ), "mismatch\n" ); + ok( row->InterfaceLuid.Value == key6->luid.Value, "mismatch\n" ); + } + ok( row->PrefixOrigin == rw->prefix_origin, "mismatch\n" ); + ok( row->SuffixOrigin == rw->suffix_origin, "mismatch\n" ); + ok( row->ValidLifetime == rw->valid_lifetime, "mismatch\n" ); + ok( row->PreferredLifetime == rw->preferred_lifetime, "mismatch\n" ); + ok( row->OnLinkPrefixLength == rw->on_link_prefix, "mismatch\n" ); + /* SkipAsSource */ + ok( row->DadState == dyn->dad_state, "mismatch\n" ); + ok( row->ScopeId.Value == dyn->scope_id, "mismatch\n" ); +todo_wine + ok( row->CreationTimeStamp.QuadPart == stat->creation_time, "mismatch\n" ); + winetest_pop_context(); + } + + FreeMibTable( table ); + NsiFreeTable( key_tbl, rw_tbl, dyn_tbl, stat_tbl ); + winetest_pop_context(); +} + START_TEST( nsi ) { test_nsi_api();
test_ndis_ifinfo(); test_ndis_index_luid(); + + test_ip_unicast( AF_INET ); + test_ip_unicast( AF_INET6 ); } diff --git a/dlls/nsiproxy.sys/Makefile.in b/dlls/nsiproxy.sys/Makefile.in index e44aacf5b54..07c2738e55b 100644 --- a/dlls/nsiproxy.sys/Makefile.in +++ b/dlls/nsiproxy.sys/Makefile.in @@ -4,5 +4,6 @@ EXTRADLLFLAGS = -Wl,--subsystem,native
C_SRCS = \ device.c \ + ip.c \ ndis.c \ nsi.c diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c new file mode 100644 index 00000000000..d5fcc689fc4 --- /dev/null +++ b/dlls/nsiproxy.sys/ip.c @@ -0,0 +1,223 @@ +/* + * nsiproxy.sys ipv4 and ipv6 modules + * + * Copyright 2003, 2006, 2011 Juan Lang + * Copyright 2021 Huw Davies + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include "config.h" +#include <stdarg.h> + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" +#define USE_WS_PREFIX +#include "winsock2.h" +#include "ws2ipdef.h" +#include "nldef.h" +#include "ifdef.h" +#include "netiodef.h" +#include "wine/nsi.h" +#include "wine/debug.h" + +#include "nsiproxy_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(nsi); + +static inline DWORD nsi_popcount( DWORD m ) +{ +#ifdef HAVE___BUILTIN_POPCOUNT + return __builtin_popcount( m ); +#else + m -= m >> 1 & 0x55555555; + m = (m & 0x33333333) + (m >> 2 & 0x33333333); + return ((m + (m >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24; +#endif +} + +static DWORD mask_v4_to_prefix( struct in_addr *addr ) +{ + return nsi_popcount( addr->s_addr ); +} + +static DWORD mask_v6_to_prefix( struct in6_addr *addr ) +{ + DWORD ret; + + ret = nsi_popcount( *(DWORD *)addr->s6_addr ); + ret += nsi_popcount( *(DWORD *)(addr->s6_addr + 4) ); + ret += nsi_popcount( *(DWORD *)(addr->s6_addr + 8) ); + ret += nsi_popcount( *(DWORD *)(addr->s6_addr + 12) ); + return ret; +} + +static ULONG64 get_boot_time( void ) +{ + SYSTEM_TIMEOFDAY_INFORMATION ti; + + NtQuerySystemInformation( SystemTimeOfDayInformation, &ti, sizeof(ti), NULL ); + return ti.BootTime.QuadPart; +} + +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 ) +{ + struct nsi_ipv6_unicast_key placeholder, *key6 = key; + struct nsi_ipv4_unicast_key *key4 = key; + DWORD scope_id = 0; + + if (!key) + { + key6 = &placeholder; + key4 = (struct nsi_ipv4_unicast_key *)&placeholder; + } + + convert_unix_name_to_luid( entry->ifa_name, &key6->luid ); + + if (entry->ifa_addr->sa_family == AF_INET) + { + memcpy( &key4->addr, &((struct sockaddr_in *)entry->ifa_addr)->sin_addr, sizeof(key4->addr) ); + key4->pad = 0; + } + else if (entry->ifa_addr->sa_family == AF_INET6) + { + memcpy( &key6->addr, &((struct sockaddr_in6 *)entry->ifa_addr)->sin6_addr, sizeof(key6->addr) ); + scope_id = ((struct sockaddr_in6 *)entry->ifa_addr)->sin6_scope_id; + } + + if (rw) + { + rw->preferred_lifetime = 60000; + rw->valid_lifetime = 60000; + + if (key6->luid.Info.IfType != IF_TYPE_SOFTWARE_LOOPBACK) + { + rw->prefix_origin = IpPrefixOriginDhcp; + rw->suffix_origin = IpSuffixOriginDhcp; + } + else + { + rw->prefix_origin = IpPrefixOriginManual; + rw->suffix_origin = IpSuffixOriginManual; + } + if (entry->ifa_netmask && entry->ifa_netmask->sa_family == AF_INET) + rw->on_link_prefix = mask_v4_to_prefix( &((struct sockaddr_in *)entry->ifa_netmask)->sin_addr ); + else if (entry->ifa_netmask && entry->ifa_netmask->sa_family == AF_INET6) + rw->on_link_prefix = mask_v6_to_prefix( &((struct sockaddr_in6 *)entry->ifa_netmask)->sin6_addr ); + else rw->on_link_prefix = 0; + rw->unk[0] = 0; + rw->unk[1] = 0; + } + + if (dyn) + { + dyn->scope_id = scope_id; + dyn->dad_state = IpDadStatePreferred; + } + + if (stat) stat->creation_time = get_boot_time(); +} + +static NTSTATUS ip_unicast_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 ) +{ + DWORD num = 0; + NTSTATUS status = STATUS_SUCCESS; + BOOL want_data = key_size || rw_size || dynamic_size || static_size; + struct ifaddrs *addrs, *entry; + int family = (key_size == sizeof(struct nsi_ipv4_unicast_key)) ? AF_INET : AF_INET6; + + TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data, key_size, rw_data, rw_size, + dynamic_data, dynamic_size, static_data, static_size, count ); + + if (getifaddrs( &addrs )) return STATUS_NO_MORE_ENTRIES; + + for (entry = addrs; entry; entry = entry->ifa_next) + { + if (!entry->ifa_addr || entry->ifa_addr->sa_family != family) continue; + + if (num < *count) + { + unicast_fill_entry( entry, key_data, rw_data, dynamic_data, static_data ); + key_data = (BYTE *)key_data + key_size; + rw_data = (BYTE *)rw_data + rw_size; + dynamic_data = (BYTE *)dynamic_data + dynamic_size; + static_data = (BYTE *)static_data + static_size; + } + num++; + } + + freeifaddrs( addrs ); + + if (!want_data || num <= *count) *count = num; + else status = STATUS_MORE_ENTRIES; + + return status; +} + +static struct module_table ipv4_tables[] = +{ + { + NSI_IP_UNICAST_TABLE, + { + sizeof(struct nsi_ipv4_unicast_key), sizeof(struct nsi_ip_unicast_rw), + sizeof(struct nsi_ip_unicast_dynamic), sizeof(struct nsi_ip_unicast_static) + }, + ip_unicast_enumerate_all, + }, + { + ~0u + } +}; + +const struct module ipv4_module = +{ + &NPI_MS_IPV4_MODULEID, + ipv4_tables +}; + +static struct module_table ipv6_tables[] = +{ + { + NSI_IP_UNICAST_TABLE, + { + sizeof(struct nsi_ipv6_unicast_key), sizeof(struct nsi_ip_unicast_rw), + sizeof(struct nsi_ip_unicast_dynamic), sizeof(struct nsi_ip_unicast_static) + }, + ip_unicast_enumerate_all, + }, + { + ~0u + } +}; + +const struct module ipv6_module = +{ + &NPI_MS_IPV6_MODULEID, + ipv6_tables +}; diff --git a/dlls/nsiproxy.sys/ndis.c b/dlls/nsiproxy.sys/ndis.c index 332df7159b9..c40dba7a01a 100644 --- a/dlls/nsiproxy.sys/ndis.c +++ b/dlls/nsiproxy.sys/ndis.c @@ -613,6 +613,27 @@ static NTSTATUS index_luid_get_parameter( const void *key, DWORD key_size, DWORD return status; }
+BOOL convert_unix_name_to_luid( const char *unix_name, NET_LUID *luid ) +{ + struct if_entry *entry; + BOOL ret = FALSE;; + + EnterCriticalSection( &if_list_cs ); + + update_if_table(); + + LIST_FOR_EACH_ENTRY( entry, &if_list, struct if_entry, entry ) + if (!strcmp( entry->if_unix_name, unix_name )) + { + *luid = entry->if_luid; + ret = TRUE; + break; + } + LeaveCriticalSection( &if_list_cs ); + + return ret; +} + static const struct module_table tables[] = { { diff --git a/dlls/nsiproxy.sys/nsi.c b/dlls/nsiproxy.sys/nsi.c index e4286b5fc93..8e084a076fd 100644 --- a/dlls/nsiproxy.sys/nsi.c +++ b/dlls/nsiproxy.sys/nsi.c @@ -38,6 +38,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(nsi); static const struct module *modules[] = { &ndis_module, + &ipv4_module, + &ipv6_module, };
static const struct module_table *get_module_table( const NPI_MODULEID *id, DWORD table ) diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index 0cd2a79d59f..1c62a3df038 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -22,6 +22,8 @@ NTSTATUS nsi_enumerate_all_ex( struct nsi_enumerate_all_ex *params ) DECLSPEC_HI NTSTATUS nsi_get_all_parameters_ex( struct nsi_get_all_parameters_ex *params ) DECLSPEC_HIDDEN; NTSTATUS nsi_get_parameter_ex( struct nsi_get_parameter_ex *params ) DECLSPEC_HIDDEN;
+BOOL convert_unix_name_to_luid( const char *unix_name, NET_LUID *luid ) DECLSPEC_HIDDEN; + struct module_table { DWORD table; @@ -43,3 +45,5 @@ struct module };
extern const struct module ndis_module DECLSPEC_HIDDEN; +extern const struct module ipv4_module DECLSPEC_HIDDEN; +extern const struct module ipv6_module DECLSPEC_HIDDEN; diff --git a/include/netioapi.h b/include/netioapi.h index bbd203fc9b3..4735620dedb 100644 --- a/include/netioapi.h +++ b/include/netioapi.h @@ -256,6 +256,7 @@ DWORD WINAPI GetIfTable2(MIB_IF_TABLE2**); DWORD WINAPI GetIfTable2Ex(MIB_IF_TABLE_LEVEL,MIB_IF_TABLE2**); DWORD WINAPI GetIpInterfaceTable(ADDRESS_FAMILY,MIB_IPINTERFACE_TABLE**); DWORD WINAPI GetUnicastIpAddressEntry(MIB_UNICASTIPADDRESS_ROW*); +DWORD WINAPI GetUnicastIpAddressTable(ADDRESS_FAMILY,MIB_UNICASTIPADDRESS_TABLE**); PCHAR WINAPI if_indextoname(NET_IFINDEX,PCHAR); NET_IFINDEX WINAPI if_nametoindex(PCSTR);
diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 3327b1f7403..bba5be11e50 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -19,6 +19,8 @@ #ifndef __WINE_NSI_H #define __WINE_NSI_H
+#include "inaddr.h" +#include "in6addr.h" #include "winioctl.h"
/* Undocumented NSI NDIS tables */ @@ -94,6 +96,43 @@ struct nsi_ndis_ifinfo_static DWORD phys_medium_type; };
+/* Undocumented NSI IP tables */ +#define NSI_IP_UNICAST_TABLE 10 + +struct nsi_ipv4_unicast_key +{ + NET_LUID luid; + IN_ADDR addr; + DWORD pad; +}; + +struct nsi_ipv6_unicast_key +{ + NET_LUID luid; + IN6_ADDR addr; +}; + +struct nsi_ip_unicast_rw +{ + DWORD preferred_lifetime; + DWORD valid_lifetime; + DWORD prefix_origin; + DWORD suffix_origin; + DWORD on_link_prefix; + DWORD unk[2]; +}; + +struct nsi_ip_unicast_dynamic +{ + DWORD scope_id; + DWORD dad_state; +}; + +struct nsi_ip_unicast_static +{ + ULONG64 creation_time; +}; + /* Wine specific ioctl interface */
#define IOCTL_NSIPROXY_WINE_ENUMERATE_ALL CTL_CODE(FILE_DEVICE_NETWORK, 0x400, METHOD_BUFFERED, 0)