blob: f7604e016c2145570dad5e440d398ada51290281 [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 Evans066dc2c2020-12-10 10:43:55 +090079int Proxy::OnInit() {
80 LOG(INFO) << "Starting DNS proxy " << opts_;
81
82 /// Run after Daemon::OnInit()
83 base::ThreadTaskRunnerHandle::Get()->PostTask(
84 FROM_HERE, base::Bind(&Proxy::Setup, weak_factory_.GetWeakPtr()));
85 return DBusDaemon::OnInit();
86}
87
88void Proxy::OnShutdown(int*) {
89 LOG(INFO) << "Stopping DNS proxy " << opts_;
Garrick Evans34650b32021-02-03 09:24:35 +090090 if (opts_.type == Type::kSystem)
91 SetShillProperty("");
Garrick Evans066dc2c2020-12-10 10:43:55 +090092}
93
94void Proxy::Setup() {
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090095 // This is only to account for the injected client for testing.
Garrick Evans5fe2a4f2021-02-03 17:04:48 +090096 if (!patchpanel_)
97 patchpanel_ = patchpanel::Client::New();
98
Garrick Evans066dc2c2020-12-10 10:43:55 +090099 CHECK(patchpanel_) << "Failed to initialize patchpanel client";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900100
101 // This is only to account for the injected client for testing.
102 if (!shill_)
103 shill_.reset(new shill::Client(bus_));
104
Garrick Evans066dc2c2020-12-10 10:43:55 +0900105 patchpanel_->RegisterOnAvailableCallback(base::BindRepeating(
106 &Proxy::OnPatchpanelReady, weak_factory_.GetWeakPtr()));
Garrick Evans4f5428c2021-02-15 11:23:54 +0900107 patchpanel_->RegisterProcessChangedCallback(base::BindRepeating(
108 &Proxy::OnPatchpanelReset, weak_factory_.GetWeakPtr()));
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900109
110 shill_->RegisterOnAvailableCallback(
111 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900112}
113
114void Proxy::OnPatchpanelReady(bool success) {
115 CHECK(success) << "Failed to connect to patchpanel";
116
117 // The default network proxy might actually be carrying Chrome, Crostini or
118 // if a VPN is on, even ARC traffic, but we attribute this as as "user"
119 // sourced.
120 patchpanel::TrafficCounter::Source traffic_source;
121 switch (opts_.type) {
122 case Type::kSystem:
123 traffic_source = patchpanel::TrafficCounter::SYSTEM;
124 break;
125 case Type::kARC:
126 traffic_source = patchpanel::TrafficCounter::ARC;
127 break;
128 default:
129 traffic_source = patchpanel::TrafficCounter::USER;
130 }
131
132 // Note that using getpid() here requires that this minijail is not creating a
133 // new PID namespace.
134 // The default proxy (only) needs to use the VPN, if applicable, the others
135 // expressly need to avoid it.
136 auto res = patchpanel_->ConnectNamespace(
137 getpid(), opts_.ifname, true /* forward_user_traffic */,
138 opts_.type == Type::kDefault /* route_on_vpn */, traffic_source);
139 CHECK(res.first.is_valid())
140 << "Failed to establish private network namespace";
141 ns_fd_ = std::move(res.first);
Garrick Evans9c7afb82021-01-29 22:38:03 +0900142 ns_ = res.second;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900143 LOG(INFO) << "Sucessfully connected private network namespace:"
Garrick Evans9c7afb82021-01-29 22:38:03 +0900144 << ns_.host_ifname() << " <--> " << ns_.peer_ifname();
Garrick Evans48c84ef2021-01-28 11:29:42 +0900145
Garrick Evans34650b32021-02-03 09:24:35 +0900146 // Now it's safe to register these handlers and respond to them.
147 shill_->RegisterDefaultDeviceChangedHandler(base::BindRepeating(
148 &Proxy::OnDefaultDeviceChanged, weak_factory_.GetWeakPtr()));
149 shill_->RegisterDeviceChangedHandler(
150 base::BindRepeating(&Proxy::OnDeviceChanged, weak_factory_.GetWeakPtr()));
151
Garrick Evans48c84ef2021-01-28 11:29:42 +0900152 if (opts_.type == Type::kSystem)
Garrick Evans34650b32021-02-03 09:24:35 +0900153 shill_->RegisterProcessChangedHandler(
154 base::BindRepeating(&Proxy::OnShillReset, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900155}
156
Garrick Evans4f5428c2021-02-15 11:23:54 +0900157void Proxy::OnPatchpanelReset(bool reset) {
158 // If patchpanel crashes, the proxy is useless since the connected virtual
159 // network is gone. So the best bet is to exit and have the controller restart
160 // us. Note if this is the system proxy, it will inform shill on shutdown.
161 LOG(ERROR) << "Patchpanel has been shutdown - restarting DNS proxy " << opts_;
162 QuitWithExitCode(EX_UNAVAILABLE);
163
164 LOG(WARNING) << "Patchpanel has been reset";
165}
166
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900167void Proxy::OnShillReady(bool success) {
168 CHECK(success) << "Failed to connect to shill";
169 shill_->Init();
170}
171
Garrick Evans9c7afb82021-01-29 22:38:03 +0900172void Proxy::OnShillReset(bool reset) {
173 if (!reset) {
174 LOG(WARNING) << "Shill has been shutdown";
Garrick Evansfe99aaa2021-02-12 14:32:50 +0900175 // Watch for it to return.
176 shill_->RegisterOnAvailableCallback(
177 base::BindOnce(&Proxy::OnShillReady, weak_factory_.GetWeakPtr()));
Garrick Evans9c7afb82021-01-29 22:38:03 +0900178 return;
179 }
180
Garrick Evans34650b32021-02-03 09:24:35 +0900181 // Really this means shill crashed. To be safe, explicitly reset the proxy
182 // address. We don't want to crash on failure here because shill might still
183 // have this address and try to use it. This probably redundant though with us
184 // rediscovering the default device.
185 // TODO(garrick): Remove this if so.
Garrick Evans9c7afb82021-01-29 22:38:03 +0900186 LOG(WARNING) << "Shill has been reset";
Garrick Evansaaf9d412021-02-15 11:25:21 +0900187 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900188}
189
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900190std::unique_ptr<Resolver> Proxy::NewResolver(base::TimeDelta timeout,
191 base::TimeDelta retry_delay,
192 int max_num_retries) {
193 return std::make_unique<Resolver>(timeout, retry_delay, max_num_retries);
Garrick Evans2ca050d2021-02-09 18:21:36 +0900194}
195
Garrick Evans34650b32021-02-03 09:24:35 +0900196void Proxy::OnDefaultDeviceChanged(const shill::Client::Device* const device) {
197 // ARC proxies will handle changes to their network in OnDeviceChanged.
198 if (opts_.type == Proxy::Type::kARC)
199 return;
Garrick Evans066dc2c2020-12-10 10:43:55 +0900200
Garrick Evans34650b32021-02-03 09:24:35 +0900201 // Default service is either not ready yet or has just disconnected.
202 if (!device) {
203 // If it disconnected, shutdown the resolver.
204 if (device_) {
205 LOG(WARNING) << opts_
206 << " is stopping because there is no default service";
207 resolver_.reset();
208 device_.reset();
209 }
210 return;
211 }
212
Garrick Evansadde9852021-02-15 20:16:53 +0900213 shill::Client::Device new_default_device = *device;
214
Garrick Evans34650b32021-02-03 09:24:35 +0900215 // The system proxy should ignore when a VPN is turned on as it must continue
216 // to work with the underlying physical interface.
Garrick Evans34650b32021-02-03 09:24:35 +0900217 if (opts_.type == Proxy::Type::kSystem &&
Garrick Evansadde9852021-02-15 20:16:53 +0900218 device->type == shill::Client::Device::Type::kVPN) {
219 if (device_)
220 return;
221
222 // No device means that the system proxy has started up with a VPN as the
223 // default network; which means we need to dig out the physical network
224 // device and use that from here forward.
225 auto dd = shill_->DefaultDevice(true /* exclude_vpn */);
226 if (!dd) {
227 LOG(ERROR) << "No default non-VPN device found";
228 return;
229 }
230 new_default_device = *dd.get();
231 }
Garrick Evans34650b32021-02-03 09:24:35 +0900232
233 // While this is enforced in shill as well, only enable resolution if the
234 // service online.
Garrick Evansadde9852021-02-15 20:16:53 +0900235 if (new_default_device.state !=
236 shill::Client::Device::ConnectionState::kOnline) {
Garrick Evans34650b32021-02-03 09:24:35 +0900237 if (device_) {
238 LOG(WARNING) << opts_ << " is stopping because the default device ["
Garrick Evansadde9852021-02-15 20:16:53 +0900239 << new_default_device.ifname << "] is offline";
Garrick Evans34650b32021-02-03 09:24:35 +0900240 resolver_.reset();
241 device_.reset();
242 }
243 return;
244 }
245
246 if (!device_)
247 device_ = std::make_unique<shill::Client::Device>();
248
249 // The default network has changed.
Garrick Evansadde9852021-02-15 20:16:53 +0900250 if (new_default_device.ifname != device_->ifname)
251 LOG(INFO) << opts_ << " is now tracking [" << new_default_device.ifname
252 << "]";
Garrick Evans34650b32021-02-03 09:24:35 +0900253
Garrick Evansadde9852021-02-15 20:16:53 +0900254 *device_.get() = new_default_device;
Garrick Evans34650b32021-02-03 09:24:35 +0900255
256 if (!resolver_) {
Jason Jeremy Iman845f2932021-01-31 16:12:13 +0900257 resolver_ =
258 NewResolver(kRequestTimeout, kRequestRetryDelay, kRequestMaxRetry);
Garrick Evans34650b32021-02-03 09:24:35 +0900259
260 struct sockaddr_in addr = {0};
261 addr.sin_family = AF_INET;
262 addr.sin_port = kDefaultPort;
263 addr.sin_addr.s_addr =
264 INADDR_ANY; // Since we're running in the private namespace.
Garrick Evans2ca050d2021-02-09 18:21:36 +0900265
Jason Jeremy Iman6fd98552021-01-27 04:19:07 +0900266 CHECK(resolver_->ListenUDP(reinterpret_cast<struct sockaddr*>(&addr)))
267 << opts_ << " failed to start UDP relay loop";
268 CHECK(resolver_->ListenTCP(reinterpret_cast<struct sockaddr*>(&addr)))
269 << opts_ << " failed to start TCP relay loop";
Garrick Evans34650b32021-02-03 09:24:35 +0900270 }
271
272 // Update the resolver with the latest DNS config.
273 auto name_servers = device_->ipconfig.ipv4_dns_addresses;
Garrick Evans304a5f42021-02-15 20:34:55 +0900274 // Shill sometimes adds 0.0.0.0 for some reason - so strip any if so.
275 name_servers.erase(
276 std::remove_if(name_servers.begin(), name_servers.end(),
277 [](const std::string& s) { return s == kIfAddrAny; }),
278 name_servers.end());
Garrick Evans34650b32021-02-03 09:24:35 +0900279 name_servers.insert(name_servers.end(),
280 device_->ipconfig.ipv6_dns_addresses.begin(),
281 device_->ipconfig.ipv6_dns_addresses.end());
282 resolver_->SetNameServers(name_servers);
283 LOG(INFO) << opts_ << " applied device DNS configuration";
284
285 // For the system proxy, we have to tell shill about it. We should start
286 // receiving DNS traffic on success. But if this fails, we don't have much
287 // choice but to just crash out and try again.
288 if (opts_.type == Type::kSystem)
Garrick Evansaaf9d412021-02-15 11:25:21 +0900289 SetShillProperty(patchpanel::IPv4AddressToString(ns_.peer_ipv4_address()),
Garrick Evans34650b32021-02-03 09:24:35 +0900290 true /* die_on_failure */);
291}
292
293void Proxy::OnDeviceChanged(const shill::Client::Device* const device) {
294 // TODO(garrick): ARC and default for VPN cases.
295}
Garrick Evans066dc2c2020-12-10 10:43:55 +0900296
Garrick Evans9c7afb82021-01-29 22:38:03 +0900297void Proxy::SetShillProperty(const std::string& addr,
298 bool die_on_failure,
299 uint8_t num_retries) {
300 if (opts_.type != Type::kSystem) {
301 LOG(DFATAL) << "Must be called from system proxy only";
302 return;
303 }
Garrick Evans48c84ef2021-01-28 11:29:42 +0900304
Garrick Evans9c7afb82021-01-29 22:38:03 +0900305 if (num_retries == 0) {
306 LOG(ERROR) << "Maximum number of retries exceeding attempt to"
307 << " set dns-proxy address property on shill";
308 CHECK(!die_on_failure);
309 return;
310 }
311
312 // This can only happen if called from OnShutdown and Setup had somehow failed
313 // to create the client... it's unlikely but regardless, that shill client
314 // isn't coming back so there's no point in retrying anything.
Garrick Evans48c84ef2021-01-28 11:29:42 +0900315 if (!shill_) {
Garrick Evans9c7afb82021-01-29 22:38:03 +0900316 LOG(ERROR)
317 << "No connection to shill - cannot set dns-proxy address property ["
318 << addr << "].";
319 return;
Garrick Evans48c84ef2021-01-28 11:29:42 +0900320 }
321
322 brillo::ErrorPtr error;
Garrick Evans9c7afb82021-01-29 22:38:03 +0900323 if (shill_->ManagerProperties()->Set(shill::kDNSProxyIPv4AddressProperty,
324 addr, &error))
325 return;
326
327 LOG(ERROR) << "Failed to set dns-proxy address property [" << addr
328 << "] on shill: " << error->GetMessage() << ". Retrying...";
329
330 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
331 FROM_HERE,
332 base::Bind(&Proxy::SetShillProperty, weak_factory_.GetWeakPtr(), addr,
333 die_on_failure, num_retries - 1),
334 kShillPropertyAttemptDelay);
Garrick Evans48c84ef2021-01-28 11:29:42 +0900335}
336
Garrick Evans066dc2c2020-12-10 10:43:55 +0900337} // namespace dns_proxy