Commit a58f3e04 authored by Paul Gofman's avatar Paul Gofman Committed by Alexandre Julliard

nsiproxy.sys: Implement change notifications for NSI_IP_UNICAST_TABLE.

parent 79165b16
......@@ -51,6 +51,12 @@ DECLARE_CRITICAL_SECTION( nsiproxy_cs );
static LIST_ENTRY request_queue = LIST_ENTRY_INIT( request_queue );
static LIST_ENTRY notification_queue = LIST_ENTRY_INIT( notification_queue );
struct notification_data
{
NPI_MODULEID module;
UINT table;
};
static NTSTATUS nsiproxy_call( unsigned int code, void *args )
{
return WINE_UNIX_CALL( code, args );
......@@ -65,6 +71,7 @@ enum unix_calls
nsi_enumerate_all_ex,
nsi_get_all_parameters_ex,
nsi_get_parameter_ex,
nsi_get_notification,
};
static NTSTATUS nsiproxy_enumerate_all( IRP *irp )
......@@ -270,6 +277,7 @@ static void WINAPI change_notification_cancel( DEVICE_OBJECT *device, IRP *irp )
EnterCriticalSection( &nsiproxy_cs );
RemoveEntryList( &irp->Tail.Overlay.ListEntry );
free( irp->Tail.Overlay.DriverContext[0] );
LeaveCriticalSection( &nsiproxy_cs );
irp->IoStatus.Status = STATUS_CANCELLED;
......@@ -281,10 +289,12 @@ static NTSTATUS nsiproxy_change_notification( IRP *irp )
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
struct nsiproxy_request_notification *in = (struct nsiproxy_request_notification *)irp->AssociatedIrp.SystemBuffer;
DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
struct notification_data *data;
FIXME( "\n" );
TRACE( "irp %p.\n", irp );
if (in_len < sizeof(*in)) return STATUS_INVALID_PARAMETER;
if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY;
/* FIXME: validate module and table. */
EnterCriticalSection( &nsiproxy_cs );
......@@ -294,10 +304,14 @@ static NTSTATUS nsiproxy_change_notification( IRP *irp )
/* IRP was canceled before we set cancel routine */
InitializeListHead( &irp->Tail.Overlay.ListEntry );
LeaveCriticalSection( &nsiproxy_cs );
free( data );
return STATUS_CANCELLED;
}
InsertTailList( &notification_queue, &irp->Tail.Overlay.ListEntry );
IoMarkIrpPending( irp );
data->module = in->module;
data->table = in->table;
irp->Tail.Overlay.DriverContext[0] = data;
LeaveCriticalSection( &nsiproxy_cs );
return STATUS_PENDING;
......@@ -466,6 +480,44 @@ static DWORD WINAPI request_thread_proc( void *arg )
return 0;
}
static DWORD WINAPI notification_thread_proc( void *arg )
{
struct nsi_get_notification_params params;
LIST_ENTRY *entry, *next;
NTSTATUS status;
while (!(status = nsiproxy_call( nsi_get_notification, &params )))
{
EnterCriticalSection( &nsiproxy_cs );
for (entry = notification_queue.Flink; entry != &notification_queue; entry = next)
{
IRP *irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
struct notification_data *data = irp->Tail.Overlay.DriverContext[0];
next = entry->Flink;
if(irp->Cancel)
{
/* Cancel routine should care of freeing data and completing IRP. */
TRACE( "irp %p canceled.\n", irp );
continue;
}
if (!NmrIsEqualNpiModuleId( &data->module, &params.module ) || data->table != params.table)
continue;
irp->IoStatus.Status = 0;
RemoveEntryList( entry );
irp->Tail.Overlay.DriverContext[0] = NULL;
free( data );
TRACE("completing irp %p.\n", irp);
IoCompleteRequest( irp, IO_NO_INCREMENT );
}
LeaveCriticalSection( &nsiproxy_cs );
}
WARN( "nsi_get_notification failed, status %#lx.\n", status );
return 0;
}
NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
{
NTSTATUS status;
......@@ -483,6 +535,8 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
request_event = CreateEventW( NULL, FALSE, FALSE, NULL );
thread = CreateThread( NULL, 0, request_thread_proc, NULL, 0, NULL );
CloseHandle( thread );
thread = CreateThread( NULL, 0, notification_thread_proc, NULL, 0, NULL );
CloseHandle( thread );
return STATUS_SUCCESS;
}
......@@ -21,7 +21,16 @@
#pragma makedep unix
#endif
#include "config.h"
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <limits.h>
#ifdef HAVE_LINUX_RTNETLINK_H
#include <linux/rtnetlink.h>
#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
......@@ -32,11 +41,13 @@
#include "ddk/wdm.h"
#include "ifdef.h"
#define __WINE_INIT_NPI_MODULEID
#define USE_WS_PREFIX
#include "netiodef.h"
#include "wine/nsi.h"
#include "wine/debug.h"
#include "wine/unixlib.h"
#include "unix_private.h"
#include "nsiproxy_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(nsi);
......@@ -145,6 +156,114 @@ static NTSTATUS unix_nsi_get_parameter_ex( void *args )
return nsi_get_parameter_ex( params );
}
#ifdef HAVE_LINUX_RTNETLINK_H
static struct
{
const NPI_MODULEID *module;
UINT32 table;
}
queued_notifications[256];
static unsigned int queued_notification_count;
static NTSTATUS add_notification( const NPI_MODULEID *module, UINT32 table )
{
unsigned int i;
for (i = 0; i < queued_notification_count; ++i)
if (queued_notifications[i].module == module && queued_notifications[i].table == table) return STATUS_SUCCESS;
if (queued_notification_count == ARRAY_SIZE(queued_notifications))
{
ERR( "Notification queue full.\n" );
return STATUS_NO_MEMORY;
}
queued_notifications[i].module = module;
queued_notifications[i].table = table;
++queued_notification_count;
return STATUS_SUCCESS;
}
static NTSTATUS poll_netlink(void)
{
static int netlink_fd = -1;
char buffer[PIPE_BUF];
struct nlmsghdr *nlh;
NTSTATUS status;
int len;
if (netlink_fd == -1)
{
struct sockaddr_nl addr;
if ((netlink_fd = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE )) == -1)
{
ERR( "netlink socket creation failed, errno %d.\n", errno );
return STATUS_NOT_IMPLEMENTED;
}
memset( &addr, 0, sizeof(addr) );
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
if (bind( netlink_fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1)
{
close( netlink_fd );
netlink_fd = -1;
ERR( "bind failed, errno %d.\n", errno );
return STATUS_NOT_IMPLEMENTED;
}
}
while (1)
{
len = recv( netlink_fd, buffer, sizeof(buffer), 0 );
if (len <= 0)
{
if (errno == EINTR) continue;
ERR( "error receivng, len %d, errno %d.\n", len, errno );
return STATUS_UNSUCCESSFUL;
}
for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len))
{
if (nlh->nlmsg_type == NLMSG_DONE) break;
if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR)
{
struct ifaddrmsg *addrmsg = (struct ifaddrmsg *)(nlh + 1);
const NPI_MODULEID *module;
if (addrmsg->ifa_family == AF_INET) module = &NPI_MS_IPV4_MODULEID;
else if (addrmsg->ifa_family == AF_INET6) module = &NPI_MS_IPV6_MODULEID;
else
{
WARN( "Unknown addrmsg->ifa_family %d.\n", addrmsg->ifa_family );
continue;
}
if ((status = add_notification( module, NSI_IP_UNICAST_TABLE))) return status;
}
}
if (queued_notification_count) break;
}
return STATUS_SUCCESS;
}
static NTSTATUS unix_nsi_get_notification( void *args )
{
struct nsi_get_notification_params *params = (struct nsi_get_notification_params *)args;
NTSTATUS status;
if (!queued_notification_count && (status = poll_netlink())) return status;
assert( queued_notification_count );
params->module = *queued_notifications[0].module;
params->table = queued_notifications[0].table;
--queued_notification_count;
memmove( queued_notifications, queued_notifications + 1, sizeof(*queued_notifications) * queued_notification_count );
return STATUS_SUCCESS;
}
#else
static NTSTATUS unix_nsi_get_notification( void *args )
{
return STATUS_NOT_IMPLEMENTED;
}
#endif
const unixlib_entry_t __wine_unix_call_funcs[] =
{
icmp_cancel_listen,
......@@ -153,5 +272,6 @@ 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
unix_nsi_get_parameter_ex,
unix_nsi_get_notification,
};
......@@ -84,3 +84,10 @@ struct icmp_echo_reply_64
ULONGLONG options_ptr;
} opts;
};
struct nsi_get_notification_params
{
/* output parameters */
NPI_MODULEID module;
UINT32 table;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment