/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    cdpsend.c

Abstract:

    TDI Receive datagram routines.

Author:

    Mike Massa (mikemas)           February 20, 1997

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    mikemas     02-20-97    created

Notes:

--*/

#include "precomp.h"
#pragma hdrstop
#include "cdprecv.tmh"

#include <sspi.h>

#ifdef ALLOC_PRAGMA


#endif // ALLOC_PRAGMA

//
// Local types
//
typedef struct {
    CL_NODE_ID   SourceNodeId;
    USHORT       SourcePort;
    ULONG        TdiReceiveDatagramFlags;
    ULONG        TsduSize;
    PCX_ADDROBJ  AddrObj;
    PCNP_NETWORK Network;
} CDP_RECEIVE_CONTEXT, *PCDP_RECEIVE_CONTEXT;


//
// Local Data
//
PCN_RESOURCE_POOL  CdpReceiveRequestPool = NULL;

#define CDP_RECEIVE_REQUEST_POOL_DEPTH 2

//
// Local utility routines
//
VOID
CdpIndicateReceivePacket(
    IN  PCX_ADDROBJ  AddrObj,
    IN  CL_NODE_ID   SourceNodeId,
    IN  USHORT       SourcePort,
    IN  ULONG        TdiReceiveDatagramFlags,
    IN  ULONG        TsduSize,
    IN  PVOID        Tsdu,
    IN  BOOLEAN      DataVerified
    )
/*++

Notes:

    Called with address object lock held.
    Returns with address object lock released.

--*/
{
    NTSTATUS                   status;
    PTDI_IND_RECEIVE_DATAGRAM  handler = AddrObj->ReceiveDatagramHandler;
    PVOID                      context = AddrObj->ReceiveDatagramContext;
    TA_CLUSTER_ADDRESS         sourceTransportAddress;
    PIRP                       irp = NULL;
    ULONG                      bytesTaken = 0;


    CnVerifyCpuLockMask(
        CX_ADDROBJ_LOCK,      // Required
        0,                    // Forbidden
        CX_ADDROBJ_LOCK_MAX   // Maximum
        );

    CnAssert(handler != NULL);

    CnReleaseLock(&(AddrObj->Lock), AddrObj->Irql);

    //
    // Build the source address buffer
    //
    CxBuildTdiAddress(
        &sourceTransportAddress,
        SourceNodeId,
        SourcePort,
        DataVerified
        );

    CnTrace(CDP_RECV_DETAIL, CdpTraceIndicateReceive,
        "[CDP] Indicating dgram, src: node %u port %u, dst: port %u, "
        "data len %u",
        SourceNodeId, // LOGULONG
        SourcePort, // LOGUSHORT
        AddrObj->LocalPort, // LOGUSHORT
        TsduSize // LOGULONG
        );        

    //
    // Call the upper layer indication handler.
    //
    status = (*handler)(
                 context,
                 sizeof(TA_CLUSTER_ADDRESS),
                 &sourceTransportAddress,
                 0, // no options
                 NULL,
                 TdiReceiveDatagramFlags,
                 TsduSize,
                 TsduSize,
                 &bytesTaken,
                 Tsdu,
                 &irp
                 );

    CnAssert(status != STATUS_MORE_PROCESSING_REQUIRED);
    CnAssert(bytesTaken == TsduSize);
    CnAssert(irp == NULL);

    if (irp != NULL) {
        irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
        irp->IoStatus.Information = 0;
        IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
    }

    //
    // Dereference the address object
    //
    CnDereferenceFsContext(&(AddrObj->FsContext));

    CnVerifyCpuLockMask(
        0,                // Required
        0xFFFFFFFF,       // Forbidden
        0                 // Maximum
        );

    return;

}  // CdpIndicateReceivePacket


NTSTATUS
CdpCompleteReceivePacket(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp,
    IN  PVOID           Context
    )
{
    NTSTATUS              status;
    PCNP_RECEIVE_REQUEST  request = Context;
    PCDP_RECEIVE_CONTEXT  context = request->UpperProtocolContext;
    PCX_ADDROBJ           addrObj = context->AddrObj;
    ULONG                 consumed;
    PVOID                 data;
    ULONG                 dataLength;
    BOOLEAN               fscontextDereferenced = FALSE;


    if (Irp->IoStatus.Status == STATUS_SUCCESS) {
        CnAssert(Irp->IoStatus.Information == context->TsduSize);

        data = request->DataBuffer;
        dataLength = (ULONG)Irp->IoStatus.Information;

        CnAcquireLock(&(addrObj->Lock), &(addrObj->Irql));

        if (addrObj->ReceiveDatagramHandler != NULL) {
            CdpIndicateReceivePacket(
                addrObj,
                context->SourceNodeId,
                context->SourcePort,
                context->TdiReceiveDatagramFlags,
                dataLength,
                data,
                FALSE   // not verified
                );
            fscontextDereferenced = TRUE;
        }
        else {
            CnReleaseLock(&(addrObj->Lock), addrObj->Irql);
        }
    }
    else {
        CnTrace(CDP_RECV_ERROR, CdpTraceCompleteReceiveFailed,
            "[CDP] Failed to fetch dgram data, src: node %u port %u, "
            "dst: port %u, status %!status!",
            context->SourceNodeId, // LOGULONG
            context->SourcePort, // LOGUSHORT
            addrObj->LocalPort, // LOGUSHORT
            Irp->IoStatus.Status // LOGSTATUS
            );        
    }

    //
    // Drop the active reference on the network.
    //
    if (context->Network != NULL) {
        CnAcquireLock(&(context->Network->Lock), &(context->Network->Irql));
        CnpActiveDereferenceNetwork(context->Network);
        context->Network = NULL;
    }

    //
    // Dereference the addr object fscontext (only necessary
    // after error condition).
    //
    if (!fscontextDereferenced) {
        CnDereferenceFsContext(&(addrObj->FsContext));
    }

    CnpFreeReceiveRequest(request);

    CnVerifyCpuLockMask(
        0,                // Required
        0xFFFFFFFF,       // Forbidden
        0                 // Maximum
        );

    return(STATUS_MORE_PROCESSING_REQUIRED);

} // CdpCompleteReceivePacket


