/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    ntemgmt.c

Abstract:

    Routines for managing IP Network Table Entries.

Author:

    Mike Massa (mikemas)           April 16, 1997

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    mikemas     04-16-97    created

Notes:

--*/

#include "clusnet.h"
#include "ntemgmt.tmh"


//
// Types
//
typedef struct {
    LIST_ENTRY   Linkage;
    ULONG        Address;
    USHORT       Context;
    ULONG        Instance;
} IPA_NTE, *PIPA_NTE;


//
// Data
//
LIST_ENTRY       IpaNteList = {NULL,NULL};
KSPIN_LOCK       IpaNteListLock = 0;
HANDLE           IpaIpHandle = NULL;
PDEVICE_OBJECT   IpaIpDeviceObject = NULL;
PFILE_OBJECT     IpaIpFileObject = NULL;


//
// Local function prototypes
//
NTSTATUS
IpaIssueDeviceControl (
    IN ULONG            IoControlCode,
    IN PVOID            InputBuffer,
    IN ULONG            InputBufferLength,
    IN PVOID            OutputBuffer,
    IN PULONG           OutputBufferLength
    );

#ifdef ALLOC_PRAGMA

#pragma alloc_text(INIT, IpaLoad)
#pragma alloc_text(PAGE, IpaIssueDeviceControl)
#pragma alloc_text(PAGE, IpaInitialize)


#endif // ALLOC_PRAGMA



NTSTATUS
IpaIssueDeviceControl(
    IN ULONG    IoControlCode,
    IN PVOID    InputBuffer,
    IN ULONG    InputBufferLength,
    IN PVOID    OutputBuffer,
    IN PULONG   OutputBufferLength
    )

/*++

Routine Description:


Arguments:


Return Value:

    NTSTATUS -- Indicates the status of the request.

Notes:

    Called in the context of the system process.

--*/

{
    NTSTATUS             status = STATUS_SUCCESS;
    IO_STATUS_BLOCK      ioStatusBlock;
    PIRP                 irp;
    PKEVENT              event;


    PAGED_CODE();

    CnAssert(IpaIpHandle != NULL);
    CnAssert(IpaIpFileObject != NULL);
    CnAssert(IpaIpDeviceObject != NULL);
    CnAssert(CnSystemProcess == (PKPROCESS) IoGetCurrentProcess());

    //
    // Reference the file object. This reference will be removed by the I/O
    // completion code.
    //
    status = ObReferenceObjectByPointer(
                 IpaIpFileObject,
                 0,
                 NULL,
                 KernelMode
                 );

    if (!NT_SUCCESS(status)) {
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Failed to reference IP device file handle, status %lx\n",
                status
                ));
        }
        CnTrace(NTEMGMT_DETAIL, IpaNteObRefFailed,
            "[Clusnet] Failed to reference IP device file handle, status %!status!.",
            status // LOGSTATUS
            );                
        return(status);
    }

    event = CnAllocatePool(sizeof(KEVENT));

    if (event != NULL) {
        KeInitializeEvent(event, NotificationEvent, FALSE);

        irp = IoBuildDeviceIoControlRequest(
                  IoControlCode,
                  IpaIpDeviceObject,
                  InputBuffer,
                  InputBufferLength,
                  OutputBuffer,
                  *OutputBufferLength,
                  FALSE,
                  event,
                  &ioStatusBlock
                  );

        if (irp != NULL) {
            status = IoCallDriver(IpaIpDeviceObject, irp);

            //
            // If necessary, wait for the I/O to complete.
            //
            if (status == STATUS_PENDING) {
                KeWaitForSingleObject(
                    event,
                    UserRequest,
                    KernelMode,
                    FALSE,
                    NULL
                    );
            }

            if (NT_SUCCESS(status)) {
                status = ioStatusBlock.Status;

                // NOTENOTE: on 64 bit this is a truncation might
                // want > check code

                *OutputBufferLength = (ULONG)ioStatusBlock.Information;
            }
            else {
                IF_CNDBG(CN_DEBUG_NTE) {
                    CNPRINT((
                        "[Clusnet] NTE request failed, status %lx\n",
                        status
                        ));
                }
                CnTrace(NTEMGMT_DETAIL, IpaNteRequestFailed,
                    "[Clusnet] NTE request failed, status %!status!.",
                    status // LOGSTATUS
                    );                
                *OutputBufferLength = 0;
            }

            CnFreePool(event);

            return(status);
        }
        else {
            status = STATUS_INSUFFICIENT_RESOURCES;
            IF_CNDBG(CN_DEBUG_NTE) {
                CNPRINT((
                    "[Clusnet] Failed to build NTE request irp, status %lx\n",
                    status
                    ));
            }
            CnTrace(NTEMGMT_DETAIL, IpaNteIrpAllocFailed,
                "[Clusnet] Failed to build NTE request irp, status %!status!.",
                status // LOGSTATUS
                );                
        }

        CnFreePool(event);
    }
    else {
        status = STATUS_INSUFFICIENT_RESOURCES;
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Failed to allocate memory for event object.\n"
                ));
        }
        CnTrace(NTEMGMT_DETAIL, IpaNteEventAllocFailed,
            "[Clusnet] Failed to allocate event object, status %!status!.",
            status // LOGSTATUS
            );                
    }

    ObDereferenceObject(IpaIpFileObject);

    return(status);

} // IpaDeviceControl


