master
cpp 386 lines 13.9 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #include "RuntimeErrorWithSourceLocation.h"
3 #include "RoutingTable.h"
4 #include "NetlinkTransactionError.h"
5 #include "NetLinkStrings.h"
6 #include "Utils.h"
7 #include "common.h"
8
9 const Address c_ipv4LoopbackRouteSource = {AF_INET, 32, "127.0.0.1"};
10
11 RoutingTable::RoutingTable(int table) : m_table(table)
12 {
13 }
14
15 void RoutingTable::ChangeTableId(int newTableId)
16 {
17 m_table = newTableId;
18 }
19
20 std::vector<Route> RoutingTable::ListRoutes(int family)
21 {
22 if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6)
23 {
24 throw RuntimeErrorWithSourceLocation(std::format("Unexpected address family: {}", family));
25 }
26
27 std::vector<Route> routes;
28 auto processRoute = [&](const NetlinkResponse& response) {
29 for (const auto& e : response.Messages<rtmsg>(RTM_NEWROUTE))
30 {
31 const auto* message = e.Payload();
32 auto tableId = e.UniqueAttribute<int>(RTA_TABLE);
33 if ((family != AF_UNSPEC && family != message->rtm_family) || !tableId.has_value() || *tableId.value() != m_table)
34 {
35 continue;
36 }
37
38 auto readOptionalAddress = [&](int type) -> std::optional<Address> {
39 auto attribute = e.UniqueAttribute<const void*>(type);
40 if (!attribute.has_value())
41 {
42 return {};
43 }
44
45 return Address::FromBinary(message->rtm_family, message->rtm_dst_len, attribute.value());
46 };
47
48 auto to = readOptionalAddress(RTA_DST);
49 auto device = e.UniqueAttribute<int>(RTA_OIF);
50 auto metric = e.UniqueAttribute<int>(RTA_PRIORITY);
51 routes.emplace_back(
52 message->rtm_family,
53 readOptionalAddress(RTA_GATEWAY),
54 device.has_value() ? *device.value() : -1,
55 !to.has_value(),
56 to,
57 metric.has_value() ? *metric.value() : 0);
58 }
59 };
60
61 rtmsg message{};
62 message.rtm_family = family;
63 auto transaction = m_channel.CreateTransaction(message, RTM_GETROUTE, NLM_F_DUMP);
64 transaction.Execute(processRoute);
65
66 return routes;
67 }
68
69 void RoutingTable::ModifyRoute(const Route& route, Operation action)
70 {
71 if (route.family != AF_INET && route.family != AF_INET6)
72 {
73 throw RuntimeErrorWithSourceLocation(std::format("Unexpected address family: {}", route.family));
74 }
75
76 assert(action == Operation::Create || action == Operation::Update || action == Operation::Remove);
77
78 if (route.family == AF_INET)
79 {
80 ModifyRouteImpl<in_addr>(route, action);
81 }
82 else
83 {
84 ModifyRouteImpl<in6_addr>(route, action);
85 }
86 }
87
88 template <typename TAddr>
89 void RoutingTable::ModifyRouteImpl(const Route& route, Operation action)
90 {
91 int flags = 0;
92 int operation = 0;
93 if (action == Update)
94 {
95 flags = NLM_F_CREATE | NLM_F_REPLACE;
96 operation = RTM_NEWROUTE;
97 }
98 else if (action == Create)
99 {
100 flags = NLM_F_CREATE;
101 operation = RTM_NEWROUTE;
102 }
103 else
104 {
105 // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK
106 // which is set later in NetlinkChannel CreateTransaction
107 operation = RTM_DELROUTE;
108 }
109
110 if (route.isLoopbackRoute)
111 {
112 ModifyLoopbackRouteImpl<TAddr>(route, operation, flags);
113 }
114 else if (route.defaultRoute && route.IsOnlink())
115 {
116 ModifyDefaultLinkLocalRouteImpl<TAddr>(route, operation, flags);
117 }
118 else if (route.defaultRoute)
119 {
120 ModifyDefaultRouteImpl<TAddr>(route, operation, flags);
121 }
122 else if (route.IsOnlink())
123 {
124 ModifyLinkLocalRouteImpl<TAddr>(route, operation, flags);
125 }
126 else
127 {
128 ModifyOfflinkRouteImpl<TAddr>(route, operation, flags);
129 }
130 }
131
132 template <DerivedRouteMessage TMessage>
133 void RoutingTable::SendMessage(const Route& route, int operation, int flags, const std::function<void(TMessage&)>& routine)
134 {
135 TMessage message{};
136 message.route.rtm_family = route.family;
137 message.route.rtm_table = RT_TABLE_UNSPEC; // == 0
138 message.route.rtm_protocol = operation == RTM_DELROUTE ? RTPROT_UNSPEC : RTPROT_KERNEL;
139 message.route.rtm_type = route.IsMulticast() ? RTN_MULTICAST : RTN_UNICAST;
140 message.route.rtm_scope = route.IsOnlink() ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
141 message.route.rtm_flags = RTM_F_NOTIFY;
142 // Default gateways received from the host are directly reachable through the specified interface,
143 // even when a VPN exposes the interface as a host route and the gateway is outside that prefix.
144 if (route.via.has_value() && (route.defaultRoute || route.via.value().IsLinkLocal()))
145 {
146 message.route.rtm_flags |= RTNH_F_ONLINK;
147 }
148 message.route.rtm_dst_len = route.to.has_value() ? route.to.value().PrefixLength() : 0;
149
150 utils::InitializeIntegerAttribute(message.tableId, m_table, RTA_TABLE);
151 utils::InitializeIntegerAttribute(message.dev, route.dev, RTA_OIF);
152
153 routine(message);
154
155 auto transaction = m_channel.CreateTransaction(message, operation, flags);
156 try
157 {
158 transaction.Execute();
159 }
160 catch (const NetlinkTransactionError& transactionErr)
161 {
162 auto errorCode = transactionErr.Error();
163 if (errorCode.has_value())
164 {
165 if (operation == RTM_DELROUTE)
166 {
167 // If the route already doesn't exist, we'll receive error "no such process". Ignore that error and return success.
168 if (errorCode.value() == -ESRCH)
169 {
170 return;
171 }
172 }
173 else
174 {
175 // Errors "file exists", "file not found", "no such process" are ignored in order to avoid keeping
176 // track in GnsDaemon of what routes were added/updated and allow the same route to be
177 // added/updated multiple times.
178 if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT || errorCode.value() == -ESRCH)
179 {
180 return;
181 }
182 }
183 }
184
185 throw;
186 }
187 }
188
189 template <typename TAddr>
190 void RoutingTable::ModifyLoopbackRouteImpl(const Route& route, int operation, int flags)
191 {
192 if (!route.to.has_value() || !route.via.has_value())
193 {
194 throw RuntimeErrorWithSourceLocation(std::format("Loopback route {} missing destination or next hop", utils::Stringify(route)));
195 }
196
197 struct Message : RouteMessage
198 {
199 utils::AddressAttribute<TAddr> to;
200 utils::AddressAttribute<TAddr> via;
201 utils::AddressAttribute<TAddr> preferredSource;
202 } __attribute__((packed));
203
204 GNS_LOG_INFO(
205 "SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
206 route.to.value().Addr().c_str(),
207 route.via.value().Addr().c_str(),
208 RouteOperationToString(operation),
209 NetLinkFormatFlagsToString(flags).c_str());
210
211 SendMessage<Message>(route, operation, flags, [&](Message& message) {
212 // For local IPs, the preferred source is set equal to destination. For a route to loopback address
213 // range 127.0.0.0/8, preferred source is set to 127.0.0.1. This is done to ensure that when routing
214 // loopback or local packets out of the guest, they won't have different source and destination
215 // IPs, since they won't be accepted by the Windows host. Having the routes with source == destination is
216 // also consistent with the how routes from the "local" routing table look like.
217 //
218 // Note: Since the IPv6 loopback range is ::1/128, we don't need separate code such as the one below
219 // converting from 127.0.0.0 to 127.0.0.1.
220 if (route.to.value().Addr().compare("127.0.0.0") == 0)
221 {
222 GNS_LOG_INFO(
223 "InitializeAddressAttribute (preferred source address) RTA_PREFSRC to {}", c_ipv4LoopbackRouteSource.Addr().c_str());
224 utils::InitializeAddressAttribute<TAddr>(message.preferredSource, c_ipv4LoopbackRouteSource, RTA_PREFSRC);
225 }
226 else
227 {
228 // Set the preferred source address to be the same as the route destination.
229 GNS_LOG_INFO("InitializeAddressAttribute (preferred source address) RTA_PREFSRC to {}", route.to.value().Addr().c_str());
230 utils::InitializeAddressAttribute<TAddr>(message.preferredSource, route.to.value(), RTA_PREFSRC);
231 }
232
233 message.route.rtm_flags |= RTNH_F_ONLINK;
234 GNS_LOG_INFO(
235 "Netlink message configuration: RTA_DST ({}) RTA_GATEWAY ({}) RTA_PRIORITY ([not set])",
236 route.to.value().Addr().c_str(),
237 route.via.value().Addr().c_str());
238 utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);
239 utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);
240 });
241 }
242
243 template <typename TAddr>
244 void RoutingTable::ModifyDefaultLinkLocalRouteImpl(const Route& route, int operation, int flags)
245 {
246 if (route.via.has_value())
247 {
248 throw RuntimeErrorWithSourceLocation("Default route has unexpected next hop");
249 }
250 if (route.to.has_value())
251 {
252 throw RuntimeErrorWithSourceLocation("Default route has unexpected destination address");
253 }
254
255 struct Message : RouteMessage
256 {
257 utils::IntegerAttribute metric;
258 } __attribute__((packed));
259
260 GNS_LOG_INFO(
261 "SendMessage Route (default onlink), operation ({}), netLinkflags ({})",
262 RouteOperationToString(operation),
263 NetLinkFormatFlagsToString(flags).c_str());
264
265 SendMessage<Message>(route, operation, flags, [&](Message& message) {
266 GNS_LOG_INFO("Netlink message configuration: RTA_DST ([not set]) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})", route.metric);
267 utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);
268 });
269 }
270
271 template <typename TAddr>
272 void RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int flags)
273 {
274 if (!route.via.has_value())
275 {
276 throw RuntimeErrorWithSourceLocation("Default route is missing its next hop");
277 }
278 if (route.to.has_value())
279 {
280 throw RuntimeErrorWithSourceLocation("Default route has unexpected destination address");
281 }
282
283 struct Message : RouteMessage
284 {
285 utils::AddressAttribute<TAddr> via;
286 utils::IntegerAttribute metric;
287 } __attribute__((packed));
288
289 GNS_LOG_INFO(
290 "SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
291 "[empty]",
292 route.via.value().Addr().c_str(),
293 RouteOperationToString(operation),
294 NetLinkFormatFlagsToString(flags).c_str());
295
296 SendMessage<Message>(route, operation, flags, [&](Message& message) {
297 GNS_LOG_INFO(
298 "Netlink message configuration: RTA_DST ([not set]) RTA_GATEWAY ({}), RTA_PRIORITY ({})",
299 route.via.value().Addr().c_str(),
300 route.metric);
301 utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);
302 utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);
303 });
304 }
305
306 template <typename TAddr>
307 void RoutingTable::ModifyLinkLocalRouteImpl(const Route& route, int operation, int flags)
308 {
309 if (!route.to.has_value())
310 {
311 throw RuntimeErrorWithSourceLocation("Link-local route is missing its destination address");
312 }
313 if (route.via.has_value())
314 {
315 throw RuntimeErrorWithSourceLocation("Link-local route has unexpected next hop");
316 }
317
318 struct Message : RouteMessage
319 {
320 utils::AddressAttribute<TAddr> to;
321 utils::IntegerAttribute metric;
322 } __attribute__((packed));
323
324 GNS_LOG_INFO(
325 "SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
326 route.to.value().Addr().c_str(),
327 "[empty]",
328 RouteOperationToString(operation),
329 NetLinkFormatFlagsToString(flags).c_str());
330
331 SendMessage<Message>(route, operation, flags, [&](Message& message) {
332 GNS_LOG_INFO(
333 "Netlink message configuration: RTA_DST ({}) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})",
334 route.to.value().Addr().c_str(),
335 route.metric);
336 utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);
337 utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);
338 });
339 }
340
341 template <typename TAddr>
342 void RoutingTable::ModifyOfflinkRouteImpl(const Route& route, int operation, int flags)
343 {
344 if (!route.via.has_value())
345 {
346 throw RuntimeErrorWithSourceLocation("Offlink route is missing its next hop");
347 }
348 if (!route.to.has_value())
349 {
350 throw RuntimeErrorWithSourceLocation("Offlink route is missing its destination address");
351 }
352
353 struct Message : RouteMessage
354 {
355 utils::AddressAttribute<TAddr> to;
356 utils::AddressAttribute<TAddr> via;
357 utils::IntegerAttribute metric;
358 } __attribute__((packed));
359
360 GNS_LOG_INFO(
361 "SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
362 route.to.value().Addr().c_str(),
363 route.via.value().Addr().c_str(),
364 RouteOperationToString(operation),
365 NetLinkFormatFlagsToString(flags).c_str());
366
367 SendMessage<Message>(route, operation, flags, [&](Message& message) {
368 GNS_LOG_INFO(
369 "Netlink message configuration: RTA_DST ({}) RTA_GATEWAY ({}), RTA_PRIORITY ({})",
370 route.to.value().Addr().c_str(),
371 route.via.value().Addr().c_str(),
372 route.metric);
373 utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);
374 utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);
375 utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);
376 });
377 }
378
379 // Delete all routes from the specified address family
380 void RoutingTable::RemoveAll(int addressFamily)
381 {
382 for (const auto& route : ListRoutes(addressFamily))
383 {
384 ModifyRoute(route, Remove);
385 }
386 }