1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/dhcp/dhcp_config.h"
18
19 #include <vector>
20
21 #include <arpa/inet.h>
22 #include <stdlib.h>
23 #include <sys/wait.h>
24
25 #include <base/files/file_util.h>
26 #include <base/strings/string_split.h>
27 #include <base/strings/stringprintf.h>
28 #if defined(__ANDROID__)
29 #include <dbus/service_constants.h>
30 #else
31 #include <chromeos/dbus/service_constants.h>
32 #endif // __ANDROID__
33 #include <brillo/minijail/minijail.h>
34
35 #include "shill/control_interface.h"
36 #include "shill/dhcp/dhcp_provider.h"
37 #include "shill/dhcp/dhcp_proxy_interface.h"
38 #include "shill/event_dispatcher.h"
39 #include "shill/logging.h"
40 #include "shill/metrics.h"
41 #include "shill/net/ip_address.h"
42 #include "shill/process_manager.h"
43
44 using std::string;
45 using std::vector;
46
47 namespace shill {
48
49 namespace Logging {
50 static auto kModuleLogScope = ScopeLogger::kDHCP;
ObjectID(DHCPConfig * d)51 static string ObjectID(DHCPConfig* d) {
52 if (d == nullptr)
53 return "(dhcp_config)";
54 else
55 return d->device_name();
56 }
57 }
58
59 // static
60 const int DHCPConfig::kAcquisitionTimeoutSeconds = 30;
61 const int DHCPConfig::kDHCPCDExitPollMilliseconds = 50;
62 const int DHCPConfig::kDHCPCDExitWaitMilliseconds = 3000;
63 #if defined(__ANDROID__)
64 const char DHCPConfig::kDHCPCDPath[] = "/system/bin/dhcpcd-6.8.2";
65 const char DHCPConfig::kDHCPCDUser[] = "dhcp";
66 const char DHCPConfig::kDHCPCDGroup[] = "dbus";
67 #else
68 const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
69 const char DHCPConfig::kDHCPCDUser[] = "dhcp";
70 const char DHCPConfig::kDHCPCDGroup[] = "dhcp";
71 #endif // __ANDROID__
72
DHCPConfig(ControlInterface * control_interface,EventDispatcher * dispatcher,DHCPProvider * provider,const string & device_name,const string & type,const string & lease_file_suffix)73 DHCPConfig::DHCPConfig(ControlInterface* control_interface,
74 EventDispatcher* dispatcher,
75 DHCPProvider* provider,
76 const string& device_name,
77 const string& type,
78 const string& lease_file_suffix)
79 : IPConfig(control_interface, device_name, type),
80 control_interface_(control_interface),
81 provider_(provider),
82 lease_file_suffix_(lease_file_suffix),
83 pid_(0),
84 is_lease_active_(false),
85 lease_acquisition_timeout_seconds_(kAcquisitionTimeoutSeconds),
86 minimum_mtu_(kMinIPv4MTU),
87 root_("/"),
88 weak_ptr_factory_(this),
89 dispatcher_(dispatcher),
90 process_manager_(ProcessManager::GetInstance()) {
91 SLOG(this, 2) << __func__ << ": " << device_name;
92 if (lease_file_suffix_.empty()) {
93 lease_file_suffix_ = device_name;
94 }
95 }
96
~DHCPConfig()97 DHCPConfig::~DHCPConfig() {
98 SLOG(this, 2) << __func__ << ": " << device_name();
99
100 // Don't leave behind dhcpcd running.
101 Stop(__func__);
102 }
103
RequestIP()104 bool DHCPConfig::RequestIP() {
105 SLOG(this, 2) << __func__ << ": " << device_name();
106 if (!pid_) {
107 return Start();
108 }
109 if (!proxy_.get()) {
110 LOG(ERROR) << "Unable to request IP before acquiring destination.";
111 return Restart();
112 }
113 return RenewIP();
114 }
115
RenewIP()116 bool DHCPConfig::RenewIP() {
117 SLOG(this, 2) << __func__ << ": " << device_name();
118 if (!pid_) {
119 return Start();
120 }
121 if (!proxy_.get()) {
122 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
123 return false;
124 }
125 StopExpirationTimeout();
126 proxy_->Rebind(device_name());
127 StartAcquisitionTimeout();
128 return true;
129 }
130
ReleaseIP(ReleaseReason reason)131 bool DHCPConfig::ReleaseIP(ReleaseReason reason) {
132 SLOG(this, 2) << __func__ << ": " << device_name();
133 if (!pid_) {
134 return true;
135 }
136
137 // If we are using static IP and haven't retrieved a lease yet, we should
138 // allow the DHCP process to continue until we have a lease.
139 if (!is_lease_active_ && reason == IPConfig::kReleaseReasonStaticIP) {
140 return true;
141 }
142
143 // If we are using gateway unicast ARP to speed up re-connect, don't
144 // give up our leases when we disconnect.
145 bool should_keep_lease =
146 reason == IPConfig::kReleaseReasonDisconnect &&
147 ShouldKeepLeaseOnDisconnect();
148
149 if (!should_keep_lease && proxy_.get()) {
150 proxy_->Release(device_name());
151 }
152 Stop(__func__);
153 return true;
154 }
155
InitProxy(const string & service)156 void DHCPConfig::InitProxy(const string& service) {
157 if (!proxy_.get()) {
158 LOG(INFO) << "Init DHCP Proxy: " << device_name() << " at " << service;
159 proxy_.reset(control_interface_->CreateDHCPProxy(service));
160 }
161 }
162
UpdateProperties(const Properties & properties,bool new_lease_acquired)163 void DHCPConfig::UpdateProperties(const Properties& properties,
164 bool new_lease_acquired) {
165 StopAcquisitionTimeout();
166 if (properties.lease_duration_seconds) {
167 UpdateLeaseExpirationTime(properties.lease_duration_seconds);
168 StartExpirationTimeout(properties.lease_duration_seconds);
169 } else {
170 LOG(WARNING) << "Lease duration is zero; not starting an expiration timer.";
171 ResetLeaseExpirationTime();
172 StopExpirationTimeout();
173 }
174 IPConfig::UpdateProperties(properties, new_lease_acquired);
175 }
176
NotifyFailure()177 void DHCPConfig::NotifyFailure() {
178 StopAcquisitionTimeout();
179 StopExpirationTimeout();
180 IPConfig::NotifyFailure();
181 }
182
IsEphemeralLease() const183 bool DHCPConfig::IsEphemeralLease() const {
184 return lease_file_suffix_ == device_name();
185 }
186
Start()187 bool DHCPConfig::Start() {
188 SLOG(this, 2) << __func__ << ": " << device_name();
189
190 // Setup program arguments.
191 vector<string> args = GetFlags();
192 string interface_arg(device_name());
193 if (lease_file_suffix_ != device_name()) {
194 interface_arg = base::StringPrintf("%s=%s", device_name().c_str(),
195 lease_file_suffix_.c_str());
196 }
197 args.push_back(interface_arg);
198
199 uint64_t capmask = CAP_TO_MASK(CAP_NET_BIND_SERVICE) |
200 CAP_TO_MASK(CAP_NET_BROADCAST) |
201 CAP_TO_MASK(CAP_NET_ADMIN) |
202 CAP_TO_MASK(CAP_NET_RAW);
203 pid_t pid = process_manager_->StartProcessInMinijail(
204 FROM_HERE,
205 base::FilePath(kDHCPCDPath),
206 args,
207 kDHCPCDUser,
208 kDHCPCDGroup,
209 capmask,
210 base::Bind(&DHCPConfig::OnProcessExited,
211 weak_ptr_factory_.GetWeakPtr()));
212 if (pid < 0) {
213 return false;
214 }
215 pid_ = pid;
216 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
217 provider_->BindPID(pid_, this);
218 StartAcquisitionTimeout();
219 return true;
220 }
221
Stop(const char * reason)222 void DHCPConfig::Stop(const char* reason) {
223 LOG_IF(INFO, pid_) << "Stopping " << pid_ << " (" << reason << ")";
224 KillClient();
225 // KillClient waits for the client to terminate so it's safe to cleanup the
226 // state.
227 CleanupClientState();
228 }
229
KillClient()230 void DHCPConfig::KillClient() {
231 if (!pid_) {
232 return;
233 }
234
235 // Pass the termination responsibility to ProcessManager.
236 // ProcessManager will try to terminate the process using SIGTERM, then
237 // SIGKill signals. It will log an error message if it is not able to
238 // terminate the process in a timely manner.
239 process_manager_->StopProcessAndBlock(pid_);
240 }
241
Restart()242 bool DHCPConfig::Restart() {
243 // Take a reference of this instance to make sure we don't get destroyed in
244 // the middle of this call.
245 DHCPConfigRefPtr me = this;
246 me->Stop(__func__);
247 return me->Start();
248 }
249
OnProcessExited(int exit_status)250 void DHCPConfig::OnProcessExited(int exit_status) {
251 CHECK(pid_);
252 if (exit_status == EXIT_SUCCESS) {
253 SLOG(nullptr, 2) << "pid " << pid_ << " exit status " << exit_status;
254 } else {
255 LOG(WARNING) << "pid " << pid_ << " exit status " << exit_status;
256 }
257 CleanupClientState();
258 }
259
CleanupClientState()260 void DHCPConfig::CleanupClientState() {
261 SLOG(this, 2) << __func__ << ": " << device_name();
262 StopAcquisitionTimeout();
263 StopExpirationTimeout();
264
265 proxy_.reset();
266 if (pid_) {
267 int pid = pid_;
268 pid_ = 0;
269 // |this| instance may be destroyed after this call.
270 provider_->UnbindPID(pid);
271 }
272 is_lease_active_ = false;
273 }
274
GetFlags()275 vector<string> DHCPConfig::GetFlags() {
276 vector<string> flags;
277 flags.push_back("-B"); // Run in foreground.
278 flags.push_back("-q"); // Only warnings+errors to stderr.
279 return flags;
280 }
281
StartAcquisitionTimeout()282 void DHCPConfig::StartAcquisitionTimeout() {
283 CHECK(lease_expiration_callback_.IsCancelled());
284 lease_acquisition_timeout_callback_.Reset(
285 Bind(&DHCPConfig::ProcessAcquisitionTimeout,
286 weak_ptr_factory_.GetWeakPtr()));
287 dispatcher_->PostDelayedTask(
288 lease_acquisition_timeout_callback_.callback(),
289 lease_acquisition_timeout_seconds_ * 1000);
290 }
291
StopAcquisitionTimeout()292 void DHCPConfig::StopAcquisitionTimeout() {
293 lease_acquisition_timeout_callback_.Cancel();
294 }
295
ProcessAcquisitionTimeout()296 void DHCPConfig::ProcessAcquisitionTimeout() {
297 LOG(ERROR) << "Timed out waiting for DHCP lease on " << device_name() << " "
298 << "(after " << lease_acquisition_timeout_seconds_ << " seconds).";
299 if (!ShouldFailOnAcquisitionTimeout()) {
300 LOG(INFO) << "Continuing to use our previous lease, due to gateway-ARP.";
301 } else {
302 NotifyFailure();
303 }
304 }
305
StartExpirationTimeout(uint32_t lease_duration_seconds)306 void DHCPConfig::StartExpirationTimeout(uint32_t lease_duration_seconds) {
307 CHECK(lease_acquisition_timeout_callback_.IsCancelled());
308 SLOG(this, 2) << __func__ << ": " << device_name()
309 << ": " << "Lease timeout is " << lease_duration_seconds
310 << " seconds.";
311 lease_expiration_callback_.Reset(
312 Bind(&DHCPConfig::ProcessExpirationTimeout,
313 weak_ptr_factory_.GetWeakPtr()));
314 dispatcher_->PostDelayedTask(
315 lease_expiration_callback_.callback(),
316 lease_duration_seconds * 1000);
317 }
318
StopExpirationTimeout()319 void DHCPConfig::StopExpirationTimeout() {
320 lease_expiration_callback_.Cancel();
321 }
322
ProcessExpirationTimeout()323 void DHCPConfig::ProcessExpirationTimeout() {
324 LOG(ERROR) << "DHCP lease expired on " << device_name()
325 << "; restarting DHCP client instance.";
326 NotifyExpiry();
327 if (!Restart()) {
328 NotifyFailure();
329 }
330 }
331
332 } // namespace shill
333