PIPA_NTE
IpaFindNTE(
    USHORT  Context
    )
{
    PIPA_NTE      nte;
    PLIST_ENTRY   entry;


    for ( entry = IpaNteList.Flink;
          entry != &IpaNteList;
          entry = entry->Flink
        )
    {
        nte = CONTAINING_RECORD(entry, IPA_NTE, Linkage);

        if (Context == nte->Context) {
            return(nte);
        }
    }

    return(NULL);

} // IpaFindNTE


NTSTATUS
IpaAddNTECompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    )
/*++

Routine Description:

    IpaAddNTECompletion is the completion routine for an
    IOCTL_IP_ADD_NTE IRP. It completes the processing for
    an IOCTL_CLUSNET_ADD_NTE request and releases CnResource.
    
Arguments:

    DeviceObject - not used
    Irp - completed IRP
    Context - local NTE data structure
        
Return value

    Must not be STATUS_MORE_PROCESSING_REQUIRED
    
--*/
{
    PIP_ADD_NTE_RESPONSE     response;
    PIPA_NTE                 nte;
    NTSTATUS                 status;
    KIRQL                    irql;

    nte = (PIPA_NTE) Context;

    status = Irp->IoStatus.Status;

    if (status == STATUS_SUCCESS) {

        response = 
            (PIP_ADD_NTE_RESPONSE) Irp->AssociatedIrp.SystemBuffer;

        nte->Context = response->Context;
        nte->Instance = response->Instance;

        CnTrace(NTEMGMT_DETAIL, IpaNteCreatedNte,
            "[Clusnet] Created new NTE, context %u, instance %u.",
            nte->Context, // LOGUSHORT
            nte->Instance // LOGULONG
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Created new NTE %lu, instance %u\n",
                nte->Context,
                nte->Instance
                ));
        }

        KeAcquireSpinLock(&IpaNteListLock, &irql);

        InsertTailList(&IpaNteList, &(nte->Linkage));

        KeReleaseSpinLock(&IpaNteListLock, irql);
    }
    else {

        CnTrace(NTEMGMT_DETAIL, IpaNteCreateNteFailed,
            "[Clusnet] Failed to create new NTE, status %!status!.",
            status // LOGSTATUS
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Failed to create new NTE, status %lx\n",
                status
                ));
        }

        CnFreePool(nte);
    }

    //
    // Irp was already marked pending in our dispatch routine, but leave
    // this code in case the dispatch routine is ever changed.
    //
    if (Irp->PendingReturned) {
        IoMarkIrpPending(Irp);
    }

    return(status);

} // IpaAddNTECompletion


