/*++ Copyright(c) 1999-2000 Microsoft Corporation Module Name: brdgmini.c Abstract: Ethernet MAC level bridge. Miniport section Author: Mark Aiken (original bridge by Jameel Hyder) Environment: Kernel mode driver Revision History: Sept 1999 - Original version Feb 2000 - Overhaul --*/ #define NDIS_MINIPORT_DRIVER #define NDIS50_MINIPORT 1 #define NDIS_WDM 1 #pragma warning( push, 3 ) #include #pragma warning( pop ) #include #include "bridge.h" #include "brdgmini.h" #include "brdgfwd.h" #include "brdgprot.h" #include "brdgbuf.h" #include "brdgsta.h" #include "brdgcomp.h" // =========================================================================== // // GLOBALS // // =========================================================================== // NDIS Wrapper handle NDIS_HANDLE gNDISWrapperHandle = NULL; // Handle to our miniport driver NDIS_HANDLE gMiniPortDriverHandle = NULL; // ---------------------------------------------- // The handle of the miniport (NULL if not initialized) NDIS_HANDLE gMiniPortAdapterHandle = NULL; // Refcount to allow waiting for other code to finish using the miniport WAIT_REFCOUNT gMiniPortAdapterRefcount; // Refcount indicating whether the bridge miniport is media-connected WAIT_REFCOUNT gMiniPortConnectedRefcount; // Refcount indicating whether the bridge miniport is in the middle of a media // state toggle. WAIT_REFCOUNT gMiniPortToggleRefcount; // ---------------------------------------------- // // Refcount for use in passing through requests to underlying NICs // This works because NDIS doesn't make requests re-entrantly. That // is, only one SetInfo operation can be pending at any given time. // LONG gRequestRefCount; // ---------------------------------------------- // Virtual characteristics of the bridge adapter ULONG gBridgeLinkSpeed = 10000L, // Start at 1Mbps, since reporting // zero makes some components unhappy. // Measured in 100's of bps. gBridgeMediaState = NdisMediaStateDisconnected; // MAC Address of the bridge. This does not change once // it has been set. UCHAR gBridgeAddress[ETH_LENGTH_OF_ADDRESS]; // Whether we have chosen an address yet BOOLEAN gHaveAddress; // Current bridge packet filter ULONG gPacketFilter = 0L; // Current multicast list PUCHAR gMulticastList = NULL; ULONG gMulticastListLength = 0L; // Device name of the bridge miniport (from the registry) PWCHAR gBridgeDeviceName = NULL; ULONG gBridgeDeviceNameSize = 0L; // RW lock to protect all above bridge variables NDIS_RW_LOCK gBridgeStateLock; //----------------------------------------------- // Name of the registry entry for the device name const PWCHAR gDeviceNameEntry = L"Device"; // Description of our miniport const PCHAR gDriverDescription = "Microsoft MAC Bridge Virtual NIC"; // ---------------------------------------------- // Device object so user-mode code can talk to us PDEVICE_OBJECT gDeviceObject = NULL; // NDIS handle to track the device object NDIS_HANDLE gDeviceHandle = NULL; // ---------------------------------------------- // List of supported OIDs NDIS_OID gSupportedOIDs[] = { // General characteristics OID_GEN_SUPPORTED_LIST, OID_GEN_HARDWARE_STATUS, OID_GEN_MEDIA_SUPPORTED, OID_GEN_MEDIA_IN_USE, OID_GEN_MAXIMUM_LOOKAHEAD, OID_GEN_MAXIMUM_FRAME_SIZE, OID_GEN_LINK_SPEED, OID_GEN_TRANSMIT_BUFFER_SPACE, OID_GEN_RECEIVE_BUFFER_SPACE, OID_GEN_TRANSMIT_BLOCK_SIZE, OID_GEN_RECEIVE_BLOCK_SIZE, OID_GEN_VENDOR_ID, OID_GEN_VENDOR_DESCRIPTION, OID_GEN_CURRENT_PACKET_FILTER, OID_GEN_CURRENT_LOOKAHEAD, OID_GEN_DRIVER_VERSION, OID_GEN_MAXIMUM_TOTAL_SIZE, OID_GEN_PROTOCOL_OPTIONS, OID_GEN_MAC_OPTIONS, OID_GEN_MEDIA_CONNECT_STATUS, OID_GEN_MAXIMUM_SEND_PACKETS, OID_GEN_VENDOR_DRIVER_VERSION, // Set only characteristics (relayed) OID_GEN_NETWORK_LAYER_ADDRESSES, OID_GEN_TRANSPORT_HEADER_OFFSET, // General statistics OID_GEN_XMIT_OK, OID_GEN_RCV_OK, OID_GEN_XMIT_ERROR, OID_GEN_RCV_NO_BUFFER, OID_GEN_RCV_NO_BUFFER, OID_GEN_DIRECTED_BYTES_XMIT, OID_GEN_DIRECTED_FRAMES_XMIT, OID_GEN_MULTICAST_BYTES_XMIT, OID_GEN_MULTICAST_FRAMES_XMIT, OID_GEN_BROADCAST_BYTES_XMIT, OID_GEN_BROADCAST_FRAMES_XMIT, OID_GEN_DIRECTED_BYTES_RCV, OID_GEN_DIRECTED_FRAMES_RCV, OID_GEN_MULTICAST_BYTES_RCV, OID_GEN_MULTICAST_FRAMES_RCV, OID_GEN_BROADCAST_BYTES_RCV, OID_GEN_BROADCAST_FRAMES_RCV, // Ethernet characteristics OID_802_3_PERMANENT_ADDRESS, OID_802_3_CURRENT_ADDRESS, OID_802_3_MULTICAST_LIST, OID_802_3_MAXIMUM_LIST_SIZE, // Ethernet statistics OID_802_3_RCV_ERROR_ALIGNMENT, OID_802_3_XMIT_ONE_COLLISION, OID_802_3_XMIT_MORE_COLLISIONS, // PnP OIDs OID_PNP_QUERY_POWER, OID_PNP_SET_POWER, // tcp oids OID_TCP_TASK_OFFLOAD }; // 1394 specific related global variables #define OID_1394_ENTER_BRIDGE_MODE 0xFF00C914 #define OID_1394_EXIT_BRIDGE_MODE 0xFF00C915 // Set when the bridge knows that tcpip has been loaded // set on receiving the OID_TCP_TASK_OFFLOAD Oid BOOLEAN g_fIsTcpIpLoaded = FALSE; // =========================================================================== // // PRIVATE PROTOTYPES // // =========================================================================== VOID BrdgMiniHalt( IN NDIS_HANDLE MiniportAdapterContext ); NDIS_STATUS BrdgMiniInitialize( OUT PNDIS_STATUS OpenErrorStatus, OUT PUINT SelectedMediumIndex, IN PNDIS_MEDIUM MediumArray, IN UINT MediumArraySize, IN NDIS_HANDLE MiniportAdapterHandle, IN NDIS_HANDLE WrapperConfigurationContext ); NDIS_STATUS BrdgMiniQueryInfo( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ); NDIS_STATUS BrdgMiniReset( OUT PBOOLEAN AddressingReset, IN NDIS_HANDLE MiniportAdapterContext ); VOID BrdgMiniSendPackets( IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets ); NDIS_STATUS BrdgMiniSetInfo( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ); BOOLEAN BrdgMiniAddrIsInMultiList( IN PUCHAR pTargetAddr ); VOID BrdgMiniRelayedRequestComplete( PNDIS_REQUEST_BETTER pRequest, PVOID unused ); VOID BrdgMiniReAcquireMiniport(); // =========================================================================== // // PUBLIC FUNCTIONS // // =========================================================================== NTSTATUS BrdgMiniDriverInit() /*++ Routine Description: Load-time initialization function Must run at PASSIVE_LEVEL since we call NdisRegisterDevice(). Arguments: None Return Value: Status of initialization. A return code != STATUS_SUCCESS causes driver load to fail. Any event causing a failure return code must be logged, as it prevents us from loading successfully. --*/ { NDIS_MINIPORT_CHARACTERISTICS MiniPortChars; NDIS_STATUS NdisStatus; PDRIVER_DISPATCH DispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1]; NDIS_STRING DeviceName, LinkName; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); NdisInitializeReadWriteLock( &gBridgeStateLock ); BrdgInitializeWaitRef( &gMiniPortAdapterRefcount, FALSE ); BrdgInitializeWaitRef( &gMiniPortConnectedRefcount, TRUE ); BrdgInitializeWaitRef( &gMiniPortToggleRefcount, FALSE ); // Put the miniport refcount into shutdown mode (so a refcount can't be acquired) // since we don't have a miniport yet BrdgShutdownWaitRefOnce( &gMiniPortAdapterRefcount ); // We start out disconnected so shutdown the media-connect waitref too. BrdgShutdownWaitRefOnce( &gMiniPortConnectedRefcount ); NdisInitUnicodeString( &DeviceName, DEVICE_NAME ); NdisInitUnicodeString( &LinkName, SYMBOLIC_NAME ); // Must first tell NDIS we're a miniport driver and initializing NdisMInitializeWrapper( &gNDISWrapperHandle, gDriverObject, &gRegistryPath, NULL ); // Fill in the description of our miniport NdisZeroMemory(&MiniPortChars, sizeof(MiniPortChars)); MiniPortChars.MajorNdisVersion = 5; MiniPortChars.MinorNdisVersion = 0; MiniPortChars.HaltHandler = BrdgMiniHalt; MiniPortChars.InitializeHandler = BrdgMiniInitialize; MiniPortChars.QueryInformationHandler = BrdgMiniQueryInfo; MiniPortChars.ResetHandler = BrdgMiniReset; MiniPortChars.SendPacketsHandler = BrdgMiniSendPackets; MiniPortChars.SetInformationHandler = BrdgMiniSetInfo; // // Wire the ReturnPacketHandler straight into the forwarding engine // MiniPortChars.ReturnPacketHandler = BrdgFwdReturnIndicatedPacket; // Create a virtual NIC NdisStatus = NdisIMRegisterLayeredMiniport( gNDISWrapperHandle, &MiniPortChars, sizeof(MiniPortChars), &gMiniPortDriverHandle ); if (NdisStatus != NDIS_STATUS_SUCCESS) { NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_MINIPORT_REGISTER_FAILED, 0, 0, NULL, sizeof(NDIS_STATUS), &NdisStatus ); DBGPRINT(MINI, ("Failed to create an NDIS virtual NIC: %08x\n", NdisStatus)); NdisTerminateWrapper( gNDISWrapperHandle, NULL ); return NdisStatus; } // // Initialize Dispatch Table array before setting selected members // NdisZeroMemory( DispatchTable, sizeof( DispatchTable ) ); // // Register a device object and symbolic link so user-mode code can talk to us // DispatchTable[IRP_MJ_CREATE] = BrdgDispatchRequest; DispatchTable[IRP_MJ_CLEANUP] = BrdgDispatchRequest; DispatchTable[IRP_MJ_CLOSE] = BrdgDispatchRequest; DispatchTable[IRP_MJ_DEVICE_CONTROL] = BrdgDispatchRequest; NdisStatus = NdisMRegisterDevice( gNDISWrapperHandle, &DeviceName, &LinkName, DispatchTable, &gDeviceObject, &gDeviceHandle ); if( NdisStatus != NDIS_STATUS_SUCCESS ) { NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_DEVICE_CREATION_FAILED, 0, 0, NULL, sizeof(NDIS_STATUS), &NdisStatus ); DBGPRINT(MINI, ("Failed to create a device object and sym link: %08x\n", NdisStatus)); NdisIMDeregisterLayeredMiniport( gMiniPortDriverHandle ); NdisTerminateWrapper( gNDISWrapperHandle, NULL ); return NdisStatus; } // Register the unload function NdisMRegisterUnloadHandler(gNDISWrapperHandle, BrdgUnload); return STATUS_SUCCESS; } VOID BrdgMiniCleanup() /*++ Routine Description: Unload-time orderly shutdown function This function is guaranteed to be called exactly once Must run at PASSIVE_LEVEL since we call NdisIMDeInitializeDeviceInstance Arguments: None Return Value: None --*/ { NDIS_STATUS NdisStatus; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); DBGPRINT(MINI, ("BrdgMiniCleanup\n")); if( gMiniPortAdapterHandle != NULL ) { SAFEASSERT( gNDISWrapperHandle != NULL ); // This should cause a call to BrdgMiniHalt where gMiniPortAdapterHandle // is NULLed out NdisStatus = NdisIMDeInitializeDeviceInstance( gMiniPortAdapterHandle ); SAFEASSERT( NdisStatus == NDIS_STATUS_SUCCESS ); } else { // // Tear down our device object. This is normally done when the miniport // shuts down, but in scenarios where the miniport was never created, // the device object still exists at this point. // NDIS_HANDLE Scratch = gDeviceHandle; if( Scratch != NULL ) { // Tear down the device object gDeviceHandle = gDeviceObject = NULL; NdisMDeregisterDevice( Scratch ); } } // Unregister ourselves as an intermediate driver NdisIMDeregisterLayeredMiniport( gMiniPortDriverHandle ); } BOOLEAN BrdgMiniIsBridgeDeviceName( IN PNDIS_STRING pDeviceName ) /*++ Routine Description: Compares a device name to the current device name of the bridge miniport. This actually requires that we allocate memory, so it should be called sparingly. Arguments: pDeviceName The name of a device Return Value: TRUE if the names match (case is ignored), FALSE otherwise. --*/ { LOCK_STATE LockState; BOOLEAN rc = FALSE; NDIS_STATUS Status; ULONG BridgeNameCopySize = 0L; PWCHAR pBridgeNameCopy = NULL; // The bridge device name must be read inside the gBridgeStateLock NdisAcquireReadWriteLock( &gBridgeStateLock, FALSE/*Read access*/, &LockState ); if( gBridgeDeviceName != NULL ) { if( gBridgeDeviceNameSize > 0 ) { // Alloc memory for the copy of the name Status = NdisAllocateMemoryWithTag( &pBridgeNameCopy, gBridgeDeviceNameSize, 'gdrB' ); if( Status == NDIS_STATUS_SUCCESS ) { // Copy the name NdisMoveMemory( pBridgeNameCopy, gBridgeDeviceName, gBridgeDeviceNameSize ); BridgeNameCopySize = gBridgeDeviceNameSize; } else { SAFEASSERT( pBridgeNameCopy == NULL ); } } else { SAFEASSERT( FALSE ); } } NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); if( pBridgeNameCopy != NULL ) { NDIS_STRING NdisStr; NdisInitUnicodeString( &NdisStr, pBridgeNameCopy ); if( NdisEqualString( &NdisStr, pDeviceName, TRUE/*Ignore case*/ ) ) { rc = TRUE; } NdisFreeMemory( pBridgeNameCopy, BridgeNameCopySize, 0 ); } return rc; } VOID BrdgMiniInstantiateMiniport() /*++ Routine Description: Instantiates the virtual NIC we expose to overlying protocols. At least one adapter must be in the global adapter list, since we build our MAC address with the MAC address of the first bound adapter. Must run at < DISPATCH_LEVEL since we call NdisIMInitializeDeviceInstanceEx Arguments: None Return Value: None --*/ { NDIS_STATUS Status; NTSTATUS NtStatus; NDIS_STRING NdisString; LOCK_STATE LockState; PWCHAR pDeviceName; ULONG DeviceNameSize; SAFEASSERT(CURRENT_IRQL < DISPATCH_LEVEL); DBGPRINT(MINI, ("About to instantiate the miniport...\n")); // // Retrieve our device name from the registry // (it is written there by our notify object during install) // NtStatus = BrdgReadRegUnicode( &gRegistryPath, gDeviceNameEntry, &pDeviceName, &DeviceNameSize ); if( NtStatus != STATUS_SUCCESS ) { NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_MINIPROT_DEVNAME_MISSING, 0, 0, NULL, sizeof(NTSTATUS), &NtStatus ); DBGPRINT(MINI, ("Failed to retrieve the miniport's device name: %08x\n", NtStatus)); return; } SAFEASSERT( pDeviceName != NULL ); DBGPRINT(MINI, ("Initializing miniport with device name %ws\n", pDeviceName)); NdisAcquireReadWriteLock( &gBridgeStateLock, TRUE/*Write access*/, &LockState ); if( ! gHaveAddress ) { // We don't have a MAC address yet. This is fatal. NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_NO_BRIDGE_MAC_ADDR, 0L, 0L, NULL, sizeof(NDIS_STATUS), &Status ); DBGPRINT(MINI, ("Failed to determine a MAC address: %08x\n", Status)); NdisFreeMemory( pDeviceName, DeviceNameSize, 0 ); return; } // // Save the device name in our global for use until we reinitialize. // Must do this before calling NdisIMInitializeDeviceInstanceEx, since NDIS calls // BrdgProtBindAdapter in the context of our call to NdisIMInitializeDeviceInstanceEx // and we want to consult the bridge's device name when binding // if( gBridgeDeviceName != NULL ) { // Free the old name NdisFreeMemory( gBridgeDeviceName, gBridgeDeviceNameSize, 0 ); } gBridgeDeviceName = pDeviceName; gBridgeDeviceNameSize = DeviceNameSize; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); // Go ahead and intantiate the miniport. NdisInitUnicodeString( &NdisString, pDeviceName ); Status = NdisIMInitializeDeviceInstanceEx(gMiniPortDriverHandle, &NdisString, NULL); if( Status != NDIS_STATUS_SUCCESS ) { // Log this error since it means we can't create the miniport NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_MINIPORT_INIT_FAILED, 0L, 0L, NULL, sizeof(NDIS_STATUS), &Status ); DBGPRINT(MINI, ("NdisIMInitializeDeviceInstanceEx failed: %08x\n", Status)); // Destroy the stored device name for the miniport NdisAcquireReadWriteLock( &gBridgeStateLock, TRUE/*Write access*/, &LockState ); if( gBridgeDeviceName != NULL ) { // Free the old name NdisFreeMemory( gBridgeDeviceName, gBridgeDeviceNameSize, 0 ); } gBridgeDeviceName = NULL; gBridgeDeviceNameSize = 0L; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); } } BOOLEAN BrdgMiniShouldIndicatePacket( IN PUCHAR pTargetAddr ) /*++ Routine Description: Determines whether an inbound packet should be indicated to overlying protocols through our virtual NIC Arguments: pTargetAddr The target MAC address of a packet Return Value: TRUE : The packet should be indicated FALSE : The packet should not be indicated --*/ { BOOLEAN bIsBroadcast, bIsMulticast, bIsLocalUnicast, rc = FALSE; LOCK_STATE LockState; if( gMiniPortAdapterHandle == NULL ) { // Yikes! The miniport isn't set up yet. Definitely don't indicate! return FALSE; } bIsBroadcast = ETH_IS_BROADCAST(pTargetAddr); bIsMulticast = ETH_IS_MULTICAST(pTargetAddr); bIsLocalUnicast = BrdgMiniIsUnicastToBridge(pTargetAddr); // Get read access to the packet filter NdisAcquireReadWriteLock( &gBridgeStateLock, FALSE /*Read-only*/, &LockState ); do { // Promiscuous / ALL_LOCAL means indicate everything if( (gPacketFilter & (NDIS_PACKET_TYPE_PROMISCUOUS | NDIS_PACKET_TYPE_ALL_LOCAL)) != 0 ) { rc = TRUE; break; } if( ((gPacketFilter & NDIS_PACKET_TYPE_BROADCAST) != 0) && bIsBroadcast ) { rc = TRUE; break; } if( ((gPacketFilter & NDIS_PACKET_TYPE_DIRECTED) != 0) && bIsLocalUnicast ) { rc = TRUE; break; } if( bIsMulticast ) { if( (gPacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) != 0 ) { rc = TRUE; break; } else if( (gPacketFilter & NDIS_PACKET_TYPE_MULTICAST) != 0 ) { rc = BrdgMiniAddrIsInMultiList( pTargetAddr ); } } } while (FALSE); NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); return rc; } BOOLEAN BrdgMiniIsUnicastToBridge ( IN PUCHAR Address ) /*++ Routine Description: Determines whether a given packet is a directed packet unicast straight to the bridge's host machine Arguments: Address The target MAC address of a packet Return Value: TRUE : This is a directed packet destined for the local machine FALSE : The above is not true --*/ { UINT Result; if( gHaveAddress ) { // Not necessary to acquire a lock to read gBridgeAddress since it cannot // change once it is set. ETH_COMPARE_NETWORK_ADDRESSES_EQ( Address, gBridgeAddress, &Result ); } else { // We have no MAC address, so this can't be addressed to us! Result = 1; // Inequality } return (BOOLEAN)(Result == 0); // Zero is equality } VOID BrdgMiniAssociate() /*++ Routine Description: Associates our miniport with our protocol Must run at PASSIVE_LEVEL Arguments: None Return Value: None --*/ { SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); // Associate ourselves with the protocol section in NDIS's tortured mind NdisIMAssociateMiniport( gMiniPortDriverHandle, gProtHandle ); } VOID BrdgMiniDeferredMediaDisconnect( IN PVOID arg ) /*++ Routine Description: Signals a media-disconnect to NDIS Must run at PASSIVE IRQL, since we have to wait for all packet indications to complete before indicating media-disconnect. Arguments: arg The bridge miniport handle (must be released) Return Value: None --*/ { NDIS_HANDLE MiniportHandle = (NDIS_HANDLE)arg; if( BrdgShutdownBlockedWaitRef(&gMiniPortConnectedRefcount) ) { // Nobody can indicate packets anymore. LOCK_STATE LockState; // // Close a timing window: we may have just gone media-connect but our high-IRQL // processing may not yet have reset the wait-refcount. Serialize here so it's // impossible for us to signal a disconnect to NDIS after we have actually // gone media-connect. // // This RELIES on high-IRQL processing acquiring gBridgeStateLock to set // gBridgeMediaState to NdisMediaStateConnected BEFORE signalling the // media-connected state to NDIS. // NdisAcquireReadWriteLock( &gBridgeStateLock, FALSE /*Read access*/, &LockState ); if( gBridgeMediaState == NdisMediaStateDisconnected ) { DBGPRINT(MINI, ("Signalled media-disconnect from deferred function\n")); NdisMIndicateStatus( MiniportHandle, NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0L ); } else { DBGPRINT(MINI, ("Aborted deferred media-disconnect: media state inconsistent\n")); } NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); } else { // Someone set us back to the connected state before we got executed DBGPRINT(MINI, ("Aborted deferred media-disconnect: wait-ref reset\n")); } BrdgMiniReleaseMiniport(); } VOID BrdgMiniDeferredMediaToggle( IN PVOID arg ) /*++ Routine Description: Signals a media-disconnect to NDIS followed quickly by a media-connect. Used to indicate to upper-layer protocols like TCPIP that the bridge may have disconnected from a network segment it could previously reach, or that we may now be able to reach a network segment that we couldn't before. Must run at PASSIVE IRQL, since we have to wait for all packet indications to complete before indicating media-disconnect. Arguments: arg The bridge miniport handle (must be released) Return Value: None --*/ { NDIS_HANDLE MiniportHandle = (NDIS_HANDLE)arg; // We need a guarantee that the miniport is media-connect to be able to do // the toggle properly. if( BrdgIncrementWaitRef(&gMiniPortConnectedRefcount) ) { // Stop people from indicating packets if( BrdgShutdownWaitRef(&gMiniPortToggleRefcount) ) { DBGPRINT(MINI, ("Doing deferred media toggle\n")); // Toggle our media state with NDIS NdisMIndicateStatus( MiniportHandle, NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0L ); NdisMIndicateStatus( MiniportHandle, NDIS_STATUS_MEDIA_CONNECT, NULL, 0L ); // Allow people to indicate packets again BrdgResetWaitRef( &gMiniPortToggleRefcount ); } else { DBGPRINT(MINI, ("Multiple toggles in progress simultaneously\n")); } BrdgDecrementWaitRef( &gMiniPortConnectedRefcount ); } // else the miniport isn't media-connect, so the toggle makes no sense. BrdgMiniReleaseMiniport(); } VOID BrdgMiniUpdateCharacteristics( IN BOOLEAN bConnectivityChange ) /*++ Routine Description: Recalculates the link speed and media status (connected / disconnected) that our virtual NIC exposes to overlying protocols Arguments: bConnectivityChange Whether the change that prompted this call is a change in connectivity (i.e., we acquired or lost an adapter). Return Value: None --*/ { LOCK_STATE LockState, ListLockState, AdaptersLockState; PADAPT pAdapt; ULONG MediaState = NdisMediaStateDisconnected; ULONG FastestSpeed = 0L; BOOLEAN UpdateSpeed = FALSE, UpdateMediaState = FALSE; NDIS_HANDLE MiniportHandle; // Need to read the adapter list and also have the adapters' characteristics not change NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*Read-only*/, &ListLockState ); NdisAcquireReadWriteLock( &gAdapterCharacteristicsLock, FALSE /*Read-only*/, &AdaptersLockState ); pAdapt = gAdapterList; while( pAdapt != NULL ) { // An adapter must be connected and actively handling packets to change our // virtual media state. if( (pAdapt->MediaState == NdisMediaStateConnected) && (pAdapt->State == Forwarding) ) { // We're connected if at least one NIC is connected MediaState = NdisMediaStateConnected; // The NIC must be connected for us to consider its speed if( pAdapt->LinkSpeed > FastestSpeed ) { FastestSpeed = pAdapt->LinkSpeed; } } pAdapt = pAdapt->Next; } NdisReleaseReadWriteLock( &gAdapterCharacteristicsLock, &AdaptersLockState ); NdisReleaseReadWriteLock( &gAdapterListLock, &ListLockState ); // Update the characteristics NdisAcquireReadWriteLock( &gBridgeStateLock, TRUE /*Write access*/, &LockState ); // // Only update our virtual link speed if we actually got at least one real speed // from our NICs. If everything is disconnected, the resulting FastestSpeed is // zero. In this case, we don't want to actually report a zero speed up to // overlying protocols; we stick at the last known speed until someone reconnects. // if( (gBridgeLinkSpeed != FastestSpeed) && (FastestSpeed != 0L) ) { UpdateSpeed = TRUE; gBridgeLinkSpeed = FastestSpeed; DBGPRINT(MINI, ("Updated bridge speed to %iMbps\n", FastestSpeed / 10000)); } if( gBridgeMediaState != MediaState ) { UpdateMediaState = TRUE; gBridgeMediaState = MediaState; if( MediaState == NdisMediaStateConnected ) { DBGPRINT(MINI, ("CONNECT\n")); } else { DBGPRINT(MINI, ("DISCONNECT\n")); } } NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); MiniportHandle = BrdgMiniAcquireMiniport(); if( MiniportHandle != NULL ) { if( UpdateMediaState ) { // Our link state has changed. if( MediaState == NdisMediaStateConnected ) { // // Tell NDIS we will be indicating packets again. // // NOTE: BrdgMiniDeferredMediaDisconnect RELIES on us doing this after // having updated gBridgeMediaState inside gBridgeStateLock so it can // close the timing window between this call and the BrdgResetWaitRef() call. // NdisMIndicateStatus( MiniportHandle, NDIS_STATUS_MEDIA_CONNECT, NULL, 0L ); // Re-enable packet indication. BrdgResetWaitRef( &gMiniPortConnectedRefcount ); } else { SAFEASSERT( MediaState == NdisMediaStateDisconnected ); // Stop people from indicating packets BrdgBlockWaitRef( &gMiniPortConnectedRefcount ); // We hand MiniportHandle to our deferred function BrdgMiniReAcquireMiniport(); // Have to do the media-disconnect indication at PASSIVE level, since we must // first wait for everyone to finish indicating packets. if( BrdgDeferFunction( BrdgMiniDeferredMediaDisconnect, MiniportHandle ) != NDIS_STATUS_SUCCESS ) { // Failed to defer the function. Avoid leaking a refcount BrdgMiniReleaseMiniport(); } } } else if( bConnectivityChange ) { // // There was no actual change to our media state. However, if the change that prompted this call // is a connectivity change and our media state is currently CONNECTED, we toggle it to // DISCONNECTED and back again to "hint" to upper-layer protocols like IP that the underlying // network may have changed. For example, in IP's case, a DHCP server may have become visible // (or a previously visible server may have disappeared) because of a connectivity change. // The hint causes IP to look for a DHCP server afresh. // if( MediaState == NdisMediaStateConnected ) { // We hand MiniportHandle to our deferred function BrdgMiniReAcquireMiniport(); // Toggle has to be done at PASSIVE level. if( BrdgDeferFunction( BrdgMiniDeferredMediaToggle, MiniportHandle ) != NDIS_STATUS_SUCCESS ) { // Failed to defer the function. Avoid leaking a refcount BrdgMiniReleaseMiniport(); } } } if( UpdateSpeed ) { // Tell overlying protocols that our speed has changed NdisMIndicateStatus( MiniportHandle, NDIS_STATUS_LINK_SPEED_CHANGE, &FastestSpeed, sizeof(ULONG) ); } BrdgMiniReleaseMiniport(); } } NDIS_HANDLE BrdgMiniAcquireMiniportForIndicate() /*++ Routine Description: Acquires the bridge miniport handle for the purpose of indicating packets. In addition to guaranteeing that the miniport will exist until the caller calls BrdgMiniReleaseMiniportForIndicate(), the caller is also allowed to indicate packets using the returned miniport handle until the miniport is released. Arguments: None Return Value: The NDIS handle for the virtual NIC. This can be used to indicate packets until a reciprocal call to BrdgMiniReleaseMiniportForIndicate(). --*/ { if( BrdgIncrementWaitRef(&gMiniPortAdapterRefcount) ) { SAFEASSERT( gMiniPortAdapterHandle != NULL ); // The miniport needs to be media-connect to indicate packets. if( BrdgIncrementWaitRef(&gMiniPortConnectedRefcount) ) { // A media-state toggle had better not be in progress if( BrdgIncrementWaitRef(&gMiniPortToggleRefcount) ) { // Caller can use the miniport return gMiniPortAdapterHandle; } // else miniport exists but is toggling its state BrdgDecrementWaitRef( &gMiniPortConnectedRefcount ); } // else miniport exists but is media-disconnected BrdgDecrementWaitRef( &gMiniPortAdapterRefcount ); } // else miniport does not exist. return NULL; } NDIS_HANDLE BrdgMiniAcquireMiniport() /*++ Routine Description: Increments the miniport's refcount so it cannot be torn down until a corresponding BrdgMiniReleaseMiniport() call is made. The caller may NOT use the returned miniport handle to indicate packets, since the miniport is not guaranteed to be in an appropriate state. Arguments: None Return Value: The NDIS handle for the virtual NIC. This can be used safely until a reciprocal call to BrdgMiniReleaseMiniport(). --*/ { if( BrdgIncrementWaitRef(&gMiniPortAdapterRefcount) ) { SAFEASSERT( gMiniPortAdapterHandle != NULL ); return gMiniPortAdapterHandle; } // else miniport does not exist. return NULL; } VOID BrdgMiniReAcquireMiniport() /*++ Routine Description: Reacquires the miniport (caller must have previously called BrdgMiniAcquireMiniport() and not yet called BrdgMiniReleaseMiniport(). Arguments: None Return Value: None. The caller should already be holding a handle for the miniport. --*/ { BrdgReincrementWaitRef(&gMiniPortAdapterRefcount); } VOID BrdgMiniReleaseMiniport() /*++ Routine Description: Decrements the miniport's refcount. The caller should no longer use the handle previously returned by BrdgMiniAcquireMiniport(). Arguments: None Return Value: None --*/ { BrdgDecrementWaitRef( &gMiniPortAdapterRefcount ); } VOID BrdgMiniReleaseMiniportForIndicate() /*++ Routine Description: Decrements the miniport's refcount. The caller should no longer use the handle previously returned by BrdgMiniAcquireMiniportForIndicate(). Arguments: None Return Value: None --*/ { BrdgDecrementWaitRef( &gMiniPortToggleRefcount ); BrdgDecrementWaitRef( &gMiniPortConnectedRefcount ); BrdgDecrementWaitRef( &gMiniPortAdapterRefcount ); } BOOLEAN BrdgMiniReadMACAddress( OUT PUCHAR pAddr ) /*++ Routine Description: Reads the bridge miniport's MAC address. Arguments: Address of a buffer to receive the address Return Value: TRUE if the value was copied out successfully FALSE if we don't yet have a MAC address (nothing was copied) --*/ { BOOLEAN rc; if( gHaveAddress ) { // Not necessary to acquire a lock to read the address since // it cannot change once it is set ETH_COPY_NETWORK_ADDRESS( pAddr, gBridgeAddress ); rc = TRUE; } else { rc = FALSE; } return rc; } // =========================================================================== // // PRIVATE FUNCTIONS // // =========================================================================== VOID BrdgMiniInitFromAdapter( IN PADAPT pAdapt ) /*++ Routine Description: Called by the protocol section to give us a chance to establish the bridge's MAC address when a new adapter arrives. If we succeed in determining a MAC address from the given adapter, we in turn call the STA module to tell it our MAC address, which it needs as early as possible. The MAC address of the bridge is set as the MAC address of the given adapter with the "locally administered" bit set. This should (hopefully) make the address unique in the local network as well as unique within our machine. This function is called for every new adapter but we only need to initialize once. Arguments: pAdapt An adapter to use to initialize Return Value: Status of the operation --*/ { if( ! gHaveAddress ) { LOCK_STATE LockState; NdisAcquireReadWriteLock( &gBridgeStateLock, TRUE/*Write access*/, &LockState ); // Possible for the gHaveAddress flag to have changed before acquiring the lock if( ! gHaveAddress ) { // Copy out the NIC's MAC address ETH_COPY_NETWORK_ADDRESS( gBridgeAddress, pAdapt->MACAddr ); // // Set the second-least significant bit of the NIC's MAC address. This moves the address // into the locally administered space. // gBridgeAddress[0] |= (UCHAR)0x02; DBGPRINT(MINI, ("Using MAC Address %02x-%02x-%02x-%02x-%02x-%02x\n", (UINT)gBridgeAddress[0], (UINT)gBridgeAddress[1], (UINT)gBridgeAddress[2], (UINT)gBridgeAddress[3], (UINT)gBridgeAddress[4], (UINT)gBridgeAddress[5])); gHaveAddress = TRUE; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); if( !gDisableSTA ) { // We are responsible for calling the STA module to complete its initialization once // we know our MAC address. BrdgSTADeferredInit( gBridgeAddress ); } // We are also responsible for letting the compatibility-mode code know about our // MAC address once it is set. BrdgCompNotifyMACAddress( gBridgeAddress ); } else { NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); } } } BOOLEAN BrdgMiniAddrIsInMultiList( IN PUCHAR pTargetAddr ) /*++ Routine Description: Determines whether a given multicast address is in the list of addresses that we must indicate to overlying protocols The caller is responsible for synchronization; it must have AT LEAST a read lock on gBridgeStateLock. Arguments: pTargetAddr The address to analyze Return Value: TRUE : This address is a multicast address that we have been asked to indicate FALSE : The above is not true --*/ { PUCHAR pCurAddr = gMulticastList; ULONG i; BOOLEAN rc = FALSE; // The list must have an integral number of addresses! SAFEASSERT( (gMulticastListLength % ETH_LENGTH_OF_ADDRESS) == 0 ); for( i = 0; i < (gMulticastListLength / ETH_LENGTH_OF_ADDRESS); i++, pCurAddr += ETH_LENGTH_OF_ADDRESS ) { UINT Result; ETH_COMPARE_NETWORK_ADDRESSES_EQ( pTargetAddr, pCurAddr, &Result ); if( Result == 0 ) { rc = TRUE; break; } } return rc; } VOID BrdgMiniHalt( IN NDIS_HANDLE MiniportAdapterContext ) /*++ Routine Description: Called when the virtual NIC is de-instantiated. We NULL out the miniport handle and stall the tear-down until everyone is done using the miniport. Must be called at PASSIVE_LEVEL since we wait on an event Arguments: MiniportAdapterContext Ignored Return Value: None --*/ { NDIS_HANDLE Scratch = gDeviceHandle; LOCK_STATE LockState; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); DBGPRINT(MINI, ("BrdgMiniHalt\n")); if( Scratch != NULL ) { // Tear down the device object gDeviceHandle = gDeviceObject = NULL; NdisMDeregisterDevice( Scratch ); } if( gMiniPortAdapterHandle != NULL ) { // Stall before returning until everyone is done using the miniport handle. // This also prevents people from acquiring the miniport handle. BrdgShutdownWaitRefOnce( &gMiniPortAdapterRefcount ); gMiniPortAdapterHandle = NULL; DBGPRINT(MINI, ("Done stall\n")); } NdisAcquireReadWriteLock( &gBridgeStateLock, TRUE/*Write access*/, &LockState ); if( gBridgeDeviceName != NULL ) { NdisFreeMemory( gBridgeDeviceName, gBridgeDeviceNameSize, 0 ); gBridgeDeviceName = NULL; gBridgeDeviceNameSize = 0L; } // Ditch our packet filter and multicast list gPacketFilter = 0L; if( gMulticastList != NULL ) { SAFEASSERT( gMulticastListLength > 0L ); NdisFreeMemory( gMulticastList, gMulticastListLength, 0 ); gMulticastList = NULL; gMulticastListLength = 0L; } NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); } NDIS_STATUS BrdgMiniInitialize( OUT PNDIS_STATUS OpenErrorStatus, OUT PUINT SelectedMediumIndex, IN PNDIS_MEDIUM MediumArray, IN UINT MediumArraySize, IN NDIS_HANDLE MiniPortAdapterHandle, IN NDIS_HANDLE WrapperConfigurationContext ) /*++ Routine Description: NDIS entry point called to initialize our virtual NIC following a call to NdisIMInitializeDeviceInstance Must run at PASSIVE_LEVEL since we call NdisMSetAttributesEx Arguments: OpenErrorStatus Where to return the specific error code if an open fails SelectedMediumIndex Where to specify which media we selected from MediumArray MediumArray A list of media types to choose from MediumArraySize Number of entries in MediumArray MiniPortAdapterHandle The handle for our virtual NIC (we save this) WrapperConfigurationContext Not used Return Value: Status of the initialization. A result != NDIS_STATUS_SUCCESS fails the NIC initialization and the miniport is not exposed to upper-layer protocols --*/ { UINT i; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); DBGPRINT(MINI, ("BrdgMiniInitialize\n")); for( i = 0; i < MediumArraySize; i++ ) { if( MediumArray[i] == NdisMedium802_3 ) // Ethernet { *SelectedMediumIndex = NdisMedium802_3; break; } } if( i == MediumArraySize ) { // Log this error since it's fatal NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_ETHERNET_NOT_OFFERED, 0L, 0L, NULL, 0L, NULL ); DBGPRINT(MINI, ("Ethernet not offered; failing\n")); return NDIS_STATUS_UNSUPPORTED_MEDIA; } NdisMSetAttributesEx( MiniPortAdapterHandle, NULL, 0, // CheckForHangTimeInSeconds NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT| NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER | NDIS_ATTRIBUTE_DESERIALIZE | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND, 0); // Save the adapter handle for future use gMiniPortAdapterHandle = MiniPortAdapterHandle; // Allow people to acquire the miniport BrdgResetWaitRef( &gMiniPortAdapterRefcount ); return NDIS_STATUS_SUCCESS; } NDIS_STATUS BrdgMiniReset( OUT PBOOLEAN AddressingReset, IN NDIS_HANDLE MiniportAdapterContext ) /*++ Routine Description: NDIS entry point called reset our miniport. We do nothing in response to this. Arguments: AddressingReset Whether NDIS needs to prod us some more by calling MiniportSetInformation after we return to restore various pieces of state MiniportAdapterContext Ignored Return Value: Status of the reset --*/ { DBGPRINT(MINI, ("BrdgMiniReset\n")); return NDIS_STATUS_SUCCESS; } VOID BrdgMiniSendPackets( IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets ) /*++ Routine Description: NDIS entry point called to send packets through our virtual NIC. We just call the forwarding logic code to handle each packet. Arguments: MiniportAdapterContext Ignored PacketArray Array of packet pointers to send NumberOfPacket Like it says Return Value: None --*/ { UINT i; NDIS_STATUS Status; for (i = 0; i < NumberOfPackets; i++) { PNDIS_PACKET pPacket = PacketArray[i]; // Hand this packet to the forwarding engine for processing Status = BrdgFwdSendPacket( pPacket ); if( Status != NDIS_STATUS_PENDING ) { // The forwarding engine completed immediately // NDIS should prevent the miniport from being shut down // until we return from this function SAFEASSERT( gMiniPortAdapterHandle != NULL ); NdisMSendComplete(gMiniPortAdapterHandle, pPacket, Status); } // else the forwarding engine will call NdisMSendComplete() } } NDIS_STATUS BrdgMiniQueryInfo( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ) /*++ Routine Description: NDIS entry point called to retrieve various pieces of info from our miniport Arguments: MiniportAdapterContext Ignored Oid The request code InformationBuffer Place to return information InformationBufferLength Size of InformationBuffer BytesWritten Output of the number of written bytes BytesNeeded If the provided buffer is too small, this is how many we need. Return Value: Status of the request --*/ { // Macros for use in this function alone #define REQUIRE_AT_LEAST(n) \ { \ if(InformationBufferLength < (n)) \ { \ *BytesNeeded = (n); \ return NDIS_STATUS_INVALID_LENGTH; \ }\ } #define RETURN_BYTES(p,n) \ { \ NdisMoveMemory( InformationBuffer, (p), (n) ); \ *BytesWritten = (n); \ return NDIS_STATUS_SUCCESS; \ } switch( Oid ) { // General characteristics case OID_GEN_SUPPORTED_LIST: { REQUIRE_AT_LEAST( sizeof(gSupportedOIDs) ); RETURN_BYTES( gSupportedOIDs, sizeof(gSupportedOIDs)); } break; case OID_GEN_HARDWARE_STATUS: { REQUIRE_AT_LEAST( sizeof(ULONG) ); *((ULONG*)InformationBuffer) = NdisHardwareStatusReady; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MEDIA_SUPPORTED: case OID_GEN_MEDIA_IN_USE: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // We support Ethernet only *((ULONG*)InformationBuffer) = NdisMedium802_3; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_TRANSMIT_BUFFER_SPACE: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Lie and claim to have 15K of send space, a common // Ethernet card value. // REVIEW: Is there a better value? *((ULONG*)InformationBuffer) = 15 * 1024; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_RECEIVE_BUFFER_SPACE: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Lie and claim to have 150K of receive space, a common // Ethernet card value. // REVIEW: Is there a better value? *((ULONG*)InformationBuffer) = 150 * 1024; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MAXIMUM_SEND_PACKETS: case OID_802_3_MAXIMUM_LIST_SIZE: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Return a generic large integer // REVIEW: Is there a better value to hand out? *((ULONG*)InformationBuffer) = 0x000000FF; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MAXIMUM_FRAME_SIZE: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Ethernet payloads can be up to 1500 bytes *((ULONG*)InformationBuffer) = 1500L; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; // We indicate full packets up to NDIS, so these values are the same and // equal to the maximum size of a packet case OID_GEN_MAXIMUM_LOOKAHEAD: case OID_GEN_CURRENT_LOOKAHEAD: // These are also just the maximum total size of a frame case OID_GEN_MAXIMUM_TOTAL_SIZE: case OID_GEN_RECEIVE_BLOCK_SIZE: case OID_GEN_TRANSMIT_BLOCK_SIZE: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Ethernet frames with header can be up to 1514 bytes *((ULONG*)InformationBuffer) = 1514L; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MAC_OPTIONS: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // We have no internal loopback support *((ULONG*)InformationBuffer) = NDIS_MAC_OPTION_NO_LOOPBACK; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_LINK_SPEED: { LOCK_STATE LockState; REQUIRE_AT_LEAST( sizeof(ULONG) ); NdisAcquireReadWriteLock( &gBridgeStateLock, FALSE /*Read only*/, &LockState ); *((ULONG*)InformationBuffer) = gBridgeLinkSpeed; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; // Ethernet characteristics case OID_802_3_PERMANENT_ADDRESS: case OID_802_3_CURRENT_ADDRESS: { SAFEASSERT( gHaveAddress ); // Don't need a read lock because the address shouldn't change once set REQUIRE_AT_LEAST( sizeof(gBridgeAddress) ); RETURN_BYTES( gBridgeAddress, sizeof(gBridgeAddress)); } break; case OID_GEN_MEDIA_CONNECT_STATUS: { LOCK_STATE LockState; REQUIRE_AT_LEAST( sizeof(ULONG) ); NdisAcquireReadWriteLock( &gBridgeStateLock, FALSE /*Read only*/, &LockState ); *((ULONG*)InformationBuffer) = gBridgeMediaState; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_VENDOR_ID: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // We don't have an IEEE-assigned ID so use this constant *((ULONG*)InformationBuffer) = 0xFFFFFF; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_VENDOR_DESCRIPTION: { UINT len = (UINT)strlen( gDriverDescription ) + 1; REQUIRE_AT_LEAST( len ); RETURN_BYTES( gDriverDescription, len); } break; case OID_GEN_VENDOR_DRIVER_VERSION: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // We are version 1.0 *((ULONG*)InformationBuffer) = 0x00010000; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_DRIVER_VERSION: { REQUIRE_AT_LEAST( sizeof(USHORT) ); // We are using version 5.0 of NDIS *((USHORT*)InformationBuffer) = 0x0500; *BytesWritten = sizeof(USHORT); return NDIS_STATUS_SUCCESS; } break; // // General Statistics // case OID_GEN_XMIT_OK: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of local-sourced sent frames *((ULONG*)InformationBuffer) = gStatTransmittedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_XMIT_ERROR: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of local-sourced frames sent with errors *((ULONG*)InformationBuffer) = gStatTransmittedErrorFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_RCV_OK: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of indicated frames *((ULONG*)InformationBuffer) = gStatIndicatedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; // Answer the same for these two case OID_GEN_RCV_NO_BUFFER: case OID_GEN_RCV_ERROR: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatIndicatedDroppedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_DIRECTED_BYTES_XMIT: { REQUIRE_AT_LEAST( sizeof(ULONG) ); *((ULONG*)InformationBuffer) = gStatDirectedTransmittedBytes.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_DIRECTED_FRAMES_XMIT: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatDirectedTransmittedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MULTICAST_BYTES_XMIT: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatMulticastTransmittedBytes.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MULTICAST_FRAMES_XMIT: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatMulticastTransmittedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_BROADCAST_BYTES_XMIT: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatBroadcastTransmittedBytes.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_BROADCAST_FRAMES_XMIT: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatBroadcastTransmittedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_DIRECTED_BYTES_RCV: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatDirectedIndicatedBytes.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_DIRECTED_FRAMES_RCV: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatDirectedIndicatedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MULTICAST_BYTES_RCV: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatMulticastIndicatedBytes.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_MULTICAST_FRAMES_RCV: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatMulticastIndicatedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_BROADCAST_BYTES_RCV: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatBroadcastIndicatedBytes.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_BROADCAST_FRAMES_RCV: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // Reply with the number of packets we wanted to indicate but couldn't *((ULONG*)InformationBuffer) = gStatBroadcastIndicatedFrames.LowPart; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; // Ethernet statistics case OID_802_3_RCV_ERROR_ALIGNMENT: case OID_802_3_XMIT_ONE_COLLISION: case OID_802_3_XMIT_MORE_COLLISIONS: { REQUIRE_AT_LEAST( sizeof(ULONG) ); // We have no way of collecting this information sensibly from lower NICs, so // pretend these types of events never happen. *((ULONG*)InformationBuffer) = 0L; *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_GEN_CURRENT_PACKET_FILTER: { LOCK_STATE LockState; REQUIRE_AT_LEAST( sizeof(ULONG) ); NdisAcquireReadWriteLock( &gBridgeStateLock, FALSE /*Read only*/, &LockState ); *((ULONG*)InformationBuffer) = gPacketFilter; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); *BytesWritten = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_802_3_MULTICAST_LIST: { LOCK_STATE LockState; NdisAcquireReadWriteLock( &gBridgeStateLock, FALSE /*Read only*/, &LockState ); if(InformationBufferLength < gMulticastListLength) { *BytesNeeded = gMulticastListLength; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); return NDIS_STATUS_INVALID_LENGTH; } NdisMoveMemory( InformationBuffer, gMulticastList, gMulticastListLength ); *BytesWritten = gMulticastListLength; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); return NDIS_STATUS_SUCCESS; } break; case OID_PNP_QUERY_POWER: { return NDIS_STATUS_SUCCESS; } break; case OID_TCP_TASK_OFFLOAD: { // Mark that Tcp.ip has been loaded g_fIsTcpIpLoaded = TRUE; // Set the underlying 1394 miniport to ON BrdgSetMiniportsToBridgeMode(NULL,TRUE); return NDIS_STATUS_NOT_SUPPORTED; } break; } // We don't understand the OID return NDIS_STATUS_NOT_SUPPORTED; #undef REQUIRE_AT_LEAST #undef RETURN_BYTES } NDIS_STATUS BrdgMiniSetInfo( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) /*++ Routine Description: NDIS entry point called to set various pieces of info to our miniport Arguments: MiniportAdapterContext Ignored Oid The request code InformationBuffer Input information buffer InformationBufferLength Size of InformationBuffer BytesRead Number of bytes read from InformationBuffer BytesNeeded If the provided buffer is too small, this is how many we need. Return Value: Status of the request --*/ { LOCK_STATE LockState; NDIS_STATUS Status; switch( Oid ) { case OID_GEN_CURRENT_PACKET_FILTER: { SAFEASSERT( InformationBufferLength == sizeof(ULONG) ); // Get write access to the packet filter NdisAcquireReadWriteLock( &gBridgeStateLock, TRUE /*Read-Write*/, &LockState ); gPacketFilter = *((ULONG*)InformationBuffer); NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); DBGPRINT(MINI, ("Set the packet filter to %08x\n", gPacketFilter)); *BytesRead = sizeof(ULONG); return NDIS_STATUS_SUCCESS; } break; case OID_802_3_MULTICAST_LIST: { PUCHAR pOldList, pNewList; ULONG oldListLength; // The incoming buffer should contain an integral number of Ethernet MAC // addresses SAFEASSERT( (InformationBufferLength % ETH_LENGTH_OF_ADDRESS) == 0 ); DBGPRINT(MINI, ("Modifying the multicast list; now has %i entries\n", InformationBufferLength / ETH_LENGTH_OF_ADDRESS)); // Alloc and copy to the new multicast list if( InformationBufferLength > 0 ) { Status = NdisAllocateMemoryWithTag( &pNewList, InformationBufferLength, 'gdrB' ); if( Status != NDIS_STATUS_SUCCESS ) { DBGPRINT(MINI, ("NdisAllocateMemoryWithTag failed while recording multicast list\n")); return NDIS_STATUS_NOT_ACCEPTED; } // Copy the list NdisMoveMemory( pNewList, InformationBuffer, InformationBufferLength ); } else { pNewList = NULL; } // Swap in the new list NdisAcquireReadWriteLock( &gBridgeStateLock, TRUE /*Read-Write*/, &LockState ); pOldList = gMulticastList; oldListLength = gMulticastListLength; gMulticastList = pNewList; gMulticastListLength = InformationBufferLength; NdisReleaseReadWriteLock( &gBridgeStateLock, &LockState ); // Free the old multicast list if there was one if( pOldList != NULL ) { NdisFreeMemory( pOldList, oldListLength, 0 ); } *BytesRead = InformationBufferLength; return NDIS_STATUS_SUCCESS; } break; case OID_GEN_CURRENT_LOOKAHEAD: case OID_GEN_PROTOCOL_OPTIONS: { // We accept these but do nothing return NDIS_STATUS_SUCCESS; } break; // Overlying protocols telling us about their network addresses case OID_GEN_NETWORK_LAYER_ADDRESSES: { // Let the compatibility-mode code note the addresses BrdgCompNotifyNetworkAddresses( InformationBuffer, InformationBufferLength ); } // // DELIBERATELY FALL THROUGH // // All relayed OIDs go here case OID_GEN_TRANSPORT_HEADER_OFFSET: { LOCK_STATE LockState; PADAPT Adapters[MAX_ADAPTERS], pAdapt; LONG NumAdapters = 0L, i; PNDIS_REQUEST_BETTER pRequest; NDIS_STATUS Status, rc; // We read the entire request *BytesRead = InformationBufferLength; // Pass these straight through to underlying NICs NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*Read only*/, &LockState ); // Make a list of the adapters to send the request to pAdapt = gAdapterList; while( pAdapt != NULL ) { if( NumAdapters < MAX_ADAPTERS ) { Adapters[NumAdapters] = pAdapt; // We will be using this adapter outside the list lock BrdgAcquireAdapterInLock( pAdapt ); NumAdapters++; } else { SAFEASSERT( FALSE ); DBGPRINT(MINI, ("Too many adapters to relay a SetInfo request to!\n")); } pAdapt = pAdapt->Next; } // The refcount is the number of requests we will make gRequestRefCount = NumAdapters; NdisReleaseReadWriteLock( &gAdapterListLock, &LockState ); if( NumAdapters == 0 ) { // Nothing to do! rc = NDIS_STATUS_SUCCESS; } else { // Request will pend unless all adapters return immediately rc = NDIS_STATUS_PENDING; for( i = 0L; i < NumAdapters; i++ ) { // Allocate memory for the request Status = NdisAllocateMemoryWithTag( &pRequest, sizeof(NDIS_REQUEST_BETTER), 'gdrB' ); if( Status != NDIS_STATUS_SUCCESS ) { LONG NewCount = InterlockedDecrement( &gRequestRefCount ); DBGPRINT(MINI, ("NdisAllocateMemoryWithTag failed while relaying an OID\n")); if( NewCount == 0 ) { // This could only have happened with the last adapter SAFEASSERT( i == NumAdapters - 1 ); // We're all done since everyone else has completed too rc = NDIS_STATUS_SUCCESS; } // Let go of the adapter BrdgReleaseAdapter( Adapters[i] ); continue; } // Set up the request as a mirror of ours pRequest->Request.RequestType = NdisRequestSetInformation; pRequest->Request.DATA.SET_INFORMATION.Oid = Oid ; pRequest->Request.DATA.SET_INFORMATION.InformationBuffer = InformationBuffer; pRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength; NdisInitializeEvent( &pRequest->Event ); NdisResetEvent( &pRequest->Event ); pRequest->pFunc = BrdgMiniRelayedRequestComplete; pRequest->FuncArg = NULL; // Fire it off NdisRequest( &Status, Adapters[i]->BindingHandle, &pRequest->Request ); // Let go of the adapter; NDIS should not permit it to be unbound while // a request is in progress BrdgReleaseAdapter( Adapters[i] ); if( Status != NDIS_STATUS_PENDING ) { // The cleanup function won't get called BrdgMiniRelayedRequestComplete( pRequest, NULL ); } } } // // Paranoia for future maintainance: can't refer to pointer parameters // at this point, as the relayed requests may have completed already, making // them stale. // InformationBuffer = NULL; BytesRead = NULL; BytesNeeded = NULL; return rc; } break; case OID_PNP_SET_POWER: { return NDIS_STATUS_SUCCESS; } break; } return NDIS_STATUS_NOT_SUPPORTED; } VOID BrdgMiniRelayedRequestComplete( PNDIS_REQUEST_BETTER pRequest, PVOID unused ) /*++ Routine Description: Called when a SetInformation request that we relayed completes. Arguments: pRequest The NDIS_REQUEST_BETTER structure we allocated in BrdgMiniSetInformation(). unused Unused Return Value: Status of the request --*/ { LONG NewCount = InterlockedDecrement( &gRequestRefCount ); if( NewCount == 0 ) { // NDIS Should not permit the miniport to shut down with a request // in progress SAFEASSERT( gMiniPortAdapterHandle != NULL ); // The operation always succeeds NdisMSetInformationComplete( gMiniPortAdapterHandle, NDIS_STATUS_SUCCESS ); } // Free the request structure since we allocated it ourselves NdisFreeMemory( pRequest, sizeof(PNDIS_REQUEST_BETTER), 0 ); } VOID BrdgMiniLocalRequestComplete( PNDIS_REQUEST_BETTER pRequest, PVOID pContext ) /*++ Routine Description: Called when bridge allocated request completes. Arguments: pRequest The NDIS_REQUEST_BETTER structure we allocated in BrdgSetMiniportsToBridgeMode. Context pAdapt structure Return Value: Status of the request --*/ { PADAPT pAdapt = (PADAPT)pContext; // Let go of the adapter; BrdgReleaseAdapter( pAdapt); // Free the request structure since we allocated it ourselves NdisFreeMemory( pRequest, sizeof(PNDIS_REQUEST_BETTER), 0 ); } VOID BrdgSetMiniportsToBridgeMode( PADAPT pAdapt, BOOLEAN fSet ) /*++ Routine Description: Sends a 1394 specific OID to the miniport informing it that TCP/IP has been activated Arguments: pAdapt - If adapt is not NULL, send Request to this adapt. Otherwise send it to all of them. fSet - if True, then set Bridge Mode ON, otherwise set it OFF Return Value: Status of the request --*/ { LOCK_STATE LockState; PADAPT Adapters[MAX_ADAPTERS]; LONG NumAdapters = 0L, i; NDIS_OID Oid; if (pAdapt != NULL) { if (pAdapt->PhysicalMedium == NdisPhysicalMedium1394) { // We have a 1394 adapt, ref it and send the request to it if (BrdgAcquireAdapter (pAdapt)) { Adapters[0] = pAdapt; NumAdapters = 1; } } } else { // walk through the list and Acquire all the 1394 adapts NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*Read only*/, &LockState ); // Make a list of the adapters to send the request to pAdapt = gAdapterList; while( pAdapt != NULL ) { if( NumAdapters < MAX_ADAPTERS && pAdapt->PhysicalMedium == NdisPhysicalMedium1394) { Adapters[NumAdapters] = pAdapt; // We will be using this adapter outside the list lock BrdgAcquireAdapterInLock( pAdapt ); // cannot fail NumAdapters++; } pAdapt = pAdapt->Next; } NdisReleaseReadWriteLock( &gAdapterListLock, &LockState ); } if (NumAdapters == 0) { return; } if (fSet == TRUE) { Oid = OID_1394_ENTER_BRIDGE_MODE ; DBGPRINT(MINI, ("Setting 1394 miniport bridge mode - ON !\n")); } else { Oid = OID_1394_EXIT_BRIDGE_MODE ; DBGPRINT(MINI, ("Setting 1394 miniport bridge mode - OFF !\n")); } for( i = 0L; i < NumAdapters; i++ ) { NDIS_STATUS Status; PNDIS_REQUEST_BETTER pRequest; Status = NdisAllocateMemoryWithTag( &pRequest, sizeof(NDIS_REQUEST_BETTER), 'gdrB' ); if( Status != NDIS_STATUS_SUCCESS ) { DBGPRINT(MINI, ("NdisAllocateMemoryWithTag failed while allocating a request structure \n")); // Let go of the adapter BrdgReleaseAdapter( Adapters[i] ); continue; } // Set up the request pRequest->Request.RequestType = NdisRequestSetInformation; pRequest->Request.DATA.SET_INFORMATION.Oid = Oid; pRequest->Request.DATA.SET_INFORMATION.InformationBuffer = NULL; pRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = 0 ; NdisInitializeEvent( &pRequest->Event ); NdisResetEvent( &pRequest->Event ); pRequest->pFunc = BrdgMiniLocalRequestComplete; pRequest->FuncArg = Adapters[i]; // Fire it off NdisRequest( &Status, Adapters[i]->BindingHandle, &pRequest->Request ); if( Status != NDIS_STATUS_PENDING ) { // The cleanup function won't get called BrdgMiniLocalRequestComplete( pRequest, Adapters[i] ); } } // end of for loop return; }