/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: info.c Abstract: This module contains code which performs the following TDI services: o TdiQueryInformation o TdiSetInformation Environment: Kernel mode Revision History: --*/ #include "st.h" // // Useful macro to obtain the total length of an MDL chain. // #define StGetMdlChainLength(Mdl, Length) { \ PMDL _Mdl = (Mdl); \ *(Length) = 0; \ while (_Mdl) { \ *(Length) += MmGetMdlByteCount(_Mdl); \ _Mdl = _Mdl->Next; \ } \ } // // Local functions used to satisfy various requests. // VOID StStoreProviderStatistics( IN PDEVICE_CONTEXT DeviceContext, IN PTDI_PROVIDER_STATISTICS ProviderStatistics ); VOID StStoreAdapterStatus( IN PDEVICE_CONTEXT DeviceContext, IN PUCHAR SourceRouting, IN UINT SourceRoutingLength, IN PVOID StatusBuffer ); VOID StStoreNameBuffers( IN PDEVICE_CONTEXT DeviceContext, IN PVOID Buffer, IN ULONG BufferLength, IN ULONG NamesToSkip, OUT PULONG NamesWritten, OUT PULONG TotalNameCount OPTIONAL, OUT PBOOLEAN Truncated ); NTSTATUS StTdiQueryInformation( IN PDEVICE_CONTEXT DeviceContext, IN PIRP Irp ) /*++ Routine Description: This routine performs the TdiQueryInformation request for the transport provider. Arguments: Irp - the Irp for the requested operation. Return Value: NTSTATUS - status of operation. --*/ { NTSTATUS status; PIO_STACK_LOCATION irpSp; PVOID adapterStatus; PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; PTA_NETBIOS_ADDRESS broadcastAddress; PTDI_PROVIDER_STATISTICS ProviderStatistics; PTDI_CONNECTION_INFO ConnectionInfo; ULONG TargetBufferLength; LARGE_INTEGER timeout = {0,0}; PTP_CONNECTION Connection; PTP_ADDRESS_FILE AddressFile; PTP_ADDRESS Address; struct { ULONG ActivityCount; TA_NETBIOS_ADDRESS TaAddressBuffer; } AddressInfo; ULONG NamesWritten, TotalNameCount, BytesWritten; PLIST_ENTRY p; KIRQL oldirql; BOOLEAN Truncated; BOOLEAN UsedConnection; // // what type of status do we want? // irpSp = IoGetCurrentIrpStackLocation (Irp); query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&irpSp->Parameters; switch (query->QueryType) { case TDI_QUERY_CONNECTION_INFO: // // Connection info is queried on a connection, // verify this. // Connection = irpSp->FileObject->FsContext; status = StVerifyConnectionObject (Connection); if (!NT_SUCCESS (status)) { return status; } ConnectionInfo = ExAllocatePool ( NonPagedPool, sizeof (TDI_CONNECTION_INFO)); if (ConnectionInfo == NULL) { PANIC ("StQueryInfo: Cannot allocate connection info!\n"); StWriteResourceErrorLog (DeviceContext, sizeof(TDI_CONNECTION_INFO), 6); status = STATUS_INSUFFICIENT_RESOURCES; } else if ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { status = Connection->Status; ExFreePool (ConnectionInfo); } else if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { status = STATUS_INVALID_CONNECTION; ExFreePool (ConnectionInfo); } else { RtlZeroMemory ((PVOID)ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); // // Fill in connection information here. // status = TdiCopyBufferToMdl ( (PVOID)ConnectionInfo, 0L, sizeof(TDI_CONNECTION_INFO), Irp->MdlAddress, 0, &(Irp->IoStatus.Information)); ExFreePool (ConnectionInfo); } StDereferenceConnection ("query connection info", Connection); break; case TDI_QUERY_ADDRESS_INFO: // // Information about an address, can also be queried on a // connection object to get information about its address. // if (irpSp->FileObject->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { AddressFile = irpSp->FileObject->FsContext; status = StVerifyAddressObject(AddressFile); if (!NT_SUCCESS (status)) { return status; } UsedConnection = FALSE; } else if (irpSp->FileObject->FsContext2 == (PVOID)TDI_CONNECTION_FILE) { Connection = irpSp->FileObject->FsContext; status = StVerifyConnectionObject (Connection); if (!NT_SUCCESS (status)) { return status; } AddressFile = Connection->AddressFile; UsedConnection = TRUE; } else { return STATUS_INVALID_ADDRESS; } Address = AddressFile->Address; TdiBuildNetbiosAddress( Address->NetworkName->NetbiosName, (BOOLEAN)(Address->Flags & ADDRESS_FLAGS_GROUP ? TRUE : FALSE), &AddressInfo.TaAddressBuffer); // // Count the active addresses. // AddressInfo.ActivityCount = 0; ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); for (p = Address->AddressFileDatabase.Flink; p != &Address->AddressFileDatabase; p = p->Flink) { ++AddressInfo.ActivityCount; } RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); status = TdiCopyBufferToMdl ( &AddressInfo, 0, sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), Irp->MdlAddress, 0, &Irp->IoStatus.Information); if (UsedConnection) { StDereferenceConnection ("query address info", Connection); } else { StDereferenceAddress ("query address info", Address); } break; case TDI_QUERY_BROADCAST_ADDRESS: // // for this provider, the broadcast address is a zero byte name, // contained in a Transport address structure. // broadcastAddress = ExAllocatePool ( NonPagedPool, sizeof (TA_NETBIOS_ADDRESS)); if (broadcastAddress == NULL) { PANIC ("StQueryInfo: Cannot allocate broadcast address!\n"); StWriteResourceErrorLog (DeviceContext, sizeof(TA_NETBIOS_ADDRESS), 2); status = STATUS_INSUFFICIENT_RESOURCES; } else { broadcastAddress->TAAddressCount = 1; broadcastAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; broadcastAddress->Address[0].AddressLength = 0; Irp->IoStatus.Information = sizeof (broadcastAddress->TAAddressCount) + sizeof (broadcastAddress->Address[0].AddressType) + sizeof (broadcastAddress->Address[0].AddressLength); status = TdiCopyBufferToMdl ( (PVOID)broadcastAddress, 0L, Irp->IoStatus.Information, Irp->MdlAddress, 0, &(Irp->IoStatus.Information)); ExFreePool (broadcastAddress); } break; case TDI_QUERY_PROVIDER_INFO: status = TdiCopyBufferToMdl ( &(DeviceContext->Information), 0, sizeof (TDI_PROVIDER_INFO), Irp->MdlAddress, 0, &Irp->IoStatus.Information); break; case TDI_QUERY_PROVIDER_STATISTICS: StGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); if (TargetBufferLength < sizeof(TDI_PROVIDER_STATISTICS) + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))) { Irp->IoStatus.Information = 0; status = STATUS_BUFFER_OVERFLOW; } else { ProviderStatistics = ExAllocatePool( NonPagedPool, sizeof(TDI_PROVIDER_STATISTICS) + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))); if (ProviderStatistics == NULL) { PANIC ("StQueryInfo: Cannot allocate provider statistics!\n"); StWriteResourceErrorLog (DeviceContext, sizeof(TDI_PROVIDER_STATISTICS), 7); status = STATUS_INSUFFICIENT_RESOURCES; } else { StStoreProviderStatistics (DeviceContext, ProviderStatistics); status = TdiCopyBufferToMdl ( (PVOID)ProviderStatistics, 0L, sizeof(TDI_PROVIDER_STATISTICS) + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS)), Irp->MdlAddress, 0, &(Irp->IoStatus.Information)); ExFreePool (ProviderStatistics); } } break; case TDI_QUERY_SESSION_STATUS: status = STATUS_NOT_IMPLEMENTED; break; case TDI_QUERY_ADAPTER_STATUS: StGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); // // Determine if this is a local or remote query. It is // local if there is no remote address specific at all, // or if it is equal to our reserved address. // if ((query->RequestConnectionInformation != NULL) && (!RtlEqualMemory( ((PTA_NETBIOS_ADDRESS)(query->RequestConnectionInformation->RemoteAddress))-> Address[0].Address[0].NetbiosName, DeviceContext->ReservedNetBIOSAddress, NETBIOS_NAME_LENGTH))) { // // Remote, not supported here. // status = STATUS_NOT_IMPLEMENTED; } else { // // Local. // adapterStatus = ExAllocatePool ( NonPagedPool, TargetBufferLength); if (adapterStatus == NULL) { PANIC("StQueryInfo: PANIC! Could not allocate adapter status buffer\n"); StWriteResourceErrorLog (DeviceContext, TargetBufferLength, 3); return STATUS_INSUFFICIENT_RESOURCES; } StStoreAdapterStatus ( DeviceContext, NULL, 0, adapterStatus); StStoreNameBuffers ( DeviceContext, (PUCHAR)adapterStatus + sizeof(ADAPTER_STATUS), TargetBufferLength - sizeof(ADAPTER_STATUS), 0, &NamesWritten, &TotalNameCount, &Truncated); ((PADAPTER_STATUS)adapterStatus)->name_count = (WORD)TotalNameCount; BytesWritten = sizeof(ADAPTER_STATUS) + (NamesWritten * sizeof(NAME_BUFFER)); status = TdiCopyBufferToMdl ( adapterStatus, 0, BytesWritten, Irp->MdlAddress, 0, &Irp->IoStatus.Information); if (Truncated) { status = STATUS_BUFFER_OVERFLOW; } ExFreePool (adapterStatus); } break; case TDI_QUERY_FIND_NAME: // // Find name, not supported here. // status = STATUS_NOT_IMPLEMENTED; break; default: status = STATUS_INVALID_DEVICE_REQUEST; break; } return status; } /* StTdiQueryInformation */ // // Quick macros, assumes DeviceContext and ProviderStatistics exist. // #define STORE_RESOURCE_STATS_1(_ResourceNum,_ResourceId,_ResourceName) \ { \ PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ RStats->ResourceId = (_ResourceId); \ RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## MaxInUse; \ if (DeviceContext->_ResourceName ## Samples > 0) { \ RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Total / DeviceContext->_ResourceName ## Samples; \ } else { \ RStats->AverageResourceUsed = 0; \ } \ RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ } #define STORE_RESOURCE_STATS_2(_ResourceNum,_ResourceId,_ResourceName) \ { \ PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ RStats->ResourceId = (_ResourceId); \ RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## Allocated; \ RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Allocated; \ RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ } VOID StStoreProviderStatistics( IN PDEVICE_CONTEXT DeviceContext, IN PTDI_PROVIDER_STATISTICS ProviderStatistics ) /*++ Routine Description: This routine writes the TDI_PROVIDER_STATISTICS structure from the device context into ProviderStatistics. Arguments: DeviceContext - a pointer to the device context. ProviderStatistics - The buffer that holds the result. It is assumed that it is long enough. Return Value: None. --*/ { ProviderStatistics->Version = 0x0100; // // Copy all the statistics from OpenConnections to WastedSpace // Packets in one move. // RtlCopyMemory( (PVOID)&(ProviderStatistics->OpenConnections), (PVOID)&(DeviceContext->OpenConnections), sizeof(TDI_PROVIDER_STATISTICS)); // // Copy the resource statistics. // ProviderStatistics->NumberOfResources = ST_TDI_RESOURCES; STORE_RESOURCE_STATS_1 (0, 12, Address); STORE_RESOURCE_STATS_1 (1, 13, AddressFile); STORE_RESOURCE_STATS_1 (2, 14, Connection); STORE_RESOURCE_STATS_1 (3, 15, Request); STORE_RESOURCE_STATS_2 (4, 22, Packet); STORE_RESOURCE_STATS_2 (5, 23, ReceivePacket); STORE_RESOURCE_STATS_2 (6, 24, ReceiveBuffer); } /* StStoreProviderStatistics */ VOID StStoreAdapterStatus( IN PDEVICE_CONTEXT DeviceContext, IN PUCHAR SourceRouting, IN UINT SourceRoutingLength, IN PVOID StatusBuffer ) /*++ Routine Description: This routine writes the ADAPTER_STATUS structure for the device context into StatusBuffer. The name_count field is initialized to zero; StStoreNameBuffers is used to write name buffers. Arguments: DeviceContext - a pointer to the device context. SourceRouting - If this is a remote request, the source routing information from the frame. SourceRoutingLength - The length of SourceRouting. StatusBuffer - The buffer that holds the result. It is assumed that it is at least sizeof(ADAPTER_STATUS) bytes long. Return Value: None. --*/ { PADAPTER_STATUS AdapterStatus = (PADAPTER_STATUS)StatusBuffer; UINT MaxUserData; RtlZeroMemory ((PVOID)AdapterStatus, sizeof(ADAPTER_STATUS)); RtlCopyMemory (AdapterStatus->adapter_address, DeviceContext->LocalAddress.Address, 6); AdapterStatus->rev_major = 0x03; switch (DeviceContext->MacInfo.MediumType) { case NdisMedium802_5: AdapterStatus->adapter_type = 0xff; break; default: AdapterStatus->adapter_type = 0xfe; break; } AdapterStatus->frmr_recv = 0; AdapterStatus->frmr_xmit = 0; AdapterStatus->recv_buff_unavail = (WORD)(DeviceContext->ReceivePacketExhausted + DeviceContext->ReceiveBufferExhausted); AdapterStatus->xmit_buf_unavail = (WORD)DeviceContext->PacketExhausted; AdapterStatus->xmit_success = (WORD)(DeviceContext->IFramesSent - DeviceContext->IFramesResent); AdapterStatus->recv_success = (WORD)DeviceContext->IFramesReceived; AdapterStatus->iframe_recv_err = (WORD)DeviceContext->IFramesRejected; AdapterStatus->iframe_xmit_err = (WORD)DeviceContext->IFramesResent; AdapterStatus->t1_timeouts = 0; AdapterStatus->ti_timeouts = 0; AdapterStatus->xmit_aborts = 0; AdapterStatus->free_ncbs = 0xffff; AdapterStatus->max_cfg_ncbs = 0xffff; AdapterStatus->max_ncbs = 0xffff; AdapterStatus->pending_sess = (WORD)DeviceContext->OpenConnections; AdapterStatus->max_cfg_sess = 0xffff; AdapterStatus->max_sess = 0xffff; MacReturnMaxDataSize( &DeviceContext->MacInfo, SourceRouting, SourceRoutingLength, DeviceContext->MaxSendPacketSize, &MaxUserData); AdapterStatus->max_dgram_size = (WORD)(MaxUserData - sizeof(ST_HEADER)); AdapterStatus->max_sess_pkt_size = (WORD)(MaxUserData - sizeof(ST_HEADER)); return; } /* StStoreAdapterStatus */ VOID StStoreNameBuffers( IN PDEVICE_CONTEXT DeviceContext, IN PVOID Buffer, IN ULONG BufferLength, IN ULONG NamesToSkip, OUT PULONG NamesWritten, OUT PULONG TotalNameCount OPTIONAL, OUT PBOOLEAN Truncated ) /*++ Routine Description: This routine writes NAME_BUFFER structures for the device context into NameBuffer. It can skip a specified number of names at the beginning, and returns the number of names written into NameBuffer. If a name will only partially fit, it is not written. Arguments: DeviceContext - a pointer to the device context. NameBuffer - The buffer to write the names into. NameBufferLength - The length of NameBuffer. NamesToSkip - The number of names to skip. NamesWritten - Returns the number of names written. TotalNameCount - Returns the total number of names available, if specified. Truncated - More names are available than were written. Return Value: None. --*/ { ULONG NameCount = 0; ULONG BytesWritten = 0; KIRQL oldirql; PLIST_ENTRY p; PNAME_BUFFER NameBuffer = (PNAME_BUFFER)Buffer; PTP_ADDRESS address; // // Spin through the address list for this device context. // ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); p = DeviceContext->AddressDatabase.Flink; for (p = DeviceContext->AddressDatabase.Flink; p != &DeviceContext->AddressDatabase; p = p->Flink) { address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); // // Ignore addresses that are shutting down. // if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { continue; } // // Ignore the broadcast address. // if (address->NetworkName == NULL) { continue; } // // Ignore the reserved address. // if ((address->NetworkName->NetbiosName[0] == 0) && (RtlEqualMemory( address->NetworkName->NetbiosName, DeviceContext->ReservedNetBIOSAddress, NETBIOS_NAME_LENGTH))) { continue; } // // Check if we are still skipping. // if (NameCount < NamesToSkip) { ++NameCount; continue; } // // Make sure we still have room. // if (BytesWritten + sizeof(NAME_BUFFER) > BufferLength) { break; } RtlCopyMemory( NameBuffer->name, address->NetworkName->NetbiosName, NETBIOS_NAME_LENGTH); ++NameCount; NameBuffer->name_num = (UCHAR)NameCount; NameBuffer->name_flags = REGISTERED; if (address->Flags & ADDRESS_FLAGS_GROUP) { NameBuffer->name_flags |= GROUP_NAME; } // BUGBUG: name_flags should be done more accurately. BytesWritten += sizeof(NAME_BUFFER); ++NameBuffer; } *NamesWritten = NameBuffer - (PNAME_BUFFER)Buffer; if (p == &DeviceContext->AddressDatabase) { *Truncated = FALSE; if (ARGUMENT_PRESENT(TotalNameCount)) { *TotalNameCount = NameCount; } } else { *Truncated = TRUE; // // If requested, continue through the list and count // all the addresses. // if (ARGUMENT_PRESENT(TotalNameCount)) { for ( ; p != &DeviceContext->AddressDatabase; p = p->Flink) { address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); // // Ignore addresses that are shutting down. // if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { continue; } // // Ignore the broadcast address. // if (address->NetworkName == NULL) { continue; } // // Ignore the reserved address, since we count it no matter what. // if ((address->NetworkName->NetbiosName[0] == 0) && (RtlEqualMemory( address->NetworkName->NetbiosName, DeviceContext->ReservedNetBIOSAddress, NETBIOS_NAME_LENGTH))) { continue; } ++NameCount; } *TotalNameCount = NameCount; } } RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); return; } /* StStoreNameBuffers */ NTSTATUS StTdiSetInformation( IN PIRP Irp ) /*++ Routine Description: This routine performs the TdiSetInformation request for the transport provider. Arguments: Irp - the Irp for the requested operation. Return Value: NTSTATUS - status of operation. --*/ { UNREFERENCED_PARAMETER (Irp); return STATUS_NOT_IMPLEMENTED; } /* StTdiQueryInformation */