NTSTATUS
IpaDeleteNTECompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    )
/*++

Routine Description:

    IpaDeleteNTECompletion is the completion routine for an
    IOCTL_IP_DELETE_NTE IRP. It completes the processing for
    an IOCTL_CLUSNET_ADD_NTE request and releases CnResource.
    
Arguments:

    DeviceObject - not used
    Irp - completed IRP
    Context - local NTE data structure
        
Return value

    Must not be STATUS_MORE_PROCESSING_REQUIRED
    
--*/
{
    PIPA_NTE                 nte;
    NTSTATUS                 status;

    nte = (PIPA_NTE) Context;

    status = Irp->IoStatus.Status;

    if (status != STATUS_SUCCESS) {
        CnTrace(NTEMGMT_DETAIL, IpaNteDeleteNteFailed,
            "[Clusnet] Failed to delete NTE context %u, status %!status!.",
            nte->Context, // LOGUSHORT
            status // LOGSTATUS
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT(("[Clusnet] Failed to delete NTE %u, status %lx\n",
                     nte->Context,
                     status
                     ));
        }
        CnAssert(status == STATUS_SUCCESS);
    }
    else {
        CnTrace(NTEMGMT_DETAIL, IpaNteNteDeleted,
            "[Clusnet] Deleted NTE %u.",
            nte->Context // LOGUSHORT
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT(("[Clusnet] Deleted NTE %u\n", nte->Context));
        }
    }

    CnFreePool(nte);

    //
    // Irp was already marked pending in our dispatch routine, but leave
    // this code in case the dispatch routine is ever changed.
    //
    if (Irp->PendingReturned) {
        IoMarkIrpPending(Irp);
    }

    return(status);

} // IpaDeleteNTECompletion


NTSTATUS
IpaSetNTEAddressCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    )
/*++

Routine Description

    IpaSetNTEAddressCompletion is the completion routine for an
    IOCTL_IP_SET_ADDRESS IRP. It completes the processing for
    an IOCTL_CLUSNET_SET_NTE_ADDRESS request and releases
    CnResource.
    
Arguments

    DeviceObject - not used
    Irp - completed IRP
    Context - former IP address of NTE, must be restored in
        IpaNteList if IOCTL failed
        
Return value

    Must not be STATUS_MORE_PROCESSING_REQUIRED
    
--*/
{
    PIP_SET_ADDRESS_REQUEST request;
    NTSTATUS                status; 
    KIRQL                   irql;
    PIPA_NTE                nte;

    request = (PIP_SET_ADDRESS_REQUEST) Irp->AssociatedIrp.SystemBuffer;
    
    status = Irp->IoStatus.Status;

    if (status != STATUS_SUCCESS) {
        CnTrace(NTEMGMT_DETAIL, IpaNteSetNteFailed,
            "[Clusnet] Failed to set address for NTE %u, status %!status!.",
            request->Context, // LOGUSHORT
            status // LOGSTATUS
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Failed to set NTE %u, status %lx\n",
                request->Context,
                status
                ));
        }

        KeAcquireSpinLock(&IpaNteListLock, &irql);

        nte = IpaFindNTE(request->Context);

        if ((nte != NULL) && (nte->Address == request->Address)) {
            nte->Address = PtrToUlong(Context);
        }

        KeReleaseSpinLock(&IpaNteListLock, irql);
    }
    else {
        CnTrace(NTEMGMT_DETAIL, IpaNteSetNteAddress,
            "[Clusnet] Set NTE %u to address %x.",
            request->Context, // LOGUSHORT
            request->Address // LOGXLONG
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Set NTE %u to address %lx\n",
                request->Context,
                request->Address
                ));
        }
    }

    //
    // Irp was already marked pending in our dispatch routine, but leave
    // this code in case the dispatch routine is ever changed.
    //
    if (Irp->PendingReturned) {
        IoMarkIrpPending(Irp);
    }

    return(status);

} // IpaSetNTEAddressCompletion

//
// Public Routines
//
NTSTATUS
IpaLoad(
    VOID
    )
{
    IF_CNDBG(CN_DEBUG_INIT) {
        CNPRINT(("[Clusnet] NTE support loading.\n"));
    }

    KeInitializeSpinLock(&IpaNteListLock);
    InitializeListHead(&IpaNteList);

    return(STATUS_SUCCESS);

}  // IpaLoad


