Signed-off-by: Huw Davies huw@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], ¶ms->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;