[PATCH 4/5] nsiproxy: Actually send the ICMP ECHO request.

Huw Davies huw at codeweavers.com
Mon Oct 4 06:23:21 CDT 2021


However, don't yet wait for the reply.

Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/nsiproxy.sys/Makefile.in        |   1 +
 dlls/nsiproxy.sys/device.c           |  37 ++-
 dlls/nsiproxy.sys/icmp_echo.c        | 321 +++++++++++++++++++++++++++
 dlls/nsiproxy.sys/nsi.c              |   1 +
 dlls/nsiproxy.sys/nsiproxy_private.h |  28 +++
 dlls/nsiproxy.sys/unix_private.h     |   2 +
 6 files changed, 387 insertions(+), 3 deletions(-)
 create mode 100644 dlls/nsiproxy.sys/icmp_echo.c
 create mode 100644 dlls/nsiproxy.sys/nsiproxy_private.h

diff --git a/dlls/nsiproxy.sys/Makefile.in b/dlls/nsiproxy.sys/Makefile.in
index 95f2a205816..87f7e165ef4 100644
--- a/dlls/nsiproxy.sys/Makefile.in
+++ b/dlls/nsiproxy.sys/Makefile.in
@@ -7,6 +7,7 @@ EXTRADLLFLAGS = -Wl,--subsystem,native
 
 C_SRCS = \
 	device.c \
+	icmp_echo.c \
 	ip.c \
 	ndis.c \
 	nsi.c \
diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c
index f4cefd4b794..5ae243a4111 100644
--- a/dlls/nsiproxy.sys/device.c
+++ b/dlls/nsiproxy.sys/device.c
@@ -33,6 +33,8 @@
 #include "wine/debug.h"
 #include "wine/unixlib.h"
 
+#include "nsiproxy_private.h"
+
 WINE_DEFAULT_DEBUG_CHANNEL(nsi);
 
 static unixlib_handle_t nsiproxy_handle;
@@ -56,6 +58,7 @@ static NTSTATUS nsiproxy_call( unsigned int code, void *args )
 
 enum unix_calls
 {
+    icmp_send_echo,
     nsi_enumerate_all_ex,
     nsi_get_all_parameters_ex,
     nsi_get_parameter_ex,
@@ -289,6 +292,36 @@ static int add_device( DRIVER_OBJECT *driver )
     return 1;
 }
 
+static void handle_queued_send_echo( IRP *irp )
+{
+    struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer;
+    struct nsiproxy_icmp_echo_reply *reply = (struct nsiproxy_icmp_echo_reply *)irp->AssociatedIrp.SystemBuffer;
+    struct icmp_send_echo_params params;
+    NTSTATUS status;
+
+    TRACE( "\n" );
+    params.request = in->data + ((in->opt_size + 3) & ~3);
+    params.request_size = in->req_size;
+    params.ttl = in->ttl;
+    params.tos = in->tos;
+    params.dst = &in->dst;
+
+    status = nsiproxy_call( icmp_send_echo, &params );
+    TRACE( "icmp_send_echo rets %08x\n", status );
+
+    if (1)
+    {
+        irp->IoStatus.Status = status;
+        if (status == STATUS_SUCCESS)
+        {
+            memset( reply, 0, sizeof(*reply) );
+            reply->status = params.ip_status;
+            irp->IoStatus.Information = sizeof(*reply);
+        }
+        IoCompleteRequest( irp, IO_NO_INCREMENT );
+    }
+}
+
 static DWORD WINAPI request_thread_proc( void *arg )
 {
     LIST_ENTRY *entry;
@@ -309,9 +342,7 @@ static DWORD WINAPI request_thread_proc( void *arg )
                 continue;
             }
 
-            /* FIXME */
-            irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
-            IoCompleteRequest( irp, IO_NO_INCREMENT );
+            handle_queued_send_echo( irp );
         }
         LeaveCriticalSection( &nsiproxy_cs );
     }
diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c
new file mode 100644
index 00000000000..8ec506f823c
--- /dev/null
+++ b/dlls/nsiproxy.sys/icmp_echo.c
@@ -0,0 +1,321 @@
+/*
+ * nsiproxy.sys icmp_echo implementation
+ *
+ * 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
+ */
+#if 0
+#pragma makedep unix
+#endif
+
+#define _NTSYSTEM_
+
+#include "config.h"
+#include <stdarg.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "winioctl.h"
+#define USE_WS_PREFIX
+#include "ddk/wdm.h"
+#include "ifdef.h"
+#include "netiodef.h"
+#include "ipexport.h"
+#include "ipmib.h"
+#include "wine/nsi.h"
+#include "wine/debug.h"
+
+#include "nsiproxy_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(nsi);
+
+static LONG icmp_sequence;
+
+struct ip_hdr
+{
+    uint8_t v_hl; /* version << 4 | hdr_len */
+    uint8_t tos;
+    uint16_t tot_len;
+    uint16_t id;
+    uint16_t frag_off;
+    uint8_t ttl;
+    uint8_t protocol;
+    uint16_t checksum;
+    uint32_t saddr;
+    uint32_t daddr;
+};
+
+struct icmp_hdr
+{
+    uint8_t type;
+    uint8_t code;
+    uint16_t checksum;
+    union
+    {
+        struct
+        {
+            uint16_t id;
+            uint16_t sequence;
+        } echo;
+    } un;
+};
+
+struct family_ops;
+struct icmp_data
+{
+    LARGE_INTEGER send_time;
+    int socket;
+    unsigned short id;
+    unsigned short seq;
+    const struct family_ops *ops;
+};
+
+static void ipv4_init_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp_hdr )
+{
+    icmp_hdr->type = ICMP4_ECHO_REQUEST;
+    icmp_hdr->code = 0;
+    icmp_hdr->checksum = 0;
+    icmp_hdr->un.echo.id = data->id = getpid() & 0xffff; /* will be overwritten for linux ping socks */
+    icmp_hdr->un.echo.sequence = data->seq = InterlockedIncrement( &icmp_sequence ) & 0xffff;
+}
+
+/* rfc 1071 checksum */
+static unsigned short chksum( BYTE *data, unsigned int count )
+{
+    unsigned int sum = 0, carry = 0;
+    unsigned short check, s;
+
+    while (count > 1)
+    {
+        s = *(unsigned short *)data;
+        data += 2;
+        sum += carry;
+        sum += s;
+        carry = s > sum;
+        count -= 2;
+    }
+    sum += carry; /* This won't produce another carry */
+    sum = (sum & 0xffff) + (sum >> 16);
+
+    if (count) sum += *data; /* LE-only */
+
+    sum = (sum & 0xffff) + (sum >> 16);
+    /* fold in any carry */
+    sum = (sum & 0xffff) + (sum >> 16);
+
+    check = ~sum;
+    return check;
+}
+
+#ifdef __linux__
+static unsigned short null_chksum( BYTE *data, unsigned int count )
+{
+    return 0;
+}
+#endif
+
+static void ipv4_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params )
+{
+    int val;
+
+    val = params->ttl;
+    if (val) setsockopt( data->socket, IPPROTO_IP, IP_TTL, &val, sizeof(val) );
+    val = params->tos;
+    if (val) setsockopt( data->socket, IPPROTO_IP, IP_TOS, &val, sizeof(val) );
+}
+
+#ifdef __linux__
+static void ipv4_linux_ping_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params )
+{
+    static const int val = 1;
+
+    ipv4_set_socket_opts( data, params );
+
+    setsockopt( data->socket, IPPROTO_IP, IP_RECVTTL, &val, sizeof(val) );
+    setsockopt( data->socket, IPPROTO_IP, IP_RECVTOS, &val, sizeof(val) );
+}
+#endif
+
+struct family_ops
+{
+    int family;
+    int icmp_protocol;
+    void (*init_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp_hdr );
+    unsigned short (*chksum)( BYTE *data, unsigned int count );
+    void (*set_socket_opts)( struct icmp_data *data, struct icmp_send_echo_params *params );
+};
+
+static const struct family_ops ipv4 =
+{
+    AF_INET,
+    IPPROTO_ICMP,
+    ipv4_init_icmp_hdr,
+    chksum,
+    ipv4_set_socket_opts,
+};
+
+#ifdef __linux__
+/* linux ipv4 ping sockets behave more like ipv6 raw sockets */
+static const struct family_ops ipv4_linux_ping =
+{
+    AF_INET,
+    IPPROTO_ICMP,
+    ipv4_init_icmp_hdr,
+    null_chksum,
+    ipv4_linux_ping_set_socket_opts,
+};
+#endif
+
+static IP_STATUS errno_to_ip_status( int err )
+{
+    switch( err )
+    {
+    case EHOSTUNREACH: return IP_DEST_HOST_UNREACHABLE;
+    default: return IP_GENERAL_FAILURE;
+    }
+}
+
+static int SOCKADDR_INET_to_sockaddr( const SOCKADDR_INET *in, struct sockaddr *out, int len )
+{
+    switch (in->si_family)
+    {
+    case WS_AF_INET:
+    {
+        struct sockaddr_in *sa = (struct sockaddr_in *)out;
+
+        if (len < sizeof(*sa)) return 0;
+        sa->sin_family = AF_INET;
+        sa->sin_port = in->Ipv4.sin_port;
+        sa->sin_addr.s_addr = in->Ipv4.sin_addr.WS_s_addr;
+        return sizeof(*sa);
+    }
+    case WS_AF_INET6:
+    {
+        struct sockaddr_in6 *sa = (struct sockaddr_in6 *)out;
+
+        if (len < sizeof(*sa)) return 0;
+        sa->sin6_family = AF_INET6;
+        sa->sin6_port = in->Ipv6.sin6_port;
+        sa->sin6_flowinfo = in->Ipv6.sin6_flowinfo;
+        memcpy( sa->sin6_addr.s6_addr, in->Ipv6.sin6_addr.WS_s6_addr, sizeof(sa->sin6_addr.s6_addr) );
+        sa->sin6_scope_id = in->Ipv6.sin6_scope_id;
+        return sizeof(*sa);
+    }
+    }
+    return 0;
+}
+
+static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data **icmp_data )
+{
+    struct icmp_data *data;
+    const struct family_ops *ops;
+
+    if (win_family == WS_AF_INET) ops = &ipv4;
+    else return STATUS_INVALID_PARAMETER;
+
+    data = malloc( sizeof(*data) );
+    if (!data) return STATUS_NO_MEMORY;
+
+    data->socket = socket( ops->family, SOCK_RAW, ops->icmp_protocol );
+    if (data->socket < 0) /* Try a ping-socket */
+    {
+        TRACE( "failed to open raw sock, trying a dgram sock\n" );
+        data->socket = socket( ops->family, SOCK_DGRAM, ops->icmp_protocol );
+        if (data->socket < 0)
+        {
+            WARN( "Unable to create socket\n" );
+            free( data );
+            return STATUS_ACCESS_DENIED;
+        }
+#ifdef __linux__
+        if (ops->family == AF_INET) ops = &ipv4_linux_ping;
+#endif
+    }
+    data->ops = ops;
+
+    *icmp_data = data;
+    return STATUS_SUCCESS;
+}
+
+static void icmp_data_free( struct icmp_data *data )
+{
+    close( data->socket );
+    free( data );
+}
+
+NTSTATUS icmp_send_echo( void *args )
+{
+    struct icmp_send_echo_params *params = args;
+    struct icmp_hdr *icmp_hdr; /* this is the same for both ipv4 and ipv6 */
+    struct sockaddr_storage dst_storage;
+    struct sockaddr *dst = (struct sockaddr *)&dst_storage;
+    struct icmp_data *data;
+    int dst_len, ret;
+    NTSTATUS status;
+
+    status = icmp_data_create( params->dst->si_family, &data );
+    if (status) return status;
+    data->ops->set_socket_opts( data, params );
+
+    icmp_hdr = malloc( sizeof(*icmp_hdr) + params->request_size );
+    if (!icmp_hdr)
+    {
+        icmp_data_free( data );
+        return STATUS_NO_MEMORY;
+    }
+    data->ops->init_icmp_hdr( data, icmp_hdr );
+    memcpy( icmp_hdr + 1, params->request, params->request_size );
+    icmp_hdr->checksum = data->ops->chksum( (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size );
+
+    dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(dst_storage) );
+
+    NtQueryPerformanceCounter( &data->send_time, NULL );
+    ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, dst_len );
+    free( icmp_hdr );
+
+    if (ret < 0)
+    {
+        TRACE( "sendto() rets %d errno %d\n", ret, errno );
+        icmp_data_free( data );
+        params->ip_status = errno_to_ip_status( errno );
+        return STATUS_SUCCESS;
+    }
+
+    /* FIXME */
+    icmp_data_free( data );
+    return STATUS_NOT_SUPPORTED;
+}
diff --git a/dlls/nsiproxy.sys/nsi.c b/dlls/nsiproxy.sys/nsi.c
index 565ae2d11dd..25219a1c3fe 100644
--- a/dlls/nsiproxy.sys/nsi.c
+++ b/dlls/nsiproxy.sys/nsi.c
@@ -147,6 +147,7 @@ static NTSTATUS unix_nsi_get_parameter_ex( void *args )
 
 const unixlib_entry_t __wine_unix_call_funcs[] =
 {
+    icmp_send_echo,
     unix_nsi_enumerate_all_ex,
     unix_nsi_get_all_parameters_ex,
     unix_nsi_get_parameter_ex
diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h
new file mode 100644
index 00000000000..9831998793d
--- /dev/null
+++ b/dlls/nsiproxy.sys/nsiproxy_private.h
@@ -0,0 +1,28 @@
+/*
+ * nsiproxy.sys
+ *
+ * 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
+ */
+
+struct icmp_send_echo_params
+{
+    SOCKADDR_INET *dst;
+    void *request;
+    DWORD request_size;
+    BYTE ttl, tos;
+    ULONG ip_status;
+};
diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h
index 97e264e9b1a..0dfe9e69dbc 100644
--- a/dlls/nsiproxy.sys/unix_private.h
+++ b/dlls/nsiproxy.sys/unix_private.h
@@ -156,3 +156,5 @@ static inline int ascii_strcasecmp( const char *s1, const char *s2 )
 {
     return ascii_strncasecmp( s1, s2, -1 );
 }
+
+NTSTATUS icmp_send_echo( void *args ) DECLSPEC_HIDDEN;
-- 
2.23.0




More information about the wine-devel mailing list