blob: 8d948ce44b5bab6c076e7e231776bd70a2a7f802 [file] [log] [blame]
Garrick Evans066dc2c2020-12-10 10:43:55 +09001// Copyright 2021 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "dns-proxy/proxy.h"
6
7#include <sys/types.h>
Garrick Evans4f5428c2021-02-15 11:23:54 +09008#include <sysexits.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +09009#include <unistd.h>
10
11#include <utility>
12
13#include <base/bind.h>
14#include <base/threading/thread_task_runner_handle.h>
Garrick Evans9c7afb82021-01-29 22:38:03 +090015#include <base/time/time.h>
Garrick Evans48c84ef2021-01-28 11:29:42 +090016#include <chromeos/patchpanel/net_util.h>
17#include <shill/dbus-constants.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +090018
19namespace dns_proxy {
20
Garrick Evans9c7afb82021-01-29 22:38:03 +090021constexpr base::TimeDelta kShillPropertyAttemptDelay =
22 base::TimeDelta::FromMilliseconds(200);
Jason Jeremy Iman1bb71c22021-01-26 21:49:55 +090023constexpr base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10000);
Jason Jeremy Iman845f2932021-01-31 16:12:13 +090024constexpr base::TimeDelta kRequestRetryDelay =
25 base::TimeDelta::FromMilliseconds(200);
Garrick Evans9c7afb82021-01-29 22:38:03 +090026
Garrick Evans066dc2c2020-12-10 10:43:55 +090027constexpr char kSystemProxyType[] = "sys";
28constexpr char kDefaultProxyType[] = "def";
29constexpr char kARCProxyType[] = "arc";
Jason Jeremy Iman845f2932021-01-31 16:12:13 +090030constexpr int32_t kRequestMaxRetry = 1;
Garrick Evans34650b32021-02-03 09:24:35 +090031constexpr uint16_t kDefaultPort = 13568; // port 53 in network order.
Garrick Evans304a5f42021-02-15 20:34:55 +090032constexpr char kIfAddrAny[] = "0.0.0.0";
33
Garrick Evans066dc2c2020-12-10 10:43:55 +090034// static
35const char* Proxy::TypeToString(Type t) {
36 switch (t) {
37 case Type::kSystem:
38 return kSystemProxyType;
39 case Type::kDefault:
40 return kDefaultProxyType;
41 case Type::kARC:
42 return kARCProxyType;
43 }
44}
45
46// static
47std::optional<Proxy::Type> Proxy::StringToType(const std::string& s) {
48 if (s == kSystemProxyType)
49 return Type::kSystem;
50
51 if (s == kDefaultProxyType)
52 return Type::kDefault;
53
54 if (s == kARCProxyType)
55 return Type::kARC;
56
57 return std::nullopt;
58}
59
60std::ostream& operator<<(std::ostream& stream, Proxy::Type type) {
61 stream << Proxy::TypeToString(type);
62 return stream;
63}
64
65std::ostream& operator<<(std::ostream& stream, Proxy::Options opt) {
66 stream << "{" << Proxy::TypeToString(opt.type) << ":" << opt.ifname << "}";
67 return stream;
68}
69
70Proxy::Proxy(const Proxy::Options& opts) : opts_(opts) {}
71
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090072Proxy::Proxy(const Options& opts,
73 std::unique_ptr<patchpanel::Client> patchpanel,
74 std::unique_ptr<shill::Client> shill)
75 : opts_(opts),
76 patchpanel_(std::move(patchpanel)),
77 shill_(std::move(shill)) {}
78
Garrick Evans9c8797d2021-02-17 21:28:10 +090079Proxy::~Proxy() {
80 if (bus_)
81 bus_->ShutdownAndBlock();
82}
83
Garrick Evans066dc2c2020-12-10 10:43:55 +090084int Proxy::OnInit() {
85 LOG(INFO) << "Starting DNS proxy " << opts_;
86
87 /// Run after Daemon::OnInit()
88 base::ThreadTaskRunnerHandle::Get()->PostTask(
89 FROM_HERE, base::Bind(&Proxy::Setup, weak_factory_.GetWeakPtr()));
90 return DBusDaemon::OnInit();
91}
92
93void Proxy::OnShutdown(int*) {
94 LOG(INFO) << "Stopping DNS proxy " << opts_;
Garrick Evans34650b32021-02-03 09:24:35 +090095 if (opts_.type == Type::kSystem)
96 SetShillProperty("");
Garrick Evans066dc2c2020-12-10 10:43:55 +090097}
98
99void Proxy::Setup() {
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900100 // This is only to account for the injected client for testing.
Garrick Evans5fe2a4f2021-02-03 17:04:48 +0900101 if (!patchpanel_)
102 patchpanel_ = patchpanel::Client::New();
103
Garrick Evans066dc2c2020-12-10 10:43:55 +0900104 CHECK(patchpanel_) << "Failed to initialize patchpanel client";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900105
106 // This is only to account for the injected client for testing.
107 if (!shill_)
108 shill_.reset(new shill::Client(bus_));
109
Garrick Evans066dc2c2020-12-10 10:43:55 +0900110 patchpanel_->RegisterOnAvailableCallback(base::BindRepeating(
111 &Proxy::OnPatchpanelReady, weak_factory_.GetWeakPtr()));
Garrick Evans4f5428c2021-02-15 11:23:54 +0900112 patchpanel_->RegisterProcessChangedCallback(base::BindRepeating(
113 &Proxy::OnPatchpanelReset, weak_factory_.GetWeakPtr()));
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900114
115 shill_->RegisterOnAvailableCallback(
116 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900117}
118
119void Proxy::OnPatchpanelReady(bool success) {
120 CHECK(success) << "Failed to connect to patchpanel";
121
122 // The default network proxy might actually be carrying Chrome, Crostini or
123 // if a VPN is on, even ARC traffic, but we attribute this as as "user"
124 // sourced.
125 patchpanel::TrafficCounter::Source traffic_source;
126 switch (opts_.type) {
127 case Type::kSystem:
128 traffic_source = patchpanel::TrafficCounter::SYSTEM;
129 break;
130 case Type::kARC:
131 traffic_source = patchpanel::TrafficCounter::ARC;
132 break;
133 default:
134 traffic_source = patchpanel::TrafficCounter::USER;
135 }
136
137 // Note that using getpid() here requires that this minijail is not creating a
138 // new PID namespace.
139 // The default proxy (only) needs to use the VPN, if applicable, the others
140 // expressly need to avoid it.
141 auto res = patchpanel_->ConnectNamespace(
142 getpid(), opts_.ifname, true /* forward_user_traffic */,
143 opts_.type == Type::kDefault /* route_on_vpn */, traffic_source);
144 CHECK(res.first.is_valid())
145 << "Failed to establish private network namespace";
146 ns_fd_ = std::move(res.first);
Garrick Evans9c7afb82021-01-29 22:38:03 +0900147 ns_ = res.second;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900148 LOG(INFO) << "Sucessfully connected private network namespace:"
Garrick Evans9c7afb82021-01-29 22:38:03 +0900149 << ns_.host_ifname() << " <--> " << ns_.peer_ifname();
Garrick Evans48c84ef2021-01-28 11:29:42 +0900150
Garrick Evans34650b32021-02-03 09:24:35 +0900151 // Now it's safe to register these handlers and respond to them.
152 shill_->RegisterDefaultDeviceChangedHandler(base::BindRepeating(
153 &Proxy::OnDefaultDeviceChanged, weak_factory_.GetWeakPtr()));
154 shill_->RegisterDeviceChangedHandler(
155 base::BindRepeating(&Proxy::OnDeviceChanged, weak_factory_.GetWeakPtr()));
156
Garrick Evans48c84ef2021-01-28 11:29:42 +0900157 if (opts_.type == Type::kSystem)
Garrick Evans34650b32021-02-03 09:24:35 +0900158 shill_->RegisterProcessChangedHandler(
159 base::BindRepeating(&Proxy::OnShillReset, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900160}
161
Garrick Evans4f5428c2021-02-15 11:23:54 +0900162void Proxy::OnPatchpanelReset(bool reset) {
163 // If patchpanel crashes, the proxy is useless since the connected virtual
164 // network is gone. So the best bet is to exit and have the controller restart
165 // us. Note if this is the system proxy, it will inform shill on shutdown.
166 LOG(ERROR) << "Patchpanel has been shutdown - restarting DNS proxy " << opts_;
167 QuitWithExitCode(EX_UNAVAILABLE);
168
169 LOG(WARNING) << "Patchpanel has been reset";
170}
171
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900172void Proxy::OnShillReady(bool success) {
173 CHECK(success) << "Failed to connect to shill";
174 shill_->Init();
175}
176
Garrick Evans9c7afb82021-01-29 22:38:03 +0900177void Proxy::OnShillReset(bool reset) {
178 if (!reset) {
179 LOG(WARNING) << "Shill has been shutdown";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900180 // Watch for it to return.
181 shill_->RegisterOnAvailableCallback(
182 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900183 return;
184 }
185
Garrick Evans34650b32021-02-03 09:24:35 +0900186 // Really this means shill crashed. To be safe, explicitly reset the proxy
187 // address. We don't want to crash on failure here because shill might still
188 // have this address and try to use it. This probably redundant though with us
189 // rediscovering the default device.
190 // TODO(garrick): Remove this if so.
Garrick Evans9c7afb82021-01-29 22:38:03 +0900191 LOG(WARNING) << "Shill has been reset";
Garrick Evansaaf9d412021-02-15 11:25:21 +0900192 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900193}
194
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900195std::unique_ptr<Resolver> Proxy::NewResolver(base::TimeDelta timeout,
196 base::TimeDelta retry_delay,
197 int max_num_retries) {
198 return std::make_unique<Resolver>(timeout, retry_delay, max_num_retries);
Garrick Evans2ca050d2021-02-09 18:21:36 +0900199}
200
Garrick Evans34650b32021-02-03 09:24:35 +0900201void Proxy::OnDefaultDeviceChanged(const shill::Client::Device* const device) {
202 // ARC proxies will handle changes to their network in OnDeviceChanged.
203 if (opts_.type == Proxy::Type::kARC)
204 return;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900205
Garrick Evans34650b32021-02-03 09:24:35 +0900206 // Default service is either not ready yet or has just disconnected.
207 if (!device) {
208 // If it disconnected, shutdown the resolver.
209 if (device_) {
210 LOG(WARNING) << opts_
211 << " is stopping because there is no default service";
212 resolver_.reset();
213 device_.reset();
214 }
215 return;
216 }
217
Garrick Evansadde9852021-02-15 20:16:53 +0900218 shill::Client::Device new_default_device = *device;
219
Garrick Evans34650b32021-02-03 09:24:35 +0900220 // The system proxy should ignore when a VPN is turned on as it must continue
221 // to work with the underlying physical interface.
Garrick Evans34650b32021-02-03 09:24:35 +0900222 if (opts_.type == Proxy::Type::kSystem &&
Garrick Evansadde9852021-02-15 20:16:53 +0900223 device->type == shill::Client::Device::Type::kVPN) {
224 if (device_)
225 return;
226
227 // No device means that the system proxy has started up with a VPN as the
228 // default network; which means we need to dig out the physical network
229 // device and use that from here forward.
230 auto dd = shill_->DefaultDevice(true /* exclude_vpn */);
231 if (!dd) {
232 LOG(ERROR) << "No default non-VPN device found";
233 return;
234 }
235 new_default_device = *dd.get();
236 }
Garrick Evans34650b32021-02-03 09:24:35 +0900237
238 // While this is enforced in shill as well, only enable resolution if the
239 // service online.
Garrick Evansadde9852021-02-15 20:16:53 +0900240 if (new_default_device.state !=
241 shill::Client::Device::ConnectionState::kOnline) {
Garrick Evans34650b32021-02-03 09:24:35 +0900242 if (device_) {
243 LOG(WARNING) << opts_ << " is stopping because the default device ["
Garrick Evansadde9852021-02-15 20:16:53 +0900244 << new_default_device.ifname << "] is offline";
Garrick Evans34650b32021-02-03 09:24:35 +0900245 resolver_.reset();
246 device_.reset();
247 }
248 return;
249 }
250
251 if (!device_)
252 device_ = std::make_unique<shill::Client::Device>();
253
254 // The default network has changed.
Garrick Evansadde9852021-02-15 20:16:53 +0900255 if (new_default_device.ifname != device_->ifname)
256 LOG(INFO) << opts_ << " is now tracking [" << new_default_device.ifname
257 << "]";
Garrick Evans34650b32021-02-03 09:24:35 +0900258
Garrick Evansadde9852021-02-15 20:16:53 +0900259 *device_.get() = new_default_device;
Garrick Evans34650b32021-02-03 09:24:35 +0900260
261 if (!resolver_) {
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900262 resolver_ =
263 NewResolver(kRequestTimeout, kRequestRetryDelay, kRequestMaxRetry);
Garrick Evans34650b32021-02-03 09:24:35 +0900264
265 struct sockaddr_in addr = {0};
266 addr.sin_family = AF_INET;
267 addr.sin_port = kDefaultPort;
268 addr.sin_addr.s_addr =
269 INADDR_ANY; // Since we're running in the private namespace.
Garrick Evans2ca050d2021-02-09 18:21:36 +0900270
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900271 CHECK(resolver_->ListenUDP(reinterpret_cast<struct sockaddr*>(&addr)))
272 << opts_ << " failed to start UDP relay loop";
Garrick Evansa6bfc322021-03-02 15:50:54 +0900273 LOG_IF(DFATAL,
274 !resolver_->ListenTCP(reinterpret_cast<struct sockaddr*>(&addr)))
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900275 << opts_ << " failed to start TCP relay loop";
Garrick Evans34650b32021-02-03 09:24:35 +0900276 }
277
278 // Update the resolver with the latest DNS config.
Garrick Evansa8c12be2021-02-17 16:06:45 +0900279 UpdateNameServers(device_->ipconfig);
Garrick Evans34650b32021-02-03 09:24:35 +0900280
281 // For the system proxy, we have to tell shill about it. We should start
282 // receiving DNS traffic on success. But if this fails, we don't have much
283 // choice but to just crash out and try again.
284 if (opts_.type == Type::kSystem)
Garrick Evansaaf9d412021-02-15 11:25:21 +0900285 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()),
Garrick Evans34650b32021-02-03 09:24:35 +0900286 true /* die_on_failure */);
287}
288
289void Proxy::OnDeviceChanged(const shill::Client::Device* const device) {
Garrick Evansa8c12be2021-02-17 16:06:45 +0900290 // Ignore if there is no tracked device or it's different.
291 if (!device || !device_ || device_->ifname != device->ifname)
292 return;
293
294 // We don't need to worry about this here since the default proxy always/only
295 // tracks the default device and any update will be handled by
296 // OnDefaultDeviceChanged.
297 if (opts_.type == Type::kDefault)
298 return;
299
300 if (device_->ipconfig == device->ipconfig)
301 return;
302
303 UpdateNameServers(device->ipconfig);
304 device_->ipconfig = device->ipconfig;
305}
306
307void Proxy::UpdateNameServers(const shill::Client::IPConfig& ipconfig) {
308 auto name_servers = ipconfig.ipv4_dns_addresses;
309 // Shill sometimes adds 0.0.0.0 for some reason - so strip any if so.
310 name_servers.erase(
311 std::remove_if(name_servers.begin(), name_servers.end(),
312 [](const std::string& s) { return s == kIfAddrAny; }),
313 name_servers.end());
314 name_servers.insert(name_servers.end(),
315 device_->ipconfig.ipv6_dns_addresses.begin(),
316 device_->ipconfig.ipv6_dns_addresses.end());
317 resolver_->SetNameServers(name_servers);
318 LOG(INFO) << opts_ << " applied device DNS configuration";
Garrick Evans34650b32021-02-03 09:24:35 +0900319}
Garrick Evans066dc2c2020-12-10 10:43:55 +0900320
Garrick Evans9c7afb82021-01-29 22:38:03 +0900321void Proxy::SetShillProperty(const std::string& addr,
322 bool die_on_failure,
323 uint8_t num_retries) {
324 if (opts_.type != Type::kSystem) {
325 LOG(DFATAL) << "Must be called from system proxy only";
326 return;
327 }
Garrick Evans48c84ef2021-01-28 11:29:42 +0900328
Garrick Evans9c7afb82021-01-29 22:38:03 +0900329 if (num_retries == 0) {
330 LOG(ERROR) << "Maximum number of retries exceeding attempt to"
331 << " set dns-proxy address property on shill";
332 CHECK(!die_on_failure);
333 return;
334 }
335
336 // This can only happen if called from OnShutdown and Setup had somehow failed
337 // to create the client... it's unlikely but regardless, that shill client
338 // isn't coming back so there's no point in retrying anything.
Garrick Evans48c84ef2021-01-28 11:29:42 +0900339 if (!shill_) {
Garrick Evans9c7afb82021-01-29 22:38:03 +0900340 LOG(ERROR)
341 << "No connection to shill - cannot set dns-proxy address property ["
342 << addr << "].";
343 return;
Garrick Evans48c84ef2021-01-28 11:29:42 +0900344 }
345
346 brillo::ErrorPtr error;
Garrick Evans9c7afb82021-01-29 22:38:03 +0900347 if (shill_->ManagerProperties()->Set(shill::kDNSProxyIPv4AddressProperty,
348 addr, &error))
349 return;
350
351 LOG(ERROR) << "Failed to set dns-proxy address property [" << addr
352 << "] on shill: " << error->GetMessage() << ". Retrying...";
353
354 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
355 FROM_HERE,
356 base::Bind(&Proxy::SetShillProperty, weak_factory_.GetWeakPtr(), addr,
357 die_on_failure, num_retries - 1),
358 kShillPropertyAttemptDelay);
Garrick Evans48c84ef2021-01-28 11:29:42 +0900359}
360
Garrick Evans066dc2c2020-12-10 10:43:55 +0900361} // namespace dns_proxy