/*++ Copyright (c) 1998, Microsoft Corporation Module Name: notify.c Abstract: This module contains code related to the NAT's notification-management. Notification may be requested by a NAT user- or kernel-mode client, by making an I/O control request which will complete when (a) the requested event occurs, or (b) the client's file-object is cleaned up, or (c) the NAT is shutting down. In the meantime, the I/O request packets are held on a list of pending notification-requests. Author: Abolade Gbadegesin (aboladeg) July-26-1998 Revision History: --*/ #include "precomp.h" #pragma hdrstop LIST_ENTRY NotificationList; KSPIN_LOCK NotificationLock; // // FORWARD DECLARATIONS // VOID NatpNotificationCancelRoutine( PDEVICE_OBJECT DeviceObject, PIRP Irp ); PIRP NatpDequeueNotification( IP_NAT_NOTIFICATION Code ); VOID NatCleanupAnyAssociatedNotification( PFILE_OBJECT FileObject ) /*++ Routine Description: This routine is invoked to cleanup any notifications associated with the client whose file-object has just been closed. Arguments: FileObject - the client's file-object Return Value: none. --*/ { PIRP Irp; KIRQL Irql; PLIST_ENTRY Link; CALLTRACE(("NatCleanupAnyAssociatedNotification\n")); KeAcquireSpinLock(&NotificationLock, &Irql); for (Link = NotificationList.Flink; Link != &NotificationList; Link = Link->Flink ) { Irp = CONTAINING_RECORD(Link, IRP, Tail.Overlay.ListEntry); if (Irp->Tail.Overlay.DriverContext[0] != FileObject) { continue; } if (NULL == IoSetCancelRoutine(Irp, NULL)) { // // This IRP has been canceled. It will be completed in // our cancel routine // continue; } // // The IRP is now uncancellable. Take it off the list. // RemoveEntryList(&Irp->Tail.Overlay.ListEntry); InitializeListHead(&Irp->Tail.Overlay.ListEntry); KeReleaseSpinLockFromDpcLevel(&NotificationLock); // // Complete the IRP // Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); DEREFERENCE_NAT(); // // Continue the search, starting over since we dropped the list lock // KeAcquireSpinLockAtDpcLevel(&NotificationLock); Link = &NotificationList; } KeReleaseSpinLock(&NotificationLock, Irql); } // NatCleanupAnyAssociatedNotification VOID NatInitializeNotificationManagement( VOID ) /*++ Routine Description: This routine is called to initialize the notification-management module. Arguments: none. Return Value: none. --*/ { CALLTRACE(("NatInitializeNotificationManagement\n")); InitializeListHead(&NotificationList); KeInitializeSpinLock(&NotificationLock); } // NatInitializeNotificationManagement PIRP NatpDequeueNotification( IP_NAT_NOTIFICATION Code ) /*++ Routine Description: This routine is invoked to dequeue a pending notification request IRP of the given type. If one is found, it is removed from the list and returned to the caller. Arguments: Code - the notification code for which an IRP is required Return Value: PIRP - the notification IRP, if any Environment: Invoked with 'NotificationLock' held by the caller. --*/ { PIRP Irp; PLIST_ENTRY Link; PIP_NAT_REQUEST_NOTIFICATION RequestNotification; CALLTRACE(("NatpDequeueNotification\n")); for (Link = NotificationList.Flink; Link != &NotificationList; Link = Link->Flink ) { Irp = CONTAINING_RECORD(Link, IRP, Tail.Overlay.ListEntry); RequestNotification = (PIP_NAT_REQUEST_NOTIFICATION)Irp->AssociatedIrp.SystemBuffer; if (RequestNotification->Code != Code) { continue; } RemoveEntryList(&Irp->Tail.Overlay.ListEntry); InitializeListHead(&Irp->Tail.Overlay.ListEntry); return Irp; } return NULL; } // NatpDequeueNotification VOID NatpNotificationCancelRoutine( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is invoked by the I/O manager upon cancellation of an IRP that is associated with a notification. Arguments: DeviceObject - the NAT's device-object Irp - the IRP to be cancelled Return Value: none. Environment: Invoked with the cancel spin-lock held by the I/O manager. It is this routine's responsibility to release the lock. --*/ { KIRQL Irql; CALLTRACE(("NatpNotificationCancelRoutine\n")); IoReleaseCancelSpinLock(Irp->CancelIrql); // // Take the IRP off our list // KeAcquireSpinLock(&NotificationLock, &Irql); RemoveEntryList(&Irp->Tail.Overlay.ListEntry); InitializeListHead(&Irp->Tail.Overlay.ListEntry); KeReleaseSpinLock(&NotificationLock, Irql); // // Complete the IRP // Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); DEREFERENCE_NAT(); } // NatpNotificationCancelRoutine NTSTATUS NatRequestNotification( PIP_NAT_REQUEST_NOTIFICATION RequestNotification, PIRP Irp, PFILE_OBJECT FileObject ) /*++ Routine Description: This routine is invoked upon receipt of a notification-request from a client. Arguments: RequeustNotification - describes the notification Irp - the associated IRP FileObject - the client's file-object Return Value: NTSTATUS - status code. --*/ { KIRQL CancelIrql; PIO_STACK_LOCATION IrpSp; CALLTRACE(("NatRequestNotification\n")); // // Check the size of the supplied output-buffer // IrpSp = IoGetCurrentIrpStackLocation(Irp); if (RequestNotification->Code == NatRoutingFailureNotification) { if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(IP_NAT_ROUTING_FAILURE_NOTIFICATION)) { return STATUS_INVALID_BUFFER_SIZE; } } else { return STATUS_INVALID_PARAMETER; } // // Attempt to queue the IRP for later completion. // If the IRP is already cancelled, though, do nothing // IoAcquireCancelSpinLock(&CancelIrql); KeAcquireSpinLockAtDpcLevel(&NotificationLock); if (Irp->Cancel || !REFERENCE_NAT()) { KeReleaseSpinLockFromDpcLevel(&NotificationLock); IoReleaseCancelSpinLock(CancelIrql); return STATUS_CANCELLED; } // // Put the IRP on the list and remember its file-object // InsertTailList(&NotificationList, &Irp->Tail.Overlay.ListEntry); Irp->Tail.Overlay.DriverContext[0] = FileObject; KeReleaseSpinLockFromDpcLevel(&NotificationLock); // // Install our cancel-routine // IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, NatpNotificationCancelRoutine); IoReleaseCancelSpinLock(CancelIrql); return STATUS_PENDING; } // NatRequestNotification VOID NatSendRoutingFailureNotification( ULONG DestinationAddress, ULONG SourceAddress ) /*++ Routine Description: This routine is invoked to notify any clients that a routing failure has occurred. Arguments: DestinationAddress - the destination address of the unroutable packet SourceAddress - the source address of the unroutable packet Return Value: none. --*/ { PIRP Irp; KIRQL Irql; PIP_NAT_ROUTING_FAILURE_NOTIFICATION RoutingFailureNotification; CALLTRACE(("NatSendRoutingFailureNotification\n")); // // See if any client wants routing-failure notification // KeAcquireSpinLock(&NotificationLock, &Irql); if (!(Irp = NatpDequeueNotification(NatRoutingFailureNotification))) { KeReleaseSpinLock(&NotificationLock, Irql); return; } KeReleaseSpinLock(&NotificationLock, Irql); // // Make the IRP uncancellable so we can complete it. // if (NULL == IoSetCancelRoutine(Irp, NULL)) { // // The IO manager canceled this IRP. It will be completed // in the cancel routine // return; } // // Fill in the notification information // RoutingFailureNotification = (PIP_NAT_ROUTING_FAILURE_NOTIFICATION)Irp->AssociatedIrp.SystemBuffer; RoutingFailureNotification->DestinationAddress = DestinationAddress; RoutingFailureNotification->SourceAddress = SourceAddress; // // Complete the IRP // Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(*RoutingFailureNotification); IoCompleteRequest(Irp, IO_NO_INCREMENT); DEREFERENCE_NAT(); } // NatSendRoutingFailureNotification VOID NatShutdownNotificationManagement( VOID ) /*++ Routine Description: This routine is invoked to shut down the module. All outstanding notifications are cancelled. Arguments: none. Return Value: none. --*/ { PIRP Irp; PLIST_ENTRY Link; KIRQL Irql; CALLTRACE(("NatShutdownNotificationManagement\n")); KeAcquireSpinLock(&NotificationLock, &Irql); while (!IsListEmpty(&NotificationList)) { // // Take the next IRP off the list // Irp = CONTAINING_RECORD( NotificationList.Flink, IRP, Tail.Overlay.ListEntry ); RemoveEntryList(&Irp->Tail.Overlay.ListEntry); InitializeListHead(&Irp->Tail.Overlay.ListEntry); // // Cancel it if necessary // if (NULL != IoSetCancelRoutine(Irp, NULL)) { KeReleaseSpinLockFromDpcLevel(&NotificationLock); // // Complete the IRP // Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); DEREFERENCE_NAT(); // // Resume emptying the list // KeAcquireSpinLockAtDpcLevel(&NotificationLock); } } KeReleaseSpinLock(&NotificationLock, Irql); } // NatShutdownNotificationManagement