NDIS_STATUS otLwfTunInitialize( _In_ PMS_FILTER pFilter ) { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; HANDLE threadHandle = NULL; LogFuncEntry(DRIVER_DEFAULT); NT_ASSERT(pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_THREAD_1_0); KeInitializeEvent( &pFilter->TunWorkerThreadStopEvent, SynchronizationEvent, // auto-clearing event FALSE // event initially non-signalled ); KeInitializeEvent( &pFilter->TunWorkerThreadAddressChangedEvent, SynchronizationEvent, // auto-clearing event FALSE // event initially non-signalled ); // Start the worker thread Status = PsCreateSystemThread( &threadHandle, // ThreadHandle THREAD_ALL_ACCESS, // DesiredAccess NULL, // ObjectAttributes NULL, // ProcessHandle NULL, // ClientId otLwfTunWorkerThread, // StartRoutine pFilter // StartContext ); if (!NT_SUCCESS(Status)) { LogError(DRIVER_DEFAULT, "PsCreateSystemThread failed, %!STATUS!", Status); goto error; } // Grab the object reference to the worker thread Status = ObReferenceObjectByHandle( threadHandle, THREAD_ALL_ACCESS, *PsThreadType, KernelMode, &pFilter->TunWorkerThread, NULL ); if (!NT_VERIFYMSG("ObReferenceObjectByHandle can't fail with a valid kernel handle", NT_SUCCESS(Status))) { LogError(DRIVER_DEFAULT, "ObReferenceObjectByHandle failed, %!STATUS!", Status); KeSetEvent(&pFilter->TunWorkerThreadStopEvent, IO_NO_INCREMENT, FALSE); } // Make sure to enable RLOC passthrough Status = otLwfCmdSetProp( pFilter, SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU, SPINEL_DATATYPE_BOOL_S, TRUE ); if (!NT_SUCCESS(Status)) { LogError(DRIVER_DEFAULT, "Enabling RLOC pass through failed, %!STATUS!", Status); goto error; } // TODO - Query other values and capabilities error: if (!NT_SUCCESS(Status)) { otLwfTunUninitialize(pFilter); } LogFuncExitNDIS(DRIVER_DEFAULT, Status); return Status; }
_Use_decl_annotations_ NDIS_STATUS FilterOidRequest( NDIS_HANDLE FilterModuleContext, PNDIS_OID_REQUEST Request ) /*++ Routine Description: Request handler Handle requests from upper layers Arguments: FilterModuleContext - our filter Request - the request passed down Return Value: NDIS_STATUS_SUCCESS NDIS_STATUS_PENDING NDIS_STATUS_XXX NOTE: Called at <= DISPATCH_LEVEL (unlike a miniport's MiniportOidRequest) --*/ { PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; NDIS_STATUS Status; PNDIS_OID_REQUEST ClonedRequest=NULL; BOOLEAN bSubmitted = FALSE; POTLWF_REQUEST_CONTEXT Context; BOOLEAN bFalse = FALSE; LogFuncEntryMsg(DRIVER_OID, "Request %p", Request); // // Most of the time, a filter will clone the OID request and pass down // the clone. When the clone completes, the filter completes the original // OID request. // // If your filter needs to modify a specific request, it can modify the // request before or after sending down the cloned request. Or, it can // complete the original request on its own without sending down any // clone at all. // // If your filter driver does not need to modify any OID requests, then // you may simply omit this routine entirely; NDIS will pass OID requests // down on your behalf. This is more efficient than implementing a // routine that does nothing but clone all requests, as in the sample here. // do { Status = NdisAllocateCloneOidRequest(pFilter->FilterHandle, Request, OTLWF_CLONED_OID_TAG, &ClonedRequest); if (Status != NDIS_STATUS_SUCCESS) { LogWarning(DRIVER_OID, "Failed to Clone Request, %!NDIS_STATUS!", Status); break; } Context = (POTLWF_REQUEST_CONTEXT)(&ClonedRequest->SourceReserved[0]); *Context = Request; bSubmitted = TRUE; // // Use same request ID // ClonedRequest->RequestId = Request->RequestId; pFilter->PendingOidRequest = ClonedRequest; LogVerbose(DRIVER_OID, "Sending (cloned) Oid Request %p", ClonedRequest); Status = NdisFOidRequest(pFilter->FilterHandle, ClonedRequest); if (Status != NDIS_STATUS_PENDING) { FilterOidRequestComplete(pFilter, ClonedRequest, Status); Status = NDIS_STATUS_PENDING; } } while(bFalse); if (bSubmitted == FALSE) { switch(Request->RequestType) { case NdisRequestMethod: Request->DATA.METHOD_INFORMATION.BytesRead = 0; Request->DATA.METHOD_INFORMATION.BytesNeeded = 0; Request->DATA.METHOD_INFORMATION.BytesWritten = 0; break; case NdisRequestSetInformation: Request->DATA.SET_INFORMATION.BytesRead = 0; Request->DATA.SET_INFORMATION.BytesNeeded = 0; break; case NdisRequestQueryInformation: case NdisRequestQueryStatistics: default: Request->DATA.QUERY_INFORMATION.BytesWritten = 0; Request->DATA.QUERY_INFORMATION.BytesNeeded = 0; break; } } LogFuncExitNDIS(DRIVER_OID, Status); return Status; }
_Use_decl_annotations_ NDIS_STATUS FilterAttach( NDIS_HANDLE NdisFilterHandle, NDIS_HANDLE FilterDriverContext, PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters ) /*++ Routine Description: Filter attach routine. Create filter's context, allocate NetBufferLists and NetBuffer pools and any other resources, and read configuration if needed. Arguments: NdisFilterHandle - Specify a handle identifying this instance of the filter. FilterAttach should save this handle. It is a required parameter in subsequent calls to NdisFxxx functions. FilterDriverContext - Filter driver context passed to NdisFRegisterFilterDriver. AttachParameters - attach parameters Return Value: NDIS_STATUS_SUCCESS: FilterAttach successfully allocated and initialize data structures for this filter instance. NDIS_STATUS_RESOURCES: FilterAttach failed due to insufficient resources. NDIS_STATUS_FAILURE: FilterAttach could not set up this instance of this filter and it has called NdisWriteErrorLogEntry with parameters specifying the reason for failure. N.B.: FILTER can use NdisRegisterDeviceEx to create a device, so the upper layer can send Irps to the filter. --*/ { PMS_FILTER pFilter = NULL; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; NTSTATUS NtStatus; NDIS_FILTER_ATTRIBUTES FilterAttributes; ULONG Size; COMPARTMENT_ID OriginalCompartmentID; OBJECT_ATTRIBUTES ObjectAttributes = {0}; const ULONG RegKeyOffset = ARRAYSIZE(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\otlwf\\Parameters\\NdisAdapters\\") - 1; DECLARE_CONST_UNICODE_STRING(RegKeyPath, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\otlwf\\Parameters\\NdisAdapters\\{00000000-0000-0000-0000-000000000000}"); RtlCopyMemory(RegKeyPath.Buffer + RegKeyOffset, AttachParameters->BaseMiniportName->Buffer + 8, sizeof(L"{00000000-0000-0000-0000-000000000000}")); LogFuncEntry(DRIVER_DEFAULT); do { ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject); if (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject) { Status = NDIS_STATUS_INVALID_PARAMETER; break; } // Verify the media type is supported. This is a last resort; the // the filter should never have been bound to an unsupported miniport // to begin with. if (AttachParameters->MiniportMediaType != NdisMediumIP) { LogError(DRIVER_DEFAULT, "Unsupported media type, 0x%x.", (ULONG)AttachParameters->MiniportMediaType); Status = NDIS_STATUS_INVALID_PARAMETER; break; } Size = sizeof(MS_FILTER) + AttachParameters->BaseMiniportInstanceName->Length; pFilter = (PMS_FILTER)FILTER_ALLOC_MEM(NdisFilterHandle, Size); if (pFilter == NULL) { LogWarning(DRIVER_DEFAULT, "Failed to allocate context structure, 0x%x bytes", Size); Status = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(pFilter, sizeof(MS_FILTER)); LogVerbose(DRIVER_DEFAULT, "Opening interface registry key %S", RegKeyPath.Buffer); InitializeObjectAttributes( &ObjectAttributes, (PUNICODE_STRING)&RegKeyPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); // Open the registry key NtStatus = ZwOpenKey(&pFilter->InterfaceRegKey, KEY_ALL_ACCESS, &ObjectAttributes); if (!NT_SUCCESS(NtStatus)) { LogError(DRIVER_DEFAULT, "ZwOpenKey failed to open %S, %!STATUS!", RegKeyPath.Buffer, NtStatus); Status = NDIS_STATUS_FAILURE; break; } // Format of "\DEVICE\{5BA90C49-0D7E-455B-8D3B-614F6714A212}" AttachParameters->BaseMiniportName->Buffer += 8; AttachParameters->BaseMiniportName->Length -= 8 * sizeof(WCHAR); NtStatus = RtlGUIDFromString(AttachParameters->BaseMiniportName, &pFilter->InterfaceGuid); AttachParameters->BaseMiniportName->Buffer -= 8; AttachParameters->BaseMiniportName->Length += 8 * sizeof(WCHAR); if (!NT_SUCCESS(NtStatus)) { LogError(DRIVER_DEFAULT, "Failed to convert FilterModuleGuidName to a GUID, %!STATUS!", NtStatus); Status = NDIS_STATUS_FAILURE; break; } pFilter->InterfaceFriendlyName.Length = pFilter->InterfaceFriendlyName.MaximumLength = AttachParameters->BaseMiniportInstanceName->Length; pFilter->InterfaceFriendlyName.Buffer = (PWSTR)((PUCHAR)pFilter + sizeof(MS_FILTER)); NdisMoveMemory(pFilter->InterfaceFriendlyName.Buffer, AttachParameters->BaseMiniportInstanceName->Buffer, pFilter->InterfaceFriendlyName.Length); pFilter->InterfaceIndex = AttachParameters->BaseMiniportIfIndex; pFilter->InterfaceLuid = AttachParameters->BaseMiniportNetLuid; pFilter->InterfaceCompartmentID = UNSPECIFIED_COMPARTMENT_ID; pFilter->FilterHandle = NdisFilterHandle; NdisZeroMemory(&FilterAttributes, sizeof(NDIS_FILTER_ATTRIBUTES)); FilterAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1; FilterAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES); FilterAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES; FilterAttributes.Flags = 0; NDIS_DECLARE_FILTER_MODULE_CONTEXT(MS_FILTER); Status = NdisFSetAttributes(NdisFilterHandle, pFilter, &FilterAttributes); if (Status != NDIS_STATUS_SUCCESS) { LogError(DRIVER_DEFAULT, "Failed to set attributes, %!NDIS_STATUS!", Status); break; } // Filter initially in Paused state pFilter->State = FilterPaused; // Initialize rundowns to disabled with no active references pFilter->ExternalRefs.Count = EX_RUNDOWN_ACTIVE; pFilter->cmdRundown.Count = EX_RUNDOWN_ACTIVE; // Query the compartment ID for this interface to use for the IP stack pFilter->InterfaceCompartmentID = GetInterfaceCompartmentID(&pFilter->InterfaceLuid); LogVerbose(DRIVER_DEFAULT, "Interface %!GUID! is in Compartment %u", &pFilter->InterfaceGuid, (ULONG)pFilter->InterfaceCompartmentID); // Make sure we are in the right compartment (VOID)otLwfSetCompartment(pFilter, &OriginalCompartmentID); // Register for address changed notifications NtStatus = NotifyUnicastIpAddressChange( AF_INET6, otLwfAddressChangeCallback, pFilter, FALSE, &pFilter->AddressChangeHandle ); // Revert the compartment, now that we have the table otLwfRevertCompartment(OriginalCompartmentID); if (!NT_SUCCESS(NtStatus)) { LogError(DRIVER_DEFAULT, "NotifyUnicastIpAddressChange failed, %!STATUS!", NtStatus); Status = NDIS_STATUS_FAILURE; break; } // Add Filter to global list of Thread Filters NdisAcquireSpinLock(&FilterListLock); InsertTailList(&FilterModuleList, &pFilter->FilterModuleLink); NdisReleaseSpinLock(&FilterListLock); LogVerbose(DRIVER_DEFAULT, "Created Filter: %p", pFilter); } while (FALSE); // Clean up on failure if (Status != NDIS_STATUS_SUCCESS) { if (pFilter != NULL) { if (pFilter->AddressChangeHandle != NULL) { CancelMibChangeNotify2(pFilter->AddressChangeHandle); pFilter->AddressChangeHandle = NULL; } NdisFreeMemory(pFilter, 0, 0); } } LogFuncExitNDIS(DRIVER_DEFAULT, Status); return Status; }
_Use_decl_annotations_ NDIS_STATUS FilterPause( NDIS_HANDLE FilterModuleContext, PNDIS_FILTER_PAUSE_PARAMETERS PauseParameters ) /*++ Routine Description: Filter pause routine. Complete all the outstanding sends and queued sends, wait for all the outstanding recvs to be returned and return all the queued receives. Arguments: FilterModuleContext - pointer to the filter context stucture PauseParameters - additional information about the pause Return Value: NDIS_STATUS_SUCCESS if filter pauses successfully, NDIS_STATUS_PENDING if not. No other return value is allowed (pause must succeed, eventually). N.B.: When the filter is in Pausing state, it can still process OID requests, complete sending, and returning packets to NDIS, and also indicate status. After this function completes, the filter must not attempt to send or receive packets, but it may still process OID requests and status indications. --*/ { PMS_FILTER pFilter = (PMS_FILTER)(FilterModuleContext); NDIS_STATUS Status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(PauseParameters); LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext); // // Set the flag that the filter is going to pause // NT_ASSERT(pFilter->State == FilterRunning); NdisAcquireSpinLock(&FilterListLock); pFilter->State = FilterPausing; NdisReleaseSpinLock(&FilterListLock); // // Send final notification of interface removal // otLwfNotifyDeviceAvailabilityChange(pFilter, FALSE); LogInfo(DRIVER_DEFAULT, "Interface %!GUID! removal.", &pFilter->InterfaceGuid); // // Disable external references and wait for existing calls to complete // LogInfo(DRIVER_DEFAULT, "Disabling and waiting for external references to release"); ExWaitForRundownProtectionRelease(&pFilter->ExternalRefs); LogInfo(DRIVER_DEFAULT, "External references released."); // // Clean up based on the device mode // if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE) { otLwfUninitializeThreadMode(pFilter); } else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE) { otLwfTunUninitialize(pFilter); } pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED; // // Clean up the Spinel command processing // otLwfCmdUninitialize(pFilter); // // Set the state back to Paused now that we are done // pFilter->State = FilterPaused; LogFuncExitNDIS(DRIVER_DEFAULT, Status); return Status; }
_Use_decl_annotations_ NDIS_STATUS FilterRestart( NDIS_HANDLE FilterModuleContext, PNDIS_FILTER_RESTART_PARAMETERS RestartParameters ) /*++ Routine Description: Filter restart routine. Start the datapath - begin sending and receiving NBLs. Arguments: FilterModuleContext - pointer to the filter context stucture. RestartParameters - additional information about the restart operation. Return Value: NDIS_STATUS_SUCCESS: if filter restarts successfully NDIS_STATUS_XXX: Otherwise. --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS; PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; PVOID SpinelCapsDataBuffer = NULL; const uint8_t* SpinelCapsPtr = NULL; spinel_size_t SpinelCapsLen = 0; NL_INTERFACE_KEY key = {0}; NL_INTERFACE_RW interfaceRw; ULONG ThreadOnHost = TRUE; PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes; PNDIS_RESTART_ATTRIBUTES NdisRestartAttributes; LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext); NT_ASSERT(pFilter->State == FilterPaused); NdisRestartAttributes = RestartParameters->RestartAttributes; // // If NdisRestartAttributes is not NULL, then the filter can modify generic // attributes and add new media specific info attributes at the end. // Otherwise, if NdisRestartAttributes is NULL, the filter should not try to // modify/add attributes. // if (NdisRestartAttributes != NULL) { ASSERT(NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES); NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data; // // Check to see if we need to change any attributes. For example, the // driver can change the current MAC address here. Or the driver can add // media specific info attributes. // NdisGeneralAttributes->LookaheadSize = 128; } // Initialize the Spinel command processing NdisStatus = otLwfCmdInitialize(pFilter); if (NdisStatus != NDIS_STATUS_SUCCESS) { LogError(DRIVER_DEFAULT, "otLwfCmdInitialize failed, %!NDIS_STATUS!", NdisStatus); goto exit; } // Query the device capabilities NtStatus = otLwfCmdGetProp(pFilter, SpinelCapsDataBuffer, SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, &SpinelCapsPtr, &SpinelCapsLen); if (!NT_SUCCESS(NtStatus)) { NdisStatus = NDIS_STATUS_NOT_SUPPORTED; LogError(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_CAPS, %!STATUS!", NtStatus); goto exit; } // Iterate and process returned capabilities while (SpinelCapsLen > 0) { ULONG SpinelCap = 0; spinel_ssize_t len = spinel_datatype_unpack(SpinelCapsPtr, SpinelCapsLen, SPINEL_DATATYPE_UINT_PACKED_S, &SpinelCap); if (len < 1) break; SpinelCapsLen -= (spinel_size_t)len; switch (SpinelCap) { case SPINEL_CAP_MAC_RAW: pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO; pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_ACK_TIMEOUT; pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_ENERGY_SCAN; break; case SPINEL_CAP_NET_THREAD_1_0: pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_THREAD_1_0; break; default: break; } } // Set the state indicating where we should be running the Thread logic (Host or Device). if (!NT_SUCCESS(GetRegDWORDValue(pFilter, L"RunOnHost", &ThreadOnHost))) { // Default to running on the host if the key isn't present ThreadOnHost = TRUE; SetRegDWORDValue(pFilter, L"RunOnHost", ThreadOnHost); } LogInfo(DRIVER_DEFAULT, "Filter: %p initializing ThreadOnHost=%d", FilterModuleContext, ThreadOnHost); // Initialize the processing logic if (ThreadOnHost) { // Ensure the device has the capabilities to support raw radio commands if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO) == 0) { LogError(DRIVER_DEFAULT, "Failed to start because device doesn't support raw radio commands"); NdisStatus = NDIS_STATUS_NOT_SUPPORTED; goto exit; } pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_RADIO_MODE; NtStatus = otLwfInitializeThreadMode(pFilter); if (!NT_SUCCESS(NtStatus)) { LogError(DRIVER_DEFAULT, "otLwfInitializeThreadMode failed, %!STATUS!", NtStatus); NdisStatus = NDIS_STATUS_FAILURE; pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED; goto exit; } } else { // Ensure the device has the capabilities to support Thread commands if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_THREAD_1_0) == 0) { LogError(DRIVER_DEFAULT, "Failed to start because device doesn't support thread commands"); NdisStatus = NDIS_STATUS_NOT_SUPPORTED; goto exit; } pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_THREAD_MODE; NtStatus = otLwfTunInitialize(pFilter); if (!NT_SUCCESS(NtStatus)) { LogError(DRIVER_DEFAULT, "otLwfInitializeTunnelMode failed, %!STATUS!", NtStatus); NdisStatus = NDIS_STATUS_FAILURE; pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED; goto exit; } } // // Disable DAD and Neighbor advertisements // key.Luid = pFilter->InterfaceLuid; NlInitializeInterfaceRw(&interfaceRw); interfaceRw.DadTransmits = 0; interfaceRw.SendUnsolicitedNeighborAdvertisementOnDad = FALSE; NtStatus = NsiSetAllParameters( NsiActive, NsiSetDefault, &NPI_MS_IPV6_MODULEID, NlInterfaceObject, &key, sizeof(key), &interfaceRw, sizeof(interfaceRw)); if (!NT_SUCCESS(NtStatus)) { LogError(DRIVER_DEFAULT, "NsiSetAllParameters (NlInterfaceObject) failed, %!STATUS!", NtStatus); NdisStatus = NDIS_STATUS_FAILURE; goto exit; } // // Enable the external references to the filter // ExReInitializeRundownProtection(&pFilter->ExternalRefs); // // If everything is OK, set the filter in running state. // pFilter->State = FilterRunning; // when successful otLwfNotifyDeviceAvailabilityChange(pFilter, TRUE); LogInfo(DRIVER_DEFAULT, "Interface %!GUID! arrival, Filter=%p", &pFilter->InterfaceGuid, pFilter); exit: // // Ensure the state is Paused if restart failed. // if (NdisStatus != NDIS_STATUS_SUCCESS) { pFilter->State = FilterPaused; if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE) { otLwfUninitializeThreadMode(pFilter); } else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE) { otLwfTunUninitialize(pFilter); } pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED; // Clean up Spinel command processing otLwfCmdUninitialize(pFilter); } // Free the buffer for the capabilities we queried if (SpinelCapsDataBuffer != NULL) { FILTER_FREE_MEM(SpinelCapsDataBuffer); } LogFuncExitNDIS(DRIVER_DEFAULT, NdisStatus); return NdisStatus; }