• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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