|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
cnprecv.c
Abstract:
Cluster Network Protocol receive processing code.
Author:
Mike Massa (mikemas) January 24, 1997
Revision History:
Who When What -------- -------- ---------------------------------------------- mikemas 01-24-97 created
Notes:
--*/
#include "precomp.h"
#pragma hdrstop
#include "cnprecv.tmh"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, CnpCreateSendRequestPool)
#endif // ALLOC_PRAGMA
//
// Local types
//
typedef struct { ULONG TdiReceiveDatagramFlags; ULONG TsduSize; PCNP_NETWORK Network; ULONG CnpReceiveFlags; } CNP_RECEIVE_CONTEXT, *PCNP_RECEIVE_CONTEXT;
//
// Local Data
//
PCN_RESOURCE_POOL CnpReceiveRequestPool = NULL;
#define CNP_RECEIVE_REQUEST_POOL_DEPTH 2
//
// Routines exported within the Cluster Transport
//
NTSTATUS CnpLoad( VOID ) { IF_CNDBG(CN_DEBUG_INIT){ CNPRINT(("[CNP] Loading...\n")); }
CnpReceiveRequestPool = CnpCreateReceiveRequestPool( sizeof(CNP_RECEIVE_CONTEXT), CNP_RECEIVE_REQUEST_POOL_DEPTH );
if (CnpReceiveRequestPool == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
IF_CNDBG(CN_DEBUG_INIT){ CNPRINT(("[CNP] Loading complete.\n")); }
return(STATUS_SUCCESS);
} // CnpInitializeReceive
VOID CnpUnload( VOID ) { IF_CNDBG(CN_DEBUG_INIT){ CNPRINT(("[CNP] Unloading...\n")); }
if (CnpReceiveRequestPool != NULL) { CnpDeleteReceiveRequestPool(CnpReceiveRequestPool); CnpReceiveRequestPool = NULL; }
IF_CNDBG(CN_DEBUG_INIT){ CNPRINT(("[CNP] Unloading complete.\n")); }
return;
} // CnpCleanupReceive
//
// Private Utility Fumctions
//
PCN_RESOURCE CnpCreateReceiveRequest( IN PVOID Context ) { PCNP_RECEIVE_REQUEST_POOL_CONTEXT context = Context; PCNP_RECEIVE_REQUEST request; PIRP irp;
//
// Allocate a new receive request. Include space for the upper protocol
// context.
//
request = CnAllocatePool( sizeof(CNP_RECEIVE_REQUEST) + context->UpperProtocolContextSize );
if (request != NULL) { request->UpperProtocolContext = request + 1; return(&(request->CnResource)); }
return(NULL);
} // CnpCreateReceiveRequest
VOID CnpDeleteReceiveRequest( PCN_RESOURCE Resource ) { PCNP_RECEIVE_REQUEST request = CONTAINING_RECORD( Resource, CNP_RECEIVE_REQUEST, CnResource );
CnFreePool(request);
return;
} // CnpDeleteReceiveRequest
//
// Routines Exported within the Cluster Transport
//
PCN_RESOURCE_POOL CnpCreateReceiveRequestPool( IN ULONG UpperProtocolContextSize, IN USHORT PoolDepth ) { PCN_RESOURCE_POOL pool; PCNP_RECEIVE_REQUEST_POOL_CONTEXT context;
PAGED_CODE();
pool = CnAllocatePool( sizeof(CN_RESOURCE_POOL) + sizeof(CNP_RECEIVE_REQUEST_POOL_CONTEXT) );
if (pool != NULL) { context = (PCNP_RECEIVE_REQUEST_POOL_CONTEXT) (pool + 1);
context->UpperProtocolContextSize = UpperProtocolContextSize;
CnInitializeResourcePool( pool, PoolDepth, CnpCreateReceiveRequest, context, CnpDeleteReceiveRequest ); }
return(pool);
} // CnpCreateReceiveRequestPool
PCNP_RECEIVE_REQUEST CnpAllocateReceiveRequest( IN PCN_RESOURCE_POOL RequestPool, IN PVOID Network, IN ULONG BytesToReceive, IN PVOID CompletionRoutine ) { PCNP_NETWORK network = Network; PCNP_RECEIVE_REQUEST request = (PCNP_RECEIVE_REQUEST) CnAllocateResource(RequestPool);
if (request != NULL) {
//
// Allocate a buffer to hold the data.
//
request->DataBuffer = CnAllocatePool(BytesToReceive);
if (request->DataBuffer != NULL) { request->Irp = IoAllocateIrp( network->DatagramDeviceObject->StackSize, FALSE );
if (request->Irp != NULL) { PMDL mdl = IoAllocateMdl( request->DataBuffer, BytesToReceive, FALSE, FALSE, NULL );
if (mdl != NULL) { PIRP irp = request->Irp;
MmBuildMdlForNonPagedPool(mdl);
//
// Build the irp.
//
irp->Flags = 0; irp->RequestorMode = KernelMode; irp->PendingReturned = FALSE; irp->UserIosb = NULL; irp->UserEvent = NULL; irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL; irp->AssociatedIrp.SystemBuffer = NULL; irp->UserBuffer = NULL; irp->Tail.Overlay.Thread = 0; irp->Tail.Overlay.OriginalFileObject = network->DatagramFileObject; irp->Tail.Overlay.AuxiliaryBuffer = NULL;
TdiBuildReceiveDatagram( irp, network->DatagramDeviceObject, network->DatagramFileObject, CompletionRoutine, request, mdl, BytesToReceive, NULL, NULL, 0 );
//
// Make the next stack location current.
// Normally IoCallDriver would do this, but
// since we're bypassing that, we do it directly.
//
IoSetNextIrpStackLocation( irp );
return(request); }
IoFreeIrp(request->Irp); request->Irp = NULL; }
CnFreePool(request->DataBuffer); request->DataBuffer = NULL; }
CnFreeResource((PCN_RESOURCE) request); }
return(NULL);
} // CnpAllocateReceiveRequest
VOID CnpFreeReceiveRequest( PCNP_RECEIVE_REQUEST Request ) { IoFreeMdl(Request->Irp->MdlAddress); Request->Irp->MdlAddress = NULL;
IoFreeIrp(Request->Irp); Request->Irp = NULL;
CnFreePool(Request->DataBuffer); Request->DataBuffer = NULL;
CnFreeResource((PCN_RESOURCE) Request);
return;
} // CnpFreeReceiveRequest
NTSTATUS CnpIndicateData( IN PCNP_NETWORK Network, IN UCHAR NextHeader, 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 ) /*++
Routine Description:
Indicate data to the next highest protocol.
--*/ { NTSTATUS status;
if (NextHeader == PROTOCOL_CDP) {
CnTrace(CNP_RECV_DETAIL, CnpTraceIndicateDataPacket, "[CNP] Indicating data packet from node %u net %u, " "BI %u, BA %u, CNP Flags %x.", SourceNodeId, // LOGULONG
Network->Id, // LOGULONG
BytesIndicated, // LOGULONG
BytesAvailable, // LOGULONG
CnpReceiveFlags // LOGXLONG
);
status = CdpReceivePacketHandler( Network, SourceNodeId, CnpReceiveFlags, TdiReceiveDatagramFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, Irp ); } else if (NextHeader == PROTOCOL_CCMP) {
CnTrace(CNP_RECV_DETAIL, CnpTraceIndicateControlPacket, "[CNP] Indicating control packet from node %u net %u, " "BI %u, BA %u, CNP Flags %x.", SourceNodeId, // LOGULONG
Network->Id, // LOGULONG
BytesIndicated, // LOGULONG
BytesAvailable, // LOGULONG
CnpReceiveFlags // LOGXLONG
);
status = CcmpReceivePacketHandler( Network, SourceNodeId, CnpReceiveFlags, TdiReceiveDatagramFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, Irp ); } else { IF_CNDBG(CN_DEBUG_CNPRECV) { CNPRINT(( "[CNP] Received packet for unknown protocol %u\n", NextHeader )); } CnTrace(CNP_RECV_DETAIL, CnpTraceRecvUnknownProtocol, "[CNP] Received packet for unknown protocol (%u) " " from node %u net %u, BI %u, BA %u, CNP Flags %x.", NextHeader, SourceNodeId, // LOGULONG
Network->Id, // LOGULONG
BytesIndicated, // LOGULONG
BytesAvailable, // LOGULONG
CnpReceiveFlags // LOGXLONG
);
status = STATUS_SUCCESS; }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CnpIndicateData
NTSTATUS CnpCompleteReceivePacket( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { NTSTATUS status; PCNP_RECEIVE_REQUEST request = Context; PCNP_RECEIVE_CONTEXT context = request->UpperProtocolContext; CNP_HEADER UNALIGNED * cnpHeader = request->DataBuffer; ULONG consumed; ULONG dataLength; PIRP irp = NULL; ULONG bytesTaken = 0; BOOLEAN currentMcastGroup = FALSE;
if (Irp->IoStatus.Status == STATUS_SUCCESS) {
CnAssert(Irp->IoStatus.Information == context->TsduSize);
CnAssert(context->CnpReceiveFlags & CNP_RECV_FLAG_MULTICAST); CnAssert( !(context->CnpReceiveFlags & CNP_RECV_FLAG_SIGNATURE_VERIFIED) ); CnAssert( !(context->CnpReceiveFlags & CNP_RECV_FLAG_CURRENT_MULTICAST_GROUP) );
dataLength = (ULONG)Irp->IoStatus.Information;
//
// This routine is only called for multicast packets,
// so we need to verify the signature.
//
status = CnpVerifyMulticastMessage( context->Network, cnpHeader + 1, dataLength - sizeof(CNP_HEADER), cnpHeader->PayloadLength, &consumed, ¤tMcastGroup ); if (status != SEC_E_OK) { CnTrace(CNP_RECV_ERROR, CnpTraceRecvBadSig, "[CNP] Failed to verify multicast " "packet, status %x, src node %u, net %u, " "data length %u, CNP flags %x.", status, cnpHeader->SourceAddress, // LOGULONG
context->Network->Id, dataLength, context->CnpReceiveFlags ); goto error_exit; }
context->CnpReceiveFlags |= CNP_RECV_FLAG_SIGNATURE_VERIFIED;
if (currentMcastGroup) { context->CnpReceiveFlags |= CNP_RECV_FLAG_CURRENT_MULTICAST_GROUP; }
consumed += sizeof(CNP_HEADER);
//
// Indicate the data to the next highest protocol.
//
status = CnpIndicateData( context->Network, cnpHeader->NextHeader, cnpHeader->SourceAddress, context->CnpReceiveFlags, context->TdiReceiveDatagramFlags, dataLength - consumed, dataLength - consumed, &bytesTaken, (PUCHAR)cnpHeader + consumed, &irp );
CnAssert(status != STATUS_MORE_PROCESSING_REQUIRED); CnAssert(bytesTaken == dataLength - consumed); CnAssert(irp == NULL);
if (irp != NULL) { CnTrace(CNP_RECV_ERROR, CnpTraceCompleteReceiveIrp, "[CNP] Upper layer protocol requires more" "processing. Failing request." ); irp->IoStatus.Status = STATUS_UNSUCCESSFUL; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NETWORK_INCREMENT); } } else { CnTrace(CNP_RECV_ERROR, CnpTraceCompleteReceiveFailed, "[CNP] Failed to fetch packet, src node %u, " "status %!status!", cnpHeader->SourceAddress, // LOGULONG
Irp->IoStatus.Status // LOGSTATUS
); }
error_exit:
//
// Drop the active reference on the network.
//
if (context->Network != NULL) { CnAcquireLock(&(context->Network->Lock), &(context->Network->Irql)); CnpActiveDereferenceNetwork(context->Network); context->Network = NULL; }
CnpFreeReceiveRequest(request);
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(STATUS_MORE_PROCESSING_REQUIRED);
} // CdpCompleteReceivePacket
NTSTATUS CnpTdiReceiveDatagramHandler( IN PVOID TdiEventContext, IN LONG SourceAddressLength, IN PVOID SourceAddress, IN LONG OptionsLength, IN PVOID Options, IN ULONG ReceiveDatagramFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT PULONG BytesTaken, IN PVOID Tsdu, OUT PIRP * Irp ) { NTSTATUS status; CNP_HEADER UNALIGNED * cnpHeader = Tsdu; PCNP_NETWORK network = TdiEventContext; PCNP_NODE srcNode; ULONG cnpRecvFlags = 0; BOOLEAN cnpSigDataIndicated = FALSE; ULONG consumed; PCNP_RECEIVE_REQUEST request;
CnAssert(KeGetCurrentIrql() == DISPATCH_LEVEL); CnAssert(network->State > ClusnetNetworkStateOffline); CnAssert(CnLocalNodeId != ClusterInvalidNodeId); CnAssert(CnpLocalNode != NULL);
//
// Validate the CNP header.
//
// First make sure it exists.
//
if (BytesIndicated < sizeof(CNP_HEADER)) { goto error_exit; }
if ((cnpHeader->SourceAddress < CnMinValidNodeId) || (cnpHeader->SourceAddress > CnMaxValidNodeId)) { goto error_exit; }
if (cnpHeader->Version == CNP_VERSION_UNICAST) { //
// Unicast checks.
//
if ((cnpHeader->PayloadLength + sizeof(CNP_HEADER) != BytesAvailable) || (cnpHeader->DestinationAddress != CnLocalNodeId)) { goto error_exit; } } else if (cnpHeader->Version == CNP_VERSION_MULTICAST) { //
// Multicast checks.
//
// Defer payload length check until the signature
// length is known.
//
if (cnpHeader->DestinationAddress != ClusterAnyNodeId) { goto error_exit; }
cnpRecvFlags |= CNP_RECV_FLAG_MULTICAST; }
//
// Validate the source and destination nodes.
//
CnAcquireLockAtDpc(&CnpNodeTableLock);
srcNode = CnpNodeTable[cnpHeader->SourceAddress];
if (srcNode == NULL) { CnReleaseLockFromDpc(&CnpNodeTableLock); goto error_exit; }
if ( (srcNode->CommState == ClusnetNodeCommStateOnline) && (CnpLocalNode->CommState == ClusnetNodeCommStateOnline) ) { cnpRecvFlags |= CNP_RECV_FLAG_NODE_STATE_CHECK_PASSED; }
CnReleaseLockFromDpc(&CnpNodeTableLock);
if ((cnpRecvFlags & CNP_RECV_FLAG_MULTICAST) != 0) {
//
// Multicast packets need to be verified. Verification
// cannot proceed unless the entire packet is present.
//
if (BytesIndicated == BytesAvailable) {
BOOLEAN currentMcastGroup = FALSE;
//
// The entire message is indicated. We can
// verify it now.
//
status = CnpVerifyMulticastMessage( network, cnpHeader + 1, BytesIndicated - sizeof(CNP_HEADER), cnpHeader->PayloadLength, &consumed, ¤tMcastGroup ); if (status != SEC_E_OK) { CnTrace(CNP_RECV_DETAIL, CdpTraceRecvBadSig, "[CNP] Failed to verify multicast " "packet, status %x, " "src node %u, BI %u, BA %u", status, cnpHeader->SourceAddress, // LOGULONG
BytesIndicated, // LOGULONG
BytesAvailable // LOGULONG
); goto error_exit; }
cnpRecvFlags |= CNP_RECV_FLAG_SIGNATURE_VERIFIED;
if (currentMcastGroup) { cnpRecvFlags |= CNP_RECV_FLAG_CURRENT_MULTICAST_GROUP; } consumed += sizeof(CNP_HEADER);
} else {
//
// The entire message is not indicated. We need
// to submit a request and wait for the rest of
// the data.
//
request = CnpAllocateReceiveRequest( CnpReceiveRequestPool, network, BytesAvailable, CnpCompleteReceivePacket ); if (request != NULL) {
PCNP_RECEIVE_CONTEXT context; ULONG refCount;
context = request->UpperProtocolContext;
context->TdiReceiveDatagramFlags = ReceiveDatagramFlags; context->TsduSize = BytesAvailable; context->Network = network; context->CnpReceiveFlags = cnpRecvFlags;
//
// Take a reference on the network so that it
// doesn't disappear before the IRP completes.
//
CnAcquireLock(&(network->Lock), &(network->Irql)); refCount = CnpActiveReferenceNetwork(network); CnReleaseLock(&(network->Lock), network->Irql);
if (refCount == 0) { // This Network is being closed down. We
// cannot retrieve or deliver the data. Drop
// the packet.
CnpFreeReceiveRequest(request); goto error_exit; }
*Irp = request->Irp;
CnTrace(CNP_RECV_DETAIL, CnpTraceCompleteReceive, "[CNP] Fetching CNP multicast data, src " "node %u, BI %u, BA %u, CNP flags %x.", cnpHeader->SourceAddress, // LOGULONG
BytesIndicated, // LOGULONG
BytesAvailable, // LOGULONG
context->CnpReceiveFlags // LOGXLONG
);
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(STATUS_MORE_PROCESSING_REQUIRED);
}
CnTrace(CNP_RECV_ERROR, CnpTraceDropReceiveNoIrp, "[CNP] Dropping packet: failed to allocate " "receive request." );
//
// Out of resources. Drop the packet.
//
goto error_exit; }
} else {
//
// Unicast packets do not need to verified.
//
consumed = sizeof(CNP_HEADER); }
//
// Deliver the packet to the appropriate upper layer protocol.
//
*BytesTaken = consumed; BytesIndicated -= consumed; BytesAvailable -= consumed;
return (CnpIndicateData( network, cnpHeader->NextHeader, cnpHeader->SourceAddress, cnpRecvFlags, ReceiveDatagramFlags, BytesIndicated, BytesAvailable, BytesTaken, (PUCHAR)Tsdu + consumed, Irp ) );
error_exit:
//
// Something went wrong. Drop the packet by
// indicating that we consumed it.
//
*BytesTaken = BytesAvailable; *Irp = NULL;
CnTrace(CNP_RECV_ERROR, CnpTraceDropReceive, "[CNP] Dropped packet from net %u, BI %u, BA %u, CNP flags %x.", network->Id, // LOGULONG
BytesIndicated, // LOGULONG
BytesAvailable, // LOGULONG
cnpRecvFlags // LOGXLONG
);
IF_CNDBG(CN_DEBUG_CNPRECV) { CNPRINT(("[CNP] Dropped packet from net %u, BI %u, BA %u.\n", network->Id, BytesIndicated, BytesAvailable)); }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(STATUS_SUCCESS);
} // CnpTdiReceiveDatagramHandler
|