3445 lines
103 KiB
C
3445 lines
103 KiB
C
/*++
|
|
|
|
Copyright (c) 1997-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pnp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the PnP and PM routines
|
|
|
|
Author:
|
|
|
|
Vadim Eydelman (vadime) Apr-1997
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "afdp.h"
|
|
|
|
NTSTATUS
|
|
AfdPassQueryDeviceRelation (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartQueryDeviceRelation (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, AfdPnpPower )
|
|
#pragma alloc_text( PAGE, AfdPassQueryDeviceRelation )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartQueryDeviceRelation )
|
|
#endif
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdPnpPower (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for PNP_POWER irp
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to I/O request packet.
|
|
|
|
IrpSp - pointer to the stack location to use for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE( );
|
|
|
|
|
|
switch (IrpSp->MinorFunction) {
|
|
|
|
//
|
|
// We only support target device relation query
|
|
//
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
if (IrpSp->Parameters.QueryDeviceRelations.Type==TargetDeviceRelation) {
|
|
|
|
NTSTATUS status;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
//
|
|
// Set up local variables.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
//
|
|
// Dispatch to correct TDI object of underlying transport
|
|
// driver depedning on endpoint type
|
|
//
|
|
|
|
switch (endpoint->Type) {
|
|
case AfdBlockTypeVcConnecting:
|
|
case AfdBlockTypeVcBoth:
|
|
connection = AfdGetConnectionReferenceFromEndpoint (endpoint);
|
|
if (connection!=NULL) {
|
|
status = AfdPassQueryDeviceRelation (connection->FileObject,
|
|
Irp, IrpSp);
|
|
DEREFERENCE_CONNECTION (connection);
|
|
return status;
|
|
}
|
|
// fall through to try address handle if we have one.
|
|
case AfdBlockTypeVcListening:
|
|
case AfdBlockTypeDatagram:
|
|
if (endpoint->State==AfdEndpointStateBound ||
|
|
endpoint->State==AfdEndpointStateConnected) {
|
|
return AfdPassQueryDeviceRelation (endpoint->AddressFileObject,
|
|
Irp, IrpSp);
|
|
}
|
|
// fall through to fail
|
|
case AfdBlockTypeHelper:
|
|
case AfdBlockTypeEndpoint:
|
|
case AfdBlockTypeSanHelper:
|
|
case AfdBlockTypeSanEndpoint:
|
|
break;
|
|
default:
|
|
ASSERT (!"Unknown endpoint type!");
|
|
__assume (0);
|
|
break;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest( Irp, AfdPriorityBoost );
|
|
|
|
//
|
|
// We do not support the rest
|
|
//
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdPassQueryDeviceRelation (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for PNP_POWER irp
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to I/O request packet.
|
|
|
|
IrpSp - pointer to the stack location to use for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION nextIrpSp;
|
|
|
|
PAGED_CODE ();
|
|
|
|
nextIrpSp = IoGetNextIrpStackLocation( Irp );
|
|
|
|
*nextIrpSp = *IrpSp;
|
|
|
|
//
|
|
// Reference file object so it does not go away until this
|
|
// IRP completes
|
|
//
|
|
|
|
ObReferenceObject (FileObject);
|
|
nextIrpSp->FileObject = FileObject;
|
|
|
|
|
|
IoSetCompletionRoutine(
|
|
Irp,
|
|
AfdRestartQueryDeviceRelation,
|
|
FileObject,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
|
|
return IoCallDriver( IoGetRelatedDeviceObject( FileObject ), Irp );
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdRestartQueryDeviceRelation (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PFILE_OBJECT fileObject = Context;
|
|
|
|
//
|
|
// Dereference file object that we referenced when calling the
|
|
// lower driver
|
|
//
|
|
|
|
ObDereferenceObject (fileObject);
|
|
|
|
//
|
|
// Tell IO system to continue with IRP completion
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
#include <tdiinfo.h>
|
|
#include <ntddip.h>
|
|
#include <ntddip6.h>
|
|
#include <ntddtcp.h>
|
|
#include <ipinfo.h>
|
|
|
|
typedef struct _AFD_PROTOCOL {
|
|
USHORT AddressType;
|
|
USHORT AddressLength;
|
|
LPWSTR NetworkLayerDeviceName;
|
|
LPWSTR TransportLayerDeviceName;
|
|
ULONG RtChangeIoctl;
|
|
ULONG RtChangeDataSize;
|
|
LONG RoutingQueryRefCount;
|
|
HANDLE DeviceHandle;
|
|
PFILE_OBJECT FileObject;
|
|
} AFD_PROTOCOL, *PAFD_PROTOCOL;
|
|
|
|
|
|
NTSTATUS
|
|
AfdOpenDevice (
|
|
LPWSTR DeviceNameStr,
|
|
HANDLE *Handle,
|
|
PFILE_OBJECT *FileObject
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
AfdGetRoutingQueryReference (
|
|
IN PAFD_PROTOCOL Protocol
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTcpQueueRoutingChangeRequest (
|
|
IN PAFD_ENDPOINT Endpoint,
|
|
IN PIRP Irp,
|
|
IN BOOLEAN Overlapped
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTcpRestartRoutingChange (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTcpSignalRoutingChange (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTcpRoutingQuery (
|
|
PTA_ADDRESS Dest,
|
|
PTA_ADDRESS Intf
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTcp6RoutingQuery (
|
|
PTA_ADDRESS Dest,
|
|
PTA_ADDRESS Intf
|
|
);
|
|
|
|
VOID
|
|
AfdDereferenceRoutingQueryEx (
|
|
PAFD_PROTOCOL Protocol
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, AfdOpenDevice )
|
|
#pragma alloc_text( PAGE, AfdRoutingInterfaceQuery )
|
|
#pragma alloc_text( PAGE, AfdTcpRoutingQuery )
|
|
#pragma alloc_text( PAGE, AfdTcp6RoutingQuery )
|
|
#pragma alloc_text( PAGE, AfdGetRoutingQueryReference )
|
|
#pragma alloc_text( PAGE, AfdDereferenceRoutingQuery )
|
|
#pragma alloc_text( PAGE, AfdDereferenceRoutingQueryEx )
|
|
#pragma alloc_text( PAGEAFD, AfdTcpQueueRoutingChangeRequest )
|
|
#pragma alloc_text( PAGEAFD, AfdTcpRestartRoutingChange )
|
|
#pragma alloc_text( PAGEAFD, AfdTcpSignalRoutingChange )
|
|
#endif
|
|
|
|
AFD_PROTOCOL Ip = { TDI_ADDRESS_TYPE_IP, TDI_ADDRESS_LENGTH_IP,
|
|
DD_IP_DEVICE_NAME, DD_TCP_DEVICE_NAME,
|
|
IOCTL_IP_RTCHANGE_NOTIFY_REQUEST,
|
|
sizeof(IPNotifyData), 0, NULL, NULL };
|
|
AFD_PROTOCOL Ip6= { TDI_ADDRESS_TYPE_IP6, TDI_ADDRESS_LENGTH_IP6,
|
|
DD_IPV6_DEVICE_NAME, DD_TCPV6_DEVICE_NAME,
|
|
IOCTL_IPV6_RTCHANGE_NOTIFY_REQUEST,
|
|
sizeof(IPV6_RTCHANGE_NOTIFY_REQUEST), 0, NULL, NULL };
|
|
|
|
const char ZeroString[16] = { 0 };
|
|
|
|
#define TaAddressesEqual(a1,a2) \
|
|
(RtlEqualMemory (a1,a2,FIELD_OFFSET (TA_ADDRESS,Address[(a1)->AddressLength])))
|
|
|
|
|
|
NTSTATUS
|
|
AfdRoutingInterfaceQuery (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONG IoctlCode,
|
|
IN KPROCESSOR_MODE RequestorMode,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength,
|
|
IN PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength,
|
|
OUT PUINT_PTR Information
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes routing query request. Protocol independent portion.
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to I/O request packet.
|
|
|
|
IrpSp - pointer to the stack location to use for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
{
|
|
CHAR addrBuffer[AFD_MAX_FAST_TRANSPORT_ADDRESS];
|
|
PTRANSPORT_ADDRESS tempAddr;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initialize locals for proper cleanup.
|
|
//
|
|
|
|
*Information = 0;
|
|
tempAddr = (PTRANSPORT_ADDRESS)addrBuffer;
|
|
|
|
//
|
|
// Validate input parameters
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(*tempAddr) ) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdRoutingInterfaceQuery: Endp: %p - invalid input buffer (%p-%d).\n",
|
|
FileObject->FsContext, InputBuffer, InputBufferLength));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto Complete;
|
|
}
|
|
|
|
|
|
try {
|
|
//
|
|
// Copy input address into the local (or allocated from pool) buffer
|
|
//
|
|
if (InputBufferLength>sizeof (addrBuffer)) {
|
|
tempAddr = AFD_ALLOCATE_POOL_WITH_QUOTA (PagedPool,
|
|
InputBufferLength,
|
|
AFD_ROUTING_QUERY_POOL_TAG);
|
|
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE
|
|
}
|
|
|
|
//
|
|
// Validate user mode pointers
|
|
//
|
|
if (RequestorMode!=KernelMode) {
|
|
ProbeForRead (InputBuffer,
|
|
InputBufferLength,
|
|
sizeof (CHAR));
|
|
}
|
|
RtlCopyMemory (tempAddr,
|
|
InputBuffer,
|
|
InputBufferLength);
|
|
|
|
//
|
|
// Validate the internal consistency of the transport address AFTER
|
|
// copying it into the internal buffer to prevent malicious app from
|
|
// changing it on the fly while we are checking.
|
|
//
|
|
if (tempAddr->TAAddressCount!=1 ||
|
|
InputBufferLength <
|
|
(ULONG)FIELD_OFFSET (TRANSPORT_ADDRESS,
|
|
Address[0].Address[tempAddr->Address[0].AddressLength])) {
|
|
ExRaiseStatus (STATUS_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode ();
|
|
goto Complete;
|
|
}
|
|
|
|
//
|
|
// PROBLEM. We only support IP for now
|
|
//
|
|
|
|
switch (tempAddr->Address[0].AddressType) {
|
|
case TDI_ADDRESS_TYPE_IP:
|
|
status = AfdTcpRoutingQuery (&tempAddr->Address[0], &tempAddr->Address[0]);
|
|
break;
|
|
case TDI_ADDRESS_TYPE_IP6:
|
|
status = AfdTcp6RoutingQuery (&tempAddr->Address[0], &tempAddr->Address[0]);
|
|
break;
|
|
default:
|
|
status = STATUS_NOT_SUPPORTED;
|
|
goto Complete;
|
|
}
|
|
|
|
|
|
//
|
|
// Convert output to socket address if we succeeded.
|
|
//
|
|
if (NT_SUCCESS (status)) {
|
|
//
|
|
// Conversion to sockaddr requires extra bytes for address family
|
|
// in addition to TDI_ADDRESS
|
|
//
|
|
if ((tempAddr->Address[0].AddressLength+sizeof(u_short)
|
|
<= OutputBufferLength)) {
|
|
try {
|
|
//
|
|
// Validate user mode pointers
|
|
//
|
|
if (RequestorMode!=KernelMode) {
|
|
ProbeForWrite (OutputBuffer,
|
|
OutputBufferLength,
|
|
sizeof (CHAR));
|
|
}
|
|
//
|
|
// Copy the data from the type on which corresponds
|
|
// to the socket address.
|
|
//
|
|
RtlCopyMemory (
|
|
OutputBuffer,
|
|
&tempAddr->Address[0].AddressType,
|
|
tempAddr->Address[0].AddressLength+sizeof(u_short));
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode ();
|
|
goto Complete;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Output buffer is not big enough, return warning
|
|
// and the required buffer size.
|
|
//
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
*Information = tempAddr->Address[0].AddressLength+sizeof(u_short);
|
|
}
|
|
|
|
|
|
Complete:
|
|
|
|
//
|
|
// Free address buffer if we allocated one.
|
|
//
|
|
if (tempAddr!=(PTRANSPORT_ADDRESS)addrBuffer) {
|
|
AFD_FREE_POOL (tempAddr, AFD_ROUTING_QUERY_POOL_TAG);
|
|
}
|
|
|
|
return status;
|
|
} //AfdRoutingInterfaceQuery
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdRoutingInterfaceChange (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes routing change IRP
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to I/O request packet.
|
|
|
|
IrpSp - pointer to the stack location to use for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
{
|
|
PTRANSPORT_ADDRESS destAddr;
|
|
NTSTATUS status;
|
|
PAFD_ENDPOINT endpoint;
|
|
BOOLEAN overlapped;
|
|
AFD_TRANSPORT_IOCTL_INFO ioctlInfo;
|
|
|
|
PAGED_CODE ();
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRoutingInterfaceChange: Endp: %p, buf: %p, inlen: %ld, outlen: %ld.\n",
|
|
IrpSp->FileObject->FsContext,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength));
|
|
}
|
|
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
PAFD_TRANSPORT_IOCTL_INFO32 ioctlInfo32;
|
|
ioctlInfo32 = Irp->AssociatedIrp.SystemBuffer;
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (*ioctlInfo32)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
ioctlInfo.Handle = ioctlInfo32->Handle;
|
|
ioctlInfo.InputBuffer = ioctlInfo32->InputBuffer;
|
|
ioctlInfo.InputBufferLength = ioctlInfo32->InputBufferLength;
|
|
ioctlInfo.IoControlCode = ioctlInfo32->IoControlCode;
|
|
ioctlInfo.AfdFlags = ioctlInfo32->AfdFlags;
|
|
ioctlInfo.PollEvent = ioctlInfo32->PollEvent;
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (ioctlInfo)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
ioctlInfo = *((PAFD_TRANSPORT_IOCTL_INFO)Irp->AssociatedIrp.SystemBuffer);
|
|
}
|
|
|
|
//
|
|
// Setup locals
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
//
|
|
// Check if request is overlapped
|
|
//
|
|
|
|
overlapped = ((ioctlInfo.AfdFlags & AFD_OVERLAPPED)!=0);
|
|
|
|
//
|
|
// Validate input parameters
|
|
//
|
|
try {
|
|
ULONG sysBufferLength;
|
|
sysBufferLength = max (
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
|
|
|
|
if (Irp->RequestorMode != KernelMode) {
|
|
ProbeForRead(
|
|
ioctlInfo.InputBuffer,
|
|
ioctlInfo.InputBufferLength,
|
|
sizeof(UCHAR)
|
|
);
|
|
}
|
|
|
|
if (ioctlInfo.InputBufferLength>sysBufferLength){
|
|
PVOID newSystemBuffer;
|
|
//
|
|
// Don't use AFD tags on this as we are substituting
|
|
// system buffer
|
|
//
|
|
newSystemBuffer = ExAllocatePoolWithQuota (
|
|
NonPagedPoolCacheAligned,
|
|
ioctlInfo.InputBufferLength
|
|
);
|
|
if (newSystemBuffer==NULL) {
|
|
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
ExFreePool (Irp->AssociatedIrp.SystemBuffer);
|
|
Irp->AssociatedIrp.SystemBuffer = newSystemBuffer;
|
|
}
|
|
|
|
//
|
|
// Copy application data to the system buffer
|
|
//
|
|
|
|
RtlCopyMemory (Irp->AssociatedIrp.SystemBuffer,
|
|
ioctlInfo.InputBuffer,
|
|
ioctlInfo.InputBufferLength);
|
|
|
|
}
|
|
except( AFD_EXCEPTION_FILTER(&status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
destAddr = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if(ioctlInfo.InputBufferLength <
|
|
sizeof(*destAddr) ||
|
|
ioctlInfo.InputBufferLength <
|
|
(ULONG)FIELD_OFFSET (TRANSPORT_ADDRESS,
|
|
Address[0].Address[destAddr->Address[0].AddressLength])
|
|
) {
|
|
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdRoutingInterfaceChange: Endp: %p - invalid parameter.\n",
|
|
IrpSp->FileObject->FsContext));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
|
|
}
|
|
|
|
//
|
|
// PROBLEM We only support IP for now
|
|
//
|
|
|
|
if (destAddr->Address[0].AddressType!=TDI_ADDRESS_TYPE_IP &&
|
|
destAddr->Address[0].AddressType!=TDI_ADDRESS_TYPE_IP6) {
|
|
status = STATUS_NOT_SUPPORTED;
|
|
goto complete;
|
|
}
|
|
|
|
|
|
//
|
|
// Reset the poll bit
|
|
//
|
|
|
|
endpoint->EventsActive &= ~AFD_POLL_ROUTING_IF_CHANGE;
|
|
|
|
return AfdTcpQueueRoutingChangeRequest (endpoint, Irp, overlapped);
|
|
|
|
complete:
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest (Irp, AfdPriorityBoost);
|
|
|
|
return status;
|
|
|
|
} // AfdRoutingInterfaceChange
|
|
|
|
|
|
NTSTATUS
|
|
AfdOpenDevice (
|
|
LPWSTR DeviceNameStr,
|
|
HANDLE *Handle,
|
|
PFILE_OBJECT *FileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens specified device driver (control channel) and returns handle and
|
|
file object
|
|
|
|
Arguments:
|
|
|
|
DeviceNameStr - device to open.
|
|
|
|
Handle - returned handle.
|
|
|
|
FileObject - returned file object.
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the device was opened OK
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING DeviceName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
PAGED_CODE( );
|
|
|
|
|
|
RtlInitUnicodeString(&DeviceName, DeviceNameStr);
|
|
|
|
//
|
|
// We ask to create a kernel handle which is
|
|
// the handle in the context of the system process
|
|
// so that application cannot close it on us while
|
|
// we are creating and referencing it.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&DeviceName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // attributes
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
status = IoCreateFile(
|
|
Handle,
|
|
MAXIMUM_ALLOWED,
|
|
&objectAttributes,
|
|
&iosb, // returned status information.
|
|
0, // block size (unused).
|
|
0, // file attributes.
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_CREATE, // create disposition.
|
|
0, // create options.
|
|
NULL, // eaInfo
|
|
0, // eaLength
|
|
CreateFileTypeNone, // CreateFileType
|
|
NULL, // ExtraCreateParameters
|
|
IO_NO_PARAMETER_CHECKING // Options
|
|
| IO_FORCE_ACCESS_CHECK
|
|
);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
status = ObReferenceObjectByHandle (
|
|
*Handle,
|
|
0L,
|
|
*IoFileObjectType,
|
|
KernelMode,
|
|
(PVOID *)FileObject,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS (status)) {
|
|
ZwClose (*Handle);
|
|
*Handle = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdOpenDevice: Opened %ls, handle: %p, file: %p, status: %lx.\n",
|
|
DeviceNameStr, *Handle, *FileObject, status));
|
|
}
|
|
return status;
|
|
} //AfdOpenDevice
|
|
|
|
|
|
VOID
|
|
AfdDereferenceRoutingQueryEx (
|
|
PAFD_PROTOCOL Protocol
|
|
)
|
|
{
|
|
|
|
//
|
|
// Make sure the thread in which we execute cannot get
|
|
// suspeneded in APC while we own the global resource.
|
|
//
|
|
KeEnterCriticalRegion ();
|
|
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
|
|
ASSERT (Protocol->RoutingQueryRefCount>0);
|
|
ASSERT (Protocol->DeviceHandle!=NULL);
|
|
if (InterlockedDecrement (&Protocol->RoutingQueryRefCount)==0) {
|
|
HANDLE DeviceHandle = Protocol->DeviceHandle;
|
|
PFILE_OBJECT FileObject = Protocol->FileObject;
|
|
|
|
Protocol->DeviceHandle = NULL;
|
|
Protocol->FileObject = NULL;
|
|
|
|
ExReleaseResourceLite( AfdResource );
|
|
KeLeaveCriticalRegion ();
|
|
|
|
ObDereferenceObject (FileObject);
|
|
|
|
//
|
|
// Do this in the context of system process so that it does not
|
|
// get closed when applications exit
|
|
//
|
|
ZwClose (DeviceHandle);
|
|
}
|
|
else {
|
|
ExReleaseResourceLite( AfdResource );
|
|
KeLeaveCriticalRegion ();
|
|
}
|
|
}
|
|
|
|
PAFD_PROTOCOL
|
|
AfdGetProtocolInfo(
|
|
IN USHORT AddressType
|
|
)
|
|
{
|
|
switch (AddressType) {
|
|
case TDI_ADDRESS_TYPE_IP: return &Ip;
|
|
case TDI_ADDRESS_TYPE_IP6: return &Ip6;
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AfdDereferenceRoutingQuery (
|
|
USHORT AddressType
|
|
)
|
|
{
|
|
PAFD_PROTOCOL Protocol;
|
|
|
|
Protocol = AfdGetProtocolInfo(AddressType);
|
|
ASSERT (Protocol!=NULL);
|
|
AfdDereferenceRoutingQueryEx(Protocol);
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdTcp6RoutingQuery (
|
|
PTA_ADDRESS Dest,
|
|
PTA_ADDRESS Intf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits routing query request to TCP6
|
|
|
|
Arguments:
|
|
|
|
Dest - destination to query
|
|
|
|
Intf - interface through which destination can be reached.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether operation succeded
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
TDIObjectID *lpObject;
|
|
CHAR byBuffer[FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX,
|
|
Context) + sizeof(TDI_ADDRESS_IP6)];
|
|
TCP_REQUEST_QUERY_INFORMATION_EX *ptrqiBuffer = (TCP_REQUEST_QUERY_INFORMATION_EX *) byBuffer;
|
|
IP6RouteEntry routeEntry;
|
|
IO_STATUS_BLOCK iosb;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
HANDLE tcpDeviceHandle;
|
|
PFILE_OBJECT tcpFileObject;
|
|
PDEVICE_OBJECT tcpDeviceObject;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initialize for cleanup.
|
|
//
|
|
tcpDeviceHandle = NULL;
|
|
|
|
if (Dest->AddressLength<TDI_ADDRESS_LENGTH_IP6) {
|
|
KdPrintEx ((DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdTcp6RoutingQuery: Destination address buffer too small.\n"));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Open TCP6 driver.
|
|
//
|
|
|
|
status = AfdOpenDevice (DD_TCPV6_DEVICE_NAME, &tcpDeviceHandle, &tcpFileObject);
|
|
if (!NT_SUCCESS (status)) {
|
|
goto complete;
|
|
}
|
|
tcpDeviceObject = IoGetRelatedDeviceObject ( tcpFileObject );
|
|
|
|
|
|
//
|
|
// Setup the query
|
|
//
|
|
|
|
RtlCopyMemory( (PVOID)ptrqiBuffer->Context, Dest->Address,
|
|
TDI_ADDRESS_LENGTH_IP6);
|
|
|
|
lpObject = &ptrqiBuffer->ID;
|
|
lpObject->toi_id = IP6_GET_BEST_ROUTE_ID;
|
|
lpObject->toi_class = INFO_CLASS_PROTOCOL;
|
|
lpObject->toi_type = INFO_TYPE_PROVIDER;
|
|
lpObject->toi_entity.tei_entity = CL_NL_ENTITY;
|
|
lpObject->toi_entity.tei_instance = 0;
|
|
|
|
|
|
KeInitializeEvent (&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Build and setup the IRP and call the driver
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest (
|
|
IOCTL_TCP_QUERY_INFORMATION_EX, //Control
|
|
tcpDeviceObject, // Device
|
|
ptrqiBuffer, // Input buffer
|
|
sizeof(byBuffer), // Input buffer size
|
|
&routeEntry, // Output buffer
|
|
sizeof(routeEntry), // Output buffer size
|
|
FALSE, // Internal ?
|
|
&event, // Event
|
|
&iosb // Status block
|
|
);
|
|
if (irp==NULL) {
|
|
IF_DEBUG(ROUTING_QUERY) {
|
|
KdPrintEx ((DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdTcp6RoutingQuery: Could not allocate IRP.\n"));
|
|
}
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
irpSp = IoGetNextIrpStackLocation (irp);
|
|
irpSp->FileObject = tcpFileObject;
|
|
|
|
status = IoCallDriver (tcpDeviceObject, irp);
|
|
|
|
if (status==STATUS_PENDING) {
|
|
status = KeWaitForSingleObject(
|
|
&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // Alertable
|
|
NULL); // Timeout
|
|
}
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx ((DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdTcp6RoutingQuery: IP6_GET_BEST_ROUTE_ID - status: %lx.\n",
|
|
status));
|
|
}
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
goto complete;
|
|
}
|
|
|
|
if (!NT_SUCCESS (iosb.Status)) {
|
|
status = iosb.Status;
|
|
goto complete;
|
|
}
|
|
|
|
// Fill in IPv6 address
|
|
Intf->AddressType = TDI_ADDRESS_TYPE_IP6;
|
|
Intf->AddressLength = TDI_ADDRESS_LENGTH_IP6;
|
|
RtlCopyMemory( ((PTDI_ADDRESS_IP6)Intf->Address)->sin6_addr,
|
|
&routeEntry.ire_Source,
|
|
sizeof(routeEntry.ire_Source) );
|
|
((PTDI_ADDRESS_IP6)Intf->Address)->sin6_flowinfo = 0;
|
|
((PTDI_ADDRESS_IP6)Intf->Address)->sin6_port = 0;
|
|
((PTDI_ADDRESS_IP6)Intf->Address)->sin6_scope_id = routeEntry.ire_ScopeId;
|
|
status = STATUS_SUCCESS;
|
|
|
|
complete:
|
|
if (tcpDeviceHandle!=NULL) {
|
|
ObDereferenceObject (tcpFileObject);
|
|
ZwClose (tcpDeviceHandle);
|
|
tcpDeviceHandle = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#define AFD_IP_ADDRESS_QUERY_SIZE PAGE_SIZE
|
|
NTSTATUS
|
|
AfdTcpRoutingQuery (
|
|
PTA_ADDRESS Dest,
|
|
PTA_ADDRESS Intf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits routing query request to TCP
|
|
|
|
Arguments:
|
|
|
|
Dest - destination to query
|
|
|
|
Intf - interface through which destination can be reached.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether operation succeded
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
TDIObjectID *lpObject;
|
|
IPRouteLookupData *pRtLookupData;
|
|
TCP_REQUEST_QUERY_INFORMATION_EX trqiBuffer;
|
|
IPRouteEntry routeEntry;
|
|
IPAddrEntry *pAddrEntry;
|
|
USHORT *pNTEContext;
|
|
PVOID addrEntryBuf;
|
|
IO_STATUS_BLOCK iosb;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
HANDLE tcpDeviceHandle;
|
|
PFILE_OBJECT tcpFileObject;
|
|
PDEVICE_OBJECT tcpDeviceObject;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initialize for cleanup.
|
|
//
|
|
tcpDeviceHandle = NULL;
|
|
|
|
if (Dest->AddressLength<TDI_ADDRESS_LENGTH_IP) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdTcpRoutingQuery: Destination address buffer too small.\n"));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Open TCP driver.
|
|
//
|
|
|
|
status = AfdOpenDevice (DD_TCP_DEVICE_NAME, &tcpDeviceHandle, &tcpFileObject);
|
|
if (!NT_SUCCESS (status)) {
|
|
goto complete;
|
|
}
|
|
tcpDeviceObject = IoGetRelatedDeviceObject ( tcpFileObject );
|
|
|
|
|
|
//
|
|
// Setup the query
|
|
//
|
|
|
|
RtlZeroMemory (&trqiBuffer, sizeof (trqiBuffer));
|
|
|
|
pRtLookupData = (IPRouteLookupData *)trqiBuffer.Context;
|
|
pRtLookupData->DestAdd = ((PTDI_ADDRESS_IP)Dest->Address)->in_addr;
|
|
pRtLookupData->SrcAdd = 0;
|
|
|
|
lpObject = &trqiBuffer.ID;
|
|
lpObject->toi_id = IP_MIB_SINGLE_RT_ENTRY_ID;
|
|
lpObject->toi_class = INFO_CLASS_PROTOCOL;
|
|
lpObject->toi_type = INFO_TYPE_PROVIDER;
|
|
lpObject->toi_entity.tei_entity = CL_NL_ENTITY;
|
|
lpObject->toi_entity.tei_instance = 0;
|
|
|
|
|
|
KeInitializeEvent (&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Build and setup the IRP and call the driver
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest (
|
|
IOCTL_TCP_QUERY_INFORMATION_EX, //Control
|
|
tcpDeviceObject, // Device
|
|
&trqiBuffer, // Input buffer
|
|
sizeof(trqiBuffer), // Input buffer size
|
|
&routeEntry, // Output buffer
|
|
sizeof(routeEntry), // Output buffer size
|
|
FALSE, // Internal ?
|
|
&event, // Event
|
|
&iosb // Status block
|
|
);
|
|
if (irp==NULL) {
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: Could not allocate IRP.\n"));
|
|
}
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
irpSp = IoGetNextIrpStackLocation (irp);
|
|
irpSp->FileObject = tcpFileObject;
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: Quering for route to %lx.\n",
|
|
((PTDI_ADDRESS_IP)Dest->Address)->in_addr));
|
|
}
|
|
|
|
status = IoCallDriver (tcpDeviceObject, irp);
|
|
|
|
if (status==STATUS_PENDING) {
|
|
status = KeWaitForSingleObject(
|
|
&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // Alertable
|
|
NULL); // Timeout
|
|
}
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: IP_MIB_SINGLE_RT_ENTRY_ID - status: %lx.\n",
|
|
status));
|
|
}
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
goto complete;
|
|
}
|
|
|
|
if (!NT_SUCCESS (iosb.Status)) {
|
|
status = iosb.Status;
|
|
goto complete;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Allocate buffer for address table
|
|
//
|
|
|
|
try {
|
|
addrEntryBuf = AFD_ALLOCATE_POOL_WITH_QUOTA (NonPagedPool,
|
|
AFD_IP_ADDRESS_QUERY_SIZE, AFD_ROUTING_QUERY_POOL_TAG);
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode ();
|
|
addrEntryBuf = NULL;
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: Could not allocate address buffer.\n"));
|
|
}
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Setup the query
|
|
//
|
|
|
|
RtlZeroMemory (&trqiBuffer, sizeof (trqiBuffer));
|
|
|
|
//
|
|
// Initialize the context to 0, TCP should update it
|
|
// if we don't have enough space to get all the interfaces.
|
|
//
|
|
pNTEContext = (USHORT *)trqiBuffer.Context;
|
|
*pNTEContext = 0;
|
|
|
|
lpObject->toi_id = IP_MIB_ADDRTABLE_ENTRY_ID;
|
|
lpObject->toi_class = INFO_CLASS_PROTOCOL;
|
|
lpObject->toi_type = INFO_TYPE_PROVIDER;
|
|
lpObject->toi_entity.tei_entity = CL_NL_ENTITY;
|
|
lpObject->toi_entity.tei_instance = 0;
|
|
|
|
//
|
|
// Keep quering until we find an entry or TCP returns
|
|
// STATUS_BUFFER_OVERFLOW.
|
|
//
|
|
|
|
do {
|
|
KeInitializeEvent (&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Build, setup and submit the request.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest (
|
|
IOCTL_TCP_QUERY_INFORMATION_EX, //Control
|
|
tcpDeviceObject, // Device
|
|
&trqiBuffer, // Input buffer
|
|
sizeof(trqiBuffer), // Input buffer size
|
|
addrEntryBuf, // Output buffer
|
|
AFD_IP_ADDRESS_QUERY_SIZE, // Output buffer size
|
|
FALSE, // Internal ?
|
|
&event, // Event
|
|
&iosb // Status block
|
|
);
|
|
if (irp==NULL) {
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: Could not allocate IRP.\n"));
|
|
}
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete_free_pool;
|
|
}
|
|
|
|
irpSp = IoGetNextIrpStackLocation (irp);
|
|
irpSp->FileObject = tcpFileObject;
|
|
|
|
status = IoCallDriver (tcpDeviceObject, irp);
|
|
|
|
if (status==STATUS_PENDING) {
|
|
status = KeWaitForSingleObject(
|
|
&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // Alertable
|
|
NULL); // Timeout
|
|
}
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: IP_MIB_ADDRTABLE_ENTRY_ID - status: %lx.\n",
|
|
status));
|
|
}
|
|
|
|
if (!NT_SUCCESS (status) && status!=STATUS_BUFFER_OVERFLOW) {
|
|
goto complete_free_pool;
|
|
}
|
|
|
|
status = iosb.Status;
|
|
if (!NT_SUCCESS (status) && status!=STATUS_BUFFER_OVERFLOW) {
|
|
goto complete_free_pool;
|
|
}
|
|
|
|
|
|
//
|
|
// Look for matching interface index in the address table
|
|
//
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: Looking for interface %ld.\n",
|
|
routeEntry.ire_index));
|
|
}
|
|
|
|
for (pAddrEntry = addrEntryBuf;
|
|
pAddrEntry<(IPAddrEntry *)((PUCHAR)addrEntryBuf+iosb.Information);
|
|
pAddrEntry++) {
|
|
if (pAddrEntry->iae_index==routeEntry.ire_index) {
|
|
Intf->AddressType = TDI_ADDRESS_TYPE_IP;
|
|
Intf->AddressLength = TDI_ADDRESS_LENGTH_IP;
|
|
((PTDI_ADDRESS_IP)Intf->Address)->in_addr = pAddrEntry->iae_addr;
|
|
((PTDI_ADDRESS_IP)Intf->Address)->sin_port = 0;
|
|
RtlFillMemory (((PTDI_ADDRESS_IP)Intf->Address)->sin_zero,
|
|
sizeof (((PTDI_ADDRESS_IP)Intf->Address)->sin_zero), 0);
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRoutingQuery: Found interface %lx.\n",
|
|
((PTDI_ADDRESS_IP)Intf->Address)->in_addr));
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
goto complete_free_pool;
|
|
}
|
|
}
|
|
}
|
|
while (status==STATUS_BUFFER_OVERFLOW);
|
|
|
|
//
|
|
// We should always be able to find a match or TCP lied to us
|
|
// when it returned the interface index
|
|
//
|
|
|
|
ASSERT (!"TCP must have failed routing query or return valid interface index!!!");
|
|
status = STATUS_HOST_UNREACHABLE;
|
|
|
|
complete_free_pool:
|
|
AFD_FREE_POOL (addrEntryBuf, AFD_ROUTING_QUERY_POOL_TAG);
|
|
|
|
complete:
|
|
if (tcpDeviceHandle!=NULL) {
|
|
ObDereferenceObject (tcpFileObject);
|
|
ZwClose (tcpDeviceHandle);
|
|
tcpDeviceHandle = NULL;
|
|
}
|
|
|
|
return status;
|
|
} // AfdTcpRoutingQuery
|
|
|
|
|
|
NTSTATUS
|
|
AfdGetRoutingQueryReference (
|
|
PAFD_PROTOCOL Protocol
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes routing query if necessary and references it
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether operation succeded
|
|
|
|
--*/
|
|
{
|
|
|
|
// KAPC_STATE apcState;
|
|
HANDLE DeviceHandle;
|
|
PFILE_OBJECT FileObject;
|
|
NTSTATUS status;
|
|
|
|
|
|
status = AfdOpenDevice (Protocol->NetworkLayerDeviceName, &DeviceHandle, &FileObject);
|
|
if (NT_SUCCESS (status)) {
|
|
|
|
//
|
|
// Make sure the thread in which we execute cannot get
|
|
// suspeneded in APC while we own the global resource.
|
|
//
|
|
KeEnterCriticalRegion ();
|
|
ExAcquireResourceExclusiveLite ( AfdResource, TRUE);
|
|
if (Protocol->DeviceHandle==NULL) {
|
|
Protocol->DeviceHandle = DeviceHandle;
|
|
Protocol->FileObject = FileObject;
|
|
ASSERT (Protocol->RoutingQueryRefCount==0);
|
|
Protocol->RoutingQueryRefCount = 1;
|
|
ExReleaseResourceLite( AfdResource );
|
|
KeLeaveCriticalRegion ();
|
|
}
|
|
else {
|
|
ASSERT (Protocol->RoutingQueryRefCount>0);
|
|
InterlockedIncrement (&Protocol->RoutingQueryRefCount);
|
|
|
|
ExReleaseResourceLite( AfdResource );
|
|
KeLeaveCriticalRegion ();
|
|
|
|
ObDereferenceObject (FileObject);
|
|
status = ZwClose (DeviceHandle);
|
|
ASSERT (status==STATUS_SUCCESS);
|
|
}
|
|
}
|
|
else {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdGetRoutingQueryReference: Network layer device open failed, status: %lx.\n",
|
|
status));
|
|
}
|
|
|
|
return status;
|
|
} // AfdGetRoutingQueryReference
|
|
|
|
NTSTATUS
|
|
AfdTcpQueueRoutingChangeRequest (
|
|
IN PAFD_ENDPOINT Endpoint,
|
|
IN PIRP Irp,
|
|
BOOLEAN Overlapped
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submits routing change request to TCP
|
|
|
|
Arguments:
|
|
|
|
Endpoint - endpoint on which request is issued
|
|
|
|
Irp - the request
|
|
|
|
Overlapped - whether request is overlapped (and thus should be
|
|
pended event on non-blocking socket)
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether operation succeded
|
|
|
|
--*/
|
|
{
|
|
PTRANSPORT_ADDRESS destAddr;
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIO_STACK_LOCATION irpSp;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
struct Notify {
|
|
ROUTING_NOTIFY Ctx;
|
|
char Data[0];
|
|
} * notify;
|
|
PIRP irp;
|
|
PIO_COMPLETION_ROUTINE compRoutine;
|
|
PAFD_PROTOCOL Protocol;
|
|
|
|
//
|
|
// Set locals for easy cleanup.
|
|
//
|
|
notify = NULL;
|
|
irp = NULL;
|
|
|
|
destAddr = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Protocol = AfdGetProtocolInfo(destAddr->Address[0].AddressType);
|
|
if (Protocol == NULL) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
if (destAddr->Address[0].AddressLength < Protocol->AddressLength) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdTcpQueueRoutingChangeRequest: Destination buffer too small.\n"));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Allocate context structures to keep IRP in the endpoint list in
|
|
// case the latter gets closed and we need to cancel the IRP.
|
|
// Also allocate buffer for passing data to IP
|
|
//
|
|
|
|
try {
|
|
notify = AFD_ALLOCATE_POOL_WITH_QUOTA (NonPagedPool,
|
|
FIELD_OFFSET(struct Notify, Data[Protocol->RtChangeDataSize]),
|
|
AFD_ROUTING_QUERY_POOL_TAG);
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode ();
|
|
notify = NULL;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Open IP driver if necessary
|
|
//
|
|
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
|
|
//
|
|
// Check if endpoint was cleaned-up and cancel the request.
|
|
//
|
|
if (Endpoint->EndpointCleanedUp) {
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
status = STATUS_CANCELLED;
|
|
goto complete;
|
|
}
|
|
|
|
if (Endpoint->RoutingQueryReferenced) {
|
|
ASSERT (Protocol->DeviceHandle != NULL);
|
|
ASSERT (Protocol->FileObject != NULL);
|
|
ASSERT (Protocol->RoutingQueryRefCount>0);
|
|
InterlockedIncrement (&Protocol->RoutingQueryRefCount);
|
|
}
|
|
else {
|
|
|
|
if (Protocol->DeviceHandle!=NULL) {
|
|
Endpoint->RoutingQueryReferenced = TRUE;
|
|
InterlockedIncrement (&Protocol->RoutingQueryRefCount);
|
|
if (Protocol->AddressType == TDI_ADDRESS_TYPE_IP6) {
|
|
Endpoint->RoutingQueryIPv6 = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
|
|
status = AfdGetRoutingQueryReference (Protocol);
|
|
if (!NT_SUCCESS (status)) {
|
|
goto complete;
|
|
}
|
|
ASSERT (Protocol->DeviceHandle!=NULL);
|
|
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
if (Endpoint->EndpointCleanedUp) {
|
|
//
|
|
// Endpoint was cleaned-up while we were
|
|
// referencing routing query. Release the reference.
|
|
//
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
AfdDereferenceRoutingQueryEx (Protocol);
|
|
status = STATUS_CANCELLED;
|
|
goto complete;
|
|
}
|
|
|
|
if (Endpoint->RoutingQueryReferenced) {
|
|
LONG result;
|
|
//
|
|
// Another thread referenced routing query for this endpoint.
|
|
// Since we know that that other's thread reference cannot
|
|
// go away while we are holding spinlock, we can simply
|
|
// decrement the reference count and be sure that it
|
|
// won't go all the way to 0.
|
|
//
|
|
result = InterlockedDecrement (&Protocol->RoutingQueryRefCount);
|
|
ASSERT (result>0);
|
|
}
|
|
else {
|
|
Endpoint->RoutingQueryReferenced = TRUE;
|
|
if (Protocol->AddressType == TDI_ADDRESS_TYPE_IP6) {
|
|
Endpoint->RoutingQueryIPv6 = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
fileObject = Protocol->FileObject;
|
|
deviceObject = IoGetRelatedDeviceObject ( fileObject );
|
|
|
|
if (Endpoint->NonBlocking && !Overlapped) {
|
|
|
|
//
|
|
// For non-blocking socket and non-overlapped requests
|
|
// we shall post the query using new IRP,
|
|
// so even if thread in which rhe request
|
|
// is originated by user exits, our request to IP does not get
|
|
// cancelled and we will still signal the event.
|
|
//
|
|
|
|
irp = IoAllocateIrp (deviceObject->StackSize, TRUE);
|
|
if (irp==NULL) {
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
|
|
//
|
|
// Save the endpoint reference in notify context.
|
|
//
|
|
REFERENCE_ENDPOINT (Endpoint);
|
|
notify->Ctx.NotifyContext = Endpoint;
|
|
|
|
//
|
|
// Setup completion routine so we can remove the IRP
|
|
// from the endpoint list and free it.
|
|
//
|
|
compRoutine = AfdTcpSignalRoutingChange;
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Blocking endpoint: just pass the original request on to the IP
|
|
//
|
|
irp = Irp;
|
|
|
|
//
|
|
// Save the original system buffer of the IRP, so we can restore
|
|
// it when TCP completes it
|
|
//
|
|
|
|
notify->Ctx.NotifyContext = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Setup completion routine so we can restore the IRP and remove it
|
|
// from the endpoint list
|
|
//
|
|
|
|
compRoutine = AfdTcpRestartRoutingChange;
|
|
|
|
}
|
|
|
|
//
|
|
// Insert notification into the endpoint list
|
|
//
|
|
|
|
InsertTailList (&Endpoint->RoutingNotifications,
|
|
¬ify->Ctx.NotifyListLink);
|
|
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
|
|
//
|
|
// Save pointer to IRP in notify structure
|
|
//
|
|
notify->Ctx.NotifyIrp = irp;
|
|
|
|
//
|
|
// Setup IP notification request
|
|
//
|
|
|
|
switch(Protocol->AddressType) {
|
|
case TDI_ADDRESS_TYPE_IP:
|
|
{
|
|
IPNotifyData *data = (IPNotifyData *)notify->Data;
|
|
data->Version = 0;
|
|
data->Add = ((PTDI_ADDRESS_IP)destAddr->Address[0].Address)->in_addr;
|
|
break;
|
|
}
|
|
case TDI_ADDRESS_TYPE_IP6:
|
|
{
|
|
IPV6_RTCHANGE_NOTIFY_REQUEST *data = (IPV6_RTCHANGE_NOTIFY_REQUEST *)notify->Data;
|
|
data->Flags = 0;
|
|
data->ScopeId = ((PTDI_ADDRESS_IP6)destAddr->Address[0].Address)->sin6_scope_id;
|
|
if (RtlEqualMemory(((PTDI_ADDRESS_IP6)destAddr->Address[0].Address)->sin6_addr, ZeroString, 16)) {
|
|
data->PrefixLength = 0;
|
|
}
|
|
else {
|
|
data->PrefixLength = 128;
|
|
}
|
|
RtlCopyMemory(
|
|
&data->Prefix,
|
|
((PTDI_ADDRESS_IP6)destAddr->Address[0].Address)->sin6_addr,
|
|
16);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup IRP stack location to forward IRP to IP
|
|
// Must be METHOD_BUFFERED or we are not setting it up correctly
|
|
//
|
|
|
|
ASSERT ( (Protocol->RtChangeIoctl & 0x03)==METHOD_BUFFERED );
|
|
irp->AssociatedIrp.SystemBuffer = notify->Data;
|
|
|
|
irpSp = IoGetNextIrpStackLocation (irp);
|
|
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
irpSp->MinorFunction = 0;
|
|
irpSp->Flags = 0;
|
|
irpSp->Control = 0;
|
|
irpSp->FileObject = fileObject;
|
|
irpSp->Parameters.DeviceIoControl.InputBufferLength = Protocol->RtChangeDataSize;
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode = Protocol->RtChangeIoctl;
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
IoSetCompletionRoutine( irp, compRoutine, notify, TRUE, TRUE, TRUE );
|
|
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpQueueRoutingChangeRequest: Passing Irp %p to IP\n",
|
|
irp));
|
|
}
|
|
|
|
if (irp==Irp) {
|
|
//
|
|
// Just pass the request through to the driver and return what it
|
|
// returns
|
|
//
|
|
return AfdIoCallDriver (Endpoint, deviceObject, irp);
|
|
}
|
|
|
|
IoCallDriver (deviceObject, irp);
|
|
|
|
status = STATUS_DEVICE_NOT_READY; // To be converted to WSAEWOULDBLOCK
|
|
|
|
|
|
|
|
//
|
|
// Error cases
|
|
//
|
|
|
|
complete:
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpQueueRoutingChangeRequest: completing with status: %lx\n",
|
|
status));
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest( Irp, AfdPriorityBoost );
|
|
|
|
return status;
|
|
} //AfdTcpQueueRoutingChangeRequest
|
|
|
|
NTSTATUS
|
|
AfdTcpRestartRoutingChange (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routing for routing change IRP forwarded to IP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - must be our device object
|
|
|
|
Irp - the request to be completed
|
|
|
|
Context - completion context
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates to the system what to do next with the IRP
|
|
|
|
--*/
|
|
{
|
|
PROUTING_NOTIFY notifyCtx = Context;
|
|
PAFD_ENDPOINT endpoint = IoGetCurrentIrpStackLocation (Irp)->FileObject->FsContext;
|
|
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpRestartRoutingChange: Irp: %p, status: %lx, info: %ld.\n",
|
|
Irp, Irp->IoStatus.Status, Irp->IoStatus.Information));
|
|
}
|
|
|
|
|
|
//
|
|
// Check if IRP is still on the endpoint's list and remove if it is
|
|
//
|
|
|
|
if (InterlockedExchangePointer ((PVOID *)¬ifyCtx->NotifyIrp, NULL)!=NULL) {
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
RemoveEntryList (¬ifyCtx->NotifyListLink);
|
|
AfdIndicateEventSelectEvent (endpoint,
|
|
AFD_POLL_ROUTING_IF_CHANGE,
|
|
Irp->IoStatus.Status);
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
//
|
|
// Indicate event as the endpoint is still active
|
|
//
|
|
|
|
AfdIndicatePollEvent (endpoint,
|
|
AFD_POLL_ROUTING_IF_CHANGE,
|
|
Irp->IoStatus.Status);
|
|
}
|
|
|
|
//
|
|
// If pending has be returned for this IRP then mark the current
|
|
// stack as pending.
|
|
//
|
|
|
|
if ( Irp->PendingReturned ) {
|
|
IoMarkIrpPending( Irp );
|
|
}
|
|
|
|
//
|
|
// Restore the IRP to its previous glory and free allocated context structure
|
|
//
|
|
|
|
Irp->AssociatedIrp.SystemBuffer = notifyCtx->NotifyContext;
|
|
AfdCompleteOutstandingIrp (endpoint, Irp);
|
|
|
|
AFD_FREE_POOL (notifyCtx, AFD_ROUTING_QUERY_POOL_TAG);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AfdTcpSignalRoutingChange (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routing for routing change IRP submitted to IP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - must be our device object
|
|
|
|
Irp - the request to be completed
|
|
|
|
Context - completion context
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates to the system what to do next with the IRP
|
|
|
|
--*/
|
|
{
|
|
PROUTING_NOTIFY notifyCtx = Context;
|
|
PAFD_ENDPOINT endpoint = notifyCtx->NotifyContext;
|
|
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
IF_DEBUG (ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTcpSignalRoutingChange: Irp: %p, status: %lx, info: %ld.\n",
|
|
Irp, Irp->IoStatus.Status, Irp->IoStatus.Information));
|
|
}
|
|
|
|
|
|
//
|
|
// Check if IRP is still on the endpoint's list and remove if it is
|
|
//
|
|
|
|
if (InterlockedExchangePointer ((PVOID *)¬ifyCtx->NotifyIrp, NULL)!=NULL) {
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
RemoveEntryList (¬ifyCtx->NotifyListLink);
|
|
AfdIndicateEventSelectEvent (endpoint,
|
|
AFD_POLL_ROUTING_IF_CHANGE,
|
|
Irp->IoStatus.Status);
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
|
|
//
|
|
// Indicate event as the endpoint is still active
|
|
//
|
|
|
|
AfdIndicatePollEvent (endpoint,
|
|
AFD_POLL_ROUTING_IF_CHANGE,
|
|
Irp->IoStatus.Status);
|
|
}
|
|
|
|
//
|
|
// Release previously acquired endpoint reference
|
|
//
|
|
|
|
DEREFERENCE_ENDPOINT (endpoint);
|
|
|
|
//
|
|
// Free allocated irp and context structure
|
|
//
|
|
|
|
IoFreeIrp (Irp);
|
|
AFD_FREE_POOL (notifyCtx, AFD_ROUTING_QUERY_POOL_TAG);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdCancelAddressListChange (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdCleanupAddressListChange (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PAFD_REQUEST_CONTEXT RequestCtx
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdInitializeAddressList (VOID);
|
|
|
|
VOID
|
|
AfdAddAddressHandler (
|
|
IN PTA_ADDRESS NetworkAddress,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PTDI_PNP_CONTEXT Context
|
|
);
|
|
|
|
VOID
|
|
AfdDelAddressHandler (
|
|
IN PTA_ADDRESS NetworkAddress,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PTDI_PNP_CONTEXT Context
|
|
);
|
|
|
|
VOID
|
|
AfdProcessAddressChangeList (
|
|
USHORT AddressType,
|
|
PUNICODE_STRING DeviceName
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdPnPPowerChange(
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PNET_PNP_EVENT PowerEvent,
|
|
IN PTDI_PNP_CONTEXT Context1,
|
|
IN PTDI_PNP_CONTEXT Context2
|
|
);
|
|
|
|
VOID
|
|
AfdReturnNicsPackets (
|
|
PVOID Pdo
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdHasHeldPacketsFromNic (
|
|
PAFD_CONNECTION Connection,
|
|
PVOID Pdo
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, AfdAddressListQuery )
|
|
#pragma alloc_text( PAGEAFD, AfdAddressListChange )
|
|
#pragma alloc_text( PAGEAFD, AfdCancelAddressListChange )
|
|
#pragma alloc_text( PAGE, AfdInitializeAddressList )
|
|
#pragma alloc_text( PAGE, AfdDeregisterPnPHandlers )
|
|
#pragma alloc_text( PAGE, AfdAddAddressHandler )
|
|
#pragma alloc_text( PAGE, AfdDelAddressHandler )
|
|
#pragma alloc_text( PAGEAFD, AfdProcessAddressChangeList )
|
|
#pragma alloc_text( PAGE, AfdPnPPowerChange )
|
|
#pragma alloc_text( PAGEAFD, AfdReturnNicsPackets )
|
|
#pragma alloc_text( PAGEAFD, AfdHasHeldPacketsFromNic )
|
|
#endif
|
|
|
|
//
|
|
// Cache the device being brought down as a result of
|
|
// removal or power down event, so we do not scan our endpoints
|
|
// unnecessarily when more than one transport propagates device down
|
|
// event for the same device to us.
|
|
//
|
|
PVOID AfdLastRemovedPdo = NULL;
|
|
ULONGLONG AfdLastRemoveTime = 0i64;
|
|
|
|
NTSTATUS
|
|
AfdAddressListQuery (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONG IoctlCode,
|
|
IN KPROCESSOR_MODE RequestorMode,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength,
|
|
IN PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength,
|
|
OUT PUINT_PTR Information
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes address list query IRP
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to I/O request packet.
|
|
|
|
IrpSp - pointer to the stack location to use for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PLIST_ENTRY listEntry;
|
|
PTRANSPORT_ADDRESS addressList;
|
|
PAFD_ENDPOINT endpoint;
|
|
PUCHAR addressBuf;
|
|
ULONG dataLen;
|
|
PAFD_ADDRESS_ENTRY addressEntry;
|
|
USHORT addressType;
|
|
|
|
PAGED_CODE ();
|
|
|
|
*Information = 0;
|
|
status = STATUS_SUCCESS;
|
|
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListQuery: Endp: %p, buf: %p, inlen: %ld, outlen: %ld.\n",
|
|
FileObject->FsContext,
|
|
OutputBuffer,
|
|
InputBufferLength,
|
|
OutputBufferLength));
|
|
}
|
|
|
|
//
|
|
// Validate input parameters
|
|
//
|
|
|
|
if( InputBufferLength < sizeof(USHORT) ||
|
|
OutputBufferLength < FIELD_OFFSET (TRANSPORT_ADDRESS, Address)
|
|
) {
|
|
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdAddressListQuery: Endp: %p - invalid parameter.\n",
|
|
FileObject->FsContext));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
endpoint = FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
try {
|
|
if (RequestorMode!=KernelMode) {
|
|
ProbeForRead (InputBuffer,
|
|
sizeof (addressType),
|
|
sizeof (USHORT));
|
|
ProbeForWrite (OutputBuffer,
|
|
OutputBufferLength,
|
|
sizeof (ULONG));
|
|
}
|
|
|
|
addressType = *((PUSHORT)InputBuffer);
|
|
|
|
addressList = OutputBuffer;
|
|
addressBuf = (PUCHAR)OutputBuffer;
|
|
dataLen = FIELD_OFFSET (TRANSPORT_ADDRESS, Address);
|
|
addressList->TAAddressCount = 0;
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (&status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Make sure the thread in which we execute cannot get
|
|
// suspeneded in APC while we own the global resource.
|
|
//
|
|
KeEnterCriticalRegion ();
|
|
ExAcquireResourceSharedLite( AfdResource, TRUE );
|
|
|
|
//
|
|
// Setup address handlers with TDI if not already done
|
|
//
|
|
if (AfdBindingHandle==NULL) {
|
|
ExReleaseResourceLite( AfdResource );
|
|
|
|
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
|
|
|
|
if (AfdBindingHandle==NULL) {
|
|
status = AfdInitializeAddressList ();
|
|
if (!NT_SUCCESS (status)) {
|
|
ExReleaseResourceLite (AfdResource);
|
|
KeLeaveCriticalRegion ();
|
|
return status;
|
|
}
|
|
}
|
|
else
|
|
status = STATUS_SUCCESS;
|
|
|
|
ASSERT (AfdBindingHandle!=NULL);
|
|
}
|
|
|
|
ExAcquireResourceSharedLite( AfdAddressListLock, TRUE );
|
|
ExReleaseResourceLite( AfdResource );
|
|
|
|
//
|
|
// Walk the address list and pick up the addresses of matching protocol
|
|
// family
|
|
//
|
|
|
|
listEntry = AfdAddressEntryList.Flink;
|
|
while (listEntry!=&AfdAddressEntryList) {
|
|
addressEntry = CONTAINING_RECORD (listEntry, AFD_ADDRESS_ENTRY, AddressListLink);
|
|
listEntry = listEntry->Flink;
|
|
|
|
//
|
|
// Found a match ?
|
|
//
|
|
|
|
if ((addressEntry->Address.AddressType==addressType)
|
|
//
|
|
// Special check for Netbios addresses because
|
|
// we have separate protocols for each lana/device
|
|
//
|
|
&& ((addressType!=TDI_ADDRESS_TYPE_NETBIOS)
|
|
|| endpoint->TransportInfo==NULL
|
|
|| RtlEqualUnicodeString (
|
|
&addressEntry->DeviceName,
|
|
&endpoint->TransportInfo->TransportDeviceName,
|
|
TRUE))) {
|
|
ULONG addressLength = FIELD_OFFSET (TA_ADDRESS,
|
|
Address[addressEntry->Address.AddressLength]);
|
|
try {
|
|
|
|
//
|
|
// Copy address to the output buffer if it is not full
|
|
//
|
|
|
|
if (status==STATUS_SUCCESS) {
|
|
if (dataLen+addressLength<OutputBufferLength) {
|
|
RtlCopyMemory (&addressBuf[dataLen],
|
|
&addressEntry->Address,
|
|
addressLength);
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListQuery: Adding address of type: %d, length: %d.\n",
|
|
addressEntry->Address.AddressType,
|
|
addressEntry->Address.AddressLength));
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// End of buffer reached. Set error code so we do not
|
|
// attempt to copy more data
|
|
//
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListQuery: Buffer is full.\n"));
|
|
}
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Count the addresses and total buffer length whether we copied
|
|
// them or not to the output buffer
|
|
//
|
|
|
|
addressList->TAAddressCount += 1;
|
|
dataLen += addressLength;
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (&status)) {
|
|
dataLen = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ExReleaseResourceLite (AfdAddressListLock);
|
|
KeLeaveCriticalRegion ();
|
|
|
|
//
|
|
// Return total number of copied/required bytes in the buffer and status
|
|
//
|
|
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListQuery: Address count: %ld, total buffer size: %ld.\n",
|
|
addressList->TAAddressCount, dataLen));
|
|
}
|
|
*Information = dataLen;
|
|
|
|
return status;
|
|
} //AfdAddressListQuery
|
|
|
|
|
|
|
|
//
|
|
// Context structure allocated for non-blocking address list change IOCTLs
|
|
//
|
|
|
|
typedef struct _AFD_NBCHANGE_CONTEXT {
|
|
AFD_REQUEST_CONTEXT Context; // Context to keep track of request
|
|
AFD_ADDRESS_CHANGE Change; // Address change parameters
|
|
} AFD_NBCHANGE_CONTEXT, *PAFD_NBCHANGE_CONTEXT;
|
|
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdAddressListChange (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes address list change IRP
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to I/O request packet.
|
|
|
|
IrpSp - pointer to the stack location to use for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether the request was successfully queued.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_PENDING;
|
|
USHORT addressType;
|
|
PAFD_ADDRESS_CHANGE change;
|
|
PAFD_REQUEST_CONTEXT requestCtx;
|
|
PAFD_ENDPOINT endpoint;
|
|
AFD_LOCK_QUEUE_HANDLE addressLockHandle, endpointLockHandle;
|
|
KIRQL oldIrql;
|
|
BOOLEAN overlapped;
|
|
AFD_TRANSPORT_IOCTL_INFO ioctlInfo;
|
|
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListChange: Endp: %p, buf: %p, inlen: %ld, outlen: %ld.\n",
|
|
IrpSp->FileObject->FsContext,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength));
|
|
}
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
//
|
|
// Validate input parameters
|
|
//
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
PAFD_TRANSPORT_IOCTL_INFO32 ioctlInfo32;
|
|
ioctlInfo32 = Irp->AssociatedIrp.SystemBuffer;
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (*ioctlInfo32)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
ioctlInfo.Handle = ioctlInfo32->Handle;
|
|
ioctlInfo.InputBuffer = ioctlInfo32->InputBuffer;
|
|
ioctlInfo.InputBufferLength = ioctlInfo32->InputBufferLength;
|
|
ioctlInfo.IoControlCode = ioctlInfo32->IoControlCode;
|
|
ioctlInfo.AfdFlags = ioctlInfo32->AfdFlags;
|
|
ioctlInfo.PollEvent = ioctlInfo32->PollEvent;
|
|
}
|
|
else
|
|
#endif // _WIN64
|
|
{
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (ioctlInfo)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Just copy the buffer verified by the IO system
|
|
//
|
|
|
|
ioctlInfo = *((PAFD_TRANSPORT_IOCTL_INFO)
|
|
Irp->AssociatedIrp.SystemBuffer);
|
|
}
|
|
|
|
if( ioctlInfo.InputBufferLength < sizeof(USHORT)) {
|
|
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdAddressListChange: Endp: %p - invalid parameter.\n",
|
|
IrpSp->FileObject->FsContext));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
try {
|
|
if (Irp->RequestorMode != KernelMode) {
|
|
ProbeForRead(
|
|
ioctlInfo.InputBuffer,
|
|
ioctlInfo.InputBufferLength,
|
|
sizeof (USHORT)
|
|
);
|
|
}
|
|
|
|
addressType = *((PUSHORT)ioctlInfo.InputBuffer);
|
|
}
|
|
except( AFD_EXCEPTION_FILTER(&status) ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Check if request is overlapped
|
|
//
|
|
|
|
overlapped = ((ioctlInfo.AfdFlags & AFD_OVERLAPPED)!=0);
|
|
|
|
//
|
|
// Reset the poll bit
|
|
//
|
|
|
|
endpoint->EventsActive &= ~AFD_POLL_ADDRESS_LIST_CHANGE;
|
|
|
|
//
|
|
// Setup address handlers with TDI if not already done
|
|
//
|
|
|
|
if (AfdBindingHandle==NULL) {
|
|
//
|
|
// Make sure the thread in which we execute cannot get
|
|
// suspeneded in APC while we own the global resource.
|
|
//
|
|
KeEnterCriticalRegion ();
|
|
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
|
|
|
|
if (AfdBindingHandle==NULL)
|
|
status = AfdInitializeAddressList ();
|
|
else
|
|
status = STATUS_SUCCESS;
|
|
|
|
ExReleaseResourceLite (AfdResource);
|
|
KeLeaveCriticalRegion ();
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
goto complete;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup locals
|
|
//
|
|
|
|
if (endpoint->NonBlocking && !overlapped) {
|
|
PAFD_NBCHANGE_CONTEXT nbCtx;
|
|
//
|
|
// If endpoint is non-blocking and request is not overlapped,
|
|
// we'll have to complete it right away and remeber that we
|
|
// need to set event when address list changes
|
|
//
|
|
|
|
|
|
//
|
|
// Allocate context to keep track of this request
|
|
//
|
|
|
|
try {
|
|
nbCtx = AFD_ALLOCATE_POOL_WITH_QUOTA (NonPagedPool,
|
|
sizeof(AFD_NBCHANGE_CONTEXT),
|
|
AFD_ADDRESS_CHANGE_POOL_TAG);
|
|
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (&status)) {
|
|
nbCtx = NULL;
|
|
IF_DEBUG(ROUTING_QUERY) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListChange: Endp: %p - can't allocate change strucure.\n",
|
|
IrpSp->FileObject->FsContext));
|
|
}
|
|
goto complete;
|
|
}
|
|
|
|
requestCtx = &nbCtx->Context;
|
|
change = &nbCtx->Change;
|
|
|
|
change->Endpoint = endpoint;
|
|
change->NonBlocking = TRUE;
|
|
status = STATUS_DEVICE_NOT_READY;
|
|
}
|
|
else {
|
|
|
|
C_ASSERT (sizeof (IrpSp->Parameters.Others)>=sizeof (*requestCtx));
|
|
C_ASSERT (sizeof (Irp->Tail.Overlay.DriverContext)>=sizeof (*change));
|
|
|
|
requestCtx = (PAFD_REQUEST_CONTEXT)&IrpSp->Parameters.Others;
|
|
|
|
change = (PAFD_ADDRESS_CHANGE)Irp->Tail.Overlay.DriverContext;
|
|
change->NonBlocking = FALSE;
|
|
change->Irp = Irp;
|
|
|
|
}
|
|
|
|
//
|
|
// Remeber the endpoint and address type for the request
|
|
//
|
|
|
|
change->AddressType = addressType;
|
|
requestCtx->CleanupRoutine = AfdCleanupAddressListChange;
|
|
requestCtx->Context = change;
|
|
|
|
//
|
|
// Insert change notification into the list
|
|
//
|
|
KeRaiseIrql (DISPATCH_LEVEL, &oldIrql);
|
|
AfdAcquireSpinLockAtDpcLevel (&AfdAddressChangeLock, &addressLockHandle);
|
|
|
|
//
|
|
// While holding the address change spinlock acquire endpoint
|
|
// spinlock so if notification occurs, neither structure can
|
|
// be deallocated or IRP completed while we are queuing
|
|
// it to endpoint list
|
|
//
|
|
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &endpointLockHandle);
|
|
|
|
//
|
|
// If request is non-blocking, check if we have another non-blocking
|
|
// request on the same endpoint. If so, we do not need to have
|
|
// two request structures in the list waiting to signal.
|
|
//
|
|
if (change->NonBlocking) {
|
|
PLIST_ENTRY listEntry = endpoint->RequestList.Flink;
|
|
while (listEntry!=&endpoint->RequestList) {
|
|
PAFD_REQUEST_CONTEXT req = CONTAINING_RECORD (
|
|
listEntry,
|
|
AFD_REQUEST_CONTEXT,
|
|
EndpointListLink);
|
|
listEntry = listEntry->Flink;
|
|
if (req->CleanupRoutine==AfdCleanupAddressListChange) {
|
|
PAFD_ADDRESS_CHANGE chg = req->Context;
|
|
if (chg->NonBlocking) {
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &endpointLockHandle);
|
|
AfdReleaseSpinLockFromDpcLevel (&AfdAddressChangeLock, &addressLockHandle);
|
|
KeLowerIrql (oldIrql);
|
|
AFD_FREE_POOL (CONTAINING_RECORD (
|
|
requestCtx,
|
|
AFD_NBCHANGE_CONTEXT,
|
|
Context),
|
|
AFD_ADDRESS_CHANGE_POOL_TAG);
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
|
|
"AfdAddressListChange: Endp: %p - non-blocking request already pending.\n",
|
|
IrpSp->FileObject->FsContext));
|
|
ASSERT (status == STATUS_DEVICE_NOT_READY);
|
|
goto complete;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
InsertTailList (&AfdAddressChangeList, &change->ChangeListLink);
|
|
AfdReleaseSpinLockFromDpcLevel (&AfdAddressChangeLock, &addressLockHandle);
|
|
InsertTailList (&endpoint->RequestList, &requestCtx->EndpointListLink);
|
|
if (!change->NonBlocking) {
|
|
|
|
//
|
|
// Set cancel routine
|
|
//
|
|
|
|
IoSetCancelRoutine( Irp, AfdCancelAddressListChange );
|
|
if ( !Irp->Cancel || IoSetCancelRoutine( Irp, NULL ) == NULL) {
|
|
IoMarkIrpPending (Irp);
|
|
//
|
|
// Either there was no cancel or cancel routine has
|
|
// been invoked already
|
|
//
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &endpointLockHandle);
|
|
KeLowerIrql (oldIrql);
|
|
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListChange: Queued change IRP: %p on endp: %p .\n",
|
|
Irp, endpoint));
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
else {
|
|
RemoveEntryList (&requestCtx->EndpointListLink);
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &endpointLockHandle);
|
|
KeLowerIrql (oldIrql);
|
|
goto complete;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT (status==STATUS_DEVICE_NOT_READY);
|
|
}
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &endpointLockHandle);
|
|
KeLowerIrql (oldIrql);
|
|
|
|
complete:
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddressListChange: Completing IRP: %ld on endp: %p with status: %lx .\n",
|
|
Irp, IrpSp->FileObject->FsContext, status));
|
|
}
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
AfdCancelAddressListChange (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancel routine for pending address list change IRP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - must be our device object
|
|
|
|
Irp - the request to be cancelled
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PAFD_ADDRESS_CHANGE change;
|
|
PAFD_REQUEST_CONTEXT requestCtx;
|
|
PAFD_ENDPOINT endpoint;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
//
|
|
// We do not use cancel spinlock to manage address list queue, so
|
|
// we can release it right away
|
|
//
|
|
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
|
|
//
|
|
// Get the request context and remove it from the queue if not
|
|
// already removed.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
endpoint = irpSp->FileObject->FsContext;
|
|
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
|
|
|
|
requestCtx = (PAFD_REQUEST_CONTEXT)&irpSp->Parameters.DeviceIoControl;
|
|
change = requestCtx->Context;
|
|
ASSERT (change==(PAFD_ADDRESS_CHANGE)Irp->Tail.Overlay.DriverContext);
|
|
ASSERT (change->NonBlocking==FALSE);
|
|
|
|
AfdAcquireSpinLock (&AfdAddressChangeLock, &lockHandle);
|
|
if (change->ChangeListLink.Flink!=NULL) {
|
|
RemoveEntryList (&change->ChangeListLink);
|
|
change->ChangeListLink.Flink = NULL;
|
|
}
|
|
AfdReleaseSpinLock (&AfdAddressChangeLock, &lockHandle);
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
if (AfdIsRequestInQueue (requestCtx)) {
|
|
//
|
|
// Context is still in the list, just remove it so
|
|
// noone can see it anymore and complete the IRP
|
|
//
|
|
RemoveEntryList (&requestCtx->EndpointListLink);
|
|
}
|
|
else if (!AfdIsRequestCompleted (requestCtx)) {
|
|
//
|
|
// During endpoint cleanup, this context was removed from the
|
|
// list and cleanup routine is about to be called, don't
|
|
// free this IRP until cleanup routine is called
|
|
// Also, indicate to the cleanup routine that we are done
|
|
// with this IRP and it can free it.
|
|
//
|
|
AfdMarkRequestCompleted (requestCtx);
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
return;
|
|
}
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCancelAddressListChange: Cancelled IRP: %p on endp: %p .\n",
|
|
Irp, endpoint));
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
AfdCleanupAddressListChange (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PAFD_REQUEST_CONTEXT RequestCtx
|
|
)
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PAFD_ADDRESS_CHANGE change;
|
|
|
|
change = RequestCtx->Context;
|
|
|
|
//
|
|
// In no case IRP and request structure
|
|
// could have been freed until we mark it as completed as
|
|
// the caller of this routine should have marked the request
|
|
// as being cancelled
|
|
//
|
|
ASSERT (RequestCtx->EndpointListLink.Flink==NULL);
|
|
|
|
AfdAcquireSpinLock (&AfdAddressChangeLock, &lockHandle);
|
|
if (change->ChangeListLink.Flink!=NULL) {
|
|
RemoveEntryList (&change->ChangeListLink);
|
|
change->ChangeListLink.Flink = NULL;
|
|
}
|
|
AfdReleaseSpinLock (&AfdAddressChangeLock, &lockHandle);
|
|
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
//
|
|
// The processing routine has either already initiated completion
|
|
// of this request and marked it as completed what it saw that the request is
|
|
// no longer on the endpoint queue, or the processing routine will
|
|
// never see the request since we removed it from the processing list.
|
|
// However, it is possible that blocking request is being cancelled in another
|
|
// thread as we cleaning up, so we need to sync with the cancel routine.
|
|
//
|
|
if (AfdIsRequestCompleted (RequestCtx) ||
|
|
change->NonBlocking ||
|
|
IoSetCancelRoutine (change->Irp, NULL)!=NULL) {
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
if (change->NonBlocking) {
|
|
ASSERT (CONTAINING_RECORD (RequestCtx,
|
|
AFD_NBCHANGE_CONTEXT,
|
|
Context)
|
|
==CONTAINING_RECORD (change,
|
|
AFD_NBCHANGE_CONTEXT,
|
|
Change));
|
|
ASSERT (Endpoint == change->Endpoint);
|
|
AFD_FREE_POOL (CONTAINING_RECORD (RequestCtx,
|
|
AFD_NBCHANGE_CONTEXT,
|
|
Context),
|
|
AFD_ADDRESS_CHANGE_POOL_TAG);
|
|
}
|
|
else {
|
|
PIRP irp = change->Irp;
|
|
ASSERT (change==(PAFD_ADDRESS_CHANGE)irp->Tail.Overlay.DriverContext);
|
|
ASSERT (Endpoint == IoGetCurrentIrpStackLocation (irp)->FileObject->FsContext);
|
|
ASSERT (RequestCtx == (PAFD_REQUEST_CONTEXT)
|
|
&IoGetCurrentIrpStackLocation (irp)->Parameters.DeviceIoControl);
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
irp->IoStatus.Information = 0;
|
|
IoCompleteRequest (irp, IO_NO_INCREMENT);
|
|
}
|
|
return TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// AFD has not completed the request before returning
|
|
// from cancel routine, mark the request to indicate
|
|
// that we are done with it and cancel routine
|
|
// can free it
|
|
//
|
|
|
|
AfdMarkRequestCompleted (RequestCtx);
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AfdInitializeAddressList (VOID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Register address handler routinges with TDI
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS -- Indicates whether registration succeded
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
TDI_CLIENT_INTERFACE_INFO info;
|
|
UNICODE_STRING afdName;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Do basic initialization if we haven't done this before.
|
|
//
|
|
if (AfdAddressListLock==NULL) {
|
|
|
|
//
|
|
// Initialize spinlock that protects address change list.
|
|
//
|
|
AfdInitializeSpinLock (&AfdAddressChangeLock);
|
|
|
|
//
|
|
// Allocate and initialize resource that protects address list
|
|
//
|
|
|
|
AfdAddressListLock = AFD_ALLOCATE_POOL_PRIORITY(
|
|
NonPagedPool,
|
|
sizeof(*AfdAddressListLock),
|
|
AFD_RESOURCE_POOL_TAG,
|
|
HighPoolPriority
|
|
);
|
|
|
|
if ( AfdAddressListLock == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ExInitializeResourceLite( AfdAddressListLock );
|
|
|
|
//
|
|
// Initialize our lists
|
|
//
|
|
|
|
InitializeListHead (&AfdAddressEntryList);
|
|
InitializeListHead (&AfdAddressChangeList);
|
|
}
|
|
|
|
//
|
|
// Setup the TDI request structure
|
|
//
|
|
|
|
RtlZeroMemory (&info, sizeof (info));
|
|
RtlInitUnicodeString(&afdName, L"AFD");
|
|
#ifdef TDI_CURRENT_VERSION
|
|
info.TdiVersion = TDI_CURRENT_VERSION;
|
|
#else
|
|
info.MajorTdiVersion = 2;
|
|
info.MinorTdiVersion = 0;
|
|
#endif
|
|
info.Unused = 0;
|
|
info.ClientName = &afdName;
|
|
info.BindingHandler = NULL;
|
|
info.AddAddressHandlerV2 = AfdAddAddressHandler;
|
|
info.DelAddressHandlerV2 = AfdDelAddressHandler;
|
|
info.PnPPowerHandler = AfdPnPPowerChange;
|
|
|
|
//
|
|
// Register handlers with TDI
|
|
//
|
|
|
|
status = TdiRegisterPnPHandlers (&info, sizeof (info), &AfdBindingHandle);
|
|
if (!NT_SUCCESS (status)) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL,
|
|
"AfdInitializeAddressList: Failed to register PnP handlers: %lx .\n",
|
|
status));
|
|
return status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdDeregisterPnPHandlers (
|
|
PVOID Param
|
|
)
|
|
{
|
|
|
|
ASSERT ( ExIsResourceAcquiredSharedLite ( AfdAddressListLock )==0
|
|
|| ExIsResourceAcquiredExclusiveLite( AfdAddressListLock ));
|
|
|
|
KeEnterCriticalRegion ();
|
|
ExAcquireResourceExclusiveLite (AfdResource, TRUE);
|
|
|
|
//
|
|
// Free address list and associated structures
|
|
//
|
|
if (AfdBindingHandle) {
|
|
ExAcquireResourceExclusiveLite( AfdAddressListLock, TRUE );
|
|
|
|
TdiDeregisterPnPHandlers (AfdBindingHandle);
|
|
AfdBindingHandle = NULL;
|
|
|
|
ASSERT (AfdAddressListLock!=NULL);
|
|
while( !IsListEmpty( &AfdAddressEntryList ) ) {
|
|
PAFD_ADDRESS_ENTRY addressEntry;
|
|
PLIST_ENTRY listEntry;
|
|
listEntry = RemoveHeadList( &AfdAddressEntryList );
|
|
|
|
addressEntry = CONTAINING_RECORD(
|
|
listEntry,
|
|
AFD_ADDRESS_ENTRY,
|
|
AddressListLink
|
|
);
|
|
|
|
AFD_FREE_POOL(
|
|
addressEntry,
|
|
AFD_TRANSPORT_ADDRESS_POOL_TAG
|
|
);
|
|
|
|
}
|
|
|
|
|
|
if (!IsListEmpty (&AfdEndpointListHead)) {
|
|
//
|
|
// Call routine to notify all the clietns
|
|
//
|
|
|
|
ASSERT (!IsListEmpty (&AfdTransportInfoListHead));
|
|
ASSERT (AfdLoaded);
|
|
|
|
AfdProcessAddressChangeList (TDI_ADDRESS_TYPE_UNSPEC, NULL);
|
|
}
|
|
|
|
ExReleaseResourceLite (AfdAddressListLock);
|
|
}
|
|
|
|
ExReleaseResourceLite (AfdResource);
|
|
KeLeaveCriticalRegion ();
|
|
}
|
|
|
|
VOID
|
|
AfdAddAddressHandler (
|
|
IN PTA_ADDRESS NetworkAddress,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PTDI_PNP_CONTEXT Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TDI add address handler
|
|
|
|
Arguments:
|
|
|
|
NetworkAddress - new network address available on the system
|
|
|
|
Context1 - name of the device to which address belongs
|
|
|
|
Context2 - PDO to which address belongs
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PAFD_ADDRESS_ENTRY addrEntry;
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Clear the cached last removed PDO when we get address add notification
|
|
// since PDO can now be reused for something else.
|
|
//
|
|
AfdLastRemovedPdo = NULL;
|
|
|
|
if (DeviceName==NULL) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL,
|
|
"AfdAddAddressHandler: "
|
|
"NO DEVICE NAME SUPPLIED when adding address of type %d., IGNORING IT!!!\n",
|
|
NetworkAddress->AddressType));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate memory to keep address in our list
|
|
// Note since the address information usually gets
|
|
// populated during boot and not used right away, we
|
|
// make it a "cold" allocation. The flag has no effect
|
|
// after system is booted.
|
|
//
|
|
|
|
addrEntry = AFD_ALLOCATE_POOL_PRIORITY (PagedPool|POOL_COLD_ALLOCATION,
|
|
ALIGN_UP(FIELD_OFFSET (AFD_ADDRESS_ENTRY,
|
|
Address.Address[NetworkAddress->AddressLength]),
|
|
WCHAR)
|
|
+DeviceName->MaximumLength,
|
|
AFD_TRANSPORT_ADDRESS_POOL_TAG,
|
|
HighPoolPriority);
|
|
|
|
if (addrEntry!=NULL) {
|
|
//
|
|
// Insert new address in the list
|
|
//
|
|
|
|
RtlCopyMemory (&addrEntry->Address, NetworkAddress,
|
|
FIELD_OFFSET (TA_ADDRESS,
|
|
Address[NetworkAddress->AddressLength]));
|
|
|
|
addrEntry->DeviceName.MaximumLength = DeviceName->MaximumLength;
|
|
addrEntry->DeviceName.Buffer =
|
|
ALIGN_UP_POINTER(&addrEntry->Address.Address[NetworkAddress->AddressLength],
|
|
WCHAR);
|
|
RtlCopyUnicodeString (&addrEntry->DeviceName, DeviceName);
|
|
|
|
|
|
//
|
|
// We shouldn't be calling into TDI while having resource
|
|
// acquired in shared mode because it can cause a deadloclk
|
|
// rigth here as TDI reenters us and we need to acquire the
|
|
// resource exclusive
|
|
//
|
|
|
|
ASSERT ( ExIsResourceAcquiredSharedLite ( AfdAddressListLock )==0
|
|
|| ExIsResourceAcquiredExclusiveLite( AfdAddressListLock ));
|
|
|
|
//
|
|
// Make sure the thread in which we execute cannot get
|
|
// suspeneded in APC while we own the global resource.
|
|
//
|
|
KeEnterCriticalRegion ();
|
|
|
|
//
|
|
// Acquire AfdResource since we will be checking if there are endpoints in
|
|
// the list to decide whether to call non-pageable routine.
|
|
//
|
|
ExAcquireResourceSharedLite (AfdResource, TRUE);
|
|
ExAcquireResourceExclusiveLite( AfdAddressListLock, TRUE );
|
|
|
|
InsertTailList (&AfdAddressEntryList, &addrEntry->AddressListLink);
|
|
|
|
//
|
|
// Don't call if endpoint list is empty, since the driver
|
|
// may be paged out. There should be no-one to notify anyway
|
|
// if there are no sockets there.
|
|
//
|
|
if (!IsListEmpty (&AfdEndpointListHead)) {
|
|
//
|
|
// Call routine to notify all the clietns
|
|
//
|
|
|
|
ASSERT (!IsListEmpty (&AfdTransportInfoListHead));
|
|
ASSERT (AfdLoaded);
|
|
|
|
AfdProcessAddressChangeList (NetworkAddress->AddressType, DeviceName);
|
|
}
|
|
|
|
ExReleaseResourceLite (AfdAddressListLock);
|
|
ExReleaseResourceLite (AfdResource);
|
|
KeLeaveCriticalRegion ();
|
|
}
|
|
else {
|
|
//
|
|
// Failed allocation - queue work item to deregister PnP
|
|
// handlers and notify all apps.
|
|
// When apps come back will re-register and our list will
|
|
// get re-populated, or we'll fail the app's call(s);
|
|
//
|
|
AfdQueueWorkItem (&AfdDeregisterPnPHandlers, &AfdPnPDeregisterWorker);
|
|
}
|
|
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAddAddressHandler: Type: %d, length: %d, device: %*ls .\n",
|
|
NetworkAddress->AddressType,
|
|
NetworkAddress->AddressLength,
|
|
DeviceName->Length/2,
|
|
DeviceName->Buffer));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AfdDelAddressHandler (
|
|
IN PTA_ADDRESS NetworkAddress,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PTDI_PNP_CONTEXT Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TDI delete address handler
|
|
|
|
Arguments:
|
|
|
|
NetworkAddress - network address that is no longer available on the system
|
|
|
|
Context1 - name of the device to which address belongs
|
|
|
|
Context2 - PDO to which address belongs
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PAFD_ADDRESS_ENTRY addrEntry;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
PAGED_CODE ();
|
|
|
|
if (DeviceName==NULL) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL,
|
|
"AfdDelAddressHandler: "
|
|
"NO DEVICE NAME SUPPLIED when deleting address of type %d.\n",
|
|
NetworkAddress->AddressType));
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// We shouldn't be calling into TDI while having resource
|
|
// acquired in shared mode because it can cause a deadloclk
|
|
// rigth here as TDI reenters us and we need to acquire the
|
|
// resource exclusive
|
|
//
|
|
|
|
ASSERT ( ExIsResourceAcquiredSharedLite ( AfdAddressListLock )==0
|
|
|| ExIsResourceAcquiredExclusiveLite( AfdAddressListLock ));
|
|
|
|
//
|
|
// Find address in our list
|
|
//
|
|
|
|
//
|
|
// Make sure the thread in which we execute cannot get
|
|
// suspeneded in APC while we own the global resource.
|
|
//
|
|
KeEnterCriticalRegion ();
|
|
//
|
|
// Acquire AfdResource since we will be checking if there are endpoints in
|
|
// the list to decide whether to call non-pageable routine.
|
|
//
|
|
ExAcquireResourceSharedLite (AfdResource, TRUE);
|
|
ExAcquireResourceExclusiveLite( AfdAddressListLock, TRUE );
|
|
listEntry = AfdAddressEntryList.Flink;
|
|
while (listEntry!=&AfdAddressEntryList) {
|
|
addrEntry = CONTAINING_RECORD (listEntry, AFD_ADDRESS_ENTRY, AddressListLink);
|
|
listEntry = listEntry->Flink;
|
|
if (RtlEqualMemory (&addrEntry->Address, NetworkAddress,
|
|
FIELD_OFFSET (TA_ADDRESS,
|
|
Address[NetworkAddress->AddressLength]))
|
|
&& RtlEqualUnicodeString (&addrEntry->DeviceName,
|
|
DeviceName,
|
|
TRUE)) {
|
|
|
|
//
|
|
// Remove it and notify the clients
|
|
//
|
|
|
|
RemoveEntryList (&addrEntry->AddressListLink);
|
|
//
|
|
// Don't call if endpoint list is empty, since the driver
|
|
// may be paged out. There should be no-one to notify anyway
|
|
// if there are no sockets there.
|
|
//
|
|
if (!IsListEmpty (&AfdEndpointListHead)) {
|
|
|
|
ASSERT (!IsListEmpty (&AfdTransportInfoListHead));
|
|
ASSERT (AfdLoaded);
|
|
|
|
AfdProcessAddressChangeList (NetworkAddress->AddressType, DeviceName);
|
|
}
|
|
|
|
ExReleaseResourceLite (AfdAddressListLock);
|
|
ExReleaseResourceLite (AfdResource);
|
|
KeLeaveCriticalRegion ();
|
|
AFD_FREE_POOL (addrEntry, AFD_TRANSPORT_ADDRESS_POOL_TAG);
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdDelAddressHandler: Type: %d, length: %d, device: %*ls .\n",
|
|
NetworkAddress->AddressType,
|
|
NetworkAddress->AddressLength,
|
|
DeviceName->Length/2,
|
|
DeviceName->Buffer));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
ExReleaseResourceLite (AfdAddressListLock);
|
|
ExReleaseResourceLite (AfdResource);
|
|
KeLeaveCriticalRegion ();
|
|
ASSERT (!"AfdDelAddressHandler: Could not find matching entry");
|
|
}
|
|
|
|
VOID
|
|
AfdProcessAddressChangeList (
|
|
USHORT AddressType,
|
|
PUNICODE_STRING DeviceName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notifuis all interested clients of address arrival/deletion
|
|
|
|
Arguments:
|
|
|
|
AddressType - type of the address that arrived/ was deleted
|
|
|
|
DeviceName - name of the device to which address belongs
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PLIST_ENTRY listEntry;
|
|
LIST_ENTRY completedChangeList;
|
|
PAFD_ADDRESS_CHANGE change;
|
|
PAFD_REQUEST_CONTEXT requestCtx;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_TRANSPORT_INFO transportInfo;
|
|
|
|
// ASSERT ((AddressType!=TDI_ADDRESS_TYPE_NETBIOS) || (DeviceName!=NULL));
|
|
//
|
|
// Special check for Netbios addresses because
|
|
// we have separate protocols for each lana/device
|
|
//
|
|
transportInfo = NULL;
|
|
if ((AddressType==TDI_ADDRESS_TYPE_NETBIOS) && (DeviceName!=NULL)) {
|
|
BOOLEAN found = FALSE;
|
|
for ( listEntry = AfdTransportInfoListHead.Flink;
|
|
listEntry != &AfdTransportInfoListHead;
|
|
listEntry = listEntry->Flink ) {
|
|
transportInfo = CONTAINING_RECORD(
|
|
listEntry,
|
|
AFD_TRANSPORT_INFO,
|
|
TransportInfoListEntry
|
|
);
|
|
if (RtlEqualUnicodeString (
|
|
DeviceName,
|
|
&transportInfo->TransportDeviceName,
|
|
TRUE)) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create local list to process notifications after spinlock is released
|
|
//
|
|
|
|
InitializeListHead (&completedChangeList);
|
|
|
|
//
|
|
// Walk the list and move matching notifications to the local list
|
|
//
|
|
|
|
AfdAcquireSpinLock (&AfdAddressChangeLock, &lockHandle);
|
|
listEntry = AfdAddressChangeList.Flink;
|
|
while (listEntry!=&AfdAddressChangeList) {
|
|
change = CONTAINING_RECORD (listEntry,
|
|
AFD_ADDRESS_CHANGE,
|
|
ChangeListLink);
|
|
if (change->NonBlocking) {
|
|
endpoint = change->Endpoint;
|
|
requestCtx = &CONTAINING_RECORD (change,
|
|
AFD_NBCHANGE_CONTEXT,
|
|
Change)->Context;
|
|
ASSERT (requestCtx->Context==change);
|
|
}
|
|
else {
|
|
irp = change->Irp;
|
|
irpSp = IoGetCurrentIrpStackLocation (irp);
|
|
requestCtx = (PAFD_REQUEST_CONTEXT)&irpSp->Parameters.DeviceIoControl;
|
|
endpoint = irpSp->FileObject->FsContext;
|
|
ASSERT (change==(PAFD_ADDRESS_CHANGE)irp->Tail.Overlay.DriverContext);
|
|
}
|
|
|
|
listEntry = listEntry->Flink;
|
|
if (((change->AddressType==AddressType) || (AddressType==TDI_ADDRESS_TYPE_UNSPEC))
|
|
//
|
|
// Special check for Netbios addresses because
|
|
// we have separate protocols for each lana/device
|
|
//
|
|
&& ((transportInfo==NULL)
|
|
|| (transportInfo==endpoint->TransportInfo)) ) {
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle2;
|
|
|
|
RemoveEntryList (&change->ChangeListLink);
|
|
change->ChangeListLink.Flink = NULL;
|
|
//
|
|
// If request is already canceled, let cancel routine complete it
|
|
//
|
|
if (!change->NonBlocking && IoSetCancelRoutine (irp, NULL)==NULL) {
|
|
continue;
|
|
}
|
|
|
|
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle2);
|
|
if (AfdIsRequestInQueue (requestCtx)) {
|
|
//
|
|
// Context is still in the list, just remove it so
|
|
// no-one can see it anymore and complete
|
|
//
|
|
RemoveEntryList (&requestCtx->EndpointListLink);
|
|
InsertTailList (&completedChangeList,
|
|
&change->ChangeListLink);
|
|
if (change->NonBlocking) {
|
|
AfdIndicateEventSelectEvent (change->Endpoint,
|
|
AFD_POLL_ADDRESS_LIST_CHANGE,
|
|
STATUS_SUCCESS);
|
|
}
|
|
}
|
|
else if (!AfdIsRequestCompleted (requestCtx)) {
|
|
//
|
|
// During endpoint cleanup, this context was removed from the
|
|
// list and cleanup routine is about to be called, don't
|
|
// free this IRP until cleanup routine is called
|
|
// Also, indicate to the cleanup routine that we are done
|
|
// with this IRP and it can free it.
|
|
//
|
|
AfdMarkRequestCompleted (requestCtx);
|
|
}
|
|
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle2);
|
|
}
|
|
}
|
|
AfdReleaseSpinLock (&AfdAddressChangeLock, &lockHandle);
|
|
|
|
//
|
|
// Signal interested clients and complete IRPs as necessary
|
|
//
|
|
|
|
while (!IsListEmpty (&completedChangeList)) {
|
|
listEntry = RemoveHeadList (&completedChangeList);
|
|
change = CONTAINING_RECORD (listEntry,
|
|
AFD_ADDRESS_CHANGE,
|
|
ChangeListLink);
|
|
if (change->NonBlocking) {
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdProcessAddressChangeList: Signalling address list change on endpoint %p .\n",
|
|
change->Endpoint));
|
|
}
|
|
AfdIndicatePollEvent (change->Endpoint,
|
|
AFD_POLL_ADDRESS_LIST_CHANGE,
|
|
STATUS_SUCCESS);
|
|
AFD_FREE_POOL (CONTAINING_RECORD (change,
|
|
AFD_NBCHANGE_CONTEXT,
|
|
Change),
|
|
AFD_ADDRESS_CHANGE_POOL_TAG);
|
|
}
|
|
else {
|
|
irp = change->Irp;
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
irp->IoStatus.Information = 0;
|
|
IF_DEBUG (ADDRESS_LIST) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdProcessAddressChangeList: Completing change IRP: %p with status: 0 .\n",
|
|
irp));
|
|
}
|
|
IoCompleteRequest (irp, AfdPriorityBoost);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
AfdHasHeldPacketsFromNic (
|
|
PAFD_CONNECTION Connection,
|
|
PVOID Pdo
|
|
)
|
|
{
|
|
PLIST_ENTRY le;
|
|
//
|
|
// Scan the list of buffers and check with TDI/NDIS
|
|
// if packet belongs to a given card
|
|
//
|
|
if (!IsListEmpty( &Connection->VcReceiveBufferListHead ) ) {
|
|
le = Connection->VcReceiveBufferListHead.Flink;
|
|
while ( le!=&Connection->VcReceiveBufferListHead ) {
|
|
PAFD_BUFFER afdBuffer;
|
|
afdBuffer = CONTAINING_RECORD( le, AFD_BUFFER, BufferListEntry );
|
|
if ((afdBuffer->BufferLength==AfdBufferTagSize) &&
|
|
TdiMatchPdoWithChainedReceiveContext (afdBuffer->Context, Pdo)) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
|
|
"AFD: Aborting connection %p due to held packet %p at power down on nic %p\n",
|
|
Connection,
|
|
afdBuffer->Context,
|
|
Pdo));
|
|
return TRUE;
|
|
}
|
|
le = le->Flink;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
AfdReturnNicsPackets (
|
|
PVOID Pdo
|
|
)
|
|
{
|
|
KIRQL oldIrql;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PLIST_ENTRY listEntry, le;
|
|
LIST_ENTRY connList;
|
|
|
|
//
|
|
// Don't scan twice for the same PDO if this event
|
|
// is less than 3 sec apart from previous.
|
|
// Several transports bound to the same NIC may indicate
|
|
// the set power event to us
|
|
//
|
|
if ((AfdLastRemovedPdo!=Pdo) ||
|
|
((KeQueryInterruptTime()-AfdLastRemoveTime)>30000000i64)) {
|
|
|
|
//
|
|
// Scan the list of endpoints and find packets
|
|
// that belong to the NIC.
|
|
//
|
|
KeEnterCriticalRegion ();
|
|
ExAcquireResourceExclusiveLite (AfdResource, TRUE);
|
|
|
|
if (!IsListEmpty (&AfdEndpointListHead)) {
|
|
KeRaiseIrql (DISPATCH_LEVEL, &oldIrql);
|
|
|
|
listEntry = AfdEndpointListHead.Flink;
|
|
while (listEntry!=&AfdEndpointListHead) {
|
|
PAFD_CONNECTION connection;
|
|
PAFD_ENDPOINT endpoint = CONTAINING_RECORD (
|
|
listEntry,
|
|
AFD_ENDPOINT,
|
|
GlobalEndpointListEntry);
|
|
listEntry = listEntry->Flink;
|
|
switch (endpoint->Type) {
|
|
case AfdBlockTypeDatagram:
|
|
//
|
|
// Afd currently does not support buffer
|
|
// ownership on datagram sockets.
|
|
//
|
|
// If such support is added, we will need to
|
|
// add code here to return all the buffers
|
|
// owned by the netcards.
|
|
//
|
|
break;
|
|
|
|
case AfdBlockTypeVcConnecting:
|
|
//
|
|
// Drop all of the connections that have unreturned packets.
|
|
//
|
|
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
connection = endpoint->Common.VcConnecting.Connection;
|
|
if (endpoint->State==AfdEndpointStateConnected &&
|
|
!IS_TDI_BUFFERRING(endpoint) &&
|
|
connection!=NULL &&
|
|
AfdHasHeldPacketsFromNic (connection, Pdo)) {
|
|
REFERENCE_CONNECTION (connection);
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
AfdBeginAbort (connection);
|
|
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
//
|
|
// Make sure we do not have any buffered data
|
|
// (could have just checked for netcard owned
|
|
// buffers, but connection is going down anyway
|
|
// - save memory).
|
|
//
|
|
connection->VcBufferredReceiveBytes = 0;
|
|
connection->VcBufferredReceiveCount = 0;
|
|
connection->VcBufferredExpeditedBytes = 0;
|
|
connection->VcBufferredExpeditedCount = 0;
|
|
connection->VcReceiveBytesInTransport = 0;
|
|
while ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) {
|
|
PAFD_BUFFER_HEADER afdBuffer;
|
|
le = RemoveHeadList( &connection->VcReceiveBufferListHead );
|
|
afdBuffer = CONTAINING_RECORD( le, AFD_BUFFER_HEADER, BufferListEntry );
|
|
|
|
DEBUG afdBuffer->BufferListEntry.Flink = NULL;
|
|
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
|
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
|
afdBuffer->ExpeditedData = FALSE;
|
|
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
|
}
|
|
}
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
DEREFERENCE_CONNECTION (connection);
|
|
}
|
|
else {
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
}
|
|
break;
|
|
case AfdBlockTypeVcBoth:
|
|
case AfdBlockTypeVcListening:
|
|
if (IS_TDI_BUFFERRING (endpoint))
|
|
break;
|
|
|
|
//
|
|
// Drop all unaccepted and/or returned connections that have
|
|
// unreturned packets.
|
|
//
|
|
InitializeListHead (&connList);
|
|
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
le = endpoint->Common.VcListening.UnacceptedConnectionListHead.Flink;
|
|
while ( le!=&endpoint->Common.VcListening.UnacceptedConnectionListHead ) {
|
|
connection = CONTAINING_RECORD (le, AFD_CONNECTION, ListEntry);
|
|
ASSERT( connection->Endpoint == endpoint );
|
|
le = le->Flink;
|
|
if (AfdHasHeldPacketsFromNic (connection, Pdo)) {
|
|
RemoveEntryList (&connection->ListEntry);
|
|
InsertTailList (&connList, &connection->ListEntry);
|
|
InterlockedIncrement (&endpoint->Common.VcListening.FailedConnectionAdds);
|
|
}
|
|
}
|
|
|
|
le = endpoint->Common.VcListening.ReturnedConnectionListHead.Flink;
|
|
while ( le!=&endpoint->Common.VcListening.ReturnedConnectionListHead ) {
|
|
connection = CONTAINING_RECORD (le, AFD_CONNECTION, ListEntry);
|
|
ASSERT( connection->Endpoint == endpoint );
|
|
le = le->Flink;
|
|
if (AfdHasHeldPacketsFromNic (connection, Pdo)) {
|
|
RemoveEntryList (&connection->ListEntry);
|
|
InsertTailList (&connList, &connection->ListEntry);
|
|
InterlockedIncrement (&endpoint->Common.VcListening.FailedConnectionAdds);
|
|
}
|
|
}
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
while (!IsListEmpty (&connList)) {
|
|
le = RemoveHeadList (&connList);
|
|
connection = CONTAINING_RECORD (le, AFD_CONNECTION, ListEntry);
|
|
AfdAbortConnection( connection );
|
|
}
|
|
|
|
if ( endpoint->Common.VcListening.FailedConnectionAdds > 0 ) {
|
|
AfdInitiateListenBacklogReplenish( endpoint );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
KeLowerIrql (oldIrql);
|
|
}
|
|
|
|
ExReleaseResourceLite (AfdResource);
|
|
KeLeaveCriticalRegion ();
|
|
}
|
|
AfdLastRemovedPdo = Pdo;
|
|
AfdLastRemoveTime = KeQueryInterruptTime ();
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AfdPnPPowerChange(
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PNET_PNP_EVENT PowerEvent,
|
|
IN PTDI_PNP_CONTEXT Context1,
|
|
IN PTDI_PNP_CONTEXT Context2
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
switch (PowerEvent->NetEvent) {
|
|
case NetEventSetPower: {
|
|
NET_DEVICE_POWER_STATE powerState =
|
|
*((PNET_DEVICE_POWER_STATE)PowerEvent->Buffer);
|
|
ASSERT (PowerEvent->BufferLength>=sizeof (NET_DEVICE_POWER_STATE));
|
|
|
|
switch (powerState) {
|
|
case NetDeviceStateD0:
|
|
//
|
|
// Clear the cached last removed PDO when we get Power UP notification
|
|
// since PDO can now be reused for something else.
|
|
//
|
|
AfdLastRemovedPdo = NULL;
|
|
goto DoNothing;
|
|
default:
|
|
ASSERTMSG ("NIC enters unknown power state", FALSE);
|
|
case NetDeviceStateD1:
|
|
case NetDeviceStateD2:
|
|
case NetDeviceStateD3:
|
|
case NetDeviceStateUnspecified:
|
|
//
|
|
// Break to execute PDO matching code
|
|
//
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case NetEventQueryRemoveDevice:
|
|
//
|
|
// Break to execute PDO matching code
|
|
//
|
|
break;
|
|
case NetEventCancelRemoveDevice:
|
|
//
|
|
// Clear the cached last removed PDO when we get Power UP notification
|
|
// since PDO can now be removed again.
|
|
//
|
|
AfdLastRemovedPdo = NULL;
|
|
goto DoNothing;
|
|
default:
|
|
goto DoNothing;
|
|
}
|
|
|
|
//
|
|
// When power is removed or device is disabled, we need to release all
|
|
// packets that we may own.
|
|
// We can only do this for transports that give us
|
|
// PDO, so NDIS can match the packet to the device.
|
|
// Note that PDO is usually the second context argument (first one is
|
|
// usually the device name), but we check the first one too since
|
|
// the TDI spec isn't crystal clear on this (it just says: for example TCP
|
|
// usually <does the above>).
|
|
//
|
|
if ((Context2!=NULL) &&
|
|
(Context2->ContextType==TDI_PNP_CONTEXT_TYPE_PDO) &&
|
|
(Context2->ContextSize==sizeof (PVOID)) ){
|
|
AfdReturnNicsPackets (*((PVOID UNALIGNED *)&Context2->ContextData));
|
|
}
|
|
else if ((Context1!=NULL) &&
|
|
(Context1->ContextType==TDI_PNP_CONTEXT_TYPE_PDO) &&
|
|
(Context1->ContextSize==sizeof (PVOID)) ) {
|
|
AfdReturnNicsPackets (*((PVOID UNALIGNED *)&Context1->ContextData));
|
|
}
|
|
|
|
DoNothing:
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|