[PATCH 3/7] nsiproxy: Add support for IPv6 scope ids.

Huw Davies huw at codeweavers.com
Tue Aug 17 03:27:13 CDT 2021


Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/nsi/tests/nsi.c    |   2 -
 dlls/nsiproxy.sys/tcp.c | 136 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 132 insertions(+), 6 deletions(-)

diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c
index 8e19df83684..f8ba9aff5da 100644
--- a/dlls/nsi/tests/nsi.c
+++ b/dlls/nsi/tests/nsi.c
@@ -916,14 +916,12 @@ todo_wine_if( !unstable(0) && row->dwOwningPid )
         {
             ok( unstable( !memcmp( row6->ucLocalAddr, keys[i].local.Ipv6.sin6_addr.s6_addr, sizeof(IN6_ADDR) ) ),
                 "mismatch\n" );
-todo_wine_if( !unstable(0) && row6->dwLocalScopeId )
             ok( unstable( row6->dwLocalScopeId == keys[i].local.Ipv6.sin6_scope_id ), "%x vs %x\n",
                 row6->dwLocalScopeId, keys[i].local.Ipv6.sin6_scope_id );
             ok( unstable( row6->dwLocalPort == keys[i].local.Ipv6.sin6_port ), "%d vs %d\n",
                 row6->dwLocalPort, keys[i].local.Ipv6.sin6_port );
             ok( unstable( !memcmp( row6->ucRemoteAddr, keys[i].remote.Ipv6.sin6_addr.s6_addr, sizeof(IN6_ADDR) ) ),
                 "mismatch\n" );
-todo_wine_if( !unstable(0) && row6->dwRemoteScopeId )
             ok( unstable( row6->dwRemoteScopeId == keys[i].remote.Ipv6.sin6_scope_id ), "%x vs %x\n",
                 row6->dwRemoteScopeId, keys[i].remote.Ipv6.sin6_scope_id );
             ok( unstable( row6->dwRemotePort == keys[i].remote.Ipv6.sin6_port ), "%d vs %d\n",
diff --git a/dlls/nsiproxy.sys/tcp.c b/dlls/nsiproxy.sys/tcp.c
index 710aed5d6c3..5b4a4b68765 100644
--- a/dlls/nsiproxy.sys/tcp.c
+++ b/dlls/nsiproxy.sys/tcp.c
@@ -54,6 +54,10 @@
 #include <sys/sysctl.h>
 #endif
 
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
@@ -209,6 +213,119 @@ static inline MIB_TCP_STATE tcp_state_to_mib_state( int state )
    }
 }
 
+struct ipv6_addr_scope
+{
+    IN6_ADDR addr;
+    DWORD scope;
+};
+
+static struct ipv6_addr_scope *get_ipv6_addr_scope_table( unsigned int *size )
+{
+    struct ipv6_addr_scope *table = NULL;
+    unsigned int table_size = 0, num = 0;
+
+#ifdef __linux__
+    {
+        char buf[512], *ptr;
+        FILE *fp;
+
+        if (!(fp = fopen( "/proc/net/if_inet6", "r" ))) goto failed;
+
+        while ((ptr = fgets( buf, sizeof(buf), fp )))
+        {
+            WORD a[8];
+            DWORD scope;
+            struct ipv6_addr_scope *entry;
+            unsigned int i;
+
+            if (sscanf( ptr, "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx %*s %*s %x",
+                        a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, &scope ) != 9)
+                continue;
+
+            if (++num > table_size)
+            {
+                if (!table_size) table_size = 4;
+                else table_size *= 2;
+                if (!(table = heap_realloc( table, table_size * sizeof(table[0]) )))
+                {
+                    fclose( fp );
+                    goto failed;
+                }
+            }
+
+            entry = table + num - 1;
+            for (i = 0; i < 8; i++)
+                entry->addr.u.Word[i] = htons( a[i] );
+            entry->scope = htons( scope );
+        }
+
+        fclose( fp );
+    }
+#elif defined(HAVE_GETIFADDRS)
+    {
+        struct ifaddrs *addrs, *cur;
+
+        if (getifaddrs( &addrs ) == -1)  goto failed;
+
+        for (cur = addrs; cur; cur = cur->ifa_next)
+        {
+            struct sockaddr_in6 *sin6;
+            struct ipv6_addr_scope *entry;
+
+            if (cur->ifa_addr->sa_family != AF_INET6) continue;
+
+            if (++num > table_size)
+            {
+                if (!table_size) table_size = 4;
+                else table_size *= 2;
+                if (!(table = heap_realloc( table, table_size * sizeof(table[0]) )))
+                {
+                    freeifaddrs( addrs );
+                    goto failed;
+                }
+            }
+
+            sin6 = (struct sockaddr_in6 *)cur->ifa_addr;
+            entry = table + num - 1;
+            memcpy( &entry->addr, &sin6->sin6_addr, sizeof(entry->addr) );
+            entry->scope = sin6->sin6_scope_id;
+        }
+
+        freeifaddrs( addrs );
+    }
+#else
+    FIXME( "not implemented\n" );
+    goto failed;
+#endif
+
+    *size = num;
+    return table;
+
+failed:
+    heap_free( table );
+    return NULL;
+}
+
+static DWORD find_ipv6_addr_scope( const IN6_ADDR *addr, const struct ipv6_addr_scope *table, unsigned int size )
+{
+    const BYTE multicast_scope_mask = 0x0F;
+    const BYTE multicast_scope_shift = 0;
+    unsigned int i;
+
+    if (WS_IN6_IS_ADDR_UNSPECIFIED( addr )) return 0;
+
+    if (WS_IN6_IS_ADDR_MULTICAST( addr ))
+        return htons( (addr->u.Byte[1] & multicast_scope_mask) >> multicast_scope_shift );
+
+    if (!table) return -1;
+
+    for (i = 0; i < size; i++)
+        if (!memcmp( &table[i].addr, addr, sizeof(table[i].addr) ))
+            return table[i].scope;
+
+    return -1;
+}
+
 static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *key_data, DWORD key_size,
                                          void *rw, DWORD rw_size,
                                          struct nsi_tcp_conn_dynamic *dynamic_data, DWORD dynamic_size,
