master
cpp 272 lines 11.6 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 #include "precomp.h"
4 #include "WslCoreNetworkingSupport.h"
5 #include "Stringify.h"
6
7 using wsl::windows::common::Context;
8 using wsl::windows::common::ExecutionContext;
9
10 /// <summary>
11 /// Used for blocked interface telemetry
12 /// </summary>
13 enum class InterfaceUnsupportedReason
14 {
15 UnknownInterface = 0,
16 NrptDnsRulesDetected,
17 InterfaceDetailsQueryFailed,
18 NotPhysicalEthernet,
19 BlockedRegistryKey
20 };
21
22 namespace details {
23
24 static bool FindInterfacesForNetworkAdapter(
25 const IF_INDEX interfaceIndex, const GUID& interfaceGuid, bool metered, std::vector<wsl::core::networking::CurrentInterfaceInformation>& returnedNetworks)
26 {
27 bool addedNetwork = false;
28
29 wsl::core::networking::unique_ifstack_table interfaceStackTable{};
30 if (FAILED_WIN32_LOG(GetIfStackTable(&interfaceStackTable)))
31 {
32 return addedNetwork;
33 }
34
35 wsl::core::networking::unique_address_table addressTable{};
36 if (FAILED_WIN32_LOG(GetUnicastIpAddressTable(AF_INET, &addressTable)))
37 {
38 return addedNetwork;
39 }
40
41 // Find the IP interface(s) in the adapter's interface stack.
42 std::vector<IF_INDEX> ipInterfaces{};
43 std::queue<IF_INDEX> interfaceStack{};
44 interfaceStack.push(interfaceIndex);
45 while (!interfaceStack.empty())
46 {
47 IF_INDEX currInterfaceIndex = interfaceStack.front();
48 interfaceStack.pop();
49
50 for (unsigned int i = 0; i < interfaceStackTable.get()->NumEntries; i++)
51 {
52 if (interfaceStackTable.get()->Table[i].LowerLayerInterfaceIndex == currInterfaceIndex)
53 {
54 interfaceStack.push(interfaceStackTable.get()->Table[i].HigherLayerInterfaceIndex);
55 }
56 }
57
58 MIB_IPINTERFACE_ROW ipInterfaceRow{};
59 ipInterfaceRow.Family = AF_INET;
60 ipInterfaceRow.InterfaceIndex = currInterfaceIndex;
61 if (SUCCEEDED_WIN32(GetIpInterfaceEntry(&ipInterfaceRow)) && ipInterfaceRow.Connected)
62 {
63 // We found a connected IP interface. Ensure it has a preferred IP address too.
64 for (unsigned int i = 0; i < addressTable.get()->NumEntries; i++)
65 {
66 if (addressTable.get()->Table[i].InterfaceIndex == currInterfaceIndex && addressTable.get()->Table[i].DadState == IpDadStatePreferred)
67 {
68 ipInterfaces.push_back(currInterfaceIndex);
69 break;
70 }
71 }
72 }
73 }
74
75 for (auto currInterfaceIndex : ipInterfaces)
76 {
77 MIB_IF_ROW2 row{};
78 row.InterfaceIndex = currInterfaceIndex;
79 if (FAILED_WIN32_LOG(GetIfEntry2Ex(MibIfEntryNormalWithoutStatistics, &row)))
80 {
81 continue;
82 }
83
84 WSL_LOG(
85 "FindInterfacesForNetworkAdapter : returning connected network profile for IP interface on NIC",
86 TraceLoggingValue(interfaceGuid, "underlyingInterfaceGuid"),
87 TraceLoggingValue(row.InterfaceGuid, "interfaceGuid"),
88 TraceLoggingValue(row.Type, "ifType"),
89 TraceLoggingValue(row.Alias, "ifAlias"),
90 TraceLoggingValue(row.Description, "ifDescription"));
91
92 returnedNetworks.emplace_back(row.InterfaceGuid, row.InterfaceLuid, row.Type, row.Alias, row.Description, metered);
93 addedNetwork = true;
94 }
95
96 return addedNetwork;
97 }
98
99 } // namespace details
100
101 bool wsl::core::networking::IsMetered(ABI::Windows::Networking::Connectivity::NetworkCostType cost) noexcept
102 {
103 return (cost == ABI::Windows::Networking::Connectivity::NetworkCostType::NetworkCostType_Fixed) ||
104 (cost == ABI::Windows::Networking::Connectivity::NetworkCostType::NetworkCostType_Variable);
105 }
106
107 bool wsl::core::networking::IsFlowSteeringSupportedByHns() noexcept
108 {
109 static bool supported = false;
110 static std::once_flag fseMethodsLoadedFlag;
111 static constexpr auto c_computeNetworkModuleName = L"ComputeNetwork.dll";
112 std::call_once(fseMethodsLoadedFlag, [&]() {
113 try
114 {
115 static LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePortRange)> allocatePortRange{DynamicFunctionErrorLogs::None};
116 RETURN_IF_FAILED_EXPECTED(
117 allocatePortRange.load(c_computeNetworkModuleName, "HcnReserveGuestNetworkServicePortRange"));
118
119 static LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePort)> allocatePort{DynamicFunctionErrorLogs::None};
120 RETURN_IF_FAILED_EXPECTED(allocatePort.load(c_computeNetworkModuleName, "HcnReserveGuestNetworkServicePort"));
121
122 static LxssDynamicFunction<decltype(HcnReleaseGuestNetworkServicePortReservationHandle)> releasePort{DynamicFunctionErrorLogs::None};
123 RETURN_IF_FAILED_EXPECTED(
124 releasePort.load(c_computeNetworkModuleName, "HcnReleaseGuestNetworkServicePortReservationHandle"));
125
126 supported = true;
127 }
128 CATCH_LOG()
129 return S_OK;
130 });
131
132 if (!supported)
133 {
134 WSL_LOG("IsFlowSteeringSupportedByHns (false) - Port reservation functions are not present");
135 }
136 return supported;
137 }
138
139 std::vector<wsl::core::networking::CurrentInterfaceInformation> wsl::core::networking::EnumerateConnectedInterfaces()
140 {
141 using ABI::Windows::Foundation::Collections::IVectorView;
142 using ABI::Windows::Networking::Connectivity::ConnectionProfile;
143 using ABI::Windows::Networking::Connectivity::IConnectionCost;
144 using ABI::Windows::Networking::Connectivity::INetworkAdapter;
145 using ABI::Windows::Networking::Connectivity::INetworkInformationStatics;
146 using ABI::Windows::Networking::Connectivity::NetworkConnectivityLevel;
147 using ABI::Windows::Networking::Connectivity::NetworkCostType;
148
149 std::vector<wsl::core::networking::CurrentInterfaceInformation> returnedNetworks;
150 try
151 {
152 const auto roInit = wil::RoInitialize();
153 auto networkInformationStatics =
154 wil::GetActivationFactory<INetworkInformationStatics>(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation);
155 THROW_HR_IF_NULL_MSG(E_OUTOFMEMORY, networkInformationStatics.get(), "null INetworkInformationStatics");
156
157 wil::com_ptr<IVectorView<ConnectionProfile*>> connectionList;
158 THROW_IF_FAILED(networkInformationStatics->GetConnectionProfiles(&connectionList));
159
160 for (const auto& connectionProfile : wil::get_range(connectionList.get()))
161 {
162 NetworkConnectivityLevel connectivityLevel{};
163 CONTINUE_IF_FAILED(connectionProfile->GetNetworkConnectivityLevel(&connectivityLevel));
164 if (connectivityLevel == NetworkConnectivityLevel::NetworkConnectivityLevel_None)
165 {
166 continue;
167 }
168
169 wil::com_ptr<IConnectionCost> connectionCost;
170 CONTINUE_IF_FAILED(connectionProfile->GetConnectionCost(&connectionCost));
171
172 NetworkCostType cost{};
173 CONTINUE_IF_FAILED(connectionCost->get_NetworkCostType(&cost));
174 bool metered = IsMetered(cost);
175
176 wil::com_ptr<INetworkAdapter> networkAdapter;
177 CONTINUE_IF_FAILED(connectionProfile->get_NetworkAdapter(&networkAdapter));
178
179 IFTYPE ifType{};
180 CONTINUE_IF_FAILED(networkAdapter->get_IanaInterfaceType(reinterpret_cast<UINT32*>(&ifType)));
181
182 GUID interfaceGuid{};
183 CONTINUE_IF_FAILED(networkAdapter->get_NetworkAdapterId(&interfaceGuid));
184
185 NET_LUID interfaceLuid{};
186 CONTINUE_IF_FAILED_WIN32(ConvertInterfaceGuidToLuid(&interfaceGuid, &interfaceLuid));
187
188 MIB_IF_ROW2 row{};
189 row.InterfaceLuid = interfaceLuid;
190 CONTINUE_IF_FAILED_WIN32(GetIfEntry2Ex(MibIfEntryNormalWithoutStatistics, &row));
191
192 MIB_IPINTERFACE_ROW ipIfRow{};
193 InitializeIpInterfaceEntry(&ipIfRow);
194 ipIfRow.Family = AF_INET;
195 ipIfRow.InterfaceLuid = interfaceLuid;
196 if (FAILED_WIN32(GetIpInterfaceEntry(&ipIfRow)))
197 {
198 // There is no IP interface directly attached to the given network adapter. One way this could happen
199 // is if the network adapter is under an external vmswitch. If that's the case, there should be at
200 // least one IP interface farther up the network adapter's interface stack. We return all such IP
201 // interfaces as connected interfaces, as we don't know which is preferred at this point.
202 WSL_LOG(
203 "EnumerateConnectedInterfaces : connection profile's network adapter is not directly bound to TCP/IP - "
204 "searching its interface stack for an IP interface",
205 TraceLoggingValue(interfaceGuid, "physicalInterfaceGuid"),
206 TraceLoggingValue(ifType, "ifType"),
207 TraceLoggingValue(row.Alias, "ifAlias"),
208 TraceLoggingValue(row.Description, "ifDescription"),
209 TraceLoggingValue(wsl::windows::common::stringify::ToString(connectivityLevel), "connectivityLevel"));
210
211 if (!details::FindInterfacesForNetworkAdapter(row.InterfaceIndex, interfaceGuid, metered, returnedNetworks))
212 {
213 WSL_LOG(
214 "EnumerateConnectedInterfaces : could not find any IP interfaces for connected network profile",
215 TraceLoggingValue(interfaceGuid, "interfaceGuid"));
216 }
217 // TODO - if FindInterfacesForNetworkAdapter returns false, what should we add to returnedNetworks
218 }
219 else
220 {
221 WSL_LOG(
222 "EnumerateConnectedInterfaces : returning connected network profile",
223 TraceLoggingValue(interfaceGuid, "interfaceGuid"),
224 TraceLoggingValue(row.Type, "ifType"),
225 TraceLoggingValue(row.Alias, "ifAlias"),
226 TraceLoggingValue(row.Description, "ifDescription"),
227 TraceLoggingValue(wsl::windows::common::stringify::ToString(connectivityLevel), "connectivityLevel"));
228
229 returnedNetworks.emplace_back(interfaceGuid, interfaceLuid, ifType, row.Alias, row.Description, metered);
230 }
231 }
232 }
233 CATCH_LOG()
234
235 return returnedNetworks;
236 }
237
238 wsl::core::networking::EphemeralHcnEndpoint wsl::core::networking::CreateEphemeralHcnEndpoint(
239 HCN_NETWORK network, const wsl::shared::hns::HostComputeEndpoint& endpointSettings)
240 {
241 wsl::core::networking::EphemeralHcnEndpoint endpoint{};
242 wil::unique_cotaskmem_string error;
243 const auto settings = wsl::shared::ToJsonW(endpointSettings);
244
245 ExecutionContext context(Context::HNS);
246 const auto result = HcnCreateEndpoint(network, endpoint.Id, settings.c_str(), &endpoint.Endpoint, &error);
247 THROW_IF_FAILED_MSG(result, "HcnCreateEndpoint(%ls) failed: %ls", settings.c_str(), error.get());
248
249 return endpoint;
250 }
251
252 std::optional<ULONG> wsl::core::networking::GetMinimumConnectedInterfaceMtu() noexcept
253 {
254 std::optional<ULONG> minMtu{};
255 try
256 {
257 unique_interface_table interfaceTable{};
258 THROW_IF_WIN32_ERROR(::GetIpInterfaceTable(AF_UNSPEC, &interfaceTable));
259
260 for (ULONG index = 0; index < interfaceTable.get()->NumEntries; index++)
261 {
262 const auto& ipInterface = interfaceTable.get()->Table[index];
263 if (ipInterface.Connected)
264 {
265 minMtu = std::min(minMtu.value_or(ipInterface.NlMtu), ipInterface.NlMtu);
266 }
267 }
268 }
269 CATCH_LOG()
270
271 return minMtu;
272 }