// 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); }
_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; }