[PATCH 1/4] nsiproxy: Implement IPv4 neighbour enumerate_all.

Huw Davies huw at codeweavers.com
Tue Aug 10 03:20:47 CDT 2021


Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/nsi/tests/nsi.c   |  78 +++++++++++++++
 dlls/nsiproxy.sys/ip.c | 219 +++++++++++++++++++++++++++++++++++++++++
 include/netioapi.h     |   2 +
 include/wine/nsi.h     |  34 +++++++
 4 files changed, 333 insertions(+)

diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c
index 9d12530cb27..7acb7a497fb 100644
--- a/dlls/nsi/tests/nsi.c
+++ b/dlls/nsi/tests/nsi.c
@@ -485,6 +485,82 @@ static void test_ip_unicast( int family )
     winetest_pop_context();
 }
 
+static void test_ip_neighbour( int family )
+{
+    const NPI_MODULEID *mod = (family == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID;
+    DWORD err, i, count, count2, attempt;
+    struct nsi_ipv4_neighbour_key *key_tbl, *key_tbl_2, *key4;
+    struct nsi_ipv6_neighbour_key *key6;
+    struct nsi_ip_neighbour_rw *rw_tbl, *rw;
+    struct nsi_ip_neighbour_dynamic *dyn_tbl, *dyn_tbl_2, *dyn;
+    MIB_IPNET_TABLE2 *table;
+    DWORD key_size = (family == AF_INET) ? sizeof(struct nsi_ipv4_neighbour_key) : sizeof(struct nsi_ipv6_neighbour_key);
+
+    winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" );
+
+    for (attempt = 0; attempt < 5; attempt++)
+    {
+        err = NsiAllocateAndGetTable( 1, mod, NSI_IP_NEIGHBOUR_TABLE, (void **)&key_tbl, key_size,
+                                      (void **)&rw_tbl, sizeof(*rw), (void **)&dyn_tbl, sizeof(*dyn),
+                                      NULL, 0, &count, 0 );
+todo_wine_if( family == AF_INET6 )
+        ok( !err, "got %x\n", err );
+        if (err) goto err;
+
+        err = GetIpNetTable2( family, &table );
+todo_wine
+        ok( !err, "got %x\n", err );
+        if (err) goto err;
+
+        err = NsiAllocateAndGetTable( 1, mod, NSI_IP_NEIGHBOUR_TABLE, (void **)&key_tbl_2, key_size,
+                                      NULL, 0, (void **)&dyn_tbl_2, sizeof(*dyn),
+                                      NULL, 0, &count2, 0 );
+        ok( !err, "got %x\n", err );
+        if (count == count2 && !memcmp( dyn_tbl, dyn_tbl_2, count * sizeof(*dyn) )) break;
+        NsiFreeTable( key_tbl_2, NULL, dyn_tbl_2, NULL );
+        NsiFreeTable( key_tbl, rw_tbl, dyn_tbl, NULL );
+    }
+
+    ok( count == table->NumEntries, "%d vs %d\n", count, table->NumEntries );
+
+    for (i = 0; i < count; i++)
+    {
+        MIB_IPNET_ROW2 *row = table->Table + i;
+        rw = rw_tbl + i;
+        dyn = dyn_tbl + i;
+
+        if (family == AF_INET)
+        {
+            key4 = key_tbl + i;
+            ok( key4->addr.s_addr == row->Address.Ipv4.sin_addr.s_addr, "%08x vs %08x\n", key4->addr.s_addr,
+                row->Address.Ipv4.sin_addr.s_addr );
+            ok( key4->luid.Value == row->InterfaceLuid.Value, "%s vs %s\n", wine_dbgstr_longlong( key4->luid.Value ),
+                wine_dbgstr_longlong( row->InterfaceLuid.Value ) );
+            ok( key4->luid2.Value == row->InterfaceLuid.Value, "mismatch\n" );
+        }
+        else if (family == AF_INET6)
+        {
+            key6 = (struct nsi_ipv6_neighbour_key *)key_tbl + i;
+            ok( !memcmp( key6->addr.s6_addr, row->Address.Ipv6.sin6_addr.s6_addr, sizeof(IN6_ADDR) ), "mismatch\n" );
+            ok( key6->luid.Value == row->InterfaceLuid.Value, "mismatch\n" );
+            ok( key6->luid2.Value == row->InterfaceLuid.Value, "mismatch\n" );
+        }
+
+        ok( dyn->phys_addr_len == row->PhysicalAddressLength, "mismatch\n" );
+        ok( !memcmp( rw->phys_addr, row->PhysicalAddress, dyn->phys_addr_len ), "mismatch\n" );
+        ok( dyn->state == row->State, "%x vs %x\n", dyn->state, row->State );
+        ok( dyn->flags.is_router == row->IsRouter, "%x vs %x\n", dyn->flags.is_router, row->IsRouter );
+        ok( dyn->flags.is_unreachable == row->IsUnreachable, "%x vs %x\n", dyn->flags.is_unreachable, row->IsUnreachable );
+        ok( dyn->time == row->ReachabilityTime.LastReachable, "%x vs %x\n", dyn->time, row->ReachabilityTime.LastReachable );
+    }
+
+    NsiFreeTable( key_tbl_2, NULL, dyn_tbl_2, NULL );
+    NsiFreeTable( key_tbl, rw_tbl, dyn_tbl, NULL );
+
+err:
+    winetest_pop_context();
+}
+
 static void test_ip_forward( int family )
 {
     DWORD rw_sizes[] = { FIELD_OFFSET(struct nsi_ip_forward_rw, unk),
@@ -606,6 +682,8 @@ START_TEST( nsi )
 
     test_ip_unicast( AF_INET );
     test_ip_unicast( AF_INET6 );
+    test_ip_neighbour( AF_INET );
+    test_ip_neighbour( AF_INET6 );
     test_ip_forward( AF_INET );
     test_ip_forward( AF_INET6 );
 }
diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c
index cb98c6a5206..9abb51ac678 100644
--- a/dlls/nsiproxy.sys/ip.c
+++ b/dlls/nsiproxy.sys/ip.c
@@ -41,10 +41,26 @@
 #include <netinet/in.h>
 #endif
 
+#ifdef HAVE_NETINET_IP_VAR_H
+#include <netinet/ip_var.h>
+#endif
+
+#ifdef HAVE_NETINET_IF_ETHER_H
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+
 #ifdef HAVE_IFADDRS_H
 #include <ifaddrs.h>
 #endif
 
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
@@ -234,6 +250,193 @@ static NTSTATUS ip_unicast_get_all_parameters( const void *key, DWORD key_size,
     return status;
 }
 
+struct ipv4_neighbour_data
+{
+    NET_LUID luid;
+    DWORD if_index;
+    struct in_addr addr;
+    BYTE phys_addr[IF_MAX_PHYS_ADDRESS_LENGTH];
+    DWORD state;
+    USHORT phys_addr_len;
+    BOOL is_router;
+    BOOL is_unreachable;
+};
+
+static void ipv4_neighbour_fill_entry( struct ipv4_neighbour_data *entry, struct nsi_ipv4_neighbour_key *key, struct nsi_ip_neighbour_rw *rw,
+                                       struct nsi_ip_neighbour_dynamic *dyn, void *stat )
+{
+    USHORT phys_addr_len = entry->phys_addr_len > sizeof(rw->phys_addr) ? 0 : entry->phys_addr_len;
+
+    if (key)
+    {
+        key->luid = entry->luid;
+        key->luid2 = entry->luid;
+        key->addr.WS_s_addr = entry->addr.s_addr;
+        key->pad = 0;
+    }
+
+    if (rw)
+    {
+        memcpy( rw->phys_addr, entry->phys_addr, phys_addr_len );
+        memset( rw->phys_addr + entry->phys_addr_len, 0, sizeof(rw->phys_addr) - phys_addr_len );
+    }
+
+    if (dyn)
+    {
+        memset( dyn, 0, sizeof(*dyn) );
+        dyn->state = entry->state;
+        dyn->flags.is_router = entry->is_router;
+        dyn->flags.is_unreachable = entry->is_unreachable;
+        dyn->phys_addr_len = phys_addr_len;
+    }
+}
+
+static NTSTATUS ipv4_neighbour_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 ipv4_neighbour_data entry;
+
+    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 );
+
+#ifdef __linux__
+    {
+        char buf[512], *ptr;
+        DWORD atf_flags;
+        FILE *fp;
+
+        if (!(fp = fopen( "/proc/net/arp", "r" ))) return STATUS_NOT_SUPPORTED;
+
+        /* skip header line */
+        ptr = fgets( buf, sizeof(buf), fp );
+        while ((ptr = fgets( buf, sizeof(buf), fp )))
+        {
+            entry.addr.s_addr = inet_addr( ptr );
+            while (*ptr && !isspace( *ptr )) ptr++;
+            strtoul( ptr + 1, &ptr, 16 ); /* hw type (skip) */
+            atf_flags = strtoul( ptr + 1, &ptr, 16 );
+
+            if (atf_flags & ATF_PERM) entry.state = NlnsPermanent;
+            else if (atf_flags & ATF_COM) entry.state = NlnsReachable;
+            else entry.state = NlnsStale;
+
+            entry.is_router = 0;
+            entry.is_unreachable = !(atf_flags & (ATF_PERM | ATF_COM));
+
+            while (*ptr && isspace( *ptr )) ptr++;
+            entry.phys_addr_len = 0;
+            while (*ptr && !isspace( *ptr ))
+            {
+                if (entry.phys_addr_len >= sizeof(entry.phys_addr))
+                {
+                    entry.phys_addr_len = 0;
+                    while (*ptr && !isspace( *ptr )) ptr++;
+                    break;
+                }
+                entry.phys_addr[entry.phys_addr_len++] = strtoul( ptr, &ptr, 16 );
+                if (*ptr) ptr++;
+            }
+            while (*ptr && isspace( *ptr )) ptr++;
+            while (*ptr && !isspace( *ptr )) ptr++;   /* mask (skip) */
+            while (*ptr && isspace( *ptr )) ptr++;
+
+            if (!convert_unix_name_to_luid( ptr, &entry.luid )) continue;
+            if (!convert_luid_to_index( &entry.luid, &entry.if_index )) continue;
+
+            if (num < *count)
+            {
+                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++;
+        }
+        fclose( fp );
+    }
+#elif defined(HAVE_SYS_SYSCTL_H)
+    {
+        int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO }, sinarp_len;
+        size_t needed;
+        char *buf = NULL, *lim, *next;
+        struct rt_msghdr *rtm;
+        struct sockaddr_inarp *sinarp;
+        struct sockaddr_dl *sdl;
+
+        if (sysctl( mib, ARRAY_SIZE(mib), NULL, &needed, NULL, 0 ) == -1) return STATUS_NOT_SUPPORTED;
+
+        buf = heap_alloc( needed );
+        if (!buf) return STATUS_NO_MEMORY;
+
+        if (sysctl( mib, ARRAY_SIZE(mib), buf, &needed, NULL, 0 ) == -1)
+        {
+            heap_free( buf );
+            return STATUS_NOT_SUPPORTED;
+        }
+
+        lim = buf + needed;
+        next = buf;
+        while (next < lim)
+        {
+            rtm = (struct rt_msghdr *)next;
+            sinarp = (struct sockaddr_inarp *)(rtm + 1);
+            if (sinarp->sin_len) sinarp_len = (sinarp->sin_len + sizeof(int)-1) & ~(sizeof(int)-1);
+            else sinarp_len = sizeof(int);
+            sdl = (struct sockaddr_dl *)((char *)sinarp + sinarp_len);
+
+            if (sdl->sdl_alen) /* arp entry */
+            {
+                entry.addr = sinarp->sin_addr;
+                entry.if_index = sdl->sdl_index;
+                if (!convert_index_to_luid( entry.if_index, &entry.luid )) break;
+                entry.phys_addr_len = min( 8, sdl->sdl_alen );
+                if (entry.phys_addr_len > sizeof(entry.phys_addr)) entry.phys_addr_len = 0;
+                memcpy( entry.phys_addr, &sdl->sdl_data[sdl->sdl_nlen], entry.phys_addr_len );
+                if (rtm->rtm_rmx.rmx_expire == 0) entry.state = NlnsPermanent;
+                else entry.state = NlnsReachable;
+                entry.is_router = sinarp->sin_other & SIN_ROUTER;
+                entry.is_unreachable = 0; /* FIXME */
+
+                if (num < *count)
+                {
+                    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++;
+            }
+            next += rtm->rtm_msglen;
+        }
+        heap_free( buf );
+    }
+#else
+    FIXME( "not implemented\n" );
+    return STATUS_NOT_IMPLEMENTED;
+#endif
+
+    if (!want_data || num <= *count) *count = num;
+    else status = STATUS_MORE_ENTRIES;
+
+    return status;
+}
+
+static NTSTATUS ipv6_neighbour_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 )
+{
+    FIXME( "not implemented\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
 struct ipv4_route_data
 {
     NET_LUID luid;
@@ -489,6 +692,14 @@ static struct module_table ipv4_tables[] =
         ip_unicast_enumerate_all,
         ip_unicast_get_all_parameters,
     },
+    {
+        NSI_IP_NEIGHBOUR_TABLE,
+        {
+            sizeof(struct nsi_ipv4_neighbour_key), sizeof(struct nsi_ip_neighbour_rw),
+            sizeof(struct nsi_ip_neighbour_dynamic), 0
+        },
+        ipv4_neighbour_enumerate_all,
+    },
     {
         NSI_IP_FORWARD_TABLE,
         {
@@ -519,6 +730,14 @@ static struct module_table ipv6_tables[] =
         ip_unicast_enumerate_all,
         ip_unicast_get_all_parameters,
     },
+    {
+        NSI_IP_NEIGHBOUR_TABLE,
+        {
+            sizeof(struct nsi_ipv6_neighbour_key), sizeof(struct nsi_ip_neighbour_rw),
+            sizeof(struct nsi_ip_neighbour_dynamic), 0
+        },
+        ipv6_neighbour_enumerate_all,
+    },
     {
         NSI_IP_FORWARD_TABLE,
         {
diff --git a/include/netioapi.h b/include/netioapi.h
index 44d877ae3c3..6005e58e216 100644
--- a/include/netioapi.h
+++ b/include/netioapi.h
@@ -257,6 +257,8 @@ DWORD WINAPI GetIfTable2Ex(MIB_IF_TABLE_LEVEL,MIB_IF_TABLE2**);
 DWORD WINAPI GetIpForwardEntry2(MIB_IPFORWARD_ROW2*);
 DWORD WINAPI GetIpForwardTable2(ADDRESS_FAMILY,MIB_IPFORWARD_TABLE2**);
 DWORD WINAPI GetIpInterfaceTable(ADDRESS_FAMILY,MIB_IPINTERFACE_TABLE**);
+DWORD WINAPI GetIpNetEntry2(MIB_IPNET_ROW2*);
+DWORD WINAPI GetIpNetTable2(ADDRESS_FAMILY,MIB_IPNET_TABLE2**);
 DWORD WINAPI GetUnicastIpAddressEntry(MIB_UNICASTIPADDRESS_ROW*);
 DWORD WINAPI GetUnicastIpAddressTable(ADDRESS_FAMILY,MIB_UNICASTIPADDRESS_TABLE**);
 PCHAR WINAPI if_indextoname(NET_IFINDEX,PCHAR);
diff --git a/include/wine/nsi.h b/include/wine/nsi.h
index dd18c7c7511..437582ad692 100644
--- a/include/wine/nsi.h
+++ b/include/wine/nsi.h
@@ -98,6 +98,7 @@ struct nsi_ndis_ifinfo_static
 
 /* Undocumented NSI IP tables */
 #define NSI_IP_UNICAST_TABLE              10
+#define NSI_IP_NEIGHBOUR_TABLE            11
 #define NSI_IP_FORWARD_TABLE              16
 
 struct nsi_ipv4_unicast_key
@@ -134,6 +135,39 @@ struct nsi_ip_unicast_static
     ULONG64 creation_time;
 };
 
+struct nsi_ipv4_neighbour_key
+{
+    NET_LUID luid;
+    NET_LUID luid2;
+    IN_ADDR addr;
+    DWORD pad;
+};
+
+struct nsi_ipv6_neighbour_key
+{
+    NET_LUID luid;
+    NET_LUID luid2;
+    IN6_ADDR addr;
+};
+
+struct nsi_ip_neighbour_rw
+{
+    BYTE phys_addr[IF_MAX_PHYS_ADDRESS_LENGTH];
+};
+
+struct nsi_ip_neighbour_dynamic
+{
+    DWORD state;
+    DWORD time;
+    struct
+    {
+        USHORT is_router : 1;
+        USHORT is_unreachable : 1;
+    } flags;
+    USHORT phys_addr_len;
+    DWORD unk;
+};
+
 struct nsi_ipv4_forward_key
 {
     DWORD unk;
-- 
2.23.0




More information about the wine-devel mailing list