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