[PATCH v5 1/1] nsiproxy.sys: Add static ARP entries which are always present on Windows.
Paul Gofman
wine at gitlab.winehq.org
Sat Jun 25 18:44:17 CDT 2022
From: Paul Gofman <pgofman at codeweavers.com>
Some apps (Roon or SCP: Secret Laboratory are examples) depend on ARP
table always containing some entries if there is a network adapter present.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53175
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
dlls/iphlpapi/tests/iphlpapi.c | 52 ++++++++++++++++-
dlls/nsiproxy.sys/ip.c | 96 +++++++++++++++++++++++++++++++-
dlls/nsiproxy.sys/unix_private.h | 6 ++
3 files changed, 152 insertions(+), 2 deletions(-)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c
index 84d5143ba8e..1a0cb76bcf5 100644
--- a/dlls/iphlpapi/tests/iphlpapi.c
+++ b/dlls/iphlpapi/tests/iphlpapi.c
@@ -120,6 +120,11 @@ static const char *ntoa6( IN6_ADDR *ip )
return buffers[i];
}
+static DWORD ipv4_addr( BYTE b1, BYTE b2, BYTE b3, BYTE b4 )
+{
+ return htonl( (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 );
+}
+
/*
still-to-be-tested 98-only functions:
GetUniDirectionalAdapterInfo
@@ -369,10 +374,16 @@ static void testGetIpForwardTable(void)
static void testGetIpNetTable(void)
{
- DWORD apiReturn;
+ DWORD apiReturn, ret, prev_idx;
+ BOOL igmp3_found, ssdp_found;
+ DWORD igmp3_addr, ssdp_addr;
+ MIB_IPNET_TABLE2 *table2;
ULONG dwSize = 0;
unsigned int i;
+ igmp3_addr = ipv4_addr( 224, 0, 0, 22 );
+ ssdp_addr = ipv4_addr( 239, 255, 255, 250 );
+
apiReturn = GetIpNetTable(NULL, NULL, FALSE);
if (apiReturn == ERROR_NOT_SUPPORTED) {
skip("GetIpNetTable is not supported\n");
@@ -406,6 +417,45 @@ static void testGetIpNetTable(void)
ok(ntohl(buf->table[i].dwAddr) <= ntohl(buf->table[i + 1].dwAddr),
"Entries are not sorted by address, i %u.\n", i );
}
+
+ igmp3_found = ssdp_found = FALSE;
+ prev_idx = ~0ul;
+ for (i = 0; i < buf->dwNumEntries; ++i)
+ {
+ if (buf->table[i].dwIndex != prev_idx)
+ {
+ if (prev_idx != ~0ul)
+ {
+ ok( igmp3_found, "%s not found, iface index %lu.\n", ntoa( igmp3_addr ), prev_idx);
+ ok( ssdp_found || broken(!ssdp_found) /* 239.255.255.250 is always present since Win10 */,
+ "%s not found.\n", ntoa( ssdp_addr ));
+ }
+ prev_idx = buf->table[i].dwIndex;
+ igmp3_found = ssdp_found = FALSE;
+ }
+ if (buf->table[i].dwAddr == igmp3_addr)
+ igmp3_found = TRUE;
+ else if (buf->table[i].dwAddr == ssdp_addr)
+ ssdp_found = TRUE;
+ }
+ ok( igmp3_found, "%s not found.\n", ntoa( igmp3_addr ));
+ ok( ssdp_found || broken(!ssdp_found) /* 239.255.255.250 is always present since Win10 */,
+ "%s not found.\n", ntoa( ssdp_addr ));
+
+ ret = GetIpNetTable2( AF_INET, &table2 );
+ ok( !ret, "got ret %lu.\n", ret );
+ for (i = 0; i < table2->NumEntries; ++i)
+ {
+ MIB_IPNET_ROW2 *row = &table2->Table[i];
+ if (row->Address.Ipv4.sin_addr.s_addr == igmp3_addr
+ || row->Address.Ipv4.sin_addr.s_addr == ssdp_addr)
+ {
+ ok( row->State == NlnsPermanent, "got state %d.\n", row->State );
+ ok( !row->IsRouter, "IsRouter is set.\n" );
+ ok( !row->IsUnreachable, "IsUnreachable is set.\n" );
+ }
+ }
+ FreeMibTable( table2 );
}
if (apiReturn == NO_ERROR && winetest_debug > 1)
diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c
index 79f3bd80bfe..dbe3a0b46b7 100644
--- a/dlls/nsiproxy.sys/ip.c
+++ b/dlls/nsiproxy.sys/ip.c
@@ -1000,18 +1000,75 @@ static void ipv4_neighbour_fill_entry( struct ipv4_neighbour_data *entry, struct
}
}
+/* ARP entries for these multicast addresses are always present on Windows for each interface. */
+static DWORD ipv4_multicast_addresses[] =
+{
+ IPV4_ADDR(224, 0, 0, 22),
+ IPV4_ADDR(239, 255, 255, 250),
+};
+
+static void update_static_address_found( DWORD address, UINT if_index, struct nsi_ndis_ifinfo_static *iface,
+ unsigned int iface_count )
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(ipv4_multicast_addresses); ++i)
+ if (ipv4_multicast_addresses[i] == address) break;
+
+ if (i == ARRAY_SIZE(ipv4_multicast_addresses)) return;
+
+ for (j = 0; j < iface_count; ++j)
+ {
+ if (iface[j].if_index == if_index)
+ {
+ iface[j].unk |= 1 << i;
+ return;
+ }
+ }
+}
+
static NTSTATUS ipv4_neighbour_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 )
{
- UINT num = 0;
+ UINT num = 0, iface_count;
NTSTATUS status = STATUS_SUCCESS;
BOOL want_data = key_size || rw_size || dynamic_size || static_size;
+ struct nsi_ndis_ifinfo_static *iface_static;
struct ipv4_neighbour_data entry;
+ NET_LUID *luid_tbl;
+ unsigned int i, j;
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 );
+ iface_count = 0;
+ if ((status = nsi_enumerate_all( 1, 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, NULL, 0, NULL, 0,
+ NULL, 0, NULL, 0, &iface_count )))
+ return status;
+
+ if (!(luid_tbl = malloc( iface_count * sizeof(*luid_tbl) )))
+ return STATUS_NO_MEMORY;
+
+ if (!(iface_static = malloc( iface_count * sizeof(*iface_static) )))
+ {
+ free( luid_tbl );
+ return STATUS_NO_MEMORY;
+ }
+
+ if ((status = nsi_enumerate_all( 1, 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, luid_tbl, sizeof(*luid_tbl),
+ NULL, 0, NULL, 0, iface_static, sizeof(*iface_static), &iface_count ))
+ && status != STATUS_BUFFER_OVERFLOW)
+ {
+ free( luid_tbl );
+ free( iface_static );
+ return status;
+ }
+
+ /* Use unk field to indicate whether we found mandatory multicast addresses in the host ARP table. */
+ for (i = 0; i < iface_count; ++i)
+ iface_static[i].unk = 0;
+
#ifdef __linux__
{
char buf[512], *ptr, *s;
@@ -1059,6 +1116,8 @@ static NTSTATUS ipv4_neighbour_enumerate_all( void *key_data, UINT key_size, voi
if (!convert_unix_name_to_luid( ptr, &entry.luid )) continue;
if (!convert_luid_to_index( &entry.luid, &entry.if_index )) continue;
+ update_static_address_found( entry.addr.s_addr, entry.if_index, iface_static, iface_count );
+
if (num < *count)
{
ipv4_neighbour_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data );
@@ -1119,6 +1178,8 @@ static NTSTATUS ipv4_neighbour_enumerate_all( void *key_data, UINT key_size, voi
#endif
entry.is_unreachable = 0; /* FIXME */
+ update_static_address_found( entry.addr.s_addr, entry.if_index, iface_static, iface_count );
+
if (num < *count)
{
ipv4_neighbour_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data );
@@ -1136,9 +1197,42 @@ static NTSTATUS ipv4_neighbour_enumerate_all( void *key_data, UINT key_size, voi
}
#else
FIXME( "not implemented\n" );
+ free( luid_tbl );
+ free( iface_static );
return STATUS_NOT_IMPLEMENTED;
#endif
+ if (!want_data || num <= *count)
+ {
+ /* Certain ipv4 multicast addresses are always present on Windows for each interface.
+ * Add those if they weren't added already. */
+ memset( &entry, 0, sizeof(entry) );
+ entry.state = NlnsPermanent;
+ for (i = 0; i < iface_count; ++i)
+ {
+ entry.if_index = iface_static[i].if_index;
+ entry.luid = luid_tbl[i];
+ for (j = 0; j < ARRAY_SIZE(ipv4_multicast_addresses); ++j)
+ {
+ if (iface_static[i].unk & (1 << j)) continue;
+ if (num <= *count)
+ {
+ entry.addr.s_addr = ipv4_multicast_addresses[j];
+ ipv4_neighbour_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data );
+
+ if (key_data) key_data = (BYTE *)key_data + key_size;
+ if (rw_data) rw_data = (BYTE *)rw_data + rw_size;
+ if (dynamic_data) dynamic_data = (BYTE *)dynamic_data + dynamic_size;
+ if (static_data) static_data = (BYTE *)static_data + static_size;
+ }
+ ++num;
+ }
+ }
+ }
+
+ free( luid_tbl );
+ free( iface_static );
+
if (!want_data || num <= *count) *count = num;
else status = STATUS_BUFFER_OVERFLOW;
diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h
index 8ab56f6d9a3..84fea0c42d2 100644
--- a/dlls/nsiproxy.sys/unix_private.h
+++ b/dlls/nsiproxy.sys/unix_private.h
@@ -18,6 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#ifdef WORDS_BIGENDIAN
+#define IPV4_ADDR(b1, b2, b3, b4) (((unsigned int)b1 << 24) | (b2 << 16) | (b3 << 8) | b4)
+#else
+#define IPV4_ADDR(b1, b2, b3, b4) (((unsigned int)b4 << 24) | (b3 << 16) | (b2 << 8) | b1)
+#endif
+
NTSTATUS nsi_enumerate_all_ex( struct nsi_enumerate_all_ex *params ) DECLSPEC_HIDDEN;
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;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/296
More information about the wine-devel
mailing list