[PATCH 3/4] nsiproxy: Implement NDIS ifinfo enumerate_all.

Huw Davies huw at codeweavers.com
Wed Jun 30 03:59:24 CDT 2021


Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/nsiproxy.sys/Makefile.in        |   3 +-
 dlls/nsiproxy.sys/ndis.c             | 499 +++++++++++++++++++++++++++
 dlls/nsiproxy.sys/nsi.c              |  40 ++-
 dlls/nsiproxy.sys/nsiproxy_private.h |  17 +
 4 files changed, 557 insertions(+), 2 deletions(-)
 create mode 100644 dlls/nsiproxy.sys/ndis.c

diff --git a/dlls/nsiproxy.sys/Makefile.in b/dlls/nsiproxy.sys/Makefile.in
index f382689d463..e44aacf5b54 100644
--- a/dlls/nsiproxy.sys/Makefile.in
+++ b/dlls/nsiproxy.sys/Makefile.in
@@ -1,7 +1,8 @@
 MODULE    = nsiproxy.sys
-IMPORTS   = ntoskrnl
+IMPORTS   = ntoskrnl uuid
 EXTRADLLFLAGS = -Wl,--subsystem,native
 
 C_SRCS = \
 	device.c \
+	ndis.c \
 	nsi.c
diff --git a/dlls/nsiproxy.sys/ndis.c b/dlls/nsiproxy.sys/ndis.c
new file mode 100644
index 00000000000..356a32635a3
--- /dev/null
+++ b/dlls/nsiproxy.sys/ndis.c
@@ -0,0 +1,499 @@
+/*
+ * nsiproxy.sys ndis module
+ *
+ * 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_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#ifdef HAVE_NET_IF_ARP_H
+#include <net/if_arp.h>
+#endif
+
+#ifdef HAVE_NETINET_IF_ETHER_H
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef HAVE_NET_ROUTE_H
+#include <net/route.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+
+#ifdef HAVE_NET_IF_TYPES_H
+#include <net/if_types.h>
+#endif
+
+#define NONAMELESSUNION
+#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 "ddk/wdm.h"
+#include "wine/nsi.h"
+#include "wine/list.h"
+#include "wine/heap.h"
+#include "wine/debug.h"
+
+#include "nsiproxy_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(nsi);
+
+struct if_entry
+{
+    struct list entry;
+    GUID if_guid;
+    NET_LUID if_luid;
+    WCHAR *if_name;
+    char if_unix_name[IFNAMSIZ];
+    IF_PHYSICAL_ADDRESS if_phys_addr;
+    DWORD if_index;
+    DWORD if_type;
+};
+
+static struct list if_list = LIST_INIT( if_list );
+
+static CRITICAL_SECTION if_list_cs;
+static CRITICAL_SECTION_DEBUG if_list_cs_debug =
+{
+    0, 0, &if_list_cs,
+    { &if_list_cs_debug.ProcessLocksList, &if_list_cs_debug.ProcessLocksList },
+    0, 0, { (DWORD_PTR)(__FILE__ ": if_list_cs") }
+};
+static CRITICAL_SECTION if_list_cs = { &if_list_cs_debug, -1, 0, 0, 0, 0 };
+
+static struct if_entry *find_entry_from_index( DWORD index )
+{
+    struct if_entry *entry;
+
+    LIST_FOR_EACH_ENTRY( entry, &if_list, struct if_entry, entry )
+        if (entry->if_index == index) return entry;
+
+    return NULL;
+}
+
+#if defined (SIOCGIFHWADDR) && defined (HAVE_STRUCT_IFREQ_IFR_HWADDR)
+static NTSTATUS if_get_physical( const char *name, DWORD *type, IF_PHYSICAL_ADDRESS *phys_addr )
+{
+    int fd, size, i;
+    struct ifreq ifr;
+    NTSTATUS ret = STATUS_SUCCESS;
+    static const struct type_lookup
+    {
+        unsigned short ifi_type;
+        IFTYPE mib_type;
+        DWORD addr_len;
+    } types[] =
+    {
+        { ARPHRD_LOOPBACK, MIB_IF_TYPE_LOOPBACK, 0 },
+        { ARPHRD_ETHER, MIB_IF_TYPE_ETHERNET, ETH_ALEN },
+        { ARPHRD_FDDI, MIB_IF_TYPE_FDDI, ETH_ALEN },
+        { ARPHRD_IEEE802, MIB_IF_TYPE_TOKENRING, ETH_ALEN },
+        { ARPHRD_IEEE802_TR, MIB_IF_TYPE_TOKENRING, ETH_ALEN },
+        { ARPHRD_SLIP, MIB_IF_TYPE_SLIP, 0 },
+        { ARPHRD_PPP, MIB_IF_TYPE_PPP, 0 }
+    };
+
+    *type = MIB_IF_TYPE_OTHER;
+    memset( phys_addr, 0, sizeof(*phys_addr) );
+
+    size = strlen( name ) + 1;
+    if (size > sizeof(ifr.ifr_name)) return STATUS_NAME_TOO_LONG;
+    memset( &ifr, 0, sizeof(ifr) );
+    memcpy( ifr.ifr_name, name, size );
+
+    fd = socket( PF_INET, SOCK_DGRAM, 0 );
+    if (fd == -1) return STATUS_TOO_MANY_OPENED_FILES;
+
+    if (ioctl( fd, SIOCGIFHWADDR, &ifr ))
+    {
+        ret = STATUS_DEVICE_DATA_ERROR;
+        goto err;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(types); i++)
+        if (ifr.ifr_hwaddr.sa_family == types[i].ifi_type)
+        {
+            *type = types[i].mib_type;
+            phys_addr->Length = types[i].addr_len;
+            memcpy( phys_addr->Address, ifr.ifr_hwaddr.sa_data, phys_addr->Length );
+            break;
+        }
+
+err:
+    close( fd );
+    return ret;
+}
+
+#elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
+
+static NTSTATUS if_get_physical( const char *name, DWORD *type, IF_PHYSICAL_ADDRESS *phys_addr )
+{
+    struct if_msghdr *ifm;
+    struct sockaddr_dl *sdl;
+    u_char *p, *buf;
+    size_t mib_len;
+    int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }, i;
+    static const struct type_lookup
+    {
+        u_char sdl_type;
+        IFTYPE mib_type;
+    } types[] =
+    {
+        { IFT_ETHER, MIB_IF_TYPE_ETHERNET },
+        { IFT_FDDI, MIB_IF_TYPE_FDDI },
+        { IFT_ISO88024, MIB_IF_TYPE_TOKENRING },
+        { IFT_ISO88025, MIB_IF_TYPE_TOKENRING },
+        { IFT_PPP, MIB_IF_TYPE_PPP },
+        { IFT_SLIP, MIB_IF_TYPE_SLIP },
+        { IFT_LOOP, MIB_IF_TYPE_LOOPBACK }
+    };
+
+    *type = MIB_IF_TYPE_OTHER;
+    memset( phys_addr, 0, sizeof(*phys_addr) );
+
+    if (sysctl( mib, 6, NULL, &mib_len, NULL, 0 ) < 0) return STATUS_TOO_MANY_OPENED_FILES;
+
+    buf = malloc( mib_len );
+    if (!buf) return STATUS_NO_MEMORY;
+
+    if (sysctl( mib, 6, buf, &mib_len, NULL, 0 ) < 0)
+    {
+        free( buf );
+        return STATUS_TOO_MANY_OPENED_FILES;
+    }
+
+    for (p = buf; p < buf + mib_len; p += ifm->ifm_msglen)
+    {
+        ifm = (struct if_msghdr *)p;
+        sdl = (struct sockaddr_dl *)(ifm + 1);
+
+        if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0) continue;
+
+        if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+            memcmp( sdl->sdl_data, name, max( sdl->sdl_nlen, strlen( name ) ) ))
+            continue;
+
+        for (i = 0; i < ARRAY_SIZE(types); i++)
+            if (sdl->sdl_type == types[i].sdl_type)
+            {
+                *type = types[i].mib_type;
+                break;
+            }
+
+        phys_addr->Length = sdl->sdl_alen;
+        if (phys_addr->Length > sizeof(phys_addr->Address)) phys_addr->Length = 0;
+        memcpy( phys_addr->Address, LLADDR(sdl), phys_addr->Length );
+        break;
+    }
+
+    free( buf );
+    return STATUS_SUCCESS;
+}
+#endif
+
+static WCHAR *strdupAtoW( const char *str )
+{
+    WCHAR *ret = NULL;
+    DWORD len;
+
+    if (!str) return ret;
+    len = MultiByteToWideChar( CP_UNIXCP, 0, str, -1, NULL, 0 );
+    ret = heap_alloc( len * sizeof(WCHAR) );
+    if (ret) MultiByteToWideChar( CP_UNIXCP, 0, str, -1, ret, len );
+    return ret;
+}
+
+static struct if_entry *add_entry( DWORD index, char *name )
+{
+    struct if_entry *entry;
+    int name_len = strlen( name );
+
+    if (name_len >= IFNAMSIZ - 1) return NULL;
+    entry = heap_alloc( sizeof(*entry) );
+    if (!entry) return NULL;
+
+    entry->if_index = index;
+    memcpy( entry->if_unix_name, name, name_len + 1 );
+    entry->if_name = strdupAtoW( name );
+    if (!entry->if_name)
+    {
+        heap_free( entry );
+        return NULL;
+    }
+
+    if_get_physical( name, &entry->if_type, &entry->if_phys_addr );
+
+    entry->if_luid.Info.Reserved = 0;
+    entry->if_luid.Info.NetLuidIndex = index;
+    entry->if_luid.Info.IfType = entry->if_type;
+
+    memset( &entry->if_guid, 0, sizeof(entry->if_guid) );
+    entry->if_guid.Data1 = index;
+    memcpy( entry->if_guid.Data4 + 2, "NetDev", 6 );
+
+    list_add_tail( &if_list, &entry->entry );
+    return entry;
+}
+
+static void update_if_table( void )
+{
+    struct if_nameindex *indices = if_nameindex(), *entry;
+
+    for (entry = indices; entry->if_index; entry++)
+    {
+        if (!find_entry_from_index( entry->if_index ))
+            add_entry( entry->if_index, entry->if_name );
+    }
+
+    if_freenameindex( indices );
+}
+
+static void if_counted_string_init( IF_COUNTED_STRING *str, const WCHAR *value )
+{
+    str->Length = value ? min( lstrlenW( value ), ARRAY_SIZE(str->String) - 1 ) * sizeof(WCHAR) : 0;
+    if (str->Length) memcpy( str->String, value, str->Length );
+    memset( (char *)str->String + str->Length, 0, sizeof(str->String) - str->Length );
+}
+
+static void ifinfo_fill_dynamic( struct if_entry *entry, struct nsi_ndis_ifinfo_dynamic *data )
+{
+    int fd, name_len = strlen( entry->if_unix_name );
+    struct ifreq req;
+
+    memset( data, 0, sizeof(*data) );
+
+    if (name_len >= sizeof(req.ifr_name)) return;
+    memcpy( req.ifr_name, entry->if_unix_name, name_len + 1 );
+
+    fd = socket( PF_INET, SOCK_DGRAM, 0 );
+    if (fd == -1) return;
+
+    if (!ioctl( fd, SIOCGIFFLAGS, &req ))
+    {
+        if (req.ifr_flags & IFF_UP) data->oper_status = IfOperStatusUp;
+#ifdef IFF_DORMANT
+        else if (req.ifr_flags & IFF_DORMANT) data->oper_status = IfOperStatusDormant;
+#endif
+        else data->oper_status = IfOperStatusDown;
+    } else data->oper_status = IfOperStatusUnknown;
+
+    data->flags.unk = 0;
+    data->flags.not_media_conn = 0;
+    data->flags.unk2 = 0;
+    data->media_conn_state = MediaConnectStateConnected;
+    data->unk = 0;
+
+    if (!ioctl( fd, SIOCGIFMTU, &req )) data->mtu = req.ifr_mtu;
+    else data->mtu = 0;
+
+    close( fd );
+
+#ifdef __linux__
+    {
+        FILE *fp;
+
+        if ((fp = fopen( "/proc/net/dev", "r" )))
+        {
+            char buf[512], *ptr;
+
+            while ((ptr = fgets( buf, sizeof(buf), fp )))
+            {
+                while (*ptr && isspace( *ptr )) ptr++;
+                if (!_strnicmp( ptr, entry->if_unix_name, name_len ) && ptr[name_len] == ':')
+                {
+                    unsigned long long values[9];
+                    ptr += name_len + 1;
+                    sscanf( ptr, "%llu %llu %llu %llu %*u %*u %*u %llu %llu %llu %llu %llu",
+                            values, values + 1, values + 2, values + 3, values + 4,
+                            values + 5, values + 6, values + 7, values + 8 );
+                    data->in_octets      = values[0];
+                    data->in_ucast_pkts  = values[1];
+                    data->in_errors      = values[2];
+                    data->in_discards    = values[3];
+                    data->in_mcast_pkts  = values[4];
+                    data->out_octets     = values[5];
+                    data->out_ucast_pkts = values[6];
+                    data->out_errors     = values[7];
+                    data->out_discards   = values[8];
+                    break;
+                }
+            }
+            fclose( fp );
+        }
+    }
+#elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
+    {
+        int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, entry->if_index };
+        size_t needed;
+        char *buf = NULL, *end;
+        struct if_msghdr *ifm;
+        struct if_data ifdata;
+
+        if (sysctl( mib, ARRAY_SIZE(mib), NULL, &needed, NULL, 0 ) == -1) goto done;
+        buf = heap_alloc( needed );
+        if (!buf) goto done;
+        if (sysctl( mib, ARRAY_SIZE(mib), buf, &needed, NULL, 0 ) == -1) goto done;
+        for (end = buf + needed; buf < end; buf += ifm->ifm_msglen)
+        {
+            ifm = (struct if_msghdr *) buf;
+            if (ifm->ifm_type == RTM_IFINFO)
+            {
+                ifdata = ifm->ifm_data;
+                data->xmit_speed = data->rcv_speed = ifdata.ifi_baudrate;
+                data->in_octets = ifdata.ifi_ibytes;
+                data->in_errors = ifdata.ifi_ierrors;
+                data->in_discards = ifdata.ifi_iqdrops;
+                data->in_ucast_pkts = ifdata.ifi_ipackets;
+                data->in_mcast_pkts = ifdata.ifi_imcasts;
+                data->out_octets = ifdata.ifi_obytes;
+                data->out_ucast_pkts = ifdata.ifi_opackets;
+                data->out_mcast_pkts = ifdata.ifi_omcasts;
+                data->out_errors = ifdata.ifi_oerrors;
+                break;
+            }
+        }
+    done:
+        heap_free( buf );
+    }
+#endif
+}
+
+static void ifinfo_fill_entry( struct if_entry *entry, NET_LUID *key, struct nsi_ndis_ifinfo_rw *rw,
+                               struct nsi_ndis_ifinfo_dynamic *dyn, struct nsi_ndis_ifinfo_static *stat )
+{
+    if (key) memcpy( key, &entry->if_luid, sizeof(entry->if_luid) );
+
+    if (rw)
+    {
+        memset( &rw->network_guid, 0, sizeof(entry->if_guid) );
+        rw->admin_status = MIB_IF_ADMIN_STATUS_UP;
+        if_counted_string_init( &rw->alias, entry->if_name );
+        memcpy( &rw->phys_addr, &entry->if_phys_addr, sizeof(entry->if_phys_addr) );
+        rw->pad = 0;
+        if_counted_string_init( &rw->name2, NULL );
+        rw->unk = 0;
+    }
+
+    if (dyn) ifinfo_fill_dynamic( entry, dyn );
+
+    if (stat)
+    {
+        stat->if_index = entry->if_index;
+        if_counted_string_init( &stat->descr, entry->if_name ); /* get a more descriptive name */
+        stat->type = entry->if_type;
+        stat->access_type = (entry->if_type == MIB_IF_TYPE_LOOPBACK) ? NET_IF_ACCESS_LOOPBACK : NET_IF_ACCESS_BROADCAST;
+        stat->unk = 0;
+        stat->conn_type = NET_IF_CONNECTION_DEDICATED;
+        memcpy( &stat->if_guid, &entry->if_guid, sizeof(entry->if_guid) );
+        stat->conn_present = entry->if_type != MIB_IF_TYPE_LOOPBACK;
+        memcpy( &stat->perm_phys_addr, &entry->if_phys_addr, sizeof(entry->if_phys_addr) );
+        stat->flags.hw = entry->if_type != MIB_IF_TYPE_LOOPBACK;
+        stat->flags.filter = 0;
+        stat->flags.unk = 0;
+        stat->media_type = 0;
+        stat->phys_medium_type = 0;
+    }
+}
+
+static NTSTATUS ifinfo_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 )
+{
+    struct if_entry *entry;
+    DWORD num = 0;
+    NTSTATUS status = STATUS_SUCCESS;
+    BOOL want_data = key_size || rw_size || dynamic_size || static_size;
+
+    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 );
+
+    EnterCriticalSection( &if_list_cs );
+
+    update_if_table();
+
+    LIST_FOR_EACH_ENTRY( entry, &if_list, struct if_entry, entry )
+    {
+        if (num < *count)
+        {
+            ifinfo_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++;
+    }
+
+    LeaveCriticalSection( &if_list_cs );
+
+    if (!want_data || num <= *count) *count = num;
+    else status = STATUS_MORE_ENTRIES;
+
+    return status;
+}
+
+static const struct module_table tables[] =
+{
+    {
+        NSI_NDIS_IFINFO_TABLE,
+        {
+            sizeof(NET_LUID), sizeof(struct nsi_ndis_ifinfo_rw),
+            sizeof(struct nsi_ndis_ifinfo_dynamic), sizeof(struct nsi_ndis_ifinfo_static)
+        },
+        ifinfo_enumerate_all,
+    },
+    { ~0u }
+};
+
+const struct module ndis_module =
+{
+    &NPI_MS_NDIS_MODULEID,
+    tables
+};
diff --git a/dlls/nsiproxy.sys/nsi.c b/dlls/nsiproxy.sys/nsi.c
index 7ff36c093ba..85b9be1e587 100644
--- a/dlls/nsiproxy.sys/nsi.c
+++ b/dlls/nsiproxy.sys/nsi.c
@@ -30,9 +30,47 @@
 #include "ifdef.h"
 #include "netiodef.h"
 #include "wine/nsi.h"