NTSTATUS
IpaInitialize(
    VOID
    )
{
    NTSTATUS             status = STATUS_SUCCESS;
    OBJECT_ATTRIBUTES    objectAttributes;
    UNICODE_STRING       nameString;
    IO_STATUS_BLOCK      ioStatusBlock;


    PAGED_CODE( );

    IF_CNDBG(CN_DEBUG_INIT) {
        CNPRINT(("[Clusnet] NTE support initializing.\n"));
    }

    CnAssert(IsListEmpty(&IpaNteList));
    CnAssert(IpaIpHandle == NULL);
    CnAssert(CnSystemProcess != NULL);

    //
    // Open handles in the context of the system process
    //
    KeAttachProcess(CnSystemProcess);

    //
    // Open the IP device.
    //
    RtlInitUnicodeString(&nameString, DD_IP_DEVICE_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &nameString,
        OBJ_CASE_INSENSITIVE,
        (HANDLE) NULL,
        (PSECURITY_DESCRIPTOR) NULL
        );

    status = ZwCreateFile(
                 &IpaIpHandle,
                 SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
                 &objectAttributes,
                 &ioStatusBlock,
                 NULL,
                 FILE_ATTRIBUTE_NORMAL,
                 FILE_SHARE_READ | FILE_SHARE_WRITE,
                 FILE_OPEN_IF,
                 0,
                 NULL,
                 0
                 );

    if (!NT_SUCCESS(status)) {
        CnTrace(NTEMGMT_DETAIL, IpaNteOpenIpFailed,
            "[Clusnet] Failed to open IP device, status %!status!.",
            status // LOGSTATUS
            );                
        
        IF_CNDBG(CN_DEBUG_INIT) {
            CNPRINT(("[Clusnet] Failed to open IP device, status %lx\n", status));
        }
        goto error_exit;
    }

    status = ObReferenceObjectByHandle(
                 IpaIpHandle,
                 0,
                 NULL,
                 KernelMode,
                 &IpaIpFileObject,
                 NULL
                 );

    if (!NT_SUCCESS(status)) {
        CnTrace(NTEMGMT_DETAIL, IpaNteRefIpFailed,
            "[Clusnet] Failed to reference IP device, status %!status!.",
            status // LOGSTATUS
            );                
        
        IF_CNDBG(CN_DEBUG_INIT) {
            CNPRINT(("[Clusnet] Failed to reference IP device file handle, status %lx\n", status));
        }
        ZwClose(IpaIpHandle); IpaIpHandle = NULL;
        goto error_exit;
    }

    IpaIpDeviceObject = IoGetRelatedDeviceObject(IpaIpFileObject);

    CnAdjustDeviceObjectStackSize(CnDeviceObject, IpaIpDeviceObject);

    status = STATUS_SUCCESS;

error_exit:

    KeDetachProcess();

    return(status);

}  // IpaInitialize


VOID
IpaShutdown(
    VOID
    )
{
    NTSTATUS                status;
    KIRQL                   irql;
    PLIST_ENTRY             entry;
    PIPA_NTE                nte;
    IP_DELETE_NTE_REQUEST   request;
    ULONG                   responseSize = 0;


    IF_CNDBG(CN_DEBUG_INIT) {
        CNPRINT(("[Clusnet] Destroying all cluster NTEs...\n"));
    }

    if (IpaIpHandle != NULL) {
        //
        // Handles was opened in the context of the system process.
        //
        CnAssert(CnSystemProcess != NULL);
        KeAttachProcess(CnSystemProcess);

        KeAcquireSpinLock(&IpaNteListLock, &irql);

        while (!IsListEmpty(&IpaNteList)) {
            entry = RemoveHeadList(&IpaNteList);

            KeReleaseSpinLock(&IpaNteListLock, irql);

            nte = CONTAINING_RECORD(entry, IPA_NTE, Linkage);

            request.Context = nte->Context;

            status = IpaIssueDeviceControl(
                         IOCTL_IP_DELETE_NTE,
                         &request,
                         sizeof(request),
                         NULL,
                         &responseSize
                         );

            if (status != STATUS_SUCCESS) {
                CnTrace(NTEMGMT_DETAIL, IpaNteShutdownDeleteNteFailed,
                    "[Clusnet] Shutdown: failed to delete NTE %u, status %!status!.",
                    nte->Context, // LOGUSHORT
                    status // LOGSTATUS
                    );                

                IF_CNDBG(CN_DEBUG_INIT) {
                    CNPRINT(("[Clusnet] Failed to delete NTE %u, status %lx\n",
                             nte->Context,
                             status
                             ));
                }
            }
            else {
                CnTrace(NTEMGMT_DETAIL, IpaNteShutdownDeletedNte,
                    "[Clusnet] Shutdown: deleted NTE context %u, instance %u.",
                    nte->Context, // LOGUSHORT
                    nte->Instance // LOGULONG
                    );                

                IF_CNDBG(CN_DEBUG_INIT) {
                    CNPRINT(("[Clusnet] Deleted NTE %u\n", request.Context));
                }
            }

            CnFreePool(nte);

            KeAcquireSpinLock(&IpaNteListLock, &irql);
        }

        KeReleaseSpinLock(&IpaNteListLock, irql);

        CnTrace(NTEMGMT_DETAIL, IpaNteShutdownNtesDeleted,
            "[Clusnet] All cluster NTEs destroyed."
            );                
        
        IF_CNDBG(CN_DEBUG_INIT) {
            CNPRINT(("[Clusnet] All cluster NTEs destroyed.\n"));
        }

        ObDereferenceObject(IpaIpFileObject);
        ZwClose(IpaIpHandle);
        IpaIpHandle = NULL;
        IpaIpFileObject = NULL;
        IpaIpDeviceObject = NULL;

        KeDetachProcess();
    }

    return;

} // IpaShutdown


NTSTATUS
IpaAddNTE(
    IN PIRP                     Irp,
    IN PIO_STACK_LOCATION       IrpSp
    )
/*++

Routine Description

    IpaAddNTE issues an IOCTL_IP_ADD_NTE to IP to add an NTE. 
    Irp is reused. It must be allocated with sufficient stack
    locations, as determined when IpaIpDeviceObject was opened
    in IpaInitialize.
    
Arguments

    Irp - IRP from I/O manager to clusnet
    IrpSp - current IRP stack location
    
Return Value

    STATUS_PENDING, or error status if request is not submitted
    to IP.
    
--*/
{
    NTSTATUS                 status;
    PIP_ADD_NTE_REQUEST      request;
    ULONG                    requestSize;
    ULONG                    responseSize;
    PIPA_NTE                 nte;
    PIO_STACK_LOCATION       nextIrpSp;


    //
    // Verify input parameters
    //
    requestSize =
        IrpSp->Parameters.DeviceIoControl.InputBufferLength;

    responseSize =
        IrpSp->Parameters.DeviceIoControl.OutputBufferLength;

    if (requestSize < sizeof(IP_ADD_NTE_REQUEST)) {
        ULONG correctSize = sizeof(IP_ADD_NTE_REQUEST);
        CnTrace(NTEMGMT_DETAIL, IpaNteAddInvalidReqSize,
            "[Clusnet] Add NTE request size %u invalid, "
            "should be %u.",
            requestSize, // LOGULONG
            correctSize // LOGULONG
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Add NTE request size %d invalid, should be %d.\n",
                requestSize,
                sizeof(IP_ADD_NTE_REQUEST)
                ));
        }
        return(STATUS_INVALID_PARAMETER);
    } else if (responseSize < sizeof(IP_ADD_NTE_RESPONSE)) {
        ULONG correctSize = sizeof(IP_ADD_NTE_RESPONSE);
        CnTrace(NTEMGMT_DETAIL, IpaNteAddInvalidResponseSize,
            "[Clusnet] Add NTE response size %u invalid, "
            "should be %u.",
            responseSize, // LOGULONG
            correctSize // LOGULONG
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Add NTE response size %d invalid, should be %d.\n",
                responseSize,
                sizeof(IP_ADD_NTE_RESPONSE)
                ));
        }
        return(STATUS_INVALID_PARAMETER);
    }

    //
    // Verify that Irp has sufficient stack locations
    //
    if (Irp->CurrentLocation - IpaIpDeviceObject->StackSize < 1) {
        UCHAR correctSize = IpaIpDeviceObject->StackSize+1;
        CnTrace(NTEMGMT_DETAIL, IpaNteAddNoIrpStack,
            "[Clusnet] Add NTE IRP has %u remaining stack locations, "
            "need %u.",
            Irp->CurrentLocation, // LOGUCHAR
            correctSize // LOGUCHAR
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Add NTE IRP has %d stack locations, need %d.\n",
                Irp->CurrentLocation,
                IpaIpDeviceObject->StackSize
                ));
        }
        return(STATUS_INVALID_PARAMETER);
    }

    request = (PIP_ADD_NTE_REQUEST) Irp->AssociatedIrp.SystemBuffer;

    CnTrace(NTEMGMT_DETAIL, IpaNteCreatingNte,
        "[Clusnet] Creating new NTE for address %x.",
        request->Address // LOGXLONG
        );                

    IF_CNDBG(CN_DEBUG_NTE) {
        CNPRINT((
            "[Clusnet] Creating new NTE for address %lx...\n",
            request->Address
            ));
    }

    //
    // Allocate a local NTE data structure.
    //
    nte = CnAllocatePool(sizeof(IPA_NTE));

    if (nte == NULL) {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    nte->Address = request->Address;

    //
    // Set up the next IRP stack location for IP.
    // IOCTL_CLUSNET_ADD_NTE uses the same request
    // and response buffer, so there is no need to
    // alter the IRP system buffer.
    //
    IoCopyCurrentIrpStackLocationToNext(Irp);

    nextIrpSp = IoGetNextIrpStackLocation(Irp);
    nextIrpSp->Parameters.DeviceIoControl.IoControlCode 
        = IOCTL_IP_ADD_NTE;
    nextIrpSp->FileObject = IpaIpFileObject;

    IoSetCompletionRoutine(
        Irp,
        IpaAddNTECompletion,
        (PVOID) nte,
        TRUE,
        TRUE,
        TRUE
        );

    //
    // Mark the IRP pending, since we return STATUS_PENDING
    // regardless of the result of IoCallDriver.
    //
    IoMarkIrpPending(Irp);

    //
    // Issue the request
    //
    IoCallDriver(IpaIpDeviceObject, Irp);

    //
    // At this point we must return STATUS_PENDING so that
    // the clusnet dispatch routine will not try to complete
    // the IRP. The lower-level driver is required to complete
    // the IRP, and errors will be handled in the completion
    // routine.
    //
    return (STATUS_PENDING);

} // IpaAddNTE


