/*++ 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