master
cpp 569 lines 21.6 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 #include "precomp.h"
4 #include "ConsommeNetworking.h"
5 #include "GuestDeviceManager.h"
6 #include "Stringify.h"
7 #include "stringshared.h"
8
9 using namespace wsl::core::networking;
10 using namespace wsl::shared;
11 using namespace wsl::windows::common::stringify;
12 using wsl::core::ConsommeNetworking;
13
14 static constexpr auto c_eth0DeviceName = L"eth0";
15 static constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME);
16 static constexpr wsl::shared::string::MacAddress c_defaultClientMacAddress{0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
17 static constexpr wsl::shared::string::MacAddress c_gatewayMacAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
18
19 namespace {
20
21 EthernetAddress ToEthernetAddress(const wsl::shared::string::MacAddress& address)
22 {
23 EthernetAddress result{};
24 std::copy(address.begin(), address.end(), std::begin(result.bytes));
25 return result;
26 }
27
28 Ipv4Address ToIpv4Address(const SOCKADDR_INET& address)
29 {
30 Ipv4Address result{};
31 if (address.si_family == AF_INET)
32 {
33 result.value = address.Ipv4.sin_addr.S_un.S_addr;
34 }
35
36 return result;
37 }
38
39 Ipv6Address ToIpv6Address(const SOCKADDR_INET& address)
40 {
41 Ipv6Address result{};
42 if (address.si_family == AF_INET6)
43 {
44 std::copy(std::begin(address.Ipv6.sin6_addr.u.Byte), std::end(address.Ipv6.sin6_addr.u.Byte), std::begin(result.bytes));
45 }
46
47 return result;
48 }
49
50 IpAddress ToIpAddress(const SOCKADDR_INET& address)
51 {
52 IpAddress result{};
53 if (address.si_family == AF_INET)
54 {
55 result.family = IpAddressFamily_V4;
56 std::copy(
57 reinterpret_cast<const BYTE*>(&address.Ipv4.sin_addr),
58 reinterpret_cast<const BYTE*>(&address.Ipv4.sin_addr) + sizeof(address.Ipv4.sin_addr),
59 std::begin(result.bytes));
60 }
61 else if (address.si_family == AF_INET6)
62 {
63 result.family = IpAddressFamily_V6;
64 std::copy(std::begin(address.Ipv6.sin6_addr.u.Byte), std::end(address.Ipv6.sin6_addr.u.Byte), std::begin(result.bytes));
65 }
66
67 return result;
68 }
69
70 bool AreEqual(const IpAddress& left, const IpAddress& right)
71 {
72 return left.family == right.family && std::equal(std::begin(left.bytes), std::end(left.bytes), std::begin(right.bytes));
73 }
74
75 bool AreEqual(const std::vector<IpAddress>& left, const std::vector<IpAddress>& right)
76 {
77 return std::ranges::equal(
78 left, right, [](const auto& leftAddress, const auto& rightAddress) { return AreEqual(leftAddress, rightAddress); });
79 }
80
81 bool AreEqual(const WslVirtioNetConfig& left, const WslVirtioNetConfig& right)
82 {
83 return left.clientIp.value == right.clientIp.value && left.hasClientIpv6 == right.hasClientIpv6 &&
84 std::equal(std::begin(left.clientIpv6.bytes), std::end(left.clientIpv6.bytes), std::begin(right.clientIpv6.bytes)) &&
85 std::equal(std::begin(left.clientMac.bytes), std::end(left.clientMac.bytes), std::begin(right.clientMac.bytes)) &&
86 left.gatewayIp.value == right.gatewayIp.value &&
87 std::equal(std::begin(left.gatewayMac.bytes), std::end(left.gatewayMac.bytes), std::begin(right.gatewayMac.bytes)) &&
88 std::equal(std::begin(left.gatewayMacIpv6.bytes), std::end(left.gatewayMacIpv6.bytes), std::begin(right.gatewayMacIpv6.bytes)) &&
89 left.netmask.value == right.netmask.value;
90 }
91
92 std::vector<IpAddress> ToIpAddresses(const DnsInfo& dns)
93 {
94 std::vector<IpAddress> result;
95 result.reserve(dns.Servers.size());
96 for (const auto& server : dns.Servers)
97 {
98 if (server.empty())
99 {
100 continue;
101 }
102
103 result.emplace_back(ToIpAddress(wsl::windows::common::string::StringToSockAddrInet(wsl::shared::string::MultiByteToWide(server))));
104 }
105
106 return result;
107 }
108
109 WslVirtioNetConfig BuildVirtioNetConfig(
110 const std::shared_ptr<NetworkSettings>& networkSettings, bool enableIpv6, std::optional<wsl::shared::string::MacAddress> clientMacAddress = {})
111 {
112 ULONG netmask{};
113 if (networkSettings->PreferredIpAddress.Address.si_family == AF_INET)
114 {
115 LOG_IF_WIN32_ERROR(ConvertLengthToIpv4Mask(networkSettings->PreferredIpAddress.PrefixLength, &netmask));
116 }
117
118 WslVirtioNetConfig config{};
119 config.clientIp = ToIpv4Address(networkSettings->PreferredIpAddress.Address);
120 config.hasClientIpv6 = enableIpv6 && networkSettings->PreferredIpv6Address.Address.si_family == AF_INET6;
121 config.clientIpv6 = ToIpv6Address(networkSettings->PreferredIpv6Address.Address);
122 config.clientMac = ToEthernetAddress(clientMacAddress.value_or(c_defaultClientMacAddress));
123 config.gatewayIp = ToIpv4Address(networkSettings->GetBestGatewayAddress());
124 config.gatewayMac = ToEthernetAddress(c_gatewayMacAddress);
125 config.gatewayMacIpv6 = ToEthernetAddress(c_gatewayMacAddress);
126 config.netmask.value = netmask;
127 return config;
128 }
129
130 } // namespace
131
132 ConsommeNetworking::ConsommeNetworking(
133 GnsChannel&& gnsChannel,
134 ConsommeNetworkingFlags flags,
135 LPCWSTR dnsOptions,
136 LPCSTR hostLoopback,
137 std::shared_ptr<GuestDeviceManager> guestDeviceManager,
138 wil::shared_handle userToken) :
139 m_guestDeviceManager(std::move(guestDeviceManager)),
140 m_userToken(std::move(userToken)),
141 m_gnsChannel(std::move(gnsChannel)),
142 m_flags(flags),
143 m_dnsOptions(dnsOptions),
144 m_hostLoopback(hostLoopback ? hostLoopback : "")
145 {
146 }
147
148 ConsommeNetworking::~ConsommeNetworking()
149 {
150 // Unregister the network notification callback to prevent it from using the GNS channel.
151 m_networkNotifyHandle.reset();
152
153 // Stop the GNS channel to unblock any stuck communications with the guest.
154 m_gnsChannel.Stop();
155 }
156
157 void ConsommeNetworking::Initialize()
158 {
159 // Initialize adapter state.
160 RefreshGuestConnection();
161
162 if (WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::LocalhostRelay))
163 {
164 SetupLoopbackDevice();
165 }
166
167 if (!m_hostLoopback.empty())
168 {
169 THROW_HR_IF(E_UNEXPECTED, !WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::LocalhostRelay));
170 SetupHostLoopback();
171 }
172
173 THROW_IF_WIN32_ERROR(NotifyNetworkConnectivityHintChange(&ConsommeNetworking::OnNetworkConnectivityChange, this, TRUE, &m_networkNotifyHandle));
174 }
175
176 void ConsommeNetworking::TraceLoggingRundown() noexcept
177 {
178 auto lock = m_lock.lock_exclusive();
179
180 WSL_LOG("ConsommeNetworking::TraceLoggingRundown", TRACE_NETWORKSETTINGS_OBJECT(m_networkSettings));
181 }
182
183 void ConsommeNetworking::FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message)
184 {
185 message.NetworkingMode = LxMiniInitNetworkingModeConsomme;
186 message.DisableIpv6 = WI_IsFlagClear(m_flags, ConsommeNetworkingFlags::Ipv6);
187 message.EnableDhcpClient = false;
188 message.PortTrackerType = LX_MINI_INIT_PORT_TRACKER_TYPE::LxMiniInitPortTrackerTypeMirrored;
189 }
190
191 void ConsommeNetworking::StartPortTracker(wil::unique_socket&& socket)
192 {
193 WI_ASSERT(!m_gnsPortTrackerChannel.has_value());
194
195 m_gnsPortTrackerChannel.emplace(
196 std::move(socket),
197 [&](const SOCKADDR_INET& addr, int protocol, bool allocate) {
198 return wil::ResultFromException([&]() {
199 HandlePortNotification(addr, protocol, INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr)), allocate);
200 });
201 },
202 [](const std::string&, bool) {}); // TODO: reconsider if InterfaceStateCallback is needed.
203 }
204
205 void NETIOAPI_API_ ConsommeNetworking::OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint)
206 try
207 {
208 static_cast<ConsommeNetworking*>(context)->RefreshGuestConnection();
209 }
210 CATCH_LOG()
211
212 uint16_t ConsommeNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int protocol, uint16_t guestPort, bool allocate) const
213 {
214 if (addr.si_family == AF_INET6 && WI_IsFlagClear(m_flags, ConsommeNetworkingFlags::Ipv6))
215 {
216 return 0;
217 }
218
219 const auto ipAddress = (addr.si_family == AF_INET) ? reinterpret_cast<const void*>(&addr.Ipv4.sin_addr)
220 : reinterpret_cast<const void*>(&addr.Ipv6.sin6_addr);
221 const bool loopback = INET_IS_ADDR_LOOPBACK(addr.si_family, ipAddress);
222 const bool unspecified = INET_IS_ADDR_UNSPECIFIED(addr.si_family, ipAddress);
223 if (addr.si_family == AF_INET && loopback)
224 {
225 // Only intercepting 127.0.0.1; any other loopback address will remain on 'lo'.
226 if (addr.Ipv4.sin_addr.s_addr != htonl(INADDR_LOOPBACK))
227 {
228 return 0;
229 }
230 }
231 SOCKADDR_INET localAddr = addr;
232 std::function<void()> removePort;
233
234 auto hostPort = INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr));
235 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&removePort]() {
236 if (removePort)
237 {
238 removePort();
239 }
240 });
241
242 if (WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::LocalhostRelay) && (unspecified || loopback))
243 {
244 if (!loopback)
245 {
246 INETADDR_SETLOOPBACK(reinterpret_cast<PSOCKADDR>(&localAddr));
247 if (addr.si_family == AF_INET)
248 {
249 localAddr.Ipv4.sin_port = addr.Ipv4.sin_port;
250 }
251 else
252 {
253 localAddr.Ipv6.sin6_port = addr.Ipv6.sin6_port;
254 }
255 }
256
257 hostPort = ModifyOpenPorts(c_loopbackDeviceName, localAddr, hostPort, guestPort, protocol, allocate);
258
259 // Revert the change on failure.
260 removePort = [&]() { ModifyOpenPorts(c_loopbackDeviceName, localAddr, hostPort, guestPort, protocol, !allocate); };
261 }
262
263 if (!loopback)
264 {
265 hostPort = ModifyOpenPorts(c_eth0DeviceName, addr, hostPort, guestPort, protocol, allocate);
266 }
267
268 cleanup.release();
269
270 return hostPort;
271 }
272
273 uint16_t ConsommeNetworking::ModifyOpenPorts(
274 _In_ PCWSTR tag, _In_ const SOCKADDR_INET& hostAddress, _In_ uint16_t HostPort, _In_ uint16_t GuestPort, _In_ int protocol, _In_ bool isOpen) const
275 {
276 THROW_HR_IF_MSG(
277 HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED),
278 protocol != IPPROTO_TCP && protocol != IPPROTO_UDP,
279 "Unsupported bind protocol %d",
280 protocol);
281
282 auto lock = m_lock.lock_exclusive();
283 const auto device = m_guestDeviceManager->GetVirtioNetDevice(tag);
284 const auto transportProtocol = (protocol == IPPROTO_UDP) ? TransportProtocol_Udp : TransportProtocol_Tcp;
285 auto listenAddress = ToIpAddress(hostAddress);
286
287 if (isOpen)
288 {
289 UINT16 allocatedPort{};
290 THROW_IF_FAILED(device->BindPort(transportProtocol, &listenAddress, HostPort, GuestPort, &allocatedPort));
291 WSL_LOG(
292 "MapVirtioPort",
293 TraceLoggingValue(tag, "Tag"),
294 TraceLoggingValue(HostPort, "HostPort"),
295 TraceLoggingValue(GuestPort, "GuestPort"));
296 return allocatedPort;
297 }
298
299 THROW_IF_FAILED(device->UnbindPort(transportProtocol, listenAddress.family, GuestPort));
300 WSL_LOG(
301 "UnmapVirtioPort",
302 TraceLoggingValue(tag, "Tag"),
303 TraceLoggingValue(HostPort, "HostPort"),
304 TraceLoggingValue(GuestPort, "GuestPort"));
305 return HostPort;
306 }
307
308 HRESULT ConsommeNetworking::MapPort(_In_ const SOCKADDR_INET& ListenAddress, _In_ USHORT GuestPort, _In_ int Protocol, _Out_ USHORT* AllocatedHostPort) const
309 try
310 {
311 RETURN_HR_IF(E_POINTER, AllocatedHostPort == nullptr);
312 RETURN_HR_IF_MSG(E_INVALIDARG, Protocol != IPPROTO_TCP && Protocol != IPPROTO_UDP, "Invalid protocol: %i", Protocol);
313
314 *AllocatedHostPort = 0;
315
316 *AllocatedHostPort = HandlePortNotification(ListenAddress, Protocol, GuestPort, true);
317
318 return S_OK;
319 }
320 CATCH_RETURN()
321
322 HRESULT ConsommeNetworking::UnmapPort(_In_ const SOCKADDR_INET& ListenAddress, _In_ USHORT GuestPort, _In_ int Protocol) const
323 try
324 {
325 RETURN_HR_IF(E_INVALIDARG, Protocol != IPPROTO_TCP && Protocol != IPPROTO_UDP);
326
327 HandlePortNotification(ListenAddress, Protocol, GuestPort, false);
328
329 return S_OK;
330 }
331 CATCH_RETURN()
332
333 void ConsommeNetworking::RefreshGuestConnection()
334 {
335 // Query current networking information before acquiring the lock.
336 auto networkSettings = GetHostEndpointSettings();
337
338 std::wstring default_route = networkSettings->GetBestGatewayAddressString();
339
340 networking::DnsInfo currentDns{};
341 if (WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::DnsTunneling))
342 {
343 currentDns = networking::HostDnsInfo::GetDnsTunnelingSettings(default_route);
344 }
345 else
346 {
347 wsl::core::networking::DnsSettingsFlags dnsFlags = networking::DnsSettingsFlags::IncludeVpn;
348 WI_SetFlagIf(dnsFlags, networking::DnsSettingsFlags::IncludeIpv6Servers, WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::Ipv6));
349 currentDns = networking::HostDnsInfo::GetDnsSettings(dnsFlags);
350 }
351
352 const auto minMtu = GetMinimumConnectedInterfaceMtu();
353 auto virtioNetConfig = BuildVirtioNetConfig(networkSettings, WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::Ipv6));
354 auto nameservers = ToIpAddresses(currentDns);
355
356 // Acquire the lock and perform device updates.
357 auto lock = m_lock.lock_exclusive();
358
359 // Add the virtio net adapter to the guest, or update its runtime configuration.
360 if (!m_adapterId.has_value())
361 {
362 WSL_LOG(
363 "RefreshVirtioNetConnection",
364 TraceLoggingValue(networkSettings->PreferredIpAddress.AddressString.c_str(), "ClientIp"),
365 TraceLoggingValue(networkSettings->PreferredIpAddress.PrefixLength, "PrefixLength"),
366 TraceLoggingValue(default_route.c_str(), "GatewayIp"),
367 TraceLoggingValue(networkSettings->PreferredIpv6Address.AddressString.c_str(), "ClientIpv6"));
368 m_adapterId = m_guestDeviceManager->AddVirtioNetDevice(c_eth0DeviceName, virtioNetConfig, nameservers, m_userToken.get());
369 }
370 else if (!m_virtioNetConfig.has_value() || !AreEqual(m_virtioNetConfig.value(), virtioNetConfig) || !AreEqual(m_virtioNetNameservers, nameservers))
371 {
372 IpAddress emptyNameserver{};
373 auto* nameserversData = nameservers.empty() ? &emptyNameserver : nameservers.data();
374 const auto device = m_guestDeviceManager->GetVirtioNetDevice(c_eth0DeviceName);
375 THROW_IF_FAILED(device->Update(&virtioNetConfig, gsl::narrow_cast<UINT32>(nameservers.size()), nameserversData));
376 }
377
378 m_virtioNetConfig = virtioNetConfig;
379 m_virtioNetNameservers = std::move(nameservers);
380
381 UpdateIpv4Address(networkSettings->PreferredIpAddress);
382 if (WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::Ipv6))
383 {
384 UpdateIpv6Address(networkSettings->PreferredIpv6Address);
385 }
386
387 UpdateDefaultRoute(default_route);
388
389 UpdateDnsSettings(currentDns);
390 UpdateMtu(minMtu);
391
392 m_networkSettings = std::move(networkSettings);
393 }
394
395 void ConsommeNetworking::SetupHostLoopback()
396 {
397 const auto loopbackDevice = m_guestDeviceManager->GetVirtioNetDevice(c_loopbackDeviceName);
398
399 SOCKADDR_INET loopback{};
400 loopback.Ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
401 loopback.Ipv4.sin_family = AF_INET;
402 auto loopbackIp = ToIpAddress(loopback);
403
404 IpAddress virtualAddress;
405 THROW_IF_FAILED(loopbackDevice->CreateVirtualAddress(&loopbackIp, &virtualAddress));
406
407 uint32_t addressBytes{};
408 std::memcpy(&addressBytes, &virtualAddress.bytes[0], sizeof(addressBytes));
409
410 std::string virtualAddressString(INET_ADDRSTRLEN, '\0');
411 RtlIpv4AddressToStringA(reinterpret_cast<const IN_ADDR*>(&virtualAddress.bytes[0]), virtualAddressString.data());
412 virtualAddressString.resize(std::strlen(virtualAddressString.data()));
413
414 const auto eth0 = m_guestDeviceManager->GetVirtioNetDevice(c_eth0DeviceName);
415 THROW_IF_FAILED(eth0->CreateDNSRecord(DnsRecordType_A, m_hostLoopback.c_str(), virtualAddressString.c_str()));
416
417 WSL_LOG(
418 "SetupHostLoopback",
419 TraceLoggingValue(m_hostLoopback.c_str(), "DnsName"),
420 TraceLoggingValue(virtualAddressString.c_str(), "VirtualAddress"));
421 }
422
423 void ConsommeNetworking::SetupLoopbackDevice()
424 {
425 auto loopbackSettings = std::make_shared<NetworkSettings>();
426 const auto* clientIp = WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::LoopbackClientIp) ? L"127.0.0.1" : L"169.254.73.250";
427 loopbackSettings->PreferredIpAddress.Address = wsl::windows::common::string::StringToSockAddrInet(clientIp);
428 loopbackSettings->PreferredIpAddress.AddressString = clientIp;
429 loopbackSettings->PreferredIpAddress.PrefixLength = 28;
430 loopbackSettings->Routes.emplace(EndpointRoute::DefaultRoute(AF_INET, wsl::windows::common::string::StringToSockAddrInet(L"169.254.73.249")));
431 m_localhostAdapterId = m_guestDeviceManager->AddVirtioNetDevice(
432 c_loopbackDeviceName, BuildVirtioNetConfig(loopbackSettings, false, c_gatewayMacAddress), {}, m_userToken.get());
433
434 // The loopback gateway (see LX_INIT_IPV4_LOOPBACK_GATEWAY_ADDRESS) is 169.254.73.249, so use a /28 subnet
435 // that includes both the client and gateway addresses.
436 // N.B. The MAC address is advertised with the virtio device so doesn't need to be explicitly set.
437 hns::HNSEndpoint endpointProperties;
438 endpointProperties.ID = m_localhostAdapterId.value();
439 endpointProperties.IPAddress = L"169.254.73.250";
440 endpointProperties.PrefixLength = 28;
441 endpointProperties.PortFriendlyName = c_loopbackDeviceName;
442 m_gnsChannel.SendEndpointState(endpointProperties);
443
444 hns::CreateDeviceRequest createLoopbackDevice;
445 createLoopbackDevice.deviceName = c_loopbackDeviceName;
446 createLoopbackDevice.type = hns::DeviceType::Loopback;
447 createLoopbackDevice.lowerEdgeAdapterId = m_localhostAdapterId.value();
448
449 // ipv6 duplicate address detection (DAD) breaks the ipv6 localhost relay since we can't predict the address before the guest tells us about it.
450 createLoopbackDevice.flags = hns::CreateDeviceFlags::DisableDAD;
451 constexpr auto loopbackType = GnsMessageType(createLoopbackDevice);
452 m_gnsChannel.SendNetworkDeviceMessage(loopbackType, ToJsonW(createLoopbackDevice).c_str());
453 }
454
455 void ConsommeNetworking::SendDefaultRoute(const std::wstring& gateway, hns::ModifyRequestType requestType)
456 {
457 if (gateway.empty() || !m_adapterId.has_value())
458 {
459 return;
460 }
461
462 wsl::shared::hns::Route route;
463 route.NextHop = gateway;
464 route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;
465 route.Family = AF_INET;
466
467 hns::ModifyGuestEndpointSettingRequest<hns::Route> request;
468 request.RequestType = requestType;
469 request.ResourceType = hns::GuestEndpointResourceType::Route;
470 request.Settings = route;
471 m_gnsChannel.SendHnsNotification(ToJsonW(request).c_str(), m_adapterId.value());
472 }
473
474 void ConsommeNetworking::UpdateDefaultRoute(const std::wstring& gateway)
475 {
476 if (gateway == m_trackedDefaultRoute || !m_adapterId.has_value())
477 {
478 return;
479 }
480
481 SendDefaultRoute(m_trackedDefaultRoute, hns::ModifyRequestType::Remove);
482 m_trackedDefaultRoute = gateway;
483 SendDefaultRoute(gateway, hns::ModifyRequestType::Add);
484 }
485
486 void ConsommeNetworking::UpdateDnsSettings(const networking::DnsInfo& dns)
487 {
488 if (dns == m_trackedDnsSettings || !m_adapterId.has_value())
489 {
490 return;
491 }
492
493 m_trackedDnsSettings = dns;
494
495 hns::ModifyGuestEndpointSettingRequest<hns::DNS> notification{};
496 notification.RequestType = hns::ModifyRequestType::Update;
497 notification.ResourceType = hns::GuestEndpointResourceType::DNS;
498 notification.Settings = networking::BuildDnsNotification(dns, m_dnsOptions);
499 m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());
500 }
501
502 void ConsommeNetworking::UpdateIpv4Address(const networking::EndpointIpAddress& ipAddress)
503 {
504 if (ipAddress == m_trackedIpv4Address || ipAddress.AddressString.empty() || !m_adapterId.has_value())
505 {
506 return;
507 }
508
509 m_trackedIpv4Address = ipAddress;
510
511 // N.B. SendEndpointState triggers SetAdapterConfiguration on the Linux side
512 // which brings the interface UP and configures the full adapter state.
513 hns::HNSEndpoint endpointProperties;
514 endpointProperties.ID = m_adapterId.value();
515 endpointProperties.IPAddress = ipAddress.AddressString;
516 endpointProperties.PrefixLength = ipAddress.PrefixLength;
517 m_gnsChannel.SendEndpointState(endpointProperties);
518 }
519
520 void ConsommeNetworking::SendIpv6Address(const networking::EndpointIpAddress& ipAddress, hns::ModifyRequestType requestType)
521 {
522 WI_ASSERT(WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::Ipv6));
523
524 if (ipAddress.AddressString.empty() || !m_adapterId.has_value())
525 {
526 return;
527 }
528
529 // The HNSEndpoint schema doesn't support IPv6 addresses, so use ModifyGuestEndpointSettingRequest.
530 hns::ModifyGuestEndpointSettingRequest<hns::IPAddress> request;
531 request.RequestType = requestType;
532 request.ResourceType = hns::GuestEndpointResourceType::IPAddress;
533 request.Settings.Address = ipAddress.AddressString;
534 request.Settings.Family = ipAddress.Address.si_family;
535 request.Settings.OnLinkPrefixLength = ipAddress.PrefixLength;
536 request.Settings.PreferredLifetime = ULONG_MAX;
537 m_gnsChannel.SendHnsNotification(ToJsonW(request).c_str(), m_adapterId.value());
538 }
539
540 void ConsommeNetworking::UpdateIpv6Address(const networking::EndpointIpAddress& ipAddress)
541 {
542 WI_ASSERT(WI_IsFlagSet(m_flags, ConsommeNetworkingFlags::Ipv6));
543
544 if (ipAddress == m_trackedIpv6Address || !m_adapterId.has_value())
545 {
546 return;
547 }
548
549 SendIpv6Address(m_trackedIpv6Address, hns::ModifyRequestType::Remove);
550 m_trackedIpv6Address = ipAddress;
551 SendIpv6Address(ipAddress, hns::ModifyRequestType::Add);
552 }
553
554 void ConsommeNetworking::UpdateMtu(std::optional<ULONG> mtu)
555 {
556 if (!mtu || mtu.value() == m_networkMtu || !m_adapterId.has_value())
557 {
558 return;
559 }
560
561 m_networkMtu = mtu.value();
562
563 hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> notification{};
564 notification.ResourceType = hns::GuestEndpointResourceType::Interface;
565 notification.RequestType = hns::ModifyRequestType::Update;
566 notification.Settings.Connected = true;
567 notification.Settings.NlMtu = m_networkMtu;
568 m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());
569 }