1051 lines
28 KiB
C
1051 lines
28 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
cnpsend.c
|
||
|
||
Abstract:
|
||
|
||
Cluster Network Protocol send 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 "cnpsend.tmh"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(PAGE, CnpCreateSendRequestPool)
|
||
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
|
||
//
|
||
// Private Utility Functions
|
||
//
|
||
PCN_RESOURCE
|
||
CnpCreateSendRequest(
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCNP_SEND_REQUEST_POOL_CONTEXT context = Context;
|
||
PCNP_SEND_REQUEST request;
|
||
PCNP_HEADER cnpHeader;
|
||
ULONG cnpHeaderSize;
|
||
|
||
//
|
||
// The CNP header size includes signature data for version 2.
|
||
//
|
||
cnpHeaderSize = sizeof(CNP_HEADER);
|
||
if (context->CnpVersionNumber == 2) {
|
||
cnpHeaderSize += CNP_SIG_LENGTH(CX_SIGNATURE_LENGTH);
|
||
}
|
||
|
||
//
|
||
// Allocate a new send request. Include space for the upper protocol
|
||
// and CNP headers.
|
||
//
|
||
request = CnAllocatePool(
|
||
sizeof(CNP_SEND_REQUEST) + cnpHeaderSize +
|
||
((ULONG) context->UpperProtocolHeaderLength) +
|
||
context->UpperProtocolContextSize
|
||
);
|
||
|
||
if (request != NULL) {
|
||
//
|
||
// Allocate an MDL to describe the CNP and upper transport headers.
|
||
//
|
||
|
||
// On I64 Context has to be 64 bit aligned,
|
||
// let's put it before CnpHeader
|
||
if (context->UpperProtocolContextSize > 0) {
|
||
request->UpperProtocolContext = request + 1;
|
||
request->CnpHeader = ( ((PCHAR) request->UpperProtocolContext) +
|
||
context->UpperProtocolContextSize );
|
||
} else {
|
||
request->UpperProtocolContext = NULL;
|
||
request->CnpHeader = request + 1;
|
||
}
|
||
|
||
request->HeaderMdl = IoAllocateMdl(
|
||
request->CnpHeader,
|
||
(ULONG) (context->UpperProtocolHeaderLength +
|
||
cnpHeaderSize),
|
||
FALSE,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
if (request->HeaderMdl != NULL) {
|
||
MmBuildMdlForNonPagedPool(request->HeaderMdl);
|
||
|
||
//
|
||
// Finish initializing the request.
|
||
//
|
||
request->UpperProtocolHeader = ( ((PCHAR) request->CnpHeader) +
|
||
cnpHeaderSize );
|
||
|
||
request->UpperProtocolHeaderLength =
|
||
context->UpperProtocolHeaderLength;
|
||
|
||
RtlZeroMemory(
|
||
&(request->TdiSendDatagramInfo),
|
||
sizeof(request->TdiSendDatagramInfo)
|
||
);
|
||
|
||
request->McastGroup = NULL;
|
||
|
||
//
|
||
// Fill in the constant CNP header values.
|
||
//
|
||
cnpHeader = request->CnpHeader;
|
||
cnpHeader->Version = context->CnpVersionNumber;
|
||
cnpHeader->NextHeader = context->UpperProtocolNumber;
|
||
|
||
return((PCN_RESOURCE) request);
|
||
}
|
||
|
||
CnFreePool(request);
|
||
}
|
||
|
||
return(NULL);
|
||
|
||
} // CnpCreateSendRequest
|
||
|
||
|
||
VOID
|
||
CnpDeleteSendRequest(
|
||
PCN_RESOURCE Resource
|
||
)
|
||
{
|
||
PCNP_SEND_REQUEST request = (PCNP_SEND_REQUEST) Resource;
|
||
|
||
IoFreeMdl(request->HeaderMdl);
|
||
CnFreePool(request);
|
||
|
||
return;
|
||
|
||
} // CnpDeleteSendRequest
|
||
|
||
|
||
//
|
||
// Routines Exported within the Cluster Transport
|
||
//
|
||
PCN_RESOURCE_POOL
|
||
CnpCreateSendRequestPool(
|
||
IN UCHAR CnpVersionNumber,
|
||
IN UCHAR UpperProtocolNumber,
|
||
IN USHORT UpperProtocolHeaderSize,
|
||
IN USHORT UpperProtocolContextSize,
|
||
IN USHORT PoolDepth
|
||
)
|
||
{
|
||
PCN_RESOURCE_POOL pool;
|
||
PCNP_SEND_REQUEST_POOL_CONTEXT context;
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
CnAssert((0xFFFF - sizeof(CNP_HEADER)) >= UpperProtocolHeaderSize);
|
||
|
||
pool = CnAllocatePool(
|
||
sizeof(CN_RESOURCE_POOL) +
|
||
sizeof(CNP_SEND_REQUEST_POOL_CONTEXT)
|
||
);
|
||
|
||
if (pool != NULL) {
|
||
context = (PCNP_SEND_REQUEST_POOL_CONTEXT) (pool + 1);
|
||
|
||
context->UpperProtocolNumber = UpperProtocolNumber;
|
||
context->UpperProtocolHeaderLength = UpperProtocolHeaderSize;
|
||
context->UpperProtocolContextSize = UpperProtocolContextSize;
|
||
context->CnpVersionNumber = CnpVersionNumber;
|
||
|
||
CnInitializeResourcePool(
|
||
pool,
|
||
PoolDepth,
|
||
CnpCreateSendRequest,
|
||
context,
|
||
CnpDeleteSendRequest
|
||
);
|
||
}
|
||
|
||
return(pool);
|
||
|
||
} // CnpCreateSendRequestPool
|
||
|
||
|
||
|
||
VOID
|
||
CnpCompleteSendPacketCommon(
|
||
IN PIRP Irp,
|
||
IN PCNP_SEND_REQUEST Request,
|
||
IN PMDL DataMdl
|
||
)
|
||
{
|
||
PCNP_NETWORK network = Request->Network;
|
||
ULONG bytesSent = (ULONG)Irp->IoStatus.Information;
|
||
NTSTATUS status = Irp->IoStatus.Status;
|
||
PCNP_HEADER cnpHeader = Request->CnpHeader;
|
||
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// Subtract the CNP header from the count of bytes sent.
|
||
//
|
||
if (bytesSent >= sizeof(CNP_HEADER)) {
|
||
bytesSent -= sizeof(CNP_HEADER);
|
||
}
|
||
else {
|
||
CnAssert(FALSE);
|
||
bytesSent = 0;
|
||
}
|
||
|
||
//
|
||
// If CNP signed the message, subtract the signature
|
||
// data from the count of bytes sent.
|
||
//
|
||
if (cnpHeader->Version == CNP_VERSION_MULTICAST) {
|
||
CNP_SIGNATURE UNALIGNED * cnpSig;
|
||
|
||
cnpSig = (CNP_SIGNATURE UNALIGNED *)(cnpHeader + 1);
|
||
|
||
if (bytesSent >= (ULONG)CNP_SIGHDR_LENGTH &&
|
||
bytesSent >= cnpSig->PayloadOffset) {
|
||
bytesSent -= cnpSig->PayloadOffset;
|
||
} else {
|
||
CnAssert(FALSE);
|
||
bytesSent = 0;
|
||
}
|
||
}
|
||
|
||
CnTrace(CNP_SEND_DETAIL, CnpTraceSendComplete,
|
||
"[CNP] Send of packet to node %u on net %u complete, "
|
||
"bytes sent %u.",
|
||
cnpHeader->DestinationAddress, // LOGULONG
|
||
network->Id, // LOGULONG
|
||
bytesSent // LOGULONG
|
||
);
|
||
}
|
||
else {
|
||
//
|
||
// It is possible to reach this path with
|
||
// status = c0000240 (STATUS_REQUEST_ABORTED) and
|
||
// bytesSent > 0.
|
||
//
|
||
bytesSent = 0;
|
||
|
||
CnTrace(CNP_SEND_ERROR, CnpTraceSendFailedBelow,
|
||
"[CNP] Tcpip failed to send packet to node %u on net %u, "
|
||
"data len %u, status %!status!",
|
||
cnpHeader->DestinationAddress, // LOGULONG
|
||
network->Id, // LOGULONG
|
||
cnpHeader->PayloadLength, // LOGUSHORT
|
||
status // LOGSTATUS
|
||
);
|
||
}
|
||
|
||
//
|
||
// Remove the active reference we put on the network.
|
||
//
|
||
CnAcquireLock(&(network->Lock), &(network->Irql));
|
||
CnpActiveDereferenceNetwork(network);
|
||
|
||
//
|
||
// Free the TDI address buffer
|
||
//
|
||
CnFreePool(Request->TdiSendDatagramInfo.RemoteAddress);
|
||
|
||
//
|
||
// Call the upper protocol's completion routine
|
||
//
|
||
if (Request->CompletionRoutine) {
|
||
(*(Request->CompletionRoutine))(
|
||
status,
|
||
&bytesSent,
|
||
Request,
|
||
DataMdl
|
||
);
|
||
}
|
||
|
||
//
|
||
// Update the Information field of the completed IRP to
|
||
// reflect the actual bytes sent (adjusted for the CNP
|
||
// and upper protocol headers).
|
||
//
|
||
Irp->IoStatus.Information = bytesSent;
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return;
|
||
|
||
} // CnpCompleteSendPacketCommon
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CnpCompleteSendPacketNewIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCNP_SEND_REQUEST request = Context;
|
||
PIRP upperIrp = request->UpperProtocolIrp;
|
||
PMDL dataMdl;
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
//
|
||
// Unlink the data MDL chain from the header MDL.
|
||
//
|
||
CnAssert(Irp->MdlAddress == request->HeaderMdl);
|
||
dataMdl = request->HeaderMdl->Next;
|
||
request->HeaderMdl->Next = NULL;
|
||
Irp->MdlAddress = NULL;
|
||
|
||
CnpCompleteSendPacketCommon(Irp, request, dataMdl);
|
||
|
||
//
|
||
// Complete the upper-level IRP, if there is one
|
||
//
|
||
if (upperIrp != NULL) {
|
||
|
||
IF_CNDBG( CN_DEBUG_CNPSEND )
|
||
CNPRINT(("[CNP] CnpCompleteSendPacketNewIrp calling "
|
||
"CnCompleteRequest for IRP %p with status "
|
||
"%08x\n",
|
||
upperIrp, Irp->IoStatus.Status));
|
||
|
||
CnAcquireCancelSpinLock(&(upperIrp->CancelIrql));
|
||
CnCompletePendingRequest(
|
||
upperIrp,
|
||
Irp->IoStatus.Status, // status
|
||
(ULONG)Irp->IoStatus.Information // bytes returned
|
||
);
|
||
|
||
//
|
||
// The IoCancelSpinLock was released by the completion routine.
|
||
//
|
||
}
|
||
|
||
//
|
||
// Free the new IRP
|
||
//
|
||
IoFreeIrp(Irp);
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
||
} // CnpCompleteSendPacketNewIrp
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CnpCompleteSendPacketReuseIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCNP_SEND_REQUEST request = Context;
|
||
PMDL dataMdl;
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
//
|
||
// Unlink the data MDL chain from the header MDL.
|
||
//
|
||
CnAssert(Irp->MdlAddress == request->HeaderMdl);
|
||
dataMdl = request->HeaderMdl->Next;
|
||
request->HeaderMdl->Next = NULL;
|
||
|
||
//
|
||
// Restore the requestor mode of the upper protocol IRP.
|
||
//
|
||
Irp->RequestorMode = request->UpperProtocolIrpMode;
|
||
|
||
//
|
||
// Restore the MDL of the upper protocol IRP.
|
||
//
|
||
Irp->MdlAddress = request->UpperProtocolMdl;
|
||
|
||
CnpCompleteSendPacketCommon(Irp, request, dataMdl);
|
||
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
IF_CNDBG( CN_DEBUG_CNPSEND )
|
||
CNPRINT(("[CNP] CnpCompleteSendPacketReuseIrp returning "
|
||
"IRP %p to I/O Manager\n",
|
||
Irp));
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
} // CnpCompleteSendPacketReuseIrp
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CnpSendPacket(
|
||
IN PCNP_SEND_REQUEST SendRequest,
|
||
IN CL_NODE_ID DestNodeId,
|
||
IN PMDL DataMdl,
|
||
IN USHORT DataLength,
|
||
IN BOOLEAN CheckDestState,
|
||
IN CL_NETWORK_ID NetworkId OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Main send routine for CNP. Handles unicast and multicast
|
||
sends.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PCNP_HEADER cnpHeader = SendRequest->CnpHeader;
|
||
PIRP upperIrp = SendRequest->UpperProtocolIrp;
|
||
CN_IRQL tableIrql;
|
||
BOOLEAN multicast = FALSE;
|
||
CL_NETWORK_ID networkId = NetworkId;
|
||
CN_IRQL cancelIrql;
|
||
BOOLEAN cnComplete = FALSE;
|
||
BOOLEAN destNodeLocked = FALSE;
|
||
PCNP_NODE destNode;
|
||
ULONG sigDataLen;
|
||
PCNP_INTERFACE interface;
|
||
PCNP_NETWORK network;
|
||
BOOLEAN networkReferenced = FALSE;
|
||
PIRP irp;
|
||
PVOID addressBuffer = NULL;
|
||
PIO_COMPLETION_ROUTINE compRoutine;
|
||
PDEVICE_OBJECT targetDeviceObject;
|
||
PFILE_OBJECT targetFileObject;
|
||
BOOLEAN mcastGroupReferenced = FALSE;
|
||
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
CNP_LOCK_RANGE, // Forbidden
|
||
CNP_PRECEEDING_LOCK_RANGE // Maximum
|
||
);
|
||
|
||
IF_CNDBG( CN_DEBUG_CNPSEND )
|
||
CNPRINT(("[CNP] CnpSendPacket called with upper IRP %p\n",
|
||
upperIrp));
|
||
|
||
//
|
||
// Make all the tests to see if we can send the packet.
|
||
//
|
||
|
||
//
|
||
// Acquire the node table lock to match the destination node id
|
||
// to a node object.
|
||
//
|
||
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
|
||
|
||
if (CnpNodeTable == NULL) {
|
||
CnReleaseLock(&CnpNodeTableLock, tableIrql);
|
||
status = STATUS_NETWORK_UNREACHABLE;
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Fill in the local node ID while we still hold the node table lock.
|
||
//
|
||
CnAssert(CnLocalNodeId != ClusterInvalidNodeId);
|
||
cnpHeader->SourceAddress = CnLocalNodeId;
|
||
|
||
//
|
||
// Check first if the destination node id indicates that this is
|
||
// a multicast.
|
||
//
|
||
if (DestNodeId == ClusterAnyNodeId) {
|
||
|
||
//
|
||
// This is a multicast. For multicasts, we use the local
|
||
// node in place of the dest node to validate the network
|
||
// and interface.
|
||
//
|
||
multicast = TRUE;
|
||
destNode = CnpLockedFindNode(CnLocalNodeId, tableIrql);
|
||
}
|
||
|
||
//
|
||
// Not a multicast. The destination node id must be valid.
|
||
//
|
||
else if (!CnIsValidNodeId(DestNodeId)) {
|
||
CnReleaseLock(&CnpNodeTableLock, tableIrql);
|
||
status = STATUS_INVALID_ADDRESS_COMPONENT;
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Find the destination node object in the node table.
|
||
//
|
||
else {
|
||
destNode = CnpLockedFindNode(DestNodeId, tableIrql);
|
||
}
|
||
|
||
//
|
||
// The NodeTableLock was released. Verify that we know about
|
||
// the destination node.
|
||
//
|
||
if (destNode == NULL) {
|
||
status = STATUS_HOST_UNREACHABLE;
|
||
goto error_exit;
|
||
}
|
||
|
||
destNodeLocked = TRUE;
|
||
|
||
//
|
||
// CNP multicast messages must be signed.
|
||
//
|
||
if (multicast) {
|
||
|
||
CnAssert(((CNP_HEADER UNALIGNED *)(SendRequest->CnpHeader))
|
||
->Version = CNP_VERSION_MULTICAST);
|
||
|
||
//
|
||
// Sign the data, starting with the upper protocol header
|
||
// and finishing with the data payload.
|
||
//
|
||
// If we are requesting the current best multicast network,
|
||
// we need to make sure that the mcast group data structure
|
||
// is dereferenced.
|
||
//
|
||
mcastGroupReferenced = (BOOLEAN)(networkId == ClusterAnyNetworkId);
|
||
|
||
status = CnpSignMulticastMessage(
|
||
SendRequest,
|
||
DataMdl,
|
||
&networkId,
|
||
&sigDataLen
|
||
);
|
||
if (status != STATUS_SUCCESS) {
|
||
mcastGroupReferenced = FALSE;
|
||
goto error_exit;
|
||
}
|
||
|
||
|
||
} else {
|
||
sigDataLen = 0;
|
||
}
|
||
|
||
//
|
||
// Choose the destination interface.
|
||
//
|
||
if (networkId != ClusterAnyNetworkId) {
|
||
|
||
//
|
||
// we really want to send this packet over the indicated
|
||
// network. walk the node's interface list matching the
|
||
// supplied network id to the interface's network ID and
|
||
// send the packet on that interface
|
||
//
|
||
|
||
PLIST_ENTRY entry;
|
||
|
||
for (entry = destNode->InterfaceList.Flink;
|
||
entry != &(destNode->InterfaceList);
|
||
entry = entry->Flink
|
||
)
|
||
{
|
||
interface = CONTAINING_RECORD(
|
||
entry,
|
||
CNP_INTERFACE,
|
||
NodeLinkage
|
||
);
|
||
|
||
if ( interface->Network->Id == networkId ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( entry == &destNode->InterfaceList ) {
|
||
//
|
||
// no network object with the specified ID. if
|
||
// this is the network the sender designated,
|
||
// fail the send.
|
||
//
|
||
status = STATUS_NETWORK_UNREACHABLE;
|
||
goto error_exit;
|
||
}
|
||
} else {
|
||
interface = destNode->CurrentInterface;
|
||
}
|
||
|
||
//
|
||
// Verify that we know about the destination interface.
|
||
//
|
||
if (interface == NULL) {
|
||
// No interface for node. Must be down. Note that the
|
||
// HOST_DOWN error code should cause the caller to give
|
||
// up immediately.
|
||
status = STATUS_HOST_DOWN;
|
||
// status = STATUS_HOST_UNREACHABLE;
|
||
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Verify that everything is online. If all looks okay,
|
||
// take an active reference on the network.
|
||
//
|
||
// For unicasts, verify the state of destination interface,
|
||
// node, and intervening network.
|
||
//
|
||
// For multicasts, verify the state of the network and
|
||
// its multicast capability.
|
||
//
|
||
network = interface->Network;
|
||
|
||
if ( (!multicast)
|
||
&&
|
||
( (interface->State > ClusnetInterfaceStateOfflinePending)
|
||
&&
|
||
(destNode->CommState == ClusnetNodeCommStateOnline)
|
||
)
|
||
)
|
||
{
|
||
//
|
||
// Everything checks out. Reference the network so
|
||
// it can't go offline while we are using it.
|
||
//
|
||
CnAcquireLockAtDpc(&(network->Lock));
|
||
CnAssert(network->State >= ClusnetNetworkStateOfflinePending);
|
||
CnpActiveReferenceNetwork(network);
|
||
CnReleaseLockFromDpc(&(network->Lock));
|
||
networkReferenced = TRUE;
|
||
|
||
} else {
|
||
//
|
||
// Either the node is not online or this is a
|
||
// multicast (in which case we don't bother checking
|
||
// the status of all the nodes). Figure out what to do.
|
||
//
|
||
if (!multicast && CheckDestState) {
|
||
//
|
||
// Caller doesn't want to send to a down node.
|
||
// Bail out. Note that the HOST_DOWN error code
|
||
// should cause the caller to give up immediately.
|
||
//
|
||
status = STATUS_HOST_DOWN;
|
||
// status = STATUS_HOST_UNREACHABLE;
|
||
|
||
goto error_exit;
|
||
}
|
||
|
||
CnAcquireLockAtDpc(&(network->Lock));
|
||
|
||
if (network->State <= ClusnetNetworkStateOfflinePending) {
|
||
//
|
||
// The chosen network is not online.
|
||
// Bail out.
|
||
//
|
||
CnReleaseLockFromDpc(&(network->Lock));
|
||
status = STATUS_HOST_UNREACHABLE;
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Multicast checks.
|
||
//
|
||
if (multicast) {
|
||
|
||
//
|
||
// Verify that the chosen network has been
|
||
// enabled for multicast.
|
||
//
|
||
if (!CnpIsNetworkMulticastCapable(network)) {
|
||
CnReleaseLockFromDpc(&(network->Lock));
|
||
status = STATUS_HOST_UNREACHABLE;
|
||
goto error_exit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// The network is online, even if the host isn't.
|
||
// The caller doesn't care. Go for it.
|
||
//
|
||
CnpActiveReferenceNetwork(network);
|
||
CnReleaseLockFromDpc(&(network->Lock));
|
||
networkReferenced = TRUE;
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer for the destination address.
|
||
//
|
||
addressBuffer = CnAllocatePool(interface->TdiAddressLength);
|
||
|
||
if (addressBuffer == NULL) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Fill in the address buffer, and save it in the send
|
||
// request data structure.
|
||
//
|
||
if (multicast) {
|
||
|
||
PCNP_MULTICAST_GROUP mcastGroup = SendRequest->McastGroup;
|
||
|
||
CnAssert(mcastGroup != NULL);
|
||
|
||
CnAssert(
|
||
CnpIsIPv4McastTransportAddress(mcastGroup->McastTdiAddress)
|
||
);
|
||
CnAssert(
|
||
mcastGroup->McastTdiAddressLength == interface->TdiAddressLength
|
||
);
|
||
|
||
RtlMoveMemory(
|
||
addressBuffer,
|
||
mcastGroup->McastTdiAddress,
|
||
mcastGroup->McastTdiAddressLength
|
||
);
|
||
|
||
SendRequest->TdiSendDatagramInfo.RemoteAddressLength =
|
||
mcastGroup->McastTdiAddressLength;
|
||
|
||
if (mcastGroupReferenced) {
|
||
CnpDereferenceMulticastGroup(mcastGroup);
|
||
mcastGroupReferenced = FALSE;
|
||
SendRequest->McastGroup = NULL;
|
||
}
|
||
|
||
targetFileObject = network->DatagramFileObject;
|
||
targetDeviceObject = network->DatagramDeviceObject;
|
||
|
||
} else {
|
||
|
||
CnAssert(mcastGroupReferenced == FALSE);
|
||
|
||
RtlMoveMemory(
|
||
addressBuffer,
|
||
&(interface->TdiAddress),
|
||
interface->TdiAddressLength
|
||
);
|
||
|
||
SendRequest->TdiSendDatagramInfo.RemoteAddressLength =
|
||
interface->TdiAddressLength;
|
||
|
||
targetFileObject = network->DatagramFileObject;
|
||
targetDeviceObject = network->DatagramDeviceObject;
|
||
}
|
||
|
||
SendRequest->TdiSendDatagramInfo.RemoteAddress =
|
||
addressBuffer;
|
||
|
||
//
|
||
// Release the node lock.
|
||
//
|
||
CnReleaseLock(&(destNode->Lock), destNode->Irql);
|
||
destNodeLocked = FALSE;
|
||
|
||
//
|
||
// If there is an upper protocol IRP, see
|
||
// if it has enough stack locations.
|
||
//
|
||
if ( (upperIrp != NULL)
|
||
&&
|
||
(CnpIsIrpStackSufficient(upperIrp, targetDeviceObject))
|
||
) {
|
||
|
||
//
|
||
// We can use the upper protocol IRP.
|
||
//
|
||
irp = upperIrp;
|
||
compRoutine = CnpCompleteSendPacketReuseIrp;
|
||
|
||
//
|
||
// Ensure that IRP is marked as a kernel request,
|
||
// but first save the current requestor mode so
|
||
// that it can be restored later.
|
||
//
|
||
SendRequest->UpperProtocolIrpMode = irp->RequestorMode;
|
||
irp->RequestorMode = KernelMode;
|
||
|
||
//
|
||
// Save the upper protocol IRP MDL to restore
|
||
// later. This is probably the same as DataMdl,
|
||
// but we don't want to make any assumptions.
|
||
//
|
||
SendRequest->UpperProtocolMdl = irp->MdlAddress;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We cannot use the upper protocol IRP.
|
||
//
|
||
// If there is an upper protocol IRP, it needs
|
||
// to be marked pending.
|
||
//
|
||
if (upperIrp != NULL) {
|
||
|
||
CnAcquireCancelSpinLock(&cancelIrql);
|
||
|
||
status = CnMarkRequestPending(
|
||
upperIrp,
|
||
IoGetCurrentIrpStackLocation(upperIrp),
|
||
NULL
|
||
);
|
||
|
||
CnReleaseCancelSpinLock(cancelIrql);
|
||
|
||
if (status == STATUS_CANCELLED) {
|
||
//
|
||
// Bail out
|
||
//
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto error_exit;
|
||
|
||
} else {
|
||
//
|
||
// If IoAllocateIrp fails, we need
|
||
// to call CnCompletePendingRequest
|
||
// now that we've called
|
||
// CnMarkRequestPending.
|
||
//
|
||
cnComplete = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate a new IRP
|
||
//
|
||
irp = IoAllocateIrp(
|
||
targetDeviceObject->StackSize,
|
||
FALSE
|
||
);
|
||
if (irp == NULL) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Use the completion routine for having
|
||
// allocated a new IRP
|
||
//
|
||
compRoutine = CnpCompleteSendPacketNewIrp;
|
||
|
||
//
|
||
// Fill in IRP fields that are not specific
|
||
// to any one stack location.
|
||
//
|
||
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 = PsGetCurrentThread();
|
||
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
||
}
|
||
|
||
//
|
||
// Ok, we can finally send the packet.
|
||
//
|
||
SendRequest->Network = network;
|
||
|
||
//
|
||
// Link the data MDL chain after the header MDL.
|
||
//
|
||
SendRequest->HeaderMdl->Next = DataMdl;
|
||
|
||
//
|
||
// Finish building the CNP header.
|
||
//
|
||
cnpHeader->DestinationAddress = DestNodeId;
|
||
cnpHeader->PayloadLength =
|
||
SendRequest->UpperProtocolHeaderLength + DataLength;
|
||
|
||
//
|
||
// Build the next irp stack location.
|
||
//
|
||
TdiBuildSendDatagram(
|
||
irp,
|
||
targetDeviceObject,
|
||
targetFileObject,
|
||
compRoutine,
|
||
SendRequest,
|
||
SendRequest->HeaderMdl,
|
||
cnpHeader->PayloadLength + sizeof(CNP_HEADER) + sigDataLen,
|
||
&(SendRequest->TdiSendDatagramInfo)
|
||
);
|
||
|
||
CnTrace(CNP_SEND_DETAIL, CnpTraceSend,
|
||
"[CNP] Sending packet to node %u on net %u, "
|
||
"data len %u",
|
||
cnpHeader->DestinationAddress, // LOGULONG
|
||
network->Id, // LOGULONG
|
||
cnpHeader->PayloadLength // LOGUSHORT
|
||
);
|
||
|
||
//
|
||
// Now send the packet.
|
||
//
|
||
status = IoCallDriver(
|
||
targetDeviceObject,
|
||
irp
|
||
);
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
CNP_LOCK_RANGE, // Forbidden
|
||
CNP_PRECEEDING_LOCK_RANGE // Maximum
|
||
);
|
||
|
||
return(status);
|
||
|
||
|
||
//
|
||
// The following code is only executed in an error condition,
|
||
// No send IRP has been submitted to a lower-level driver.
|
||
//
|
||
|
||
error_exit:
|
||
|
||
CnTrace(CNP_SEND_ERROR, CnpTraceSendFailedInternal,
|
||
"[CNP] Failed to send packet to node %u on net %u, "
|
||
"data len %u, status %!status!",
|
||
cnpHeader->DestinationAddress, // LOGULONG
|
||
NetworkId, // LOGULONG
|
||
cnpHeader->PayloadLength, // LOGUSHORT
|
||
status // LOGSTATUS
|
||
);
|
||
|
||
if (destNodeLocked) {
|
||
CnReleaseLock(&(destNode->Lock), destNode->Irql);
|
||
destNodeLocked = FALSE;
|
||
}
|
||
|
||
if (networkReferenced) {
|
||
//
|
||
// Remove the active reference we put on the network.
|
||
//
|
||
CnAcquireLock(&(network->Lock), &(network->Irql));
|
||
CnpActiveDereferenceNetwork(network);
|
||
networkReferenced = FALSE;
|
||
}
|
||
|
||
if (mcastGroupReferenced) {
|
||
CnAssert(SendRequest->McastGroup != NULL);
|
||
CnpDereferenceMulticastGroup(SendRequest->McastGroup);
|
||
SendRequest->McastGroup = NULL;
|
||
mcastGroupReferenced = FALSE;
|
||
}
|
||
|
||
if (addressBuffer != NULL) {
|
||
CnFreePool(addressBuffer);
|
||
}
|
||
|
||
//
|
||
// Call the upper protocol completion routine
|
||
//
|
||
if (SendRequest->CompletionRoutine) {
|
||
|
||
ULONG bytesSent = 0;
|
||
|
||
(*SendRequest->CompletionRoutine)(
|
||
status,
|
||
&bytesSent,
|
||
SendRequest,
|
||
DataMdl
|
||
);
|
||
}
|
||
|
||
//
|
||
// Complete the upper protocol IRP, if there is one
|
||
//
|
||
if (upperIrp) {
|
||
|
||
if (cnComplete) {
|
||
|
||
//
|
||
// CnMarkRequestPending was called for upperIrp.
|
||
//
|
||
IF_CNDBG( CN_DEBUG_CNPSEND )
|
||
CNPRINT(("[CNP] Calling CnCompletePendingRequest "
|
||
"for IRP %p with status %08x\n",
|
||
upperIrp, status));
|
||
|
||
CnCompletePendingRequest(upperIrp, status, 0);
|
||
|
||
} else {
|
||
|
||
IF_CNDBG( CN_DEBUG_CNPSEND )
|
||
CNPRINT(("[CNP] Completing IRP %p with status %08x\n",
|
||
upperIrp, status));
|
||
|
||
upperIrp->IoStatus.Status = status;
|
||
upperIrp->IoStatus.Information = 0;
|
||
IoCompleteRequest(upperIrp, IO_NO_INCREMENT);
|
||
}
|
||
}
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
CNP_LOCK_RANGE, // Forbidden
|
||
CNP_PRECEEDING_LOCK_RANGE // Maximum
|
||
);
|
||
|
||
return(status);
|
||
|
||
} // CnpSendPacket
|
||
|