+#include "wine/debug.h"
 #include "nsiproxy_private.h"
 
+WINE_DEFAULT_DEBUG_CHANNEL(nsi);
+
+static const struct module *modules[] =
+{
+    &ndis_module,
+};
+
+static const struct module_table *get_module_table( const NPI_MODULEID *id, DWORD table )
+{
+    const struct module_table *entry;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(modules); i++)
+        if (NmrIsEqualNpiModuleId( modules[i]->module, id ))
+            for (entry = modules[i]->tables; entry->table != ~0u; entry++)
+                if (entry->table == table) return entry;
+
+    return NULL;
+}
+
 NTSTATUS nsi_enumerate_all_ex( struct nsi_enumerate_all_ex *params )
 {
-    return STATUS_NOT_IMPLEMENTED;
+    const struct module_table *entry = get_module_table( params->module, params->table );
+    DWORD sizes[4] = { params->key_size, params->rw_size, params->dynamic_size, params->static_size };
+    void *data[4] = { params->key_data, params->rw_data, params->dynamic_data, params->static_data };
+    int i;
+
+    if (!entry || !entry->enumerate_all)
+    {
+        WARN( "table not found\n" );
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(sizes); i++)
+    {
+        if (!sizes[i]) data[i] = NULL;
+        else if (sizes[i] != entry->sizes[i]) return STATUS_INVALID_PARAMETER;
+    }
+
+    return entry->enumerate_all( data[0], sizes[0], data[1], sizes[1], data[2], sizes[2], data[3], sizes[3], &params->count );
 }
diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h
index 19b520d17cc..6745dbc6b56 100644
--- a/dlls/nsiproxy.sys/nsiproxy_private.h
+++ b/dlls/nsiproxy.sys/nsiproxy_private.h
@@ -19,3 +19,20 @@
  */
 
 NTSTATUS nsi_enumerate_all_ex( struct nsi_enumerate_all_ex *params ) DECLSPEC_HIDDEN;
+
+struct module_table
+{
+    DWORD table;
+    DWORD sizes[4];
+    NTSTATUS (*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 );
+};
+
+struct module
+{
+    const NPI_MODULEID *module;
+    const struct module_table *tables;
+};
+
+extern const struct module ndis_module DECLSPEC_HIDDEN;
-- 
2.23.0




More information about the wine-devel mailing list