blob: d5804787fe61028247531b0d4098cab629e0ff87 [file] [log] [blame]
Thieu Le94eed562012-02-21 15:57:29 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Darin Petkov50308cd2011-06-01 18:25:07 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/dhcp_config.h"
6
Alex Vakulenko8a532292014-06-16 17:18:44 -07007#include <vector>
8
Darin Petkove7cb7f82011-06-03 13:21:51 -07009#include <arpa/inet.h>
mukesh agrawal6c6655d2012-12-06 14:49:50 -080010#include <stdlib.h>
Thieu Le94eed562012-02-21 15:57:29 -080011#include <sys/wait.h>
Darin Petkove7cb7f82011-06-03 13:21:51 -070012
Ben Chan11c213f2014-09-05 08:21:06 -070013#include <base/files/file_util.h>
Ben Chana0ddf462014-02-06 11:32:42 -080014#include <base/strings/string_split.h>
15#include <base/strings/stringprintf.h>
Chris Masone43b48a12011-07-01 13:37:07 -070016#include <chromeos/dbus/service_constants.h>
Utkarsh Sanghi83bd64b2014-07-29 16:01:43 -070017#include <chromeos/minijail/minijail.h>
Darin Petkovd1b715b2011-06-02 21:21:22 -070018
Darin Petkovd1b715b2011-06-02 21:21:22 -070019#include "shill/dhcp_provider.h"
Alex Vakulenkoa41ab512014-07-23 14:24:23 -070020#include "shill/dhcpcd_proxy.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070021#include "shill/event_dispatcher.h"
Darin Petkov3258a812011-06-23 11:28:45 -070022#include "shill/glib.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070023#include "shill/logging.h"
Paul Stewart3bdf1ab2014-07-17 19:22:26 -070024#include "shill/metrics.h"
Peter Qiu8d6b5972014-10-28 15:33:34 -070025#include "shill/net/ip_address.h"
Darin Petkovaceede32011-07-18 15:32:38 -070026#include "shill/proxy_factory.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070027
Darin Petkove7cb7f82011-06-03 13:21:51 -070028using std::string;
29using std::vector;
30
Darin Petkov50308cd2011-06-01 18:25:07 -070031namespace shill {
32
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070033namespace Logging {
34static auto kModuleLogScope = ScopeLogger::kDHCP;
35static string ObjectID(DHCPConfig *d) {
36 if (d == nullptr)
37 return "(dhcp_config)";
38 else
39 return d->device_name();
40}
41}
42
Chris Masone0756f232011-07-21 17:24:00 -070043// static
Paul Stewart1f916e42013-12-23 09:52:54 -080044const int DHCPConfig::kAcquisitionTimeoutSeconds = 30;
Darin Petkove7cb7f82011-06-03 13:21:51 -070045const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
Paul Stewart65bcd082012-11-16 09:37:14 -080046const char DHCPConfig::kConfigurationKeyClasslessStaticRoutes[] =
47 "ClasslessStaticRoutes";
Darin Petkove7cb7f82011-06-03 13:21:51 -070048const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
49const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
50const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
51const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
Paul Stewart1f916e42013-12-23 09:52:54 -080052const char DHCPConfig::kConfigurationKeyLeaseTime[] = "DHCPLeaseTime";
Darin Petkove7cb7f82011-06-03 13:21:51 -070053const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
54const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
55const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Paul Stewartc3fdba92013-12-02 11:12:38 -080056const char DHCPConfig::kConfigurationKeyVendorEncapsulatedOptions[] =
57 "VendorEncapsulatedOptions";
Paul Stewarta63f5212013-06-25 15:29:40 -070058const char DHCPConfig::kConfigurationKeyWebProxyAutoDiscoveryUrl[] =
59 "WebProxyAutoDiscoveryUrl";
Thieu Le94eed562012-02-21 15:57:29 -080060const int DHCPConfig::kDHCPCDExitPollMilliseconds = 50;
61const int DHCPConfig::kDHCPCDExitWaitMilliseconds = 3000;
Darin Petkovd1b715b2011-06-02 21:21:22 -070062const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Jorge Lucangeli Obes2f3169d2012-04-25 11:38:25 -070063const char DHCPConfig::kDHCPCDPathFormatPID[] =
64 "var/run/dhcpcd/dhcpcd-%s.pid";
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -070065const char DHCPConfig::kDHCPCDUser[] = "dhcp";
Darin Petkov14c29ec2012-03-02 11:34:19 +010066const int DHCPConfig::kMinMTU = 576;
Darin Petkovf9b0ca82011-06-20 12:10:23 -070067const char DHCPConfig::kReasonBound[] = "BOUND";
68const char DHCPConfig::kReasonFail[] = "FAIL";
Paul Stewarta02ee492012-05-16 10:04:53 -070069const char DHCPConfig::kReasonGatewayArp[] = "GATEWAY-ARP";
Paul Stewart94571f12013-09-10 17:26:07 -070070const char DHCPConfig::kReasonNak[] = "NAK";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070071const char DHCPConfig::kReasonRebind[] = "REBIND";
72const char DHCPConfig::kReasonReboot[] = "REBOOT";
73const char DHCPConfig::kReasonRenew[] = "RENEW";
Paul Stewart3bdf1ab2014-07-17 19:22:26 -070074const char DHCPConfig::kStatusArpGateway[] = "ArpGateway";
75const char DHCPConfig::kStatusArpSelf[] = "ArpSelf";
76const char DHCPConfig::kStatusBound[] = "Bound";
77const char DHCPConfig::kStatusDiscover[] = "Discover";
Paul Stewart93920d22014-07-21 13:20:21 -070078const char DHCPConfig::kStatusIgnoreAdditionalOffer[] = "IgnoreAdditionalOffer";
Paul Stewart3bdf1ab2014-07-17 19:22:26 -070079const char DHCPConfig::kStatusIgnoreFailedOffer[] = "IgnoreFailedOffer";
80const char DHCPConfig::kStatusIgnoreInvalidOffer[] = "IgnoreInvalidOffer";
81const char DHCPConfig::kStatusIgnoreNonOffer[] = "IgnoreNonOffer";
82const char DHCPConfig::kStatusInform[] = "Inform";
83const char DHCPConfig::kStatusInit[] = "Init";
84const char DHCPConfig::kStatusNakDefer[] = "NakDefer";
85const char DHCPConfig::kStatusRebind[] = "Rebind";
86const char DHCPConfig::kStatusReboot[] = "Reboot";
87const char DHCPConfig::kStatusRelease[] = "Release";
88const char DHCPConfig::kStatusRenew[] = "Renew";
89const char DHCPConfig::kStatusRequest[] = "Request";
Chris Masone0756f232011-07-21 17:24:00 -070090const char DHCPConfig::kType[] = "dhcp";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070091
Darin Petkove7cb7f82011-06-03 13:21:51 -070092
Chris Masone19e30402011-07-19 15:48:47 -070093DHCPConfig::DHCPConfig(ControlInterface *control_interface,
Darin Petkova7b89492011-07-27 12:48:17 -070094 EventDispatcher *dispatcher,
Chris Masone19e30402011-07-19 15:48:47 -070095 DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070096 const string &device_name,
Paul Stewartd32f4842012-01-11 16:08:13 -080097 const string &request_hostname,
Paul Stewartd408fdf2012-05-07 17:15:57 -070098 const string &lease_file_suffix,
99 bool arp_gateway,
Paul Stewart3bdf1ab2014-07-17 19:22:26 -0700100 GLib *glib,
101 Metrics *metrics)
Chris Masone0756f232011-07-21 17:24:00 -0700102 : IPConfig(control_interface, device_name, kType),
Darin Petkovab565bb2011-10-06 02:55:51 -0700103 proxy_factory_(ProxyFactory::GetInstance()),
Darin Petkovd1b715b2011-06-02 21:21:22 -0700104 provider_(provider),
Paul Stewartd32f4842012-01-11 16:08:13 -0800105 request_hostname_(request_hostname),
Paul Stewartd408fdf2012-05-07 17:15:57 -0700106 lease_file_suffix_(lease_file_suffix),
107 arp_gateway_(arp_gateway),
Darin Petkovf7897bc2011-06-08 17:13:36 -0700108 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -0700109 child_watch_tag_(0),
Paul Stewart217c61d2013-06-13 15:12:02 -0700110 is_lease_active_(false),
Paul Stewart94571f12013-09-10 17:26:07 -0700111 is_gateway_arp_active_(false),
Paul Stewart1f916e42013-12-23 09:52:54 -0800112 lease_acquisition_timeout_seconds_(kAcquisitionTimeoutSeconds),
Darin Petkov92c43902011-06-09 20:46:06 -0700113 root_("/"),
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700114 weak_ptr_factory_(this),
Darin Petkova7b89492011-07-27 12:48:17 -0700115 dispatcher_(dispatcher),
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -0700116 glib_(glib),
Paul Stewart3bdf1ab2014-07-17 19:22:26 -0700117 metrics_(metrics),
Utkarsh Sanghi83bd64b2014-07-29 16:01:43 -0700118 minijail_(chromeos::Minijail::GetInstance()) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700119 SLOG(this, 2) << __func__ << ": " << device_name;
Paul Stewartd408fdf2012-05-07 17:15:57 -0700120 if (lease_file_suffix_.empty()) {
121 lease_file_suffix_ = device_name;
122 }
Darin Petkov50308cd2011-06-01 18:25:07 -0700123}
124
125DHCPConfig::~DHCPConfig() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700126 SLOG(this, 2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -0700127
128 // Don't leave behind dhcpcd running.
mukesh agrawal1835e772013-01-15 18:35:03 -0800129 Stop(__func__);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700130}
131
Darin Petkov92c43902011-06-09 20:46:06 -0700132bool DHCPConfig::RequestIP() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700133 SLOG(this, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700134 if (!pid_) {
135 return Start();
136 }
137 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700138 LOG(ERROR) << "Unable to request IP before acquiring destination.";
139 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700140 }
Darin Petkov92c43902011-06-09 20:46:06 -0700141 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700142}
143
Darin Petkov92c43902011-06-09 20:46:06 -0700144bool DHCPConfig::RenewIP() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700145 SLOG(this, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700146 if (!pid_) {
Paul Stewart21f7b342013-11-19 10:15:58 -0800147 return Start();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700148 }
Paul Stewartc02344a2012-08-17 16:57:37 -0700149 if (!proxy_.get()) {
150 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
151 return false;
152 }
Paul Stewart1f916e42013-12-23 09:52:54 -0800153 StopExpirationTimeout();
Darin Petkovaceede32011-07-18 15:32:38 -0700154 proxy_->Rebind(device_name());
Paul Stewart1f916e42013-12-23 09:52:54 -0800155 StartAcquisitionTimeout();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700156 return true;
157}
158
Paul Stewart217c61d2013-06-13 15:12:02 -0700159bool DHCPConfig::ReleaseIP(ReleaseReason reason) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700160 SLOG(this, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700161 if (!pid_) {
162 return true;
163 }
Paul Stewart217c61d2013-06-13 15:12:02 -0700164
165 // If we are using static IP and haven't retrieved a lease yet, we should
166 // allow the DHCP process to continue until we have a lease.
167 if (!is_lease_active_ && reason == IPConfig::kReleaseReasonStaticIP) {
168 return true;
169 }
170
Paul Stewarta02ee492012-05-16 10:04:53 -0700171 // If we are using gateway unicast ARP to speed up re-connect, don't
172 // give up our leases when we disconnect.
Paul Stewart217c61d2013-06-13 15:12:02 -0700173 bool should_keep_lease =
174 reason == IPConfig::kReleaseReasonDisconnect && arp_gateway_;
175
176 if (!should_keep_lease && proxy_.get()) {
Darin Petkova7b89492011-07-27 12:48:17 -0700177 proxy_->Release(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700178 }
mukesh agrawal1835e772013-01-15 18:35:03 -0800179 Stop(__func__);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700180 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700181}
182
Darin Petkova7b89492011-07-27 12:48:17 -0700183void DHCPConfig::InitProxy(const string &service) {
Darin Petkova7b89492011-07-27 12:48:17 -0700184 if (!proxy_.get()) {
Christopher Wiley0236e762012-11-16 14:11:05 -0800185 LOG(INFO) << "Init DHCP Proxy: " << device_name() << " at " << service;
Darin Petkovab565bb2011-10-06 02:55:51 -0700186 proxy_.reset(proxy_factory_->CreateDHCPProxy(service));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700187 }
188}
189
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700190void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700191 const Configuration &configuration) {
192 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700193 if (reason == kReasonFail) {
194 LOG(ERROR) << "Received failure event from DHCP client.";
Paul Stewartc5099532013-12-12 07:53:15 -0800195 NotifyFailure();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700196 return;
Paul Stewart94571f12013-09-10 17:26:07 -0700197 } else if (reason == kReasonNak) {
198 // If we got a NAK, this means the DHCP server is active, and any
199 // Gateway ARP state we have is no longer sufficient.
200 LOG_IF(ERROR, is_gateway_arp_active_)
201 << "Received NAK event for our gateway-ARP lease.";
202 is_gateway_arp_active_ = false;
203 return;
204 } else if (reason != kReasonBound &&
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700205 reason != kReasonRebind &&
206 reason != kReasonReboot &&
Paul Stewarta02ee492012-05-16 10:04:53 -0700207 reason != kReasonRenew &&
208 reason != kReasonGatewayArp) {
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700209 LOG(WARNING) << "Event ignored.";
210 return;
211 }
212 IPConfig::Properties properties;
213 CHECK(ParseConfiguration(configuration, &properties));
Paul Stewart217c61d2013-06-13 15:12:02 -0700214
215 // This needs to be set before calling UpdateProperties() below since
216 // those functions may indirectly call other methods like ReleaseIP that
217 // depend on or change this value.
218 is_lease_active_ = true;
219
Paul Stewarta02ee492012-05-16 10:04:53 -0700220 if (reason == kReasonGatewayArp) {
221 // This is a non-authoritative confirmation that we or on the same
222 // network as the one we received a lease on previously. The DHCP
223 // client is still running, so we should not cancel the timeout
224 // until that completes. In the meantime, however, we can tentatively
225 // configure our network in anticipation of successful completion.
Paul Stewartc5099532013-12-12 07:53:15 -0800226 IPConfig::UpdateProperties(properties);
Paul Stewart94571f12013-09-10 17:26:07 -0700227 is_gateway_arp_active_ = true;
Paul Stewarta02ee492012-05-16 10:04:53 -0700228 } else {
Paul Stewartc5099532013-12-12 07:53:15 -0800229 UpdateProperties(properties);
Paul Stewart94571f12013-09-10 17:26:07 -0700230 is_gateway_arp_active_ = false;
Paul Stewarta02ee492012-05-16 10:04:53 -0700231 }
Darin Petkove7cb7f82011-06-03 13:21:51 -0700232}
233
Paul Stewart3bdf1ab2014-07-17 19:22:26 -0700234void DHCPConfig::ProcessStatusChangeSignal(const string &status) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700235 SLOG(this, 2) << __func__ << ": " << status;
Paul Stewart3bdf1ab2014-07-17 19:22:26 -0700236
237 if (status == kStatusArpGateway) {
238 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusArpGateway);
239 } else if (status == kStatusArpSelf) {
240 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusArpSelf);
241 } else if (status == kStatusBound) {
242 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusBound);
243 } else if (status == kStatusDiscover) {
244 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusDiscover);
Paul Stewart93920d22014-07-21 13:20:21 -0700245 } else if (status == kStatusIgnoreAdditionalOffer) {
Paul Stewart3bdf1ab2014-07-17 19:22:26 -0700246 metrics_->NotifyDhcpClientStatus(
Paul Stewart93920d22014-07-21 13:20:21 -0700247 Metrics::kDhcpClientStatusIgnoreAdditionalOffer);
Paul Stewart3bdf1ab2014-07-17 19:22:26 -0700248 } else if (status == kStatusIgnoreFailedOffer) {
249 metrics_->NotifyDhcpClientStatus(
250 Metrics::kDhcpClientStatusIgnoreFailedOffer);
251 } else if (status == kStatusIgnoreInvalidOffer) {
252 metrics_->NotifyDhcpClientStatus(
253 Metrics::kDhcpClientStatusIgnoreInvalidOffer);
254 } else if (status == kStatusIgnoreNonOffer) {
255 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusIgnoreNonOffer);
256 } else if (status == kStatusInform) {
257 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusInform);
258 } else if (status == kStatusInit) {
259 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusInit);
260 } else if (status == kStatusNakDefer) {
261 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusNakDefer);
262 } else if (status == kStatusRebind) {
263 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRebind);
264 } else if (status == kStatusReboot) {
265 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusReboot);
266 } else if (status == kStatusRelease) {
267 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRelease);
268 } else if (status == kStatusRenew) {
269 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRenew);
270 } else if (status == kStatusRequest) {
271 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRequest);
272 } else {
273 LOG(ERROR) << "DHCP client reports unknown status " << status;
274 }
275}
276
Paul Stewartc5099532013-12-12 07:53:15 -0800277void DHCPConfig::UpdateProperties(const Properties &properties) {
Paul Stewart1f916e42013-12-23 09:52:54 -0800278 StopAcquisitionTimeout();
Paul Stewart1f916e42013-12-23 09:52:54 -0800279 if (properties.lease_duration_seconds) {
Samuel Tan815a6fb2014-10-23 16:53:59 -0700280 UpdateLeaseExpirationTime(properties.lease_duration_seconds);
Paul Stewart1f916e42013-12-23 09:52:54 -0800281 StartExpirationTimeout(properties.lease_duration_seconds);
282 } else {
283 LOG(WARNING) << "Lease duration is zero; not starting an expiration timer.";
Samuel Tan815a6fb2014-10-23 16:53:59 -0700284 ResetLeaseExpirationTime();
Paul Stewart1f916e42013-12-23 09:52:54 -0800285 StopExpirationTimeout();
286 }
Paul Stewart475c0722014-04-18 08:52:00 -0700287 IPConfig::UpdateProperties(properties);
Paul Stewartc5099532013-12-12 07:53:15 -0800288}
289
290void DHCPConfig::NotifyFailure() {
Paul Stewart1f916e42013-12-23 09:52:54 -0800291 StopAcquisitionTimeout();
292 StopExpirationTimeout();
Paul Stewartc5099532013-12-12 07:53:15 -0800293 IPConfig::NotifyFailure();
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700294}
295
Darin Petkovd1b715b2011-06-02 21:21:22 -0700296bool DHCPConfig::Start() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700297 SLOG(this, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700298
mukesh agrawalae30e9e2013-05-28 14:09:16 -0700299 // TODO(quiche): This should be migrated to use ExternalTask.
300 // (crbug.com/246263).
Paul Stewartd32f4842012-01-11 16:08:13 -0800301 vector<char *> args;
302 args.push_back(const_cast<char *>(kDHCPCDPath));
Paul Stewartd408fdf2012-05-07 17:15:57 -0700303 args.push_back(const_cast<char *>("-B")); // Run in foreground.
mukesh agrawal7eb02892012-05-29 11:22:37 -0700304 args.push_back(const_cast<char *>("-q")); // Only warnings+errors to stderr.
Paul Stewartd32f4842012-01-11 16:08:13 -0800305 if (!request_hostname_.empty()) {
Paul Stewartd408fdf2012-05-07 17:15:57 -0700306 args.push_back(const_cast<char *>("-h")); // Request hostname from server.
Paul Stewartd32f4842012-01-11 16:08:13 -0800307 args.push_back(const_cast<char *>(request_hostname_.c_str()));
308 }
Paul Stewartd408fdf2012-05-07 17:15:57 -0700309 if (arp_gateway_) {
310 args.push_back(const_cast<char *>("-R")); // ARP for default gateway.
Paul Stewarta02ee492012-05-16 10:04:53 -0700311 args.push_back(const_cast<char *>("-U")); // Enable unicast ARP on renew.
Paul Stewartd408fdf2012-05-07 17:15:57 -0700312 }
313 string interface_arg(device_name());
314 if (lease_file_suffix_ != device_name()) {
315 interface_arg = base::StringPrintf("%s=%s", device_name().c_str(),
316 lease_file_suffix_.c_str());
317 }
318 args.push_back(const_cast<char *>(interface_arg.c_str()));
Ben Chancc225ef2014-09-30 13:26:51 -0700319 args.push_back(nullptr);
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -0700320
321 struct minijail *jail = minijail_->New();
Utkarsh Sanghie4c6aff2014-07-30 14:49:03 -0700322 minijail_->DropRoot(jail, kDHCPCDUser, kDHCPCDUser);
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -0700323 minijail_->UseCapabilities(jail,
324 CAP_TO_MASK(CAP_NET_BIND_SERVICE) |
325 CAP_TO_MASK(CAP_NET_BROADCAST) |
326 CAP_TO_MASK(CAP_NET_ADMIN) |
327 CAP_TO_MASK(CAP_NET_RAW));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700328
Darin Petkov98dd6a02011-06-10 15:12:57 -0700329 CHECK(!pid_);
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -0700330 if (!minijail_->RunAndDestroy(jail, args, &pid_)) {
331 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath << " in a jail.";
Darin Petkovd1b715b2011-06-02 21:21:22 -0700332 return false;
333 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700334 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700335 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700336 CHECK(!child_watch_tag_);
337 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Paul Stewart1f916e42013-12-23 09:52:54 -0800338 StartAcquisitionTimeout();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700339 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700340}
341
mukesh agrawal1835e772013-01-15 18:35:03 -0800342void DHCPConfig::Stop(const char *reason) {
Darin Petkov3fe17662013-02-04 14:19:08 +0100343 LOG_IF(INFO, pid_) << "Stopping " << pid_ << " (" << reason << ")";
344 KillClient();
345 // KillClient waits for the client to terminate so it's safe to cleanup the
346 // state.
347 CleanupClientState();
348}
349
350void DHCPConfig::KillClient() {
351 if (!pid_) {
352 return;
Darin Petkov92c43902011-06-09 20:46:06 -0700353 }
Darin Petkov3fe17662013-02-04 14:19:08 +0100354 if (kill(pid_, SIGTERM) < 0) {
355 PLOG(ERROR);
356 return;
357 }
358 pid_t ret;
359 int num_iterations =
360 kDHCPCDExitWaitMilliseconds / kDHCPCDExitPollMilliseconds;
361 for (int count = 0; count < num_iterations; ++count) {
Ben Chancc225ef2014-09-30 13:26:51 -0700362 ret = waitpid(pid_, nullptr, WNOHANG);
Darin Petkov3fe17662013-02-04 14:19:08 +0100363 if (ret == pid_ || ret == -1)
364 break;
365 usleep(kDHCPCDExitPollMilliseconds * 1000);
366 if (count == num_iterations / 2) {
367 // Make one last attempt to kill dhcpcd.
368 LOG(WARNING) << "Terminating " << pid_ << " with SIGKILL.";
369 kill(pid_, SIGKILL);
370 }
371 }
372 if (ret != pid_)
373 PLOG(ERROR);
Darin Petkov92c43902011-06-09 20:46:06 -0700374}
375
Darin Petkov98dd6a02011-06-10 15:12:57 -0700376bool DHCPConfig::Restart() {
Darin Petkov3fe17662013-02-04 14:19:08 +0100377 // Take a reference of this instance to make sure we don't get destroyed in
378 // the middle of this call.
379 DHCPConfigRefPtr me = this;
380 me->Stop(__func__);
381 return me->Start();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700382}
383
mukesh agrawal28fd88e2013-05-02 14:06:10 -0700384// static
Darin Petkove7cb7f82011-06-03 13:21:51 -0700385string DHCPConfig::GetIPv4AddressString(unsigned int address) {
386 char str[INET_ADDRSTRLEN];
387 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
388 return str;
389 }
390 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
391 return "";
392}
393
Paul Stewart65bcd082012-11-16 09:37:14 -0800394// static
395bool DHCPConfig::ParseClasslessStaticRoutes(const string &classless_routes,
396 IPConfig::Properties *properties) {
397 if (classless_routes.empty()) {
398 // It is not an error for this string to be empty.
399 return true;
400 }
401
402 vector<string> route_strings;
403 base::SplitString(classless_routes, ' ', &route_strings);
404 if (route_strings.size() % 2) {
405 LOG(ERROR) << "In " << __func__ << ": Size of route_strings array "
406 << "is a non-even number: " << route_strings.size();
407 return false;
408 }
409
410 vector<IPConfig::Route> routes;
411 vector<string>::iterator route_iterator = route_strings.begin();
412 // Classless routes are a space-delimited array of
413 // "destination/prefix gateway" values. As such, we iterate twice
414 // for each pass of the loop below.
415 while (route_iterator != route_strings.end()) {
416 const string &destination_as_string(*route_iterator);
417 route_iterator++;
418 IPAddress destination(IPAddress::kFamilyIPv4);
419 if (!destination.SetAddressAndPrefixFromString(
420 destination_as_string)) {
421 LOG(ERROR) << "In " << __func__ << ": Expected an IP address/prefix "
422 << "but got an unparsable: " << destination_as_string;
423 return false;
424 }
425
426 CHECK(route_iterator != route_strings.end());
427 const string &gateway_as_string(*route_iterator);
428 route_iterator++;
429 IPAddress gateway(IPAddress::kFamilyIPv4);
430 if (!gateway.SetAddressFromString(gateway_as_string)) {
431 LOG(ERROR) << "In " << __func__ << ": Expected a router IP address "
432 << "but got an unparsable: " << gateway_as_string;
433 return false;
434 }
435
436 if (destination.prefix() == 0 && properties->gateway.empty()) {
437 // If a default route is provided in the classless parameters and
438 // we don't already have one, apply this as the default route.
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700439 SLOG(nullptr, 2) << "In " << __func__ << ": Setting default gateway to "
Paul Stewart65bcd082012-11-16 09:37:14 -0800440 << gateway_as_string;
441 CHECK(gateway.IntoString(&properties->gateway));
442 } else {
443 IPConfig::Route route;
444 CHECK(destination.IntoString(&route.host));
445 IPAddress netmask(IPAddress::GetAddressMaskFromPrefix(
446 destination.family(), destination.prefix()));
447 CHECK(netmask.IntoString(&route.netmask));
448 CHECK(gateway.IntoString(&route.gateway));
449 routes.push_back(route);
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700450 SLOG(nullptr, 2) << "In " << __func__ << ": Adding route to to "
Paul Stewart65bcd082012-11-16 09:37:14 -0800451 << destination_as_string << " via " << gateway_as_string;
452 }
453 }
454
455 if (!routes.empty()) {
456 properties->routes.swap(routes);
457 }
458
459 return true;
460}
461
mukesh agrawal28fd88e2013-05-02 14:06:10 -0700462// static
Paul Stewart65bcd082012-11-16 09:37:14 -0800463bool DHCPConfig::ParseConfiguration(const Configuration &configuration,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700464 IPConfig::Properties *properties) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700465 SLOG(nullptr, 2) << __func__;
Ben Chan0295e0f2013-09-20 13:47:29 -0700466 properties->method = kTypeDHCP;
Paul Stewart7355ce12011-09-02 10:47:01 -0700467 properties->address_family = IPAddress::kFamilyIPv4;
Paul Stewart65bcd082012-11-16 09:37:14 -0800468 string classless_static_routes;
469 bool default_gateway_parse_error = false;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700470 for (Configuration::const_iterator it = configuration.begin();
471 it != configuration.end(); ++it) {
472 const string &key = it->first;
473 const DBus::Variant &value = it->second;
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700474 SLOG(nullptr, 2) << "Processing key: " << key;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700475 if (key == kConfigurationKeyIPAddress) {
476 properties->address = GetIPv4AddressString(value.reader().get_uint32());
477 if (properties->address.empty()) {
478 return false;
479 }
480 } else if (key == kConfigurationKeySubnetCIDR) {
Paul Stewart48100b02012-03-19 07:53:52 -0700481 properties->subnet_prefix = value.reader().get_byte();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700482 } else if (key == kConfigurationKeyBroadcastAddress) {
483 properties->broadcast_address =
484 GetIPv4AddressString(value.reader().get_uint32());
485 if (properties->broadcast_address.empty()) {
486 return false;
487 }
488 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700489 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700490 if (routers.empty()) {
491 LOG(ERROR) << "No routers provided.";
Paul Stewart65bcd082012-11-16 09:37:14 -0800492 default_gateway_parse_error = true;
493 } else {
494 properties->gateway = GetIPv4AddressString(routers[0]);
495 if (properties->gateway.empty()) {
496 LOG(ERROR) << "Failed to parse router parameter provided.";
497 default_gateway_parse_error = true;
498 }
Darin Petkove7cb7f82011-06-03 13:21:51 -0700499 }
500 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700501 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700502 for (vector<unsigned int>::const_iterator it = servers.begin();
503 it != servers.end(); ++it) {
504 string server = GetIPv4AddressString(*it);
505 if (server.empty()) {
506 return false;
507 }
508 properties->dns_servers.push_back(server);
509 }
510 } else if (key == kConfigurationKeyDomainName) {
511 properties->domain_name = value.reader().get_string();
512 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700513 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700514 } else if (key == kConfigurationKeyMTU) {
515 int mtu = value.reader().get_uint16();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100516 if (mtu >= kMinMTU) {
Darin Petkove7cb7f82011-06-03 13:21:51 -0700517 properties->mtu = mtu;
518 }
Paul Stewart65bcd082012-11-16 09:37:14 -0800519 } else if (key == kConfigurationKeyClasslessStaticRoutes) {
520 classless_static_routes = value.reader().get_string();
Paul Stewartc3fdba92013-12-02 11:12:38 -0800521 } else if (key == kConfigurationKeyVendorEncapsulatedOptions) {
522 properties->vendor_encapsulated_options = value.reader().get_string();
Paul Stewarta63f5212013-06-25 15:29:40 -0700523 } else if (key == kConfigurationKeyWebProxyAutoDiscoveryUrl) {
524 properties->web_proxy_auto_discovery = value.reader().get_string();
Paul Stewart1f916e42013-12-23 09:52:54 -0800525 } else if (key == kConfigurationKeyLeaseTime) {
526 properties->lease_duration_seconds = value.reader().get_uint32();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700527 } else {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700528 SLOG(nullptr, 2) << "Key ignored.";
Darin Petkove7cb7f82011-06-03 13:21:51 -0700529 }
530 }
Paul Stewart65bcd082012-11-16 09:37:14 -0800531 ParseClasslessStaticRoutes(classless_static_routes, properties);
532 if (default_gateway_parse_error && properties->gateway.empty()) {
533 return false;
534 }
Darin Petkove7cb7f82011-06-03 13:21:51 -0700535 return true;
536}
537
Darin Petkov92c43902011-06-09 20:46:06 -0700538void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
mukesh agrawal6c6655d2012-12-06 14:49:50 -0800539 if (status == EXIT_SUCCESS) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700540 SLOG(nullptr, 2) << "pid " << pid << " exit status " << status;
mukesh agrawal6c6655d2012-12-06 14:49:50 -0800541 } else {
542 LOG(WARNING) << "pid " << pid << " exit status " << status;
543 }
Darin Petkov92c43902011-06-09 20:46:06 -0700544 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
545 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700546 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700547 // |config| instance may be destroyed after this call.
Darin Petkov3fe17662013-02-04 14:19:08 +0100548 config->CleanupClientState();
Darin Petkov92c43902011-06-09 20:46:06 -0700549}
550
551void DHCPConfig::CleanupClientState() {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700552 SLOG(this, 2) << __func__ << ": " << device_name();
Paul Stewart1f916e42013-12-23 09:52:54 -0800553 StopAcquisitionTimeout();
554 StopExpirationTimeout();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700555 if (child_watch_tag_) {
556 glib_->SourceRemove(child_watch_tag_);
557 child_watch_tag_ = 0;
558 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700559 proxy_.reset();
Paul Stewartd408fdf2012-05-07 17:15:57 -0700560 if (lease_file_suffix_ == device_name()) {
561 // If the lease file suffix was left as default, clean it up at exit.
Ben Chana0ddf462014-02-06 11:32:42 -0800562 base::DeleteFile(root_.Append(
Albert Chaulk0e1cdea2013-02-27 15:32:55 -0800563 base::StringPrintf(DHCPProvider::kDHCPCDPathFormatLease,
Paul Stewartd408fdf2012-05-07 17:15:57 -0700564 device_name().c_str())), false);
565 }
Ben Chana0ddf462014-02-06 11:32:42 -0800566 base::DeleteFile(root_.Append(
Paul Stewartd408fdf2012-05-07 17:15:57 -0700567 base::StringPrintf(kDHCPCDPathFormatPID, device_name().c_str())), false);
Darin Petkov3fe17662013-02-04 14:19:08 +0100568 if (pid_) {
569 int pid = pid_;
570 pid_ = 0;
571 // |this| instance may be destroyed after this call.
572 provider_->UnbindPID(pid);
573 }
Paul Stewart217c61d2013-06-13 15:12:02 -0700574 is_lease_active_ = false;
Darin Petkov92c43902011-06-09 20:46:06 -0700575}
576
Paul Stewart1f916e42013-12-23 09:52:54 -0800577void DHCPConfig::StartAcquisitionTimeout() {
578 CHECK(lease_expiration_callback_.IsCancelled());
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700579 lease_acquisition_timeout_callback_.Reset(
Paul Stewart1f916e42013-12-23 09:52:54 -0800580 Bind(&DHCPConfig::ProcessAcquisitionTimeout,
581 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700582 dispatcher_->PostDelayedTask(
583 lease_acquisition_timeout_callback_.callback(),
584 lease_acquisition_timeout_seconds_ * 1000);
585}
586
Paul Stewart1f916e42013-12-23 09:52:54 -0800587void DHCPConfig::StopAcquisitionTimeout() {
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700588 lease_acquisition_timeout_callback_.Cancel();
589}
590
Paul Stewart1f916e42013-12-23 09:52:54 -0800591void DHCPConfig::ProcessAcquisitionTimeout() {
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700592 LOG(ERROR) << "Timed out waiting for DHCP lease on " << device_name() << " "
593 << "(after " << lease_acquisition_timeout_seconds_ << " seconds).";
Paul Stewart94571f12013-09-10 17:26:07 -0700594 if (is_gateway_arp_active_) {
595 LOG(INFO) << "Continuing to use our previous lease, due to gateway-ARP.";
596 } else {
Paul Stewartc5099532013-12-12 07:53:15 -0800597 NotifyFailure();
Paul Stewart94571f12013-09-10 17:26:07 -0700598 }
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700599}
600
Ben Chan7fab8972014-08-10 17:14:46 -0700601void DHCPConfig::StartExpirationTimeout(uint32_t lease_duration_seconds) {
Paul Stewart1f916e42013-12-23 09:52:54 -0800602 CHECK(lease_acquisition_timeout_callback_.IsCancelled());
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700603 SLOG(this, 2) << __func__ << ": " << device_name()
Paul Stewart1f916e42013-12-23 09:52:54 -0800604 << ": " << "Lease timeout is " << lease_duration_seconds
605 << " seconds.";
606 lease_expiration_callback_.Reset(
607 Bind(&DHCPConfig::ProcessExpirationTimeout,
608 weak_ptr_factory_.GetWeakPtr()));
609 dispatcher_->PostDelayedTask(
610 lease_expiration_callback_.callback(),
611 lease_duration_seconds * 1000);
612}
613
614void DHCPConfig::StopExpirationTimeout() {
615 lease_expiration_callback_.Cancel();
616}
617
618void DHCPConfig::ProcessExpirationTimeout() {
619 LOG(ERROR) << "DHCP lease expired on " << device_name()
620 << "; restarting DHCP client instance.";
621 NotifyExpiry();
622 if (!Restart()) {
623 NotifyFailure();
624 }
625}
626
Darin Petkov50308cd2011-06-01 18:25:07 -0700627} // namespace shill