/*++ Copyright (c) 1997 Microsoft Corporation Module Name: cxaddr.c Abstract: TDI Address Object management code. Author: Mike Massa (mikemas) February 20, 1997 Revision History: Who When What -------- -------- ---------------------------------------------- mikemas 02-20-97 created Notes: --*/ #include "precomp.h" #pragma hdrstop #include "cxaddr.tmh" #define CX_WILDCARD_PORT 0 // 0 means assign a port. #define CX_MIN_USER_PORT 1025 // Minimum value for a wildcard port #define CX_MAX_USER_PORT 5000 // Maximim value for a user port. #define CX_NUM_USER_PORTS (CX_MAX_USER_PORT - CX_MIN_USER_PORT + 1) // // Address Object Data // USHORT CxNextUserPort = CX_MIN_USER_PORT; LIST_ENTRY CxAddrObjTable[CX_ADDROBJ_TABLE_SIZE]; #if DBG CN_LOCK CxAddrObjTableLock = {0,0}; #else // DBG CN_LOCK CxAddrObjTableLock = 0; #endif // DBG NTSTATUS CxParseTransportAddress( IN TRANSPORT_ADDRESS UNALIGNED *AddrList, IN ULONG AddressListLength, OUT CL_NODE_ID * Node, OUT PUSHORT Port ) { LONG i; PTA_ADDRESS currentAddr; TDI_ADDRESS_CLUSTER UNALIGNED * validAddr; if (AddressListLength >= sizeof(TA_CLUSTER_ADDRESS)) { // // Find an address we can use. // currentAddr = (PTA_ADDRESS) AddrList->Address; for (i = 0; i < AddrList->TAAddressCount; i++) { if ( (currentAddr->AddressType == TDI_ADDRESS_TYPE_CLUSTER) && (currentAddr->AddressLength >= TDI_ADDRESS_LENGTH_CLUSTER) ) { validAddr = (TDI_ADDRESS_CLUSTER UNALIGNED *) currentAddr->Address; *Node = validAddr->Node; *Port = validAddr->Port; return(STATUS_SUCCESS); } else { if ( AddressListLength >= (currentAddr->AddressLength + sizeof(TA_CLUSTER_ADDRESS)) ) { AddressListLength -= currentAddr->AddressLength; currentAddr = (PTA_ADDRESS) ( currentAddr->Address + currentAddr->AddressLength ); } else { break; } } } } return(STATUS_INVALID_ADDRESS_COMPONENT); } // CxParseTransportAddress PCX_ADDROBJ CxFindAddressObject( IN USHORT Port ) /*++ Notes: Called with AO Table lock held. Returns with address object lock held. --*/ { PLIST_ENTRY entry; ULONG hashBucket = CX_ADDROBJ_TABLE_HASH(Port); PCX_ADDROBJ addrObj; for ( entry = CxAddrObjTable[hashBucket].Flink; entry != &(CxAddrObjTable[hashBucket]); entry = entry->Flink ) { addrObj = CONTAINING_RECORD( entry, CX_ADDROBJ, AOTableLinkage ); if (addrObj->LocalPort == Port) { CnAcquireLockAtDpc(&(addrObj->Lock)); addrObj->Irql = DISPATCH_LEVEL; return(addrObj); } } return(NULL); } // CxFindAddressObject NTSTATUS CxOpenAddress( OUT PCN_FSCONTEXT * CnFsContext, IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, IN ULONG TransportAddressLength ) { PCX_ADDROBJ addrObj, oldAddrObj; NTSTATUS status; CL_NODE_ID nodeId; USHORT port; CN_IRQL tableIrql; ULONG i; ULONG hashBucket; status = CxParseTransportAddress( TransportAddress, TransportAddressLength, &nodeId, &port ); if (status != STATUS_SUCCESS) { IF_CNDBG(CN_DEBUG_ADDROBJ) { CNPRINT(( "[Clusnet] Open address - failed to parse address, status %lx\n", status )); } return(status); } addrObj = CnAllocatePool(sizeof(CX_ADDROBJ)); if (addrObj == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } RtlZeroMemory(addrObj, sizeof(CX_ADDROBJ)); CN_INIT_SIGNATURE(&(addrObj->FsContext), CX_ADDROBJ_SIG); CnInitializeLock(&(addrObj->Lock), CX_ADDROBJ_LOCK); addrObj->Flags |= CX_AO_FLAG_CHECKSTATE; CnAcquireLock(&CxAddrObjTableLock, &tableIrql); // If no port is specified we have to assign one. If there is a // port specified, we need to make sure that the port isn't // already open. If the input address is a wildcard, we need to // assign one ourselves. if (port == CX_WILDCARD_PORT) { port = CxNextUserPort; for (i = 0; i < CX_NUM_USER_PORTS; i++, port++) { if (port > CX_MAX_USER_PORT) { port = CX_MIN_USER_PORT; } oldAddrObj = CxFindAddressObject(port); if (oldAddrObj == NULL) { IF_CNDBG(CN_DEBUG_ADDROBJ) { CNPRINT(("[Clusnet] Assigning port %u\n", port)); } break; // Found an unused port. } CnReleaseLockFromDpc(&(oldAddrObj->Lock)); } if (i == CX_NUM_USER_PORTS) { // Couldn't find a free port. IF_CNDBG(CN_DEBUG_ADDROBJ) { CNPRINT(( "[Clusnet] No free wildcard ports.\n" )); } CnReleaseLock(&CxAddrObjTableLock, tableIrql); CnFreePool(addrObj); return (STATUS_TOO_MANY_ADDRESSES); } CxNextUserPort = port + 1; } else { // Address was specificed oldAddrObj = CxFindAddressObject(port); if (oldAddrObj != NULL) { IF_CNDBG(CN_DEBUG_ADDROBJ) { CNPRINT(( "[Clusnet] Port %u is already in use.\n", port )); } CnReleaseLockFromDpc(&(oldAddrObj->Lock)); CnReleaseLock(&CxAddrObjTableLock, tableIrql); CnFreePool(addrObj); return (STATUS_ADDRESS_ALREADY_EXISTS); } } addrObj->LocalPort = port; hashBucket = CX_ADDROBJ_TABLE_HASH(port); InsertHeadList( &(CxAddrObjTable[hashBucket]), &(addrObj->AOTableLinkage) ); *CnFsContext = (PCN_FSCONTEXT) addrObj; IF_CNDBG(CN_DEBUG_ADDROBJ) { CNPRINT(( "[Clusnet] Opened address object %p for port %u\n", addrObj, port )); } CnTrace( CDP_ADDR_DETAIL, CdpTraceOpenAO, "[Clusnet] Opened address object %p for port %u.", addrObj, port ); CnReleaseLock(&CxAddrObjTableLock, tableIrql); return(STATUS_SUCCESS); } // CxOpenAddress NTSTATUS CxCloseAddress( IN PCN_FSCONTEXT CnFsContext ) { PCX_ADDROBJ addrObj = (PCX_ADDROBJ) CnFsContext; CN_IRQL tableIrql; IF_CNDBG(CN_DEBUG_ADDROBJ) { CNPRINT(( "[Clusnet] Closed address object %p for port %u\n", addrObj, addrObj->LocalPort )); } CnTrace( CDP_ADDR_DETAIL, CdpTraceCloseAO, "[Clusnet] Closed address object %p for port %u.", addrObj, addrObj->LocalPort ); CnAcquireLock(&CxAddrObjTableLock, &tableIrql); CnAcquireLockAtDpc(&(addrObj->Lock)); RemoveEntryList(&(addrObj->AOTableLinkage)); CnReleaseLockFromDpc(&(addrObj->Lock)); CnReleaseLock(&CxAddrObjTableLock, tableIrql); // // The address object memory will be freed by the common code. // return(STATUS_SUCCESS); } // CxCloseAddress NTSTATUS CxSetEventHandler( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { NTSTATUS status = STATUS_SUCCESS; PTDI_REQUEST_KERNEL_SET_EVENT request; PCX_ADDROBJ addrObj; CN_IRQL irql; // // Since this ioctl registers a callback function pointer, ensure // that it was issued by a kernel-mode component. // if (Irp->RequestorMode != KernelMode) { return(STATUS_ACCESS_DENIED); } addrObj = (PCX_ADDROBJ) IrpSp->FileObject->FsContext; request = (PTDI_REQUEST_KERNEL_SET_EVENT) &(IrpSp->Parameters); IF_CNDBG(CN_DEBUG_ADDROBJ) { CNPRINT(( "[Clusnet] TdiSetEvent type %u handler %p context %p\n", request->EventType, request->EventHandler, request->EventContext )); } CnAcquireLock(&(addrObj->Lock), &irql); switch (request->EventType) { case TDI_EVENT_ERROR: addrObj->ErrorHandler = request->EventHandler; addrObj->ErrorContext = request->EventContext; break; case TDI_EVENT_RECEIVE_DATAGRAM: addrObj->ReceiveDatagramHandler = request->EventHandler; addrObj->ReceiveDatagramContext = request->EventContext; break; case TDI_EVENT_CHAINED_RECEIVE_DATAGRAM: addrObj->ChainedReceiveDatagramHandler = request->EventHandler; addrObj->ChainedReceiveDatagramContext = request->EventContext; break; default: status = STATUS_INVALID_PARAMETER; break; } CnReleaseLock(&(addrObj->Lock), irql); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } // CxSetEventHandler VOID CxBuildTdiAddress( PVOID Buffer, CL_NODE_ID Node, USHORT Port, BOOLEAN Verified ) /*++ Routine Description: Called when we need to build a TDI address structure. We fill in the specifed buffer with the correct information in the correct format. Arguments: Buffer - Buffer to be filled in as TDI address structure. Node - Node ID to fill in. Port - Port to be filled in. Verified - During a receive, whether clusnet verified the signature and data Return Value: Nothing --*/ { PTRANSPORT_ADDRESS xportAddr; PTA_ADDRESS taAddr; xportAddr = (PTRANSPORT_ADDRESS) Buffer; xportAddr->TAAddressCount = 1; taAddr = xportAddr->Address; taAddr->AddressType = TDI_ADDRESS_TYPE_CLUSTER; taAddr->AddressLength = sizeof(TDI_ADDRESS_CLUSTER); ((PTDI_ADDRESS_CLUSTER) taAddr->Address)->Port = Port; ((PTDI_ADDRESS_CLUSTER) taAddr->Address)->Node = Node; ((PTDI_ADDRESS_CLUSTER) taAddr->Address)->ReservedMBZ = ((Verified) ? 1 : 0); }