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; 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 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 97 DHCPConfig::~DHCPConfig() { 98 SLOG(this, 2) << __func__ << ": " << device_name(); 99 100 // Don't leave behind dhcpcd running. 101 Stop(__func__); 102 } 103 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 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 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 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 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 177 void DHCPConfig::NotifyFailure() { 178 StopAcquisitionTimeout(); 179 StopExpirationTimeout(); 180 IPConfig::NotifyFailure(); 181 } 182 183 bool DHCPConfig::IsEphemeralLease() const { 184 return lease_file_suffix_ == device_name(); 185 } 186 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 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 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 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 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 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 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 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 292 void DHCPConfig::StopAcquisitionTimeout() { 293 lease_acquisition_timeout_callback_.Cancel(); 294 } 295 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 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 319 void DHCPConfig::StopExpirationTimeout() { 320 lease_expiration_callback_.Cancel(); 321 } 322 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