/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: query.c Abstract: This module contains code which performs the following TDI services: o TdiQueryInformation o TdiSetInformation Environment: Kernel mode Revision History: --*/ #include "precomp.h" #pragma hdrstop // // Remove the warning -- this is defined in windef also. // #ifdef FAR #undef FAR #endif #include #include // // Useful macro to obtain the total length of a buffer chain. // BUGBUG: Make this use NDIS macros. // #define NbiGetBufferChainLength(Buffer, Length) { \ PNDIS_BUFFER _Buffer = (Buffer); \ *(Length) = 0; \ while (_Buffer) { \ *(Length) += MmGetMdlByteCount(_Buffer); \ _Buffer = _Buffer->Next; \ } \ } NTSTATUS NbiTdiQueryInformation( IN PDEVICE Device, IN PREQUEST Request ) /*++ Routine Description: This routine performs the TdiQueryInformation request for the transport provider. Arguments: Request - the request for the operation. Return Value: The status of operation. --*/ { NTSTATUS Status; PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query; PADDRESS_FILE AddressFile; PADDRESS Address; PCONNECTION Connection; union { struct { ULONG ActivityCount; TA_NETBIOS_ADDRESS NbiAddress; } AddressInfo; TA_NETBIOS_ADDRESS BroadcastAddress; TDI_ADDRESS_IPX IpxAddress; TDI_DATAGRAM_INFO DatagramInfo; struct { FIND_NAME_HEADER Header; FIND_NAME_BUFFER Buffer; } FindNameInfo; } TempBuffer; IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; PADAPTER_STATUS AdapterStatus; BOOLEAN RemoteAdapterStatus; TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; ULONG TargetBufferLength; ULONG AdapterStatusLength; ULONG ValidStatusLength; ULONG ElementSize, TransportAddressSize; PTRANSPORT_ADDRESS TransportAddress; TA_ADDRESS UNALIGNED * CurAddress; PNETBIOS_CACHE CacheName; FIND_NAME_HEADER UNALIGNED * FindNameHeader; UINT FindNameBufferLength; NTSTATUS QueryStatus; CTELockHandle LockHandle; PLIST_ENTRY p; BOOLEAN UsedConnection; UINT i; // // what type of status do we want? // Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); switch (Query->QueryType) { case TDI_QUERY_ADDRESS_INFO: // // The caller wants the exact address value. // if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); #if defined(_PNP_POWER) Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); #else Status = NbiVerifyAddressFile (AddressFile); #endif _PNP_POWER if (!NT_SUCCESS(Status)) { break; } UsedConnection = FALSE; } else if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE) { Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); Status = NbiVerifyConnection (Connection); if (!NT_SUCCESS(Status)) { break; } UsedConnection = TRUE; AddressFile = Connection->AddressFile; } else { Status = STATUS_INVALID_ADDRESS; break; } Address = AddressFile->Address; NB_DEBUG2 (QUERY, ("Query address info on %lx\n", AddressFile)); TempBuffer.AddressInfo.ActivityCount = 0; NB_GET_LOCK (&Address->Lock, &LockHandle); for (p = Address->AddressFileDatabase.Flink; p != &Address->AddressFileDatabase; p = p->Flink) { if (CONTAINING_RECORD (p, ADDRESS_FILE, Linkage)->State == ADDRESSFILE_STATE_OPEN) { ++TempBuffer.AddressInfo.ActivityCount; } } NB_FREE_LOCK (&Address->Lock, LockHandle); TdiBuildNetbiosAddress( AddressFile->Address->NetbiosAddress.NetbiosName, (BOOLEAN)(AddressFile->Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP), &TempBuffer.AddressInfo.NbiAddress); Status = TdiCopyBufferToMdl( &TempBuffer.AddressInfo, 0, sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); if (UsedConnection) { NbiDereferenceConnection (Connection, CREF_VERIFY); } else { NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); } break; case TDI_QUERY_CONNECTION_INFO: // // Connection info is queried on a connection, // verify this. // Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); Status = NbiVerifyConnection (Connection); if (!NT_SUCCESS (Status)) { return Status; } if (Connection->State != CONNECTION_STATE_ACTIVE) { Status = STATUS_INVALID_CONNECTION; } else { // // Assume 50 ms of delay for every hop after the // first. The delay is returned as a negative number. // if (Connection->HopCount > 1) { Connection->ConnectionInfo.Delay.HighPart = (ULONG)-1; Connection->ConnectionInfo.Delay.LowPart = -((Connection->HopCount-1) * 50 * MILLISECONDS); } else { Connection->ConnectionInfo.Delay.HighPart = 0; Connection->ConnectionInfo.Delay.LowPart = 0; } // // We have tick count; to convert to bytes/second we do: // // packet 576 bytes 18.21 ticks // ---------------- * --------- * ----------- // tick_count ticks packet seconds // // to get 10489/tick_count = bytes/second. We // double this because routers tend to // overestimate it. // // Since tick_count has such a low granularity, // a tick count of 1 gives us a throughput of // only 84 kbps, which is much too low. In // that case we return twice the link speed // which is in 100 bps units; that corresponds // to about 1/6 of our bandwidth in bytes/sec. // if (Connection->TickCount <= Connection->HopCount) { Connection->ConnectionInfo.Throughput.QuadPart = UInt32x32To64 (Connection->LineInfo.LinkSpeed, 2); } else { Connection->ConnectionInfo.Throughput.HighPart = 0; Connection->ConnectionInfo.Throughput.LowPart = 20978 / (Connection->TickCount - Connection->HopCount); } Connection->ConnectionInfo.Unreliable = FALSE; Status = TdiCopyBufferToMdl ( &Connection->ConnectionInfo, 0, sizeof(TDI_CONNECTION_INFO), REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); } NbiDereferenceConnection (Connection, CREF_VERIFY); break; case TDI_QUERY_PROVIDER_INFO: NB_DEBUG2 (QUERY, ("Query provider info\n")); Status = TdiCopyBufferToMdl ( &Device->Information, 0, sizeof (TDI_PROVIDER_INFO), REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); break; case TDI_QUERY_BROADCAST_ADDRESS: // // for this provider, the broadcast address is a zero byte name, // contained in a Transport address structure. // NB_DEBUG2 (QUERY, ("Query broadcast address\n")); TempBuffer.BroadcastAddress.TAAddressCount = 1; TempBuffer.BroadcastAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; TempBuffer.BroadcastAddress.Address[0].AddressLength = 0; Status = TdiCopyBufferToMdl ( (PVOID)&TempBuffer.BroadcastAddress, 0L, sizeof (TempBuffer.BroadcastAddress.TAAddressCount) + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressType) + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressLength), REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); break; case TDI_QUERY_ADAPTER_STATUS: // // Determine if this is a local or remote query. // RemoteAdapterStatus = FALSE; if (Query->RequestConnectionInformation != NULL) { RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE); if (RemoteAddress == NULL) { return STATUS_BAD_NETWORK_PATH; } #if defined(_PNP_POWER) if ( !NbiFindAdapterAddress( RemoteAddress->NetbiosName, LOCK_NOT_ACQUIRED ) ) { RemoteAdapterStatus = TRUE; } #else if (!RtlEqualMemory( RemoteAddress->NetbiosName, Device->ReservedNetbiosName, 16)) { RemoteAdapterStatus = TRUE; } #endif _PNP_POWER } if (RemoteAdapterStatus) { // // See if we have this name cached. // NB_GET_LOCK (&Device->Lock, &LockHandle); Status = CacheFindName( Device, FindNameOther, RemoteAddress->NetbiosName, &CacheName); if (Status == STATUS_PENDING) { // // A request for routes to this name has been // sent out on the net, we queue up this status // request and processing will be resumed when // we get a response. // // The status field in the request will hold // the cache entry for the remote. The information // field will hold the remote netbios name while // it is in the WaitingAdapterStatus queue, and // will hold a timeout value while we it is in // the ActiveAdapterStatus queue. // NB_DEBUG2 (QUERY, ("Queueing up adapter status %lx\n", Request)); NbiReferenceDevice (Device, DREF_STATUS_QUERY); REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; InsertTailList( &Device->WaitingAdapterStatus, REQUEST_LINKAGE (Request)); NB_FREE_LOCK (&Device->Lock, LockHandle); } else if (Status == STATUS_SUCCESS) { NB_DEBUG2 (QUERY, ("Found adapter status cached %lx\n", Request)); // // We reference the cache name entry so it won't // go away while we are using it. // REQUEST_STATUS(Request) = (NTSTATUS)CacheName; ++CacheName->ReferenceCount; NbiReferenceDevice (Device, DREF_STATUS_QUERY); REQUEST_INFORMATION (Request) = 0; InsertTailList( &Device->ActiveAdapterStatus, REQUEST_LINKAGE (Request)); NB_FREE_LOCK (&Device->Lock, LockHandle); NbiSendStatusQuery (Request); Status = STATUS_PENDING; } else { if (Status != STATUS_INSUFFICIENT_RESOURCES) { Status = STATUS_IO_TIMEOUT; } REQUEST_INFORMATION (Request) = 0; NB_FREE_LOCK (&Device->Lock, LockHandle); } } else { // // Local adapter status. // NbiGetBufferChainLength (REQUEST_NDIS_BUFFER(Request), &TargetBufferLength); Status = NbiStoreAdapterStatus( TargetBufferLength, 1, // NIC ID, was 0, changed to 1 for Bug #18026 // because for NicId = 0, Ipx returns virtual // address. Netbios uses that to register the // name (00...01) and fails. &AdapterStatus, &AdapterStatusLength, &ValidStatusLength); if (Status != STATUS_INSUFFICIENT_RESOURCES) { // // This should succeed since we know the length // will fit. // (VOID)TdiCopyBufferToMdl( AdapterStatus, 0, ValidStatusLength, REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); } } break; case TDI_QUERY_FIND_NAME: // // Check that there is a valid Netbios remote address. // if ((Query->RequestConnectionInformation == NULL) || ((RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE)) == NULL)) { return STATUS_BAD_NETWORK_PATH; } // // We assume the entire request buffer is in the first // piece of the MDL chain (BUGBUG: Can we do this?). // Make sure there is room for at least the header. // NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); if (FindNameBufferLength < sizeof(FIND_NAME_HEADER)) { return STATUS_INSUFFICIENT_RESOURCES; } // // See if we have this name cached. We specify that this is // a netbios name query, so this will only succeed if this is a // unique name -- for a group name it will queue up a find // name query and when we get the response we will fill in // the request's buffer based on it. // NB_GET_LOCK (&Device->Lock, &LockHandle); Status = CacheFindName( Device, FindNameNetbiosFindName, RemoteAddress->NetbiosName, &CacheName); if (Status == STATUS_PENDING) { // // A request for routes to this name has been // sent out on the net, we queue up this find // name request and processing will be resumed when // we get a response. // // The information field will hold the remote // netbios name while it is in the WaitingNetbiosFindName // queue. The status will hold the current status -- // initially failure, then success, then overflow // if the buffer is too small. // NB_DEBUG2 (QUERY, ("Queueing up find name %lx\n", Request)); NbiReferenceDevice (Device, DREF_NB_FIND_NAME); FindNameHeader->node_count = 0; FindNameHeader->reserved = 0; FindNameHeader->unique_group = 0; REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; // // Assume it fails, we update the status to // SUCCESS or BUFFER_OVERFLOW if needed. // REQUEST_STATUS (Request) = STATUS_IO_TIMEOUT; InsertTailList( &Device->WaitingNetbiosFindName, REQUEST_LINKAGE (Request)); NB_FREE_LOCK (&Device->Lock, LockHandle); } else if (Status == STATUS_SUCCESS) { NB_DEBUG2 (QUERY, ("Found find name cached %lx\n", Request)); // // We don't need to reference the cache entry since // we only use it here with the lock still held. // // // Query the local address, which we will return as // the destination address of this query. Since we // use TempBuffer.IpxAddress for this query, we have // to immediately copy it to its correct place in // TempBuffer.FindNameInfo.Buffer. // #if defined(_PNP_POWER) if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_IPX_ADDRESS, &CacheName->Networks[0].LocalTarget.NicHandle, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX), NULL) != STATUS_SUCCESS ) { NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, CacheName->Networks[0].LocalTarget.NicHandle.NicId )); goto QueryFindNameFailed; } #else (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_IPX_ADDRESS, CacheName->Networks[0].LocalTarget.NicId, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX), NULL); #endif _PNP_POWER RtlMoveMemory (TempBuffer.FindNameInfo.Buffer.destination_addr, TempBuffer.IpxAddress.NodeAddress, 6); TempBuffer.FindNameInfo.Buffer.access_control = 0x10; // standard token-ring values TempBuffer.FindNameInfo.Buffer.frame_control = 0x40; RtlCopyMemory (TempBuffer.FindNameInfo.Buffer.source_addr, CacheName->FirstResponse.NodeAddress, 6); // // Query source routing information about this remote, if any. // SourceRoutingInfo.Identifier = IDENTIFIER_NB; RtlCopyMemory (SourceRoutingInfo.RemoteAddress, CacheName->FirstResponse.NodeAddress, 6); QueryStatus = (*Device->Bind.QueryHandler)( IPX_QUERY_SOURCE_ROUTING, #if defined(_PNP_POWER) &CacheName->Networks[0].LocalTarget.NicHandle, #else CacheName->Networks[0].LocalTarget.NicId, #endif _PNP_POWER &SourceRoutingInfo, sizeof(IPX_SOURCE_ROUTING_INFO), NULL); RtlZeroMemory(TempBuffer.FindNameInfo.Buffer.routing_info, 18); if (QueryStatus != STATUS_SUCCESS) { SourceRoutingInfo.SourceRoutingLength = 0; } else if (SourceRoutingInfo.SourceRoutingLength > 0) { RtlMoveMemory( TempBuffer.FindNameInfo.Buffer.routing_info, SourceRoutingInfo.SourceRouting, SourceRoutingInfo.SourceRoutingLength); } TempBuffer.FindNameInfo.Buffer.length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); TempBuffer.FindNameInfo.Header.node_count = 1; TempBuffer.FindNameInfo.Header.reserved = 0; TempBuffer.FindNameInfo.Header.unique_group = 0; // unique NB_FREE_LOCK (&Device->Lock, LockHandle); // // 33 is sizeof(FIND_NAME_BUFFER) without the padding. // Status = TdiCopyBufferToMdl ( (PVOID)&TempBuffer.FindNameInfo, 0, sizeof(FIND_NAME_HEADER) + 33, REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); } else { #if defined(_PNP_POWER) QueryFindNameFailed: #endif _PNP_POWER if (Status != STATUS_INSUFFICIENT_RESOURCES) { Status = STATUS_IO_TIMEOUT; } REQUEST_INFORMATION (Request) = 0; NB_FREE_LOCK (&Device->Lock, LockHandle); } break; case TDI_QUERY_PROVIDER_STATISTICS: // // BETABUGBUG: Keep track of more of these. // NB_DEBUG2 (QUERY, ("Query provider statistics\n")); Status = TdiCopyBufferToMdl ( &Device->Statistics, 0, FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); break; case TDI_QUERY_DATAGRAM_INFO: NB_DEBUG2 (QUERY, ("Query datagram info\n")); TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; TempBuffer.DatagramInfo.MaximumDatagramCount = 0; Status = TdiCopyBufferToMdl ( &TempBuffer.DatagramInfo, 0, sizeof(TempBuffer.DatagramInfo), REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); break; case TDI_QUERY_DATA_LINK_ADDRESS: case TDI_QUERY_NETWORK_ADDRESS:{ #if defined(_PNP_POWER) Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS ? IPX_QUERY_DATA_LINK_ADDRESS : IPX_QUERY_NETWORK_ADDRESS ), NULL, Request, 0, NULL); #else ULONG TransportAddressAllocSize; if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { ElementSize = (2 * sizeof(USHORT)) + 6; } else { ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); } // TransportAddress = CTEAllocMem(sizeof(int) + (ElementSize * Device->MaximumNicId)); TransportAddressAllocSize = sizeof(int) + ( ElementSize * Device->MaximumNicId); TransportAddress = NbiAllocateMemory( TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); if (TransportAddress == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { TransportAddress->TAAddressCount = 0; TransportAddressSize = sizeof(int); CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; for (i = 1; i <= Device->MaximumNicId; i++) { Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_IPX_ADDRESS, (USHORT)i, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX), NULL); if (Status != STATUS_SUCCESS) { continue; } if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { CurAddress->AddressLength = 6; CurAddress->AddressType = TDI_ADDRESS_TYPE_UNSPEC; RtlCopyMemory (CurAddress->Address, TempBuffer.IpxAddress.NodeAddress, 6); } else { CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; RtlCopyMemory (CurAddress->Address, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX)); } ++TransportAddress->TAAddressCount; TransportAddressSize += ElementSize; CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); } Status = TdiCopyBufferToMdl ( TransportAddress, 0, TransportAddressSize, REQUEST_NDIS_BUFFER(Request), 0, &REQUEST_INFORMATION(Request)); // CTEFreeMem (TransportAddress); NbiFreeMemory( TransportAddress, TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); } #endif _PNP_POWER break; } default: NB_DEBUG (QUERY, ("Invalid query type %d\n", Query->QueryType)); Status = STATUS_INVALID_DEVICE_REQUEST; break; } return Status; } /* NbiTdiQueryInformation */ NTSTATUS NbiStoreAdapterStatus( IN ULONG MaximumLength, IN USHORT NicId, OUT PVOID * StatusBuffer, OUT ULONG * StatusBufferLength, OUT ULONG * ValidBufferLength ) /*++ Routine Description: This routine allocates an ADAPTER_STATUS buffer and fills it in. The buffer will be allocated at most MaximumLength size. The caller is responsible for freeing the buffer. Arguments: MaximumLength - The maximum length to allocate. NicId - The NIC ID the query was received on, or 0 for a local query. StatusBuffer - Returns the allocated buffer. StatusBufferLength - Returns the length of the buffer. ValidBufferLength - Returns the length of the buffer which contains valid adapter status data. Return Value: STATUS_SUCCESS - The buffer was written successfully. STATUS_BUFFER_OVERFLOW - The buffer was written but not all data could fit in MaximumLength bytes. STATUS_INSUFFICIENT_RESOURCES - The buffer could not be allocated. --*/ { PADAPTER_STATUS AdapterStatus; PNAME_BUFFER NameBuffer; ADAPTER_STATUS TempAdapterStatus; #if !defined(_PNP_POWER) TDI_ADDRESS_IPX IpxAddress; #endif !_PNP_POWER PDEVICE Device = NbiDevice; PADDRESS Address; UCHAR NameCount; ULONG LengthNeeded; ULONG BytesWritten; NTSTATUS Status; PLIST_ENTRY p; CTELockHandle LockHandle; // // First fill in the basic adapter status structure, to make // it easier to copy over if the target buffer is really short. // RtlZeroMemory ((PVOID)&TempAdapterStatus, sizeof(ADAPTER_STATUS)); #if defined(_PNP_POWER) RtlCopyMemory (TempAdapterStatus.adapter_address, Device->Bind.Node, 6); #else (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_IPX_ADDRESS, NicId, &IpxAddress, sizeof(TDI_ADDRESS_IPX), NULL); RtlCopyMemory (TempAdapterStatus.adapter_address, IpxAddress.NodeAddress, 6); #endif _PNP_POWER // // Some of the fields mean different things for Novell Netbios, // as described in the comments. // TempAdapterStatus.rev_major = 0; // Jumpers TempAdapterStatus.reserved0 = 0; // SelfTest TempAdapterStatus.adapter_type = 0; // MajorVersion TempAdapterStatus.rev_minor = 0; // MinorVersion TempAdapterStatus.duration = 0; // ReportingPeriod TempAdapterStatus.frmr_recv = 0; // ReceiveCRCErrors TempAdapterStatus.frmr_xmit = 0; // ReceiveAlignErrors TempAdapterStatus.iframe_recv_err = 0; // XmitCollisions TempAdapterStatus.xmit_aborts = 0; // XmitAbort TempAdapterStatus.xmit_success = Device->Statistics.DataFramesSent; // SuccessfulXmits TempAdapterStatus.recv_success = Device->Statistics.DataFramesReceived; // SuccessfulReceive TempAdapterStatus.iframe_xmit_err = (WORD)Device->Statistics.DataFramesResent; // XmitRetries TempAdapterStatus.recv_buff_unavail = (WORD)Device->Statistics.DataFramesRejected; // ExhaustedResource // t1_timeouts, ti_timeouts, and reserved1 are unused. TempAdapterStatus.free_ncbs = 0xffff; // FreeBlocks TempAdapterStatus.max_cfg_ncbs = 0xffff; // ConfiguredNCB TempAdapterStatus.max_ncbs = 0xffff; // MaxNCB // xmit_bug_unavail and max_dgram_size are unused. TempAdapterStatus.pending_sess = (WORD)Device->Statistics.OpenConnections; // CurrentSessions TempAdapterStatus.max_cfg_sess = 0xffff; // MaxSessionConfigured TempAdapterStatus.max_sess = 0xffff; // MaxSessionPossible TempAdapterStatus.max_sess_pkt_size = Device->Bind.LineInfo.MaximumSendSize - sizeof(NB_CONNECTION); // MaxSessionPacketSize TempAdapterStatus.name_count = 0; // // Do a quick estimate of how many names we need room for. // This includes stopping addresses and the broadcast // address, for the moment. BUGBUG: Fix this? // NB_GET_LOCK (&Device->Lock, &LockHandle); LengthNeeded = sizeof(ADAPTER_STATUS) + (Device->AddressCount * sizeof(NAME_BUFFER)); if (LengthNeeded > MaximumLength) { LengthNeeded = MaximumLength; } AdapterStatus = NbiAllocateMemory(LengthNeeded, MEMORY_STATUS, "Adapter Status"); if (AdapterStatus == NULL) { NB_FREE_LOCK (&Device->Lock, LockHandle); return STATUS_INSUFFICIENT_RESOURCES; } *StatusBuffer = AdapterStatus; *StatusBufferLength = LengthNeeded; if (LengthNeeded < sizeof(ADAPTER_STATUS)) { RtlCopyMemory (AdapterStatus, &TempAdapterStatus, LengthNeeded); *ValidBufferLength = LengthNeeded; NB_FREE_LOCK (&Device->Lock, LockHandle); return STATUS_BUFFER_OVERFLOW; } RtlCopyMemory (AdapterStatus, &TempAdapterStatus, sizeof(ADAPTER_STATUS)); BytesWritten = sizeof(ADAPTER_STATUS); NameBuffer = (PNAME_BUFFER)(AdapterStatus+1); NameCount = 0; // // Scan through the device's address database, filling in // the NAME_BUFFERs. // Status = STATUS_SUCCESS; for (p = Device->AddressDatabase.Flink; p != &Device->AddressDatabase; p = p->Flink) { Address = CONTAINING_RECORD (p, ADDRESS, Linkage); // // Ignore addresses that are shutting down. // #if defined(_PNP_POWER) if ((Address->State != ADDRESS_STATE_OPEN) || (Address->Flags & ADDRESS_FLAGS_CONFLICT)) { continue; } #else if ((Address->State != ADDRESS_STATE_OPEN) != 0) { continue; } #endif _PNP_POWER // // Ignore the broadcast address. // if (Address->NetbiosAddress.Broadcast) { continue; } // // Ignore our reserved address. // #if defined(_PNP_POWER) if ( NbiFindAdapterAddress( Address->NetbiosAddress.NetbiosName, LOCK_ACQUIRED )) { continue; } #else if (RtlEqualMemory( Address->NetbiosAddress.NetbiosName, Device->ReservedNetbiosName, 16)) { continue; } #endif _PNP_POWER // // Make sure we still have room. // if (BytesWritten + sizeof(NAME_BUFFER) > LengthNeeded) { Status = STATUS_BUFFER_OVERFLOW; break; } RtlCopyMemory( NameBuffer->name, Address->NetbiosAddress.NetbiosName, 16); ++NameCount; NameBuffer->name_num = NameCount; NameBuffer->name_flags = REGISTERED; if (Address->NameTypeFlag == NB_NAME_GROUP) { NameBuffer->name_flags |= GROUP_NAME; } // // BUGBUG: name_flags should be done more accurately. // BytesWritten += sizeof(NAME_BUFFER); ++NameBuffer; } AdapterStatus->name_count = (WORD)NameCount; *ValidBufferLength = BytesWritten; NB_FREE_LOCK (&Device->Lock, LockHandle); return Status; } /* NbiStoreAdapterStatus */ VOID NbiUpdateNetbiosFindName( IN PREQUEST Request, #if defined(_PNP_POWER) IN PNIC_HANDLE NicHandle, #else IN USHORT NicId, #endif _PNP_POWER IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, IN BOOLEAN Unique ) /*++ Routine Description: This routine updates the find name request with the new information received. It updates the status in the request if needed. Arguments: Request - The netbios find name request. NicId - The NIC ID the response was received on. RemoteIpxAddress - The IPX address of the remote. Unique - TRUE if the name is unique. Return Value: None. --*/ { FIND_NAME_HEADER UNALIGNED * FindNameHeader; FIND_NAME_BUFFER UNALIGNED * FindNameBuffer; UINT FindNameBufferLength; TDI_ADDRESS_IPX LocalIpxAddress; IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; NTSTATUS QueryStatus; UINT i; NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); // // Scan through the names saved so far and see if this one // is there. // FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *)(FindNameHeader+1); for (i = 0; i < FindNameHeader->node_count; i++) { if (RtlEqualMemory( FindNameBuffer->source_addr, RemoteIpxAddress->NodeAddress, 6)) { // // This remote already responded, ignore it. // return; } FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *) (((PUCHAR)FindNameBuffer) + 33); } // // Make sure there is room for this new node. 33 is // sizeof(FIND_NAME_BUFFER) without padding. // if (FindNameBufferLength < sizeof(FIND_NAME_HEADER) + ((FindNameHeader->node_count+1) * 33)) { REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; return; } // // Query the local address, which we will return as // the destination address of this query. // #if defined(_PNP_POWER) if( (*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_IPX_ADDRESS, NicHandle, &LocalIpxAddress, sizeof(TDI_ADDRESS_IPX), NULL) != STATUS_SUCCESS ) { // // Ignore this response if the query fails. maybe the NicHandle // is bad or it just got removed. // NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, NicHandle->NicId )); return; } #else (VOID)(*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_IPX_ADDRESS, NicId, &LocalIpxAddress, sizeof(TDI_ADDRESS_IPX), NULL); #endif _PNP_POWER FindNameBuffer->access_control = 0x10; // standard token-ring values FindNameBuffer->frame_control = 0x40; RtlMoveMemory (FindNameBuffer->destination_addr, LocalIpxAddress.NodeAddress, 6); RtlCopyMemory (FindNameBuffer->source_addr, RemoteIpxAddress->NodeAddress, 6); // // Query source routing information about this remote, if any. // SourceRoutingInfo.Identifier = IDENTIFIER_NB; RtlCopyMemory (SourceRoutingInfo.RemoteAddress, RemoteIpxAddress->NodeAddress, 6); QueryStatus = (*NbiDevice->Bind.QueryHandler)( IPX_QUERY_SOURCE_ROUTING, #if defined(_PNP_POWER) NicHandle, #else NicId, #endif _PNP_POWER &SourceRoutingInfo, sizeof(IPX_SOURCE_ROUTING_INFO), NULL); RtlZeroMemory(FindNameBuffer->routing_info, 18); if (QueryStatus != STATUS_SUCCESS) { SourceRoutingInfo.SourceRoutingLength = 0; } else if (SourceRoutingInfo.SourceRoutingLength > 0) { RtlMoveMemory( FindNameBuffer->routing_info, SourceRoutingInfo.SourceRouting, SourceRoutingInfo.SourceRoutingLength); } FindNameBuffer->length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); ++FindNameHeader->node_count; if (!Unique) { FindNameHeader->unique_group = 1; // group } REQUEST_STATUS(Request) = STATUS_SUCCESS; } /* NbiUpdateNetbiosFindName */ VOID NbiSetNetbiosFindNameInformation( IN PREQUEST Request ) /*++ Routine Description: This routine sets the REQUEST_INFORMATION field to the right value based on the number of responses recorded in the netbios find name request's buffer. Arguments: Request - The netbios find name request. Return Value: None. --*/ { FIND_NAME_HEADER UNALIGNED * FindNameHeader; UINT FindNameBufferLength; NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); // // 33 is sizeof(FIND_NAME_BUFFER) without the padding. // REQUEST_INFORMATION(Request) = sizeof(FIND_NAME_HEADER) + (FindNameHeader->node_count * 33); } /* NbiSetNetbiosFindNameInformation */ NTSTATUS NbiTdiSetInformation( IN PDEVICE Device, IN PREQUEST Request ) /*++ Routine Description: This routine performs the TdiSetInformation request for the transport provider. Arguments: Device - the device. Request - the request for the operation. Return Value: NTSTATUS - status of operation. --*/ { UNREFERENCED_PARAMETER (Device); UNREFERENCED_PARAMETER (Request); return STATUS_NOT_IMPLEMENTED; } /* NbiTdiSetInformation */ VOID NbiProcessStatusQuery( IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR PacketBuffer, IN UINT PacketSize ) /*++ Routine Description: This routine handles NB_CMD_STATUS_QUERY frames. Arguments: RemoteAddress - The local target this packet was received from. MacOptions - The MAC options for the underlying NDIS binding. LookaheadBuffer - The packet data, starting at the IPX header. PacketSize - The total length of the packet, starting at the IPX header. Return Value: None. --*/ { PSINGLE_LIST_ENTRY s; PNB_SEND_RESERVED Reserved; PNDIS_PACKET Packet; NB_CONNECTIONLESS UNALIGNED * Header; NDIS_STATUS NdisStatus; IPX_LINE_INFO LineInfo; ULONG ResponseSize; NTSTATUS Status; PNDIS_BUFFER AdapterStatusBuffer; PADAPTER_STATUS AdapterStatus; ULONG AdapterStatusLength; ULONG ValidStatusLength; PDEVICE Device = NbiDevice; NB_CONNECTIONLESS UNALIGNED * Connectionless = (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; // // The old stack does not include the 14 bytes of padding in // the 802.3 or IPX length of the packet. // if (PacketSize < (sizeof(IPX_HEADER) + 2)) { return; } // // Get the maximum size we can send. // #if defined(_PNP_POWER) if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_LINE_INFO, &RemoteAddress->NicHandle, &LineInfo, sizeof(IPX_LINE_INFO), NULL) != STATUS_SUCCESS ) { // // Bad NicHandle or it just got removed. // NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_LINE_INFO, RemoteAddress->NicHandle.NicId )); return; } // // Allocate a packet from the pool. // s = NbiPopSendPacket(Device, FALSE); if (s == NULL) { return; } #else // // Allocate a packet from the pool. // s = NbiPopSendPacket(Device, FALSE); if (s == NULL) { return; } // // Get the maximum size we can send. // (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code IPX_QUERY_LINE_INFO, RemoteAddress->NicId, &LineInfo, sizeof(IPX_LINE_INFO), NULL); #endif _PNP_POWER ResponseSize = LineInfo.MaximumSendSize - sizeof(IPX_HEADER) - sizeof(NB_STATUS_RESPONSE); // // Get the local adapter status (this allocates a buffer). // Status = NbiStoreAdapterStatus( ResponseSize, #if defined(_PNP_POWER) RemoteAddress->NicHandle.NicId, #else RemoteAddress->NicId, #endif _PNP_POWER &AdapterStatus, &AdapterStatusLength, &ValidStatusLength); if (Status == STATUS_INSUFFICIENT_RESOURCES) { ExInterlockedPushEntrySList( &Device->SendPacketList, s, &NbiGlobalPoolInterlock); return; } // // Allocate an NDIS buffer to map the extra buffer. // NdisAllocateBuffer( &NdisStatus, &AdapterStatusBuffer, Device->NdisBufferPoolHandle, AdapterStatus, ValidStatusLength); if (NdisStatus != NDIS_STATUS_SUCCESS) { NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); ExInterlockedPushEntrySList( &Device->SendPacketList, s, &NbiGlobalPoolInterlock); return; } NB_DEBUG2 (QUERY, ("Reply to AdapterStatus from %lx %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", *(UNALIGNED ULONG *)Connectionless->IpxHeader.SourceNetwork, Connectionless->IpxHeader.SourceNode[0], Connectionless->IpxHeader.SourceNode[1], Connectionless->IpxHeader.SourceNode[2], Connectionless->IpxHeader.SourceNode[3], Connectionless->IpxHeader.SourceNode[4], Connectionless->IpxHeader.SourceNode[5])); Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); CTEAssert (Reserved->SendInProgress == FALSE); Reserved->SendInProgress = TRUE; Reserved->Type = SEND_TYPE_STATUS_RESPONSE; Reserved->u.SR_AS.ActualBufferLength = AdapterStatusLength; // // Fill in the IPX header -- the default header has the broadcast // address on net 0 as the destination IPX address. // Header = (NB_CONNECTIONLESS UNALIGNED *) (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, Connectionless->IpxHeader.SourceNetwork, 12); Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) / 256; Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) % 256; Header->IpxHeader.PacketType = 0x04; // // Now fill in the Netbios header. // Header->StatusResponse.ConnectionControlFlag = 0x00; Header->StatusResponse.DataStreamType = NB_CMD_STATUS_RESPONSE; NbiReferenceDevice (Device, DREF_STATUS_RESPONSE); NdisChainBufferAtBack (Packet, AdapterStatusBuffer); // // Now send the frame, IPX will adjust the length of the // first buffer correctly. // NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); if ((NdisStatus = (*Device->Bind.SendHandler)( RemoteAddress, Packet, sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + ValidStatusLength, sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) != STATUS_PENDING) { NbiSendComplete( Packet, NdisStatus); } } /* NbiProcessStatusQuery */ VOID NbiSendStatusQuery( IN PREQUEST Request ) /*++ Routine Description: This routine sends NB_CMD_STATUS_QUERY frames. Arguments: Request - Holds the request describing the remote adapter status query. REQUEST_STATUS(Request) points to the netbios cache entry for the remote name. Return Value: None. --*/ { PSINGLE_LIST_ENTRY s; PNB_SEND_RESERVED Reserved; PNDIS_PACKET Packet; NB_CONNECTIONLESS UNALIGNED * Header; NDIS_STATUS NdisStatus; PNETBIOS_CACHE CacheName; PIPX_LOCAL_TARGET LocalTarget; PDEVICE Device = NbiDevice; // // Allocate a packet from the pool. // s = NbiPopSendPacket(Device, FALSE); if (s == NULL) { return; } Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); CTEAssert (Reserved->SendInProgress == FALSE); Reserved->SendInProgress = TRUE; Reserved->Type = SEND_TYPE_STATUS_QUERY; CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(Request); // // Fill in the IPX header -- the default header has the broadcast // address on net 0 as the destination IPX address. // Header = (NB_CONNECTIONLESS UNALIGNED *) (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); LocalTarget = &CacheName->Networks[0].LocalTarget; Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) / 256; Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) % 256; Header->IpxHeader.PacketType = 0x04; // // Now fill in the Netbios header. // Header->StatusResponse.ConnectionControlFlag = 0x00; Header->StatusResponse.DataStreamType = NB_CMD_STATUS_QUERY; NbiReferenceDevice (Device, DREF_STATUS_FRAME); // // Now send the frame, IPX will adjust the length of the // first buffer correctly. // NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY)); if ((NdisStatus = (*Device->Bind.SendHandler)( LocalTarget, Packet, sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY), sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY))) != STATUS_PENDING) { NbiSendComplete( Packet, NdisStatus); } } /* NbiProcessStatusQuery */ VOID NbiProcessStatusResponse( IN NDIS_HANDLE MacBindingHandle, IN NDIS_HANDLE MacReceiveContext, IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR LookaheadBuffer, IN UINT LookaheadBufferSize, IN UINT LookaheadBufferOffset, IN UINT PacketSize ) /*++ Routine Description: This routine handles NB_CMD_STATUS_RESPONSE frames. Arguments: MacBindingHandle - A handle to use when calling NdisTransferData. MacReceiveContext - A context to use when calling NdisTransferData. RemoteAddress - The local target this packet was received from. MacOptions - The MAC options for the underlying NDIS binding. LookaheadBuffer - The lookahead buffer, starting at the IPX header. LookaheadBufferSize - The length of the lookahead data. LookaheadBufferOffset - The offset to add when calling NdisTransferData. PacketSize - The total length of the packet, starting at the IPX header. Return Value: None. --*/ { PDEVICE Device = NbiDevice; CTELockHandle LockHandle; PREQUEST AdapterStatusRequest; PNETBIOS_CACHE CacheName; PLIST_ENTRY p; PSINGLE_LIST_ENTRY s; PNDIS_BUFFER TargetBuffer; ULONG TargetBufferLength, BytesToTransfer; ULONG BytesTransferred; NDIS_STATUS NdisStatus; PNB_RECEIVE_RESERVED ReceiveReserved; PNDIS_PACKET Packet; BOOLEAN Found; PNAME_BUFFER NameBuffer; UINT i,NameCount = 0; NB_CONNECTIONLESS UNALIGNED * Connectionless = (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) { return; } // // Find out how many names are there. // NameBuffer = (PNAME_BUFFER)(LookaheadBuffer + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)); if ( LookaheadBufferSize > sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS) ) { NameCount = (LookaheadBufferSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)) ) / sizeof(NAME_BUFFER); } // // Find a request queued to this remote. If there are // multiple requests outstanding for the same name we // should get multiple responses, so we only need to // find one. // NB_GET_LOCK (&Device->Lock, &LockHandle); Found = FALSE; p = Device->ActiveAdapterStatus.Flink; while (p != &Device->ActiveAdapterStatus) { AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); p = p->Flink; CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); if ( CacheName->Unique ) { if (RtlEqualMemory( &CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12)) { Found = TRUE; break; } } else if ( RtlEqualMemory( CacheName->NetbiosName,NetbiosBroadcastName,16)){ // // It's a broadcast name. Any response is fine. // Found = TRUE; break; } else { // // It's group name. Make sure that this remote // has this group name registered with him. // for (i =0;iNetbiosName, NameBuffer[i].name, 16)) && (NameBuffer[i].name_flags & GROUP_NAME) ) { Found = TRUE; break; } } } } if (!Found) { NB_FREE_LOCK (&Device->Lock, LockHandle); return; } NB_DEBUG2 (QUERY, ("Got response to AdapterStatus %lx\n", AdapterStatusRequest)); RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); if (--CacheName->ReferenceCount == 0) { NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); NbiFreeMemory( CacheName, sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Name deleted"); } NB_FREE_LOCK (&Device->Lock, LockHandle); s = NbiPopReceivePacket (Device); if (s == NULL) { REQUEST_INFORMATION (AdapterStatusRequest) = 0; REQUEST_STATUS (AdapterStatusRequest) = STATUS_INSUFFICIENT_RESOURCES; NbiCompleteRequest (AdapterStatusRequest); NbiFreeRequest (Device, AdapterStatusRequest); NbiDereferenceDevice (Device, DREF_STATUS_QUERY); return; } ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); // // Initialize the receive packet. // ReceiveReserved->Type = RECEIVE_TYPE_ADAPTER_STATUS; ReceiveReserved->u.RR_AS.Request = AdapterStatusRequest; REQUEST_STATUS(AdapterStatusRequest) = STATUS_SUCCESS; CTEAssert (!ReceiveReserved->TransferInProgress); ReceiveReserved->TransferInProgress = TRUE; // // Now that we have a packet and a buffer, set up the transfer. // We will complete the request when the transfer completes. // TargetBuffer = REQUEST_NDIS_BUFFER (AdapterStatusRequest); NdisChainBufferAtFront (Packet, TargetBuffer); NbiGetBufferChainLength (TargetBuffer, &TargetBufferLength); BytesToTransfer = PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); if (TargetBufferLength < BytesToTransfer) { BytesToTransfer = TargetBufferLength; REQUEST_STATUS(AdapterStatusRequest) = STATUS_BUFFER_OVERFLOW; } (*Device->Bind.TransferDataHandler) ( &NdisStatus, MacBindingHandle, MacReceiveContext, LookaheadBufferOffset + (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)), BytesToTransfer, Packet, &BytesTransferred); if (NdisStatus != NDIS_STATUS_PENDING) { #if DBG if (NdisStatus == STATUS_SUCCESS) { CTEAssert (BytesTransferred == BytesToTransfer); } #endif NbiTransferDataComplete( Packet, NdisStatus, BytesTransferred); } } /* NbiProcessStatusResponse */