master
h 426 lines 16.6 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 #pragma once
4 #include <algorithm>
5 #include <set>
6 #include <string>
7
8 #include <windows.h>
9 #include <mstcpip.h>
10 #include <ws2ipdef.h>
11 #include <netioapi.h>
12
13 #include "hcs.hpp"
14 #include "lxinitshared.h"
15 #include "Stringify.h"
16 #include "stringshared.h"
17 #include "WslCoreNetworkingSupport.h"
18 #include "hns_schema.h"
19
20 namespace wsl::core::networking {
21
22 constexpr auto AddEndpointRetryPeriod = std::chrono::milliseconds(100);
23 constexpr auto AddEndpointRetryTimeout = std::chrono::seconds(3);
24 constexpr auto AddEndpointRetryPredicate = [] {
25 // Don't retry if ModifyComputeSystem fails with:
26 // HCN_E_ENDPOINT_NOT_FOUND - indicates that the underlying network object was deleted.
27 // HCN_E_ENDPOINT_ALREADY_ATTACHED - occurs when HNS was restarted before the endpoints were removed.
28 // VM_E_INVALID_STATE - occurs when the VM has been terminated.
29 const auto result = wil::ResultFromCaughtException();
30 return result != HCN_E_ENDPOINT_NOT_FOUND && result != HCN_E_ENDPOINT_ALREADY_ATTACHED && result != VM_E_INVALID_STATE;
31 };
32
33 struct EndpointIpAddress
34 {
35 SOCKADDR_INET Address{};
36 std::wstring AddressString{};
37 unsigned char PrefixLength = 0;
38 unsigned int PrefixOrigin = 0;
39 unsigned int SuffixOrigin = 0;
40
41 // The following field can be changed from a const iterator in SyncIpStateWithLinux - that's why it's marked mutable.
42 mutable unsigned int PreferredLifetime = 0;
43
44 EndpointIpAddress() = default;
45 ~EndpointIpAddress() noexcept = default;
46
47 EndpointIpAddress(EndpointIpAddress&&) = default;
48 EndpointIpAddress& operator=(EndpointIpAddress&&) = default;
49 EndpointIpAddress(const EndpointIpAddress&) = default;
50 EndpointIpAddress& operator=(const EndpointIpAddress&) = default;
51
52 explicit EndpointIpAddress(const MIB_UNICASTIPADDRESS_ROW& AddressRow) :
53 Address(AddressRow.Address),
54 AddressString(windows::common::string::SockAddrInetToWstring(AddressRow.Address)),
55 PrefixLength(AddressRow.OnLinkPrefixLength),
56 PrefixOrigin(AddressRow.PrefixOrigin),
57 SuffixOrigin(AddressRow.SuffixOrigin),
58 // We treat the preferred lifetime field as effective DAD state - 0 is not preferred, anything else is preferred.
59 // We do this for convenience, as we can't directly set the DAD state of an address into the guest, but we
60 // we can set an address's preferred lifetime (in Linux, at least).
61 PreferredLifetime(AddressRow.DadState == IpDadStatePreferred ? 0xFFFFFFFF : 0)
62 {
63 }
64
65 // operator== is deliberately not comparing PreferredLifetime (DAD state) for equality - only the address portion
66 bool operator==(const EndpointIpAddress& rhs) const noexcept
67 {
68 return Address == rhs.Address && PrefixLength == rhs.PrefixLength;
69 }
70
71 bool operator<(const EndpointIpAddress& rhs) const noexcept
72 {
73 if (Address == rhs.Address)
74 {
75 return PrefixLength < rhs.PrefixLength;
76 }
77 return Address < rhs.Address;
78 }
79
80 void Clear() noexcept
81 {
82 Address = {};
83 AddressString.clear();
84 PrefixLength = 0;
85 PrefixOrigin = 0;
86 SuffixOrigin = 0;
87 }
88
89 std::wstring GetPrefix() const
90 {
91 SOCKADDR_INET address{Address};
92 unsigned char* addressPointer{nullptr};
93
94 if (Address.si_family == AF_INET)
95 {
96 addressPointer = reinterpret_cast<unsigned char*>(&address.Ipv4.sin_addr);
97 }
98 else if (Address.si_family == AF_INET6)
99 {
100 addressPointer = address.Ipv6.sin6_addr.u.Byte;
101 }
102 else
103 {
104 return L"";
105 }
106
107 constexpr int c_numBitsPerByte = 8;
108 for (int i = 0, currPrefixLength = PrefixLength; i < INET_ADDR_LENGTH(Address.si_family); i++, currPrefixLength -= c_numBitsPerByte)
109 {
110 if (currPrefixLength < c_numBitsPerByte)
111 {
112 const int bitShiftAmt = c_numBitsPerByte - std::max(currPrefixLength, 0);
113 addressPointer[i] &= (0xFF >> bitShiftAmt) << bitShiftAmt;
114 }
115 }
116
117 const auto addressString = windows::common::string::SockAddrInetToWstring(address);
118 WI_ASSERT(!addressString.empty());
119 if (addressString.empty())
120 {
121 // just return an empty string if we have a bad address
122 return addressString;
123 }
124
125 return std::format(L"{}/{}", addressString, PrefixLength);
126 }
127
128 std::wstring GetIpv4BroadcastMask() const
129 {
130 // start with all bits set, then shift off the prefix
131 ULONG prefixMask{0xffffffff};
132 prefixMask <<= PrefixLength;
133 prefixMask >>= PrefixLength;
134
135 SOCKADDR_INET address{Address};
136 // flip to host-order, then apply the mask
137 ULONG hostOrder = ntohl(address.Ipv4.sin_addr.S_un.S_addr);
138 hostOrder |= prefixMask;
139 address.Ipv4.sin_addr.S_un.S_addr = htonl(hostOrder);
140
141 return windows::common::string::SockAddrInetToWstring(address);
142 }
143
144 bool IsPreferred() const noexcept
145 {
146 return PreferredLifetime > 0;
147 }
148
149 bool IsLinkLocal() const
150 {
151 return (Address.si_family == AF_INET && IN4_IS_ADDR_LINKLOCAL(&Address.Ipv4.sin_addr)) ||
152 (Address.si_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&Address.Ipv6.sin6_addr));
153 }
154 };
155
156 struct EndpointRoute
157 {
158 ADDRESS_FAMILY Family = AF_INET;
159 IP_ADDRESS_PREFIX DestinationPrefix{};
160 std::wstring DestinationPrefixString{};
161 SOCKADDR_INET NextHop{};
162 std::wstring NextHopString{};
163 unsigned char SitePrefixLength = 0;
164 unsigned int Metric = 0;
165 bool IsAutoGeneratedPrefixRoute = false;
166
167 EndpointRoute() = default;
168 ~EndpointRoute() noexcept = default;
169
170 EndpointRoute(EndpointRoute&&) = default;
171 EndpointRoute& operator=(EndpointRoute&&) = default;
172 EndpointRoute(const EndpointRoute&) = default;
173 EndpointRoute& operator=(const EndpointRoute&) = default;
174
175 // Build a default route (0.0.0.0/0 or ::/0) with the given next hop.
176 static EndpointRoute DefaultRoute(ADDRESS_FAMILY family, const SOCKADDR_INET& nextHop)
177 {
178 EndpointRoute route{};
179 route.Family = family;
180 route.DestinationPrefix.PrefixLength = 0;
181 if (family == AF_INET)
182 {
183 IN4ADDR_SETANY(&route.DestinationPrefix.Prefix.Ipv4);
184 route.DestinationPrefixString = LX_INIT_UNSPECIFIED_ADDRESS;
185 }
186 else
187 {
188 IN6ADDR_SETANY(&route.DestinationPrefix.Prefix.Ipv6);
189 route.DestinationPrefixString = LX_INIT_UNSPECIFIED_V6_ADDRESS;
190 }
191 route.NextHop = nextHop;
192 route.NextHopString = windows::common::string::SockAddrInetToWstring(nextHop);
193 return route;
194 }
195
196 EndpointRoute(const MIB_IPFORWARD_ROW2& RouteRow) :
197 Family(RouteRow.NextHop.si_family),
198 DestinationPrefix(RouteRow.DestinationPrefix),
199 DestinationPrefixString(windows::common::string::SockAddrInetToWstring(RouteRow.DestinationPrefix.Prefix)),
200 NextHop(RouteRow.NextHop),
201 NextHopString(windows::common::string::SockAddrInetToWstring(RouteRow.NextHop)),
202 SitePrefixLength(RouteRow.SitePrefixLength),
203 Metric(RouteRow.Metric)
204 {
205 }
206
207 unsigned char GetMaxPrefixLength() const
208 {
209 return (Family == AF_INET) ? 32 : 128;
210 }
211
212 std::wstring GetFullDestinationPrefix() const
213 {
214 return std::format(L"{}/{}", DestinationPrefixString, static_cast<unsigned int>(DestinationPrefix.PrefixLength));
215 }
216
217 bool IsNextHopOnlink() const noexcept
218 {
219 return (Family == AF_INET && NextHopString == LX_INIT_UNSPECIFIED_ADDRESS) ||
220 (Family == AF_INET6 && NextHopString == LX_INIT_UNSPECIFIED_V6_ADDRESS);
221 }
222
223 bool IsDefault() const noexcept
224 {
225 return DestinationPrefix.PrefixLength == 0 && ((Family == AF_INET && DestinationPrefixString == LX_INIT_UNSPECIFIED_ADDRESS) ||
226 (Family == AF_INET6 && DestinationPrefixString == LX_INIT_UNSPECIFIED_V6_ADDRESS));
227 }
228
229 bool IsUnicastAddressRoute() const noexcept
230 {
231 return (Family == AF_INET && DestinationPrefix.PrefixLength == 32) || (Family == AF_INET6 && DestinationPrefix.PrefixLength == 128);
232 }
233
234 std::wstring ToString() const
235 {
236 return std::format(L"{}=>{} [metric {}]", GetFullDestinationPrefix(), NextHopString, Metric);
237 }
238
239 bool operator==(const EndpointRoute& rhs) const noexcept
240 {
241 return Family == rhs.Family && DestinationPrefix.PrefixLength == rhs.DestinationPrefix.PrefixLength &&
242 DestinationPrefix.Prefix == rhs.DestinationPrefix.Prefix && NextHop == rhs.NextHop &&
243 SitePrefixLength == rhs.SitePrefixLength && Metric == rhs.Metric;
244 }
245
246 bool operator!=(const EndpointRoute& other) const
247 {
248 return !(*this == other);
249 }
250
251 // sort by family, then by next-hop (on-link routes first), then by prefix, then by metric
252 bool operator<(const EndpointRoute& rhs) const noexcept
253 {
254 if (Family == rhs.Family)
255 {
256 if (NextHop == rhs.NextHop)
257 {
258 if (DestinationPrefix.Prefix == rhs.DestinationPrefix.Prefix)
259 {
260 if (DestinationPrefix.PrefixLength == rhs.DestinationPrefix.PrefixLength)
261 {
262 if (Metric == rhs.Metric)
263 {
264 return SitePrefixLength < rhs.SitePrefixLength;
265 }
266 return Metric < rhs.Metric;
267 }
268 return DestinationPrefix.PrefixLength < rhs.DestinationPrefix.PrefixLength;
269 }
270 return DestinationPrefix.Prefix < rhs.DestinationPrefix.Prefix;
271 }
272 return NextHop < rhs.NextHop;
273 }
274 return Family < rhs.Family;
275 }
276 };
277
278 struct NetworkSettings
279 {
280 NetworkSettings() = default;
281
282 NetworkSettings(
283 const GUID& interfaceGuid,
284 EndpointIpAddress preferredIpAddress,
285 EndpointIpAddress preferredIpv6Address,
286 EndpointRoute gateway,
287 EndpointRoute v6Gateway,
288 std::wstring macAddress,
289 uint32_t interfaceIndex,
290 uint32_t mediaType) :
291 InterfaceGuid(interfaceGuid),
292 PreferredIpAddress(std::move(preferredIpAddress)),
293 PreferredIpv6Address(std::move(preferredIpv6Address)),
294 MacAddress(std::move(macAddress)),
295 InterfaceIndex(interfaceIndex),
296 InterfaceType(mediaType)
297 {
298 // Only insert routes that have a valid next hop. A default-constructed or empty
299 // EndpointRoute indicates no gateway was found for that address family.
300 if (!gateway.NextHopString.empty())
301 {
302 Routes.emplace(std::move(gateway));
303 }
304
305 if (!v6Gateway.NextHopString.empty())
306 {
307 Routes.emplace(std::move(v6Gateway));
308 }
309 }
310
311 GUID InterfaceGuid{};
312 EndpointIpAddress PreferredIpAddress{};
313 EndpointIpAddress PreferredIpv6Address{};
314 std::set<EndpointIpAddress> IpAddresses{}; // Does not include PreferredIpAddress or PreferredIpv6Address.
315 std::set<EndpointRoute> Routes{};
316 std::wstring MacAddress;
317 IF_INDEX InterfaceIndex = 0;
318 IFTYPE InterfaceType = 0;
319 ULONG IPv4InterfaceMtu = 0;
320 ULONG IPv6InterfaceMtu = 0;
321 // some interfaces will only have an IPv4 or IPv6 interface
322 std::optional<ULONG> IPv4InterfaceMetric = 0;
323 std::optional<ULONG> IPv6InterfaceMetric = 0;
324 bool IsHidden = false;
325 bool IsConnected = false;
326 bool IsMetered = false;
327 bool DisableIpv4DefaultRoutes = false;
328 bool DisableIpv6DefaultRoutes = false;
329 bool PendingUpdateToReconnectForMetered = false;
330 bool PendingIPInterfaceUpdate = false;
331
332 auto operator<=>(const NetworkSettings&) const = default;
333
334 // Returns the next-hop string of the first default route matching the given address family.
335 std::wstring GetBestGatewayAddressString(ADDRESS_FAMILY family = AF_INET) const
336 {
337 const auto& unspecified = (family == AF_INET) ? LX_INIT_UNSPECIFIED_ADDRESS : LX_INIT_UNSPECIFIED_V6_ADDRESS;
338 for (const auto& route : Routes)
339 {
340 if (route.Family == family && route.DestinationPrefix.PrefixLength == 0 && route.DestinationPrefixString == unspecified)
341 {
342 return route.NextHopString;
343 }
344 }
345
346 return {};
347 }
348
349 // Returns the next-hop address of the first default route matching the given address family.
350 SOCKADDR_INET GetBestGatewayAddress(ADDRESS_FAMILY family = AF_INET) const
351 {
352 const auto& unspecified = (family == AF_INET) ? LX_INIT_UNSPECIFIED_ADDRESS : LX_INIT_UNSPECIFIED_V6_ADDRESS;
353 for (const auto& route : Routes)
354 {
355 if (route.Family == family && route.DestinationPrefix.PrefixLength == 0 && route.DestinationPrefixString == unspecified)
356 {
357 return route.NextHop;
358 }
359 }
360
361 return {};
362 }
363
364 std::wstring IpAddressesString() const
365 {
366 return std::accumulate(std::begin(IpAddresses), std::end(IpAddresses), std::wstring{}, [](const std::wstring& prev, const auto& addr) {
367 return addr.AddressString + (prev.empty() ? L"" : L"," + prev);
368 });
369 }
370
371 std::wstring RoutesString() const
372 {
373 return std::accumulate(std::begin(Routes), std::end(Routes), std::wstring{}, [](const std::wstring& prev, const EndpointRoute& route) {
374 return route.ToString() + (prev.empty() ? L"" : L"," + prev);
375 });
376 }
377
378 // will return ULONG_MAX if there's no configured MTU
379 ULONG GetEffectiveMtu() const noexcept
380 {
381 return std::min(IPv4InterfaceMtu > 0 ? IPv4InterfaceMtu : ULONG_MAX, IPv6InterfaceMtu > 0 ? IPv6InterfaceMtu : ULONG_MAX);
382 }
383
384 // will return zero if there's no configured metric
385 ULONG GetMinimumMetric() const noexcept
386 {
387 if (!IPv4InterfaceMetric.has_value() && !IPv6InterfaceMetric.has_value())
388 {
389 return 0;
390 }
391 if (!IPv4InterfaceMetric.has_value())
392 {
393 return IPv6InterfaceMetric.value();
394 }
395 if (!IPv6InterfaceMetric.has_value())
396 {
397 return IPv4InterfaceMetric.value();
398 }
399 return std::min(IPv4InterfaceMetric.value(), IPv6InterfaceMetric.value());
400 }
401 };
402
403 std::shared_ptr<NetworkSettings> GetEndpointSettings(const wsl::shared::hns::HNSEndpoint& properties);
404 std::shared_ptr<NetworkSettings> GetHostEndpointSettings();
405
406 #define TRACE_NETWORKSETTINGS_OBJECT(settings) \
407 TraceLoggingValue((settings)->InterfaceGuid, "interfaceGuid"), TraceLoggingValue((settings)->InterfaceIndex, "interfaceIndex"), \
408 TraceLoggingValue((settings)->InterfaceType, "interfaceType"), \
409 TraceLoggingValue((settings)->IsConnected, "isConnected"), TraceLoggingValue((settings)->IsMetered, "isMetered"), \
410 TraceLoggingValue((settings)->GetBestGatewayAddressString().c_str(), "bestGatewayAddress"), \
411 TraceLoggingValue((settings)->PreferredIpAddress.AddressString.c_str(), "preferredIpAddress"), \
412 TraceLoggingValue((settings)->PreferredIpAddress.PrefixLength, "preferredIpAddressPrefixLength"), \
413 TraceLoggingValue((settings)->PreferredIpv6Address.AddressString.c_str(), "preferredIpv6Address"), \
414 TraceLoggingValue((settings)->PreferredIpv6Address.PrefixLength, "preferredIpv6AddressPrefixLength"), \
415 TraceLoggingValue((settings)->GetBestGatewayAddressString(AF_INET6).c_str(), "bestGatewayV6Address"), \
416 TraceLoggingValue((settings)->IpAddressesString().c_str(), "ipAddresses"), \
417 TraceLoggingValue((settings)->RoutesString().c_str(), "routes"), \
418 TraceLoggingValue((settings)->MacAddress.c_str(), "macAddress"), \
419 TraceLoggingValue((settings)->IPv4InterfaceMtu, "IPv4InterfaceMtu"), \
420 TraceLoggingValue((settings)->IPv6InterfaceMtu, "IPv6InterfaceMtu"), \
421 TraceLoggingValue((settings)->IPv4InterfaceMetric.value_or(0xffffffff), "IPv4InterfaceMetric"), \
422 TraceLoggingValue((settings)->IPv6InterfaceMetric.value_or(0xffffffff), "IPv6InterfaceMetric"), \
423 TraceLoggingValue((settings)->PendingIPInterfaceUpdate, "PendingIPInterfaceUpdate"), \
424 TraceLoggingValue((settings)->PendingUpdateToReconnectForMetered, "PendingUpdateToReconnectForMetered")
425
426 } // namespace wsl::core::networking