@@ -220,6 +337,8 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *
     struct nsi_tcp_conn_key key;
     struct nsi_tcp_conn_dynamic dyn;
     struct nsi_tcp_conn_static stat;
+    struct ipv6_addr_scope *addr_scopes = NULL;
+    unsigned int addr_scopes_size = 0;
 
 #ifdef __linux__
     {
@@ -269,6 +388,8 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *
             memset( &dyn, 0, sizeof(dyn) );
             memset( &stat, 0, sizeof(stat) );
 
+            addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size );
+
             /* skip header line */
             ptr = fgets( buf, sizeof(buf), fp );
             while ((ptr = fgets( buf, sizeof(buf), fp )))
@@ -286,8 +407,10 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *
                 key.local.Ipv6.sin6_family = key.remote.Ipv6.sin6_family = WS_AF_INET6;
                 key.local.Ipv6.sin6_port = htons( key.local.Ipv6.sin6_port );
                 key.remote.Ipv6.sin6_port = htons( key.remote.Ipv6.sin6_port );
-                key.local.Ipv6.sin6_scope_id = 0; /* FIXME */
-                key.remote.Ipv6.sin6_scope_id = 0; /* FIXME */
+                key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes,
+                                                                     addr_scopes_size );
+                key.remote.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.remote.Ipv6.sin6_addr, addr_scopes,
+                                                                      addr_scopes_size );
 
                 stat.pid = 0; /* FIXME */
                 stat.create_time = 0; /* FIXME */
@@ -335,6 +458,8 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *
         /* Might be nothing here; first entry is just a header it seems */
         if (len <= sizeof(struct xinpgen)) goto err;
 
+        addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size );
+
         orig_xig = (struct xinpgen *)buf;
         xig = orig_xig;
 
@@ -384,10 +509,12 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *
                 key.local.Ipv6.sin6_family = key.remote.Ipv6.sin6_family = WS_AF_INET6;
                 memcpy( &key.local.Ipv6.sin6_addr, &in->in6p_laddr, sizeof(in->in6p_laddr) );
                 key.local.Ipv6.sin6_port = in->inp_lport;
+                key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes,
+                                                                     addr_scopes_size );
                 memcpy( &key.remote.Ipv6.sin6_addr, &in->in6p_faddr, sizeof(in->in6p_faddr) );
                 key.remote.Ipv6.sin6_port = in->inp_fport;
-                key.local.Ipv6.sin6_scope_id = 0; /* FIXME */
-                key.remote.Ipv6.sin6_scope_id = 0; /* FIXME */
+                key.remote.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.remote.Ipv6.sin6_addr, addr_scopes,
+                                                                      addr_scopes_size );
             }
 
             stat.pid = 0; /* FIXME */
@@ -413,6 +540,7 @@ static NTSTATUS tcp_conns_enumerate_all( DWORD filter, struct nsi_tcp_conn_key *
     if (!want_data || num <= *count) *count = num;
     else status = STATUS_MORE_ENTRIES;
 
+    heap_free( addr_scopes );
     return status;
 }
 
-- 
2.23.0




More information about the wine-devel mailing list