virtionet: improve state tracking (#14215)
* virtionet: improve state tracking * remove unused var --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Ben Hillis committed
Feb 13, 2026 at 12:10 UTC
a27d4725f7fc980a98458035d803455edd1f50fa
2 files changed
+137
-105
src/windows/common/VirtioNetworking.cpp
+131
-104
@@ -46,34 +46,19 @@ void VirtioNetworking::Initialize()
46
THROW_IF_WIN32_ERROR(NotifyNetworkConnectivityHintChange(&VirtioNetworking::OnNetworkConnectivityChange, this, TRUE, &m_networkNotifyHandle));
47
}
48
49
-void VirtioNetworking::SetupLoopbackDevice()
49
+void VirtioNetworking::TraceLoggingRundown() noexcept
50
{
51
- m_localhostAdapterId = m_guestDeviceManager->AddGuestDevice(
52
- VIRTIO_NET_DEVICE_ID,
53
- VIRTIO_NET_CLASS_ID,
54
- c_loopbackDeviceName,
55
- nullptr,
56
- L"client_ip=127.0.0.1;client_mac=00:11:22:33:44:55",
57
- 0,
58
- m_userToken.get());
51
+ auto lock = m_lock.lock_exclusive();
52
60
- // The loopback gateway (see LX_INIT_IPV4_LOOPBACK_GATEWAY_ADDRESS) is 169.254.73.152, so assign loopback0 an
61
- // address of 169.254.73.153 with a netmask of 30 so that the only addresses associated with this adapter are
62
- // itself and the gateway.
63
- // N.B. The MAC address is advertised with the virtio device so doesn't need to be explicitly set.
64
- hns::HNSEndpoint endpointProperties;
65
- endpointProperties.ID = m_localhostAdapterId.value();
66
- endpointProperties.IPAddress = L"169.254.73.153";
67
- endpointProperties.PrefixLength = 30;
68
- endpointProperties.PortFriendlyName = c_loopbackDeviceName;
69
- m_gnsChannel.SendEndpointState(endpointProperties);
53
+ WSL_LOG("VirtioNetworking::TraceLoggingRundown", TRACE_NETWORKSETTINGS_OBJECT(m_networkSettings));
54
+}
55
71
- hns::CreateDeviceRequest createLoopbackDevice;
72
- createLoopbackDevice.deviceName = c_loopbackDeviceName;
73
- createLoopbackDevice.type = hns::DeviceType::Loopback;
74
- createLoopbackDevice.lowerEdgeAdapterId = m_localhostAdapterId.value();
75
- constexpr auto loopbackType = GnsMessageType(createLoopbackDevice);
76
- m_gnsChannel.SendNetworkDeviceMessage(loopbackType, ToJsonW(createLoopbackDevice).c_str());
56
+void VirtioNetworking::FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message)
57
+{
58
+ message.NetworkingMode = LxMiniInitNetworkingModeVirtioProxy;
59
+ message.DisableIpv6 = false;
60
+ message.EnableDhcpClient = false;
61
+ message.PortTrackerType = LX_MINI_INIT_PORT_TRACKER_TYPE::LxMiniInitPortTrackerTypeMirrored;
62
}
63
64
void VirtioNetworking::StartPortTracker(wil::unique_socket&& socket)
@@ -86,6 +71,11 @@ void VirtioNetworking::StartPortTracker(wil::unique_socket&& socket)
71
[](const std::string&, bool) {}); // TODO: reconsider if InterfaceStateCallback is needed.
72
}
73
74
+void NETIOAPI_API_ VirtioNetworking::OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint)
75
+{
76
+ static_cast<VirtioNetworking*>(context)->RefreshGuestConnection();
77
+}
78
+
79
HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int protocol, bool allocate) const noexcept
80
{
81
int result = 0;
@@ -176,141 +166,178 @@ int VirtioNetworking::ModifyOpenPorts(_In_ PCWSTR tag, _In_ const SOCKADDR_INET&
166
return 0;
167
}
168
179
-void NETIOAPI_API_ VirtioNetworking::OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint)
180
-{
181
- static_cast<VirtioNetworking*>(context)->RefreshGuestConnection();
182
-}
183
-
169
void VirtioNetworking::RefreshGuestConnection() noexcept
170
try
171
{
187
- // Acquire the lock and perform device updates.
188
- auto lock = m_lock.lock_exclusive();
189
-
190
- m_networkSettings = GetHostEndpointSettings();
172
+ // Query current networking information before acquiring the lock.
173
+ auto networkSettings = GetHostEndpointSettings();
174
175
// TODO: Determine gateway MAC address
193
- std::wstringstream device_options;
194
- auto client_ip = m_networkSettings->PreferredIpAddress.AddressString;
176
+ std::wstring device_options;
177
+ auto client_ip = networkSettings->PreferredIpAddress.AddressString;
178
if (!client_ip.empty())
179
{
197
- if (device_options.tellp() > 0)
198
- {
199
- device_options << L";";
200
- }
201
- device_options << L"client_ip=" << client_ip;
180
+ device_options += L"client_ip=" + client_ip;
181
}
182
204
- if (!m_networkSettings->MacAddress.empty())
183
+ if (!networkSettings->MacAddress.empty())
184
{
206
- if (device_options.tellp() > 0)
185
+ if (!device_options.empty())
186
{
208
- device_options << L";";
187
+ device_options += L';';
188
}
210
- device_options << L"client_mac=" << m_networkSettings->MacAddress;
189
+ device_options += L"client_mac=" + networkSettings->MacAddress;
190
}
191
213
- std::wstring default_route = m_networkSettings->GetBestGatewayAddressString();
192
+ std::wstring default_route = networkSettings->GetBestGatewayAddressString();
193
if (!default_route.empty())
194
{
216
- if (device_options.tellp() > 0)
195
+ if (!device_options.empty())
196
{
218
- device_options << L";";
197
+ device_options += L';';
198
}
220
- device_options << L"gateway_ip=" << default_route;
199
+ device_options += L"gateway_ip=" + default_route;
200
}
201
223
- const auto newDeviceOptions = device_options.str();
224
-
225
- if (newDeviceOptions != m_trackedDeviceOptions)
202
+ networking::DnsInfo currentDns{};
203
+ if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::DnsTunneling))
204
+ {
205
+ currentDns = networking::HostDnsInfo::GetDnsTunnelingSettings(default_route);
206
+ }
207
+ else
208
{
227
- m_trackedDeviceOptions = newDeviceOptions;
209
+ currentDns = networking::HostDnsInfo::GetDnsSettings(networking::DnsSettingsFlags::IncludeVpn);
210
+ }
211
+
212
+ const auto minMtu = GetMinimumConnectedInterfaceMtu();
213
229
- // Add virtio net adapter to guest. If the adapter already exists update adapter state.
214
+ // Acquire the lock and perform device updates.
215
+ auto lock = m_lock.lock_exclusive();
216
+
217
+ // Add virtio net adapter to guest. If the adapter already exists update adapter state.
218
+ if (device_options != m_trackedDeviceOptions)
219
+ {
220
+ m_trackedDeviceOptions = device_options;
221
if (!m_adapterId.has_value())
222
{
223
m_adapterId = m_guestDeviceManager->AddGuestDevice(
233
- VIRTIO_NET_DEVICE_ID, VIRTIO_NET_CLASS_ID, c_eth0DeviceName, nullptr, newDeviceOptions.c_str(), 0, m_userToken.get());
224
+ VIRTIO_NET_DEVICE_ID, VIRTIO_NET_CLASS_ID, c_eth0DeviceName, nullptr, device_options.c_str(), 0, m_userToken.get());
225
}
226
else
227
{
228
const auto server = m_guestDeviceManager->GetRemoteFileSystem(VIRTIO_NET_CLASS_ID, c_defaultDeviceTag);
229
if (server)
230
{
240
- LOG_IF_FAILED(server->AddSharePath(c_eth0DeviceName, newDeviceOptions.c_str(), 0));
231
+ LOG_IF_FAILED(server->AddSharePath(c_eth0DeviceName, device_options.c_str(), 0));
232
}
233
}
243
-
244
- // N.B. The MAC address is advertised with the virtio device so doesn't need to be explicitly set.
245
- hns::HNSEndpoint endpointProperties;
246
- endpointProperties.ID = m_adapterId.value();
247
- endpointProperties.IPAddress = m_networkSettings->PreferredIpAddress.AddressString;
248
- endpointProperties.PrefixLength = m_networkSettings->PreferredIpAddress.PrefixLength;
249
- m_gnsChannel.SendEndpointState(endpointProperties);
250
-
251
- // Send the default route to GNS.
252
- if (!default_route.empty())
253
- {
254
- wsl::shared::hns::Route route;
255
- route.NextHop = default_route;
256
- route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;
257
- route.Family = AF_INET;
258
-
259
- hns::ModifyGuestEndpointSettingRequest<hns::Route> request;
260
- request.RequestType = hns::ModifyRequestType::Add;
261
- request.ResourceType = hns::GuestEndpointResourceType::Route;
262
- request.Settings = route;
263
- m_gnsChannel.SendHnsNotification(ToJsonW(request).c_str(), m_adapterId.value());
264
- }
234
}
235
267
- // Send DNS update if needed.
268
- networking::DnsInfo currentDns{};
269
- if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::DnsTunneling))
236
+ // Update IP address if needed.
237
+ if (!m_networkSettings || networkSettings->PreferredIpAddress != m_networkSettings->PreferredIpAddress)
238
{
271
- currentDns = networking::HostDnsInfo::GetDnsTunnelingSettings(default_route);
239
+ UpdateIpAddress(networkSettings->PreferredIpAddress);
240
}
273
- else
241
+
242
+ // Send default route update if needed.
243
+ if (default_route != m_trackedDefaultRoute)
244
{
275
- currentDns = networking::HostDnsInfo::GetDnsSettings(networking::DnsSettingsFlags::IncludeVpn);
245
+ m_trackedDefaultRoute = default_route;
246
+ UpdateDefaultRoute(default_route, AF_INET);
247
}
248
249
+ // Send DNS update if needed.
250
if (currentDns != m_trackedDnsSettings)
251
{
252
m_trackedDnsSettings = currentDns;
281
- hns::ModifyGuestEndpointSettingRequest<hns::DNS> notification{};
282
- notification.RequestType = hns::ModifyRequestType::Update;
283
- notification.ResourceType = hns::GuestEndpointResourceType::DNS;
284
- notification.Settings = networking::BuildDnsNotification(currentDns, m_dnsOptions);
285
- m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());
253
+ UpdateDnsSettings(currentDns);
254
}
255
256
// Send MTU update if needed.
289
- const auto minMtu = GetMinimumConnectedInterfaceMtu();
257
if (minMtu && minMtu.value() != m_networkMtu)
258
{
259
m_networkMtu = minMtu.value();
293
- hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> notification{};
294
- notification.ResourceType = hns::GuestEndpointResourceType::Interface;
295
- notification.RequestType = hns::ModifyRequestType::Update;
296
- notification.Settings.Connected = true;
297
- notification.Settings.NlMtu = m_networkMtu;
298
- m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());
260
+ UpdateMtu(m_networkMtu);
261
}
262
+
263
+ m_networkSettings = std::move(networkSettings);
264
}
265
CATCH_LOG();
266
303
-void VirtioNetworking::TraceLoggingRundown() noexcept
267
+void VirtioNetworking::SetupLoopbackDevice()
268
{
305
- auto lock = m_lock.lock_exclusive();
269
+ m_localhostAdapterId = m_guestDeviceManager->AddGuestDevice(
270
+ VIRTIO_NET_DEVICE_ID,
271
+ VIRTIO_NET_CLASS_ID,
272
+ c_loopbackDeviceName,
273
+ nullptr,
274
+ L"client_ip=127.0.0.1;client_mac=00:11:22:33:44:55",
275
+ 0,
276
+ m_userToken.get());
277
307
- WSL_LOG("VirtioNetworking::TraceLoggingRundown", TRACE_NETWORKSETTINGS_OBJECT(m_networkSettings));
278
+ // The loopback gateway (see LX_INIT_IPV4_LOOPBACK_GATEWAY_ADDRESS) is 169.254.73.152, so assign loopback0 an
279
+ // address of 169.254.73.153 with a netmask of 30 so that the only addresses associated with this adapter are
280
+ // itself and the gateway.
281
+ // N.B. The MAC address is advertised with the virtio device so doesn't need to be explicitly set.
282
+ hns::HNSEndpoint endpointProperties;
283
+ endpointProperties.ID = m_localhostAdapterId.value();
284
+ endpointProperties.IPAddress = L"169.254.73.153";
285
+ endpointProperties.PrefixLength = 30;
286
+ endpointProperties.PortFriendlyName = c_loopbackDeviceName;
287
+ m_gnsChannel.SendEndpointState(endpointProperties);
288
+
289
+ hns::CreateDeviceRequest createLoopbackDevice;
290
+ createLoopbackDevice.deviceName = c_loopbackDeviceName;
291
+ createLoopbackDevice.type = hns::DeviceType::Loopback;
292
+ createLoopbackDevice.lowerEdgeAdapterId = m_localhostAdapterId.value();
293
+ constexpr auto loopbackType = GnsMessageType(createLoopbackDevice);
294
+ m_gnsChannel.SendNetworkDeviceMessage(loopbackType, ToJsonW(createLoopbackDevice).c_str());
295
}
296
310
-void VirtioNetworking::FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message)
297
+void VirtioNetworking::UpdateDefaultRoute(const std::wstring& gateway, ADDRESS_FAMILY family)
298
{
312
- message.NetworkingMode = LxMiniInitNetworkingModeVirtioProxy;
313
- message.DisableIpv6 = false;
314
- message.EnableDhcpClient = false;
315
- message.PortTrackerType = LX_MINI_INIT_PORT_TRACKER_TYPE::LxMiniInitPortTrackerTypeMirrored;
299
+ if (gateway.empty())
300
+ {
301
+ return;
302
+ }
303
+
304
+ wsl::shared::hns::Route route;
305
+ route.NextHop = gateway;
306
+ route.DestinationPrefix = (family == AF_INET) ? LX_INIT_DEFAULT_ROUTE_PREFIX : LX_INIT_DEFAULT_ROUTE_V6_PREFIX;
307
+ route.Family = family;
308
+
309
+ hns::ModifyGuestEndpointSettingRequest<hns::Route> request;
310
+ request.RequestType = hns::ModifyRequestType::Add;
311
+ request.ResourceType = hns::GuestEndpointResourceType::Route;
312
+ request.Settings = route;
313
+ m_gnsChannel.SendHnsNotification(ToJsonW(request).c_str(), m_adapterId.value());
314
+}
315
+
316
+void VirtioNetworking::UpdateDnsSettings(const networking::DnsInfo& dns)
317
+{
318
+ hns::ModifyGuestEndpointSettingRequest<hns::DNS> notification{};
319
+ notification.RequestType = hns::ModifyRequestType::Update;
320
+ notification.ResourceType = hns::GuestEndpointResourceType::DNS;
321
+ notification.Settings = networking::BuildDnsNotification(dns, m_dnsOptions);
322
+ m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());
323
+}
324
+
325
+void VirtioNetworking::UpdateIpAddress(const networking::EndpointIpAddress& ipAddress)
326
+{
327
+ // N.B. The MAC address is advertised with the virtio device so doesn't need to be explicitly set.
328
+ hns::HNSEndpoint endpointProperties;
329
+ endpointProperties.ID = m_adapterId.value();
330
+ endpointProperties.IPAddress = ipAddress.AddressString;
331
+ endpointProperties.PrefixLength = ipAddress.PrefixLength;
332
+ m_gnsChannel.SendEndpointState(endpointProperties);
333
+}
334
+
335
+void VirtioNetworking::UpdateMtu(ULONG mtu)
336
+{
337
+ hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> notification{};
338
+ notification.ResourceType = hns::GuestEndpointResourceType::Interface;
339
+ notification.RequestType = hns::ModifyRequestType::Update;
340
+ notification.Settings.Connected = true;
341
+ notification.Settings.NlMtu = mtu;
342
+ m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());
343
}
src/windows/common/VirtioNetworking.h
+6
-1
@@ -43,6 +43,10 @@ private:
43
int ModifyOpenPorts(_In_ PCWSTR tag, _In_ const SOCKADDR_INET& addr, _In_ int protocol, _In_ bool isOpen) const;
44
void RefreshGuestConnection() noexcept;
45
void SetupLoopbackDevice();
46
+ void UpdateDefaultRoute(const std::wstring& gateway, ADDRESS_FAMILY family);
47
+ void UpdateDnsSettings(const networking::DnsInfo& dns);
48
+ void UpdateIpAddress(const networking::EndpointIpAddress& ipAddress);
49
+ void UpdateMtu(ULONG mtu);
50
51
mutable wil::srwlock m_lock;
52
@@ -58,7 +62,8 @@ private:
62
63
ULONG m_networkMtu = 0;
64
std::wstring m_trackedDeviceOptions;
61
- networking::DnsInfo m_trackedDnsSettings;
65
+ std::wstring m_trackedDefaultRoute;
66
+ networking::DnsInfo m_trackedDnsSettings{};
67
68
// Note: this field must be destroyed first to stop the callbacks before any other field is destroyed.
69
networking::unique_notify_handle m_networkNotifyHandle;