NTSTATUS
IpaDeleteNTE(
    IN PIRP                     Irp,
    IN PIO_STACK_LOCATION       IrpSp
    )
/*++

Routine Description

    IpaDeleteNTE issues an IOCTL_IP_DELETE_NTE to IP to delete
    an NTE. Irp is reused. It must be allocated with sufficient
    stack locations, as determined when IpaIpDeviceObject was
    opened in IpaInitialize.
    
Arguments

    Irp - IRP from I/O manager to clusnet
    IrpSp - current IRP stack location
    
Return Value

    STATUS_PENDING, or error status if request is not submitted
    to IP.
    
--*/
{
    NTSTATUS                 status;
    PIP_DELETE_NTE_REQUEST   request;
    ULONG                    requestSize;
    PIPA_NTE                 nte;
    KIRQL                    irql;
    PIO_STACK_LOCATION       nextIrpSp;


    //
    // Verify input parameters
    //
    requestSize =
        IrpSp->Parameters.DeviceIoControl.InputBufferLength;

    if (requestSize < sizeof(IP_DELETE_NTE_REQUEST)) {
        ULONG correctSize = sizeof(IP_DELETE_NTE_REQUEST);
        CnTrace(NTEMGMT_DETAIL, IpaNteDelInvalidReqSize,
            "[Clusnet] Delete NTE request size %u invalid, "
            "should be %u.",
            requestSize, // LOGULONG
            correctSize // LOGULONG
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Delete NTE request size %d invalid, "
                "should be %d.\n",
                requestSize,
                sizeof(IP_DELETE_NTE_REQUEST)
                ));
        }
        return(STATUS_INVALID_PARAMETER);
    }

    //
    // Verify that Irp has sufficient stack locations
    //
    if (Irp->CurrentLocation - IpaIpDeviceObject->StackSize < 1) {
        UCHAR correctSize = IpaIpDeviceObject->StackSize+1;
        CnTrace(NTEMGMT_DETAIL, IpaNteDeleteNoIrpStack,
            "[Clusnet] Delete NTE IRP has %u remaining stack locations, "
            "need %u.",
            Irp->CurrentLocation, // LOGUCHAR
            correctSize // LOGUCHAR
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Delete NTE IRP has %d stack locations, "
                "need %d.\n",
                Irp->CurrentLocation,
                IpaIpDeviceObject->StackSize
                ));
        }
        return(STATUS_INVALID_PARAMETER);
    }

    request = (PIP_DELETE_NTE_REQUEST) Irp->AssociatedIrp.SystemBuffer;

    //
    // Find the NTE in local NTE list and remove.
    //
    KeAcquireSpinLock(&IpaNteListLock, &irql);

    nte = IpaFindNTE(request->Context);

    if (nte == NULL) {
        KeReleaseSpinLock(&IpaNteListLock, irql);

        CnTrace(NTEMGMT_DETAIL, IpaNteDeleteNteUnknown,
            "[Clusnet] NTE %u does not exist.",
            request->Context // LOGUSHORT
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] NTE %u does not exist.\n", 
                request->Context
                ));
        }

        return(STATUS_UNSUCCESSFUL);
    }

    RemoveEntryList(&(nte->Linkage));

    KeReleaseSpinLock(&IpaNteListLock, irql);

    //
    // Set up the next IRP stack location for IP.
    // IOCTL_CLUSNET_ADD_NTE uses the same request
    // and response buffer, so there is no need to
    // alter the IRP system buffer.
    //
    IoCopyCurrentIrpStackLocationToNext(Irp);

    nextIrpSp = IoGetNextIrpStackLocation(Irp);
    nextIrpSp->Parameters.DeviceIoControl.IoControlCode 
        = IOCTL_IP_DELETE_NTE;
    nextIrpSp->FileObject = IpaIpFileObject;

    IoSetCompletionRoutine(
        Irp,
        IpaDeleteNTECompletion,
        (PVOID) nte,
        TRUE,
        TRUE,
        TRUE
        );

    //
    // Mark the IRP pending, since we return STATUS_PENDING
    // regardless of the result of IoCallDriver.
    //
    IoMarkIrpPending(Irp);

    //
    // Issue the request
    //
    IoCallDriver(IpaIpDeviceObject, Irp);

    //
    // At this point we must return STATUS_PENDING so that
    // the clusnet dispatch routine will not try to complete
    // the IRP. The lower-level driver is required to complete
    // the IRP, and errors will be handled in the completion
    // routine.
    //
    return (STATUS_PENDING);

} // IpaDeleteNTE


NTSTATUS
IpaSetNTEAddress(
    IN PIRP                     Irp,
    IN PIO_STACK_LOCATION       IrpSp
    )
/*++

Routine Description

    IpaSetNTEAddress issues an IOCTL_IP_SET_ADDRESS to IP in order
    to set the IP address for an NTE. Irp is reused. It must be
    allocated with sufficient stack locations, as determined when
    IpaIpDeviceObject was opened in IpaInitialize.
    
Arguments

    Irp - IRP from I/O manager to clusnet
    IrpSp - current IRP stack location
    
Return Value

    STATUS_PENDING, or error status if request is not submitted
    to IP.
    
--*/
{
    NTSTATUS                    status;
    PIP_SET_ADDRESS_REQUEST_EX  request;
    ULONG                       requestSize;
    PIPA_NTE                    nte;
    KIRQL                       irql;
    PIO_STACK_LOCATION          nextIrpSp;
    ULONG                       oldAddress;


    //
    // Verify input parameters
    //
    requestSize =
        IrpSp->Parameters.DeviceIoControl.InputBufferLength;

    if (requestSize < sizeof(IP_SET_ADDRESS_REQUEST_EX)) {
        ULONG correctSize = sizeof(IP_SET_ADDRESS_REQUEST_EX);
        CnTrace(NTEMGMT_DETAIL, IpaNteSetInvalidReqSize,
            "[Clusnet] Set NTE request size %u invalid, "
            "should be %u.",
            requestSize, // LOGULONG
            correctSize // LOGULONG
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Set NTE request size %d invalid, should be %d.\n",
                requestSize,
                sizeof(IP_SET_ADDRESS_REQUEST_EX)
                ));
        }
        return(STATUS_INVALID_PARAMETER);
    }

    //
    // Verify that Irp has sufficient stack locations
    //
    if (Irp->CurrentLocation - IpaIpDeviceObject->StackSize < 1) {
        UCHAR correctSize = IpaIpDeviceObject->StackSize+1;
        CnTrace(NTEMGMT_DETAIL, IpaNteSetNoIrpStack,
            "[Clusnet] Set NTE IRP has %u remaining stack locations, "
            "need %u.",
            Irp->CurrentLocation, // LOGUCHAR
            correctSize // LOGUCHAR
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT((
                "[Clusnet] Set NTE IRP has %d stack locations, need %d.\n",
                Irp->CurrentLocation,
                IpaIpDeviceObject->StackSize
                ));
        }
        return(STATUS_INVALID_PARAMETER);
    }

    request = (PIP_SET_ADDRESS_REQUEST_EX)
              Irp->AssociatedIrp.SystemBuffer;

    IF_CNDBG(CN_DEBUG_NTE) {
        CNPRINT((
            "[Clusnet] Attempting to set NTE %u to address %lx...\n",
            request->Context,
            request->Address
            ));
    }

    KeAcquireSpinLock(&IpaNteListLock, &irql);

    nte = IpaFindNTE(request->Context);

    if (nte != NULL) {
        oldAddress = nte->Address;
        nte->Address = request->Address;

        KeReleaseSpinLock(&IpaNteListLock, irql);

        //
        // Set up the next IRP stack location for IP.
        // IOCTL_CLUSNET_SET_NTE_ADDRESS uses the same request
        // and response buffer, so there is no need to alter the
        // IRP system buffer.
        //
        IoCopyCurrentIrpStackLocationToNext(Irp);
        
        nextIrpSp = IoGetNextIrpStackLocation(Irp);
        nextIrpSp->Parameters.DeviceIoControl.IoControlCode 
            = IOCTL_IP_SET_ADDRESS_EX;
        nextIrpSp->FileObject = IpaIpFileObject;

        IoSetCompletionRoutine(
            Irp,
            IpaSetNTEAddressCompletion,
            UlongToPtr(oldAddress),
            TRUE,
            TRUE,
            TRUE
            );

        //
        // Mark the IRP pending, since we return STATUS_PENDING
        // regardless of the result of IoCallDriver.
        //
        IoMarkIrpPending(Irp);

        //
        // Issue the request
        //
        IoCallDriver(IpaIpDeviceObject, Irp);

        //
        // At this point we must return STATUS_PENDING so that
        // the clusnet dispatch routine will not try to complete
        // the IRP. The lower-level driver is required to complete
        // the IRP, and errors will be handled in the completion
        // routine.
        //
        status = STATUS_PENDING;

    } else {
        
        KeReleaseSpinLock(&IpaNteListLock, irql);

        CnTrace(NTEMGMT_DETAIL, IpaNteSetNteUnknown,
            "[Clusnet] NTE %u does not exist.",
            request->Context // LOGUSHORT
            );                
        
        IF_CNDBG(CN_DEBUG_NTE) {
            CNPRINT(("[Clusnet] NTE %u does not exist.\n", 
                     request->Context
                     ));
        }

        status = STATUS_UNSUCCESSFUL;
    }

    return(status);

} // IpaSetNTEAddress


BOOLEAN
IpaIsAddressRegistered(
    ULONG  Address
    )
{
    PIPA_NTE      nte;
    KIRQL         irql;
    PLIST_ENTRY   entry;
    BOOLEAN       isAddressRegistered = FALSE;


    KeAcquireSpinLock(&IpaNteListLock, &irql);

    for ( entry = IpaNteList.Flink;
          entry != &IpaNteList;
          entry = entry->Flink
        )
    {
        nte = CONTAINING_RECORD(entry, IPA_NTE, Linkage);

        if (nte->Address == Address) {
            isAddressRegistered = TRUE;
            break;
        }
    }

    KeReleaseSpinLock(&IpaNteListLock, irql);

    return(isAddressRegistered);

} // IpaIsAddressRegistered