// Worker thread for processing all tunnel events _Use_decl_annotations_ VOID otLwfTunWorkerThread( PVOID Context ) { PMS_FILTER pFilter = (PMS_FILTER)Context; NT_ASSERT(pFilter); LogFuncEntry(DRIVER_DEFAULT); PKEVENT WaitEvents[] = { &pFilter->TunWorkerThreadStopEvent, &pFilter->TunWorkerThreadAddressChangedEvent }; LogFuncExit(DRIVER_DEFAULT); while (true) { // Wait for event to stop or process event to fire NTSTATUS status = KeWaitForMultipleObjects( ARRAYSIZE(WaitEvents), (PVOID*)WaitEvents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL); // If it is the first event, then we are shutting down. Exit loop and terminate thread if (status == STATUS_WAIT_0) { LogInfo(DRIVER_DEFAULT, "Received tunnel worker thread shutdown event."); break; } else if (status == STATUS_WAIT_0 + 1) // TunWorkerThreadAddressChangedEvent fired { PVOID DataBuffer = NULL; const uint8_t* value_data_ptr = NULL; spinel_size_t value_data_len = 0; // Query the current addresses status = otLwfCmdGetProp( pFilter, &DataBuffer, SPINEL_PROP_IPV6_ADDRESS_TABLE, SPINEL_DATATYPE_DATA_S, &value_data_ptr, &value_data_len); if (NT_SUCCESS(status)) { uint32_t aNotifFlags = 0; otLwfTunAddressesUpdated(pFilter, value_data_ptr, value_data_len, &aNotifFlags); // Send notification if (aNotifFlags != 0) { PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter); if (NotifEntry) { RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY)); NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid; NotifEntry->Notif.NotifType = OTLWF_NOTIF_STATE_CHANGE; NotifEntry->Notif.StateChangePayload.Flags = aNotifFlags; otLwfIndicateNotification(NotifEntry); } } } else { LogWarning(DRIVER_DEFAULT, "Failed to query addresses, %!STATUS!", status); } if (DataBuffer) FILTER_FREE_MEM(DataBuffer); } else { LogWarning(DRIVER_DEFAULT, "Unexpected wait result, %!STATUS!", status); } } PsTerminateSystemThread(STATUS_SUCCESS); }
NTSTATUS FilterReadSetting(_In_ PMS_FILTER pFilter, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) { HANDLE regKey = NULL; OBJECT_ATTRIBUTES attributes; DECLARE_UNICODE_STRING_SIZE(Name, 20); PKEY_VALUE_PARTIAL_INFORMATION pInfo = NULL; ULONG InfoLength = sizeof(*pInfo) + *aValueLength; // Convert 'aKey' to a string RtlIntegerToUnicodeString((ULONG)aKey, 16, &Name); InitializeObjectAttributes( &attributes, &Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, pFilter->otSettingsRegKey, NULL); // Open the registry key NTSTATUS status = ZwOpenKey( ®Key, KEY_ALL_ACCESS, &attributes); if (!NT_SUCCESS(status)) { // Key doesn't exist goto error; } // Convert 'aIndex' to a string RtlIntegerToUnicodeString((ULONG)aIndex, 16, &Name); // Allocate buffer for query pInfo = FILTER_ALLOC_MEM(pFilter->FilterHandle, InfoLength); if (pInfo == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto error; } // Query the data status = ZwQueryValueKey( regKey, &Name, KeyValuePartialInformation, pInfo, InfoLength, &InfoLength); if (!NT_SUCCESS(status)) { LogVerbose(DRIVER_DEFAULT, "ZwQueryValueKey for %S value failed, %!STATUS!", Name.Buffer, status); goto error; } NT_ASSERT(*aValueLength >= pInfo->DataLength); *aValueLength = (uint16_t)pInfo->DataLength; if (aValue) { memcpy(aValue, pInfo->Data, pInfo->DataLength); } error: if (pInfo) FILTER_FREE_MEM(pInfo); if (regKey) ZwClose(regKey); return status; }
NTSTATUS FilterDeleteSetting(_In_ PMS_FILTER pFilter, uint16_t aKey, int aIndex) { HANDLE regKey = NULL; OBJECT_ATTRIBUTES attributes; DECLARE_UNICODE_STRING_SIZE(Name, 20); // Convert 'aKey' to a string RtlIntegerToUnicodeString((ULONG)aKey, 16, &Name); InitializeObjectAttributes( &attributes, &Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, pFilter->otSettingsRegKey, NULL); // Open the registry key NTSTATUS status = ZwOpenKey( ®Key, KEY_ALL_ACCESS, &attributes); if (!NT_SUCCESS(status)) { // Key doesn't exist goto error; } // If 'aIndex' is -1 then delete the whole key, otherwise delete the individual value if (aIndex == -1) { // Delete the registry key status = ZwDeleteKey(regKey); } else { UCHAR KeyInfoBuffer[128] = { 0 }; PKEY_FULL_INFORMATION pKeyInfo = (PKEY_FULL_INFORMATION)KeyInfoBuffer; ULONG KeyInfoLength = sizeof(KeyInfoBuffer); // When deleting an individual value, since order doesn't matter, we will actually // copy the last value over the one being deleted and then delete the last value; so // we maintain a contiguous list of numbered values // Query the number of values // Note: Can't use helper function because we already have the key open status = ZwQueryKey( regKey, KeyValueFullInformation, pKeyInfo, KeyInfoLength, &KeyInfoLength); if (!NT_SUCCESS(status)) { LogError(DRIVER_DEFAULT, "ZwQueryKey for %S value failed, %!STATUS!", Name.Buffer, status); goto error; } if ((ULONG)aIndex >= pKeyInfo->Values) { // Attempt to delete beyond the end of the list status = STATUS_OBJECT_NAME_NOT_FOUND; goto error; } else if (pKeyInfo->Values == 1) { // Deleting the only value on the key, go ahead and delete the entire key status = ZwDeleteKey(regKey); } else if (pKeyInfo->Values - 1 != (ULONG)aIndex) { // We aren't deleting the last value so we need to copy the last value // over this one, and then delete the last one. PKEY_VALUE_PARTIAL_INFORMATION pValueInfo = NULL; ULONG ValueInfoLength = 0; // Convert pKeyInfo->Values-1 to a string RtlIntegerToUnicodeString(pKeyInfo->Values - 1, 16, &Name); // Query the key data buffer size status = ZwQueryValueKey( regKey, &Name, KeyValuePartialInformation, pValueInfo, 0, &ValueInfoLength); NT_ASSERT(status != STATUS_SUCCESS); if (status != STATUS_BUFFER_TOO_SMALL) { LogVerbose(DRIVER_DEFAULT, "ZwQueryValueKey for %S value failed, %!STATUS!", Name.Buffer, status); goto error; } pValueInfo = FILTER_ALLOC_MEM(pFilter->FilterHandle, ValueInfoLength); if (pValueInfo == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto error; } // Query the data buffer status = ZwQueryValueKey( regKey, &Name, KeyValuePartialInformation, pValueInfo, ValueInfoLength, &ValueInfoLength); if (!NT_SUCCESS(status)) { LogError(DRIVER_DEFAULT, "ZwQueryValueKey for %S value failed, %!STATUS!", Name.Buffer, status); goto cleanup; } // Delete the registry value status = ZwDeleteValueKey( regKey, &Name); if (!NT_SUCCESS(status)) { LogError(DRIVER_DEFAULT, "ZwDeleteValueKey for %S value failed, %!STATUS!", Name.Buffer, status); goto cleanup; } // Convert 'aIndex' to a string RtlIntegerToUnicodeString((ULONG)aIndex, 16, &Name); // Write the data to the registry key we are deleting status = ZwSetValueKey( regKey, &Name, 0, REG_BINARY, (PVOID)pValueInfo->Data, pValueInfo->DataLength); if (!NT_SUCCESS(status)) { LogError(DRIVER_DEFAULT, "ZwSetValueKey for %S value failed, %!STATUS!", Name.Buffer, status); goto cleanup; } cleanup: if (pValueInfo) FILTER_FREE_MEM(pValueInfo); } else { // Deleting the last value in the list (but not the only value) // Just delete the value directly. No need to copy any others. // Convert 'aIndex' to a string RtlIntegerToUnicodeString((ULONG)aIndex, 16, &Name); // Delete the registry value status = ZwDeleteValueKey( regKey, &Name); } } error: if (regKey) ZwClose(regKey); 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; }