//
// Routines exported within the Cluster Transport
//
NTSTATUS
CdpInitializeReceive(
    VOID
    )
{
    IF_CNDBG(CN_DEBUG_INIT){
        CNPRINT(("[CDP] Initializing receive...\n"));
    }

    CdpReceiveRequestPool = CnpCreateReceiveRequestPool(
                                sizeof(CDP_RECEIVE_CONTEXT),
                                CDP_RECEIVE_REQUEST_POOL_DEPTH
                                );

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

    IF_CNDBG(CN_DEBUG_INIT){
        CNPRINT(("[CDP] Receive initialized.\n"));
    }

    return(STATUS_SUCCESS);

}  // CdpInitializeReceive


VOID
CdpCleanupReceive(
    VOID
    )
{
    IF_CNDBG(CN_DEBUG_INIT){
        CNPRINT(("[CDP] Cleaning up receive...\n"));
    }

    if (CdpReceiveRequestPool != NULL) {
        CnpDeleteReceiveRequestPool(CdpReceiveRequestPool);
        CdpReceiveRequestPool = NULL;
    }

    IF_CNDBG(CN_DEBUG_INIT){
        CNPRINT(("[CDP] Receive cleanup complete.\n"));
    }

    return;

}  // CdpCleanupReceive


NTSTATUS
CdpReceivePacketHandler(
    IN  PVOID          Network,
    IN  CL_NODE_ID     SourceNodeId,
    IN  ULONG          CnpReceiveFlags,
    IN  ULONG          TdiReceiveDatagramFlags,
    IN  ULONG          BytesIndicated,
    IN  ULONG          BytesAvailable,
    OUT PULONG         BytesTaken,
    IN  PVOID          Tsdu,
    OUT PIRP *         Irp
    )
{
    NTSTATUS                   status;
    CDP_HEADER UNALIGNED *     header = Tsdu;
    PCX_ADDROBJ                addrObj;
    ULONG                      bytesTaken = 0;
    PCNP_RECEIVE_REQUEST       request;
    USHORT                     srcPort = 0;
    USHORT                     destPort = 0;
    ULONG                      consumed = 0;


    CnAssert(KeGetCurrentIrql() == DISPATCH_LEVEL);

    if (BytesIndicated >= sizeof(CDP_HEADER))
    {
        destPort = header->DestinationPort;
        srcPort =  header->SourcePort;

        //
        // Consume the CDP header
        //
        consumed = sizeof(CDP_HEADER);

        //
        // Verify that the remaining packet is consistent.
        //
        if (header->PayloadLength != (BytesAvailable - consumed)) {
            goto error_exit;
        }

        BytesIndicated -= consumed;
        BytesAvailable -= consumed;
        *BytesTaken += consumed;
        Tsdu = (PUCHAR)Tsdu + consumed;

        CnAcquireLockAtDpc(&CxAddrObjTableLock);

        addrObj = CxFindAddressObject(destPort);

        if (addrObj != NULL) {

            CnReleaseLockFromDpc(&CxAddrObjTableLock);

            if ( ( !(addrObj->Flags & CX_AO_FLAG_CHECKSTATE)
                   ||
                   (CnpReceiveFlags & CNP_RECV_FLAG_NODE_STATE_CHECK_PASSED)
                 )
                 &&
                 (addrObj->ReceiveDatagramHandler != NULL)
               )
            {
                //
                // Reference the address object so it can't go away during
                // the indication.
                //
                CnReferenceFsContext(&(addrObj->FsContext));

                if (BytesAvailable == BytesIndicated) {

                    CdpIndicateReceivePacket(
                        addrObj,
                        SourceNodeId,
                        srcPort,
                        TdiReceiveDatagramFlags,
                        BytesAvailable,
                        ((BytesAvailable > 0) ? Tsdu : NULL),
                        (BOOLEAN)(
                            CnpReceiveFlags & CNP_RECV_FLAG_SIGNATURE_VERIFIED
                        )
                        );

                    //
                    // The addrObj lock was released.
                    //

                    *BytesTaken += BytesAvailable;
                    *Irp = NULL;

                    CnVerifyCpuLockMask(
                        0,                // Required
                        0xFFFFFFFF,       // Forbidden
                        0                 // Maximum
                        );

                    return(STATUS_SUCCESS);
                }

                CnReleaseLockFromDpc(&(addrObj->Lock));

                //
                // This message cannot be a CNP multicast, and it
                // cannot have been verified, because the CNP layer 
                // could not have verified an incomplete message.
                //
                CnAssert(!(CnpReceiveFlags & CNP_RECV_FLAG_MULTICAST));
                CnAssert(!(CnpReceiveFlags & CNP_RECV_FLAG_SIGNATURE_VERIFIED));

                //
                // We need to fetch the rest of the packet before we
                // can indicate it to the upper layer.
                //
                request = CnpAllocateReceiveRequest(
                              CdpReceiveRequestPool,
                              Network,
                              BytesAvailable,
                              CdpCompleteReceivePacket
                              );

                if (request != NULL) {

                    PCDP_RECEIVE_CONTEXT  context;
                    PCNP_NETWORK          network = (PCNP_NETWORK)Network;

                    context = request->UpperProtocolContext;

                    context->SourceNodeId = SourceNodeId;
                    context->SourcePort = header->SourcePort;
                    context->TdiReceiveDatagramFlags = TdiReceiveDatagramFlags;
                    context->TsduSize = BytesAvailable;
                    context->AddrObj = addrObj;
                    context->Network = Network;

                    //
                    // Take a reference on the network so that it 
                    // doesn't disappear before the IRP completes.
                    //
                    CnAcquireLock(&(network->Lock), &(network->Irql));
                    CnpActiveReferenceNetwork(Network);
                    CnReleaseLock(&(network->Lock), network->Irql);

                    *Irp = request->Irp;

                    CnTrace(CDP_RECV_DETAIL, CdpTraceCompleteReceive,
                        "[CDP] Fetching dgram data, src: node %u port %u, "
                        "dst: port %u, BI %u, BA %u, CNP Flags %x.",
                        SourceNodeId, // LOGULONG
                        srcPort, // LOGUSHORT
                        destPort, // LOGUSHORT
                        BytesIndicated, // LOGULONG
                        BytesAvailable, // LOGULONG
                        CnpReceiveFlags // LOGXLONG
                        );        

                    CnVerifyCpuLockMask(
                        0,                // Required
                        0xFFFFFFFF,       // Forbidden
                        0                 // Maximum
                        );

                    return(STATUS_MORE_PROCESSING_REQUIRED);

                }

                CnTrace(
                    CDP_RECV_ERROR, CdpTraceDropReceiveNoIrp,
                    "[CDP] Dropping dgram: failed to allocate "
                    "receive request."
                    );

                //
                // Out of resources. Drop the packet.
                //
            }
            else {
                //
                // No receive handler or node state check failed.
                //
                CnReleaseLockFromDpc(&(addrObj->Lock));

                CnTrace(
                    CDP_RECV_ERROR, CdpTraceDropReceiveState,
                    "[CDP] Dropping dgram: addr obj flags %x, "
                    "CNP flags %x, dgram recv handler %p.",
                    addrObj->Flags,
                    CnpReceiveFlags,
                    addrObj->ReceiveDatagramHandler
                    );
            }
        }
        else {
            CnReleaseLockFromDpc(&CxAddrObjTableLock);

            CnTrace(
                CDP_RECV_ERROR, CdpTraceDropReceiveNoAO,
                "[CDP] Dropping dgram: no clusnet addr obj found "
                "for dest port %u.",
                destPort
                );
        }
    }

error_exit:

    //
    // Something went wrong. Drop the packet by
    // indicating that we consumed it.
    //
    *BytesTaken += BytesAvailable;
    *Irp = NULL;

    CnTrace(CDP_RECV_ERROR, CdpTraceDropReceive,
        "[CDP] Dropped dgram, src: node %u port %u, dst: port %u, "
        "BI %u, BA %u, CNP flags %x.",
        SourceNodeId, // LOGULONG
        srcPort, // LOGUSHORT
        destPort, // LOGUSHORT
        BytesIndicated, // LOGULONG
        BytesAvailable, // LOGULONG
        CnpReceiveFlags // LOGXLONG
        );        

    CnVerifyCpuLockMask(
        0,                // Required
        0xFFFFFFFF,       // Forbidden
        0                 // Maximum
        );

    return(STATUS_SUCCESS);

}  // CdpReceivePacketHandler


//
// Routines exported within the Cluster Network driver
//
NTSTATUS
CxReceiveDatagram(
    IN PIRP                  Irp,
    IN PIO_STACK_LOCATION    IrpSp
    )
{
    NTSTATUS status = STATUS_NOT_IMPLEMENTED;


    CNPRINT(("[Clusnet] CxReceiveDatagram called!\n"));

    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);

    return(status);

}  // CxReceiveDatagram