blob: baf74a323b42493871c736f1c1888346b1f45b35 [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
Darin Petkove7cb7f82011-06-03 13:21:51 -07007#include <arpa/inet.h>
mukesh agrawal6c6655d2012-12-06 14:49:50 -08008#include <stdlib.h>
Thieu Le94eed562012-02-21 15:57:29 -08009#include <sys/wait.h>
Darin Petkove7cb7f82011-06-03 13:21:51 -070010
Darin Petkov92c43902011-06-09 20:46:06 -070011#include <base/file_util.h>
Ben Chana0ddf462014-02-06 11:32:42 -080012#include <base/strings/string_split.h>
13#include <base/strings/stringprintf.h>
Chris Masone43b48a12011-07-01 13:37:07 -070014#include <chromeos/dbus/service_constants.h>
Darin Petkovd1b715b2011-06-02 21:21:22 -070015
16#include "shill/dhcpcd_proxy.h"
17#include "shill/dhcp_provider.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070018#include "shill/event_dispatcher.h"
Darin Petkov3258a812011-06-23 11:28:45 -070019#include "shill/glib.h"
Paul Stewart1d18e8c2011-07-15 11:00:31 -070020#include "shill/ip_address.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070021#include "shill/logging.h"
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -070022#include "shill/minijail.h"
Darin Petkovaceede32011-07-18 15:32:38 -070023#include "shill/proxy_factory.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070024
Darin Petkove7cb7f82011-06-03 13:21:51 -070025using std::string;
26using std::vector;
27
Darin Petkov50308cd2011-06-01 18:25:07 -070028namespace shill {
29
Chris Masone0756f232011-07-21 17:24:00 -070030// static
Paul Stewart1f916e42013-12-23 09:52:54 -080031const int DHCPConfig::kAcquisitionTimeoutSeconds = 30;
Darin Petkove7cb7f82011-06-03 13:21:51 -070032const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
Paul Stewart65bcd082012-11-16 09:37:14 -080033const char DHCPConfig::kConfigurationKeyClasslessStaticRoutes[] =
34 "ClasslessStaticRoutes";
Darin Petkove7cb7f82011-06-03 13:21:51 -070035const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
36const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
37const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
38const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
Paul Stewart1f916e42013-12-23 09:52:54 -080039const char DHCPConfig::kConfigurationKeyLeaseTime[] = "DHCPLeaseTime";
Darin Petkove7cb7f82011-06-03 13:21:51 -070040const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
41const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
42const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Paul Stewartc3fdba92013-12-02 11:12:38 -080043const char DHCPConfig::kConfigurationKeyVendorEncapsulatedOptions[] =
44 "VendorEncapsulatedOptions";
Paul Stewarta63f5212013-06-25 15:29:40 -070045const char DHCPConfig::kConfigurationKeyWebProxyAutoDiscoveryUrl[] =
46 "WebProxyAutoDiscoveryUrl";
Thieu Le94eed562012-02-21 15:57:29 -080047const int DHCPConfig::kDHCPCDExitPollMilliseconds = 50;
48const int DHCPConfig::kDHCPCDExitWaitMilliseconds = 3000;
Paul Stewart75a68b92013-10-24 10:50:27 -070049const char DHCPConfig::kDHCPCDMinimalConfig[] = "/etc/dhcpcd-minimal.conf";
Darin Petkovd1b715b2011-06-02 21:21:22 -070050const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Jorge Lucangeli Obes2f3169d2012-04-25 11:38:25 -070051const char DHCPConfig::kDHCPCDPathFormatPID[] =
52 "var/run/dhcpcd/dhcpcd-%s.pid";
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -070053const char DHCPConfig::kDHCPCDUser[] = "dhcp";
Darin Petkov14c29ec2012-03-02 11:34:19 +010054const int DHCPConfig::kMinMTU = 576;
Darin Petkovf9b0ca82011-06-20 12:10:23 -070055const char DHCPConfig::kReasonBound[] = "BOUND";
56const char DHCPConfig::kReasonFail[] = "FAIL";
Paul Stewarta02ee492012-05-16 10:04:53 -070057const char DHCPConfig::kReasonGatewayArp[] = "GATEWAY-ARP";
Paul Stewart94571f12013-09-10 17:26:07 -070058const char DHCPConfig::kReasonNak[] = "NAK";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070059const char DHCPConfig::kReasonRebind[] = "REBIND";
60const char DHCPConfig::kReasonReboot[] = "REBOOT";
61const char DHCPConfig::kReasonRenew[] = "RENEW";
Chris Masone0756f232011-07-21 17:24:00 -070062// static
63const char DHCPConfig::kType[] = "dhcp";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070064
Darin Petkove7cb7f82011-06-03 13:21:51 -070065
Chris Masone19e30402011-07-19 15:48:47 -070066DHCPConfig::DHCPConfig(ControlInterface *control_interface,
Darin Petkova7b89492011-07-27 12:48:17 -070067 EventDispatcher *dispatcher,
Chris Masone19e30402011-07-19 15:48:47 -070068 DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070069 const string &device_name,
Paul Stewartd32f4842012-01-11 16:08:13 -080070 const string &request_hostname,
Paul Stewartd408fdf2012-05-07 17:15:57 -070071 const string &lease_file_suffix,
72 bool arp_gateway,
Paul Stewart75a68b92013-10-24 10:50:27 -070073 bool is_minimal_config,
Darin Petkov3258a812011-06-23 11:28:45 -070074 GLib *glib)
Chris Masone0756f232011-07-21 17:24:00 -070075 : IPConfig(control_interface, device_name, kType),
Darin Petkovab565bb2011-10-06 02:55:51 -070076 proxy_factory_(ProxyFactory::GetInstance()),
Darin Petkovd1b715b2011-06-02 21:21:22 -070077 provider_(provider),
Paul Stewartd32f4842012-01-11 16:08:13 -080078 request_hostname_(request_hostname),
Paul Stewartd408fdf2012-05-07 17:15:57 -070079 lease_file_suffix_(lease_file_suffix),
80 arp_gateway_(arp_gateway),
Paul Stewart75a68b92013-10-24 10:50:27 -070081 is_minimal_config_(is_minimal_config),
Darin Petkovf7897bc2011-06-08 17:13:36 -070082 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070083 child_watch_tag_(0),
Paul Stewart217c61d2013-06-13 15:12:02 -070084 is_lease_active_(false),
Paul Stewart94571f12013-09-10 17:26:07 -070085 is_gateway_arp_active_(false),
Paul Stewart1f916e42013-12-23 09:52:54 -080086 lease_acquisition_timeout_seconds_(kAcquisitionTimeoutSeconds),
Darin Petkov92c43902011-06-09 20:46:06 -070087 root_("/"),
mukesh agrawalcc0fded2012-05-09 13:40:58 -070088 weak_ptr_factory_(this),
Darin Petkova7b89492011-07-27 12:48:17 -070089 dispatcher_(dispatcher),
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -070090 glib_(glib),
91 minijail_(Minijail::GetInstance()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070092 SLOG(DHCP, 2) << __func__ << ": " << device_name;
Paul Stewartd408fdf2012-05-07 17:15:57 -070093 if (lease_file_suffix_.empty()) {
94 lease_file_suffix_ = device_name;
95 }
Darin Petkov50308cd2011-06-01 18:25:07 -070096}
97
98DHCPConfig::~DHCPConfig() {
Ben Chanfad4a0b2012-04-18 15:49:59 -070099 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -0700100
101 // Don't leave behind dhcpcd running.
mukesh agrawal1835e772013-01-15 18:35:03 -0800102 Stop(__func__);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700103}
104
Darin Petkov92c43902011-06-09 20:46:06 -0700105bool DHCPConfig::RequestIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700106 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700107 if (!pid_) {
108 return Start();
109 }
110 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700111 LOG(ERROR) << "Unable to request IP before acquiring destination.";
112 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700113 }
Darin Petkov92c43902011-06-09 20:46:06 -0700114 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700115}
116
Darin Petkov92c43902011-06-09 20:46:06 -0700117bool DHCPConfig::RenewIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700118 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700119 if (!pid_) {
Paul Stewart21f7b342013-11-19 10:15:58 -0800120 return Start();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700121 }
Paul Stewartc02344a2012-08-17 16:57:37 -0700122 if (!proxy_.get()) {
123 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
124 return false;
125 }
Paul Stewart1f916e42013-12-23 09:52:54 -0800126 StopExpirationTimeout();
Darin Petkovaceede32011-07-18 15:32:38 -0700127 proxy_->Rebind(device_name());
Paul Stewart1f916e42013-12-23 09:52:54 -0800128 StartAcquisitionTimeout();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700129 return true;
130}
131
Paul Stewart217c61d2013-06-13 15:12:02 -0700132bool DHCPConfig::ReleaseIP(ReleaseReason reason) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700133 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700134 if (!pid_) {
135 return true;
136 }
Paul Stewart217c61d2013-06-13 15:12:02 -0700137
138 // If we are using static IP and haven't retrieved a lease yet, we should
139 // allow the DHCP process to continue until we have a lease.
140 if (!is_lease_active_ && reason == IPConfig::kReleaseReasonStaticIP) {
141 return true;
142 }
143
Paul Stewarta02ee492012-05-16 10:04:53 -0700144 // If we are using gateway unicast ARP to speed up re-connect, don't
145 // give up our leases when we disconnect.
Paul Stewart217c61d2013-06-13 15:12:02 -0700146 bool should_keep_lease =
147 reason == IPConfig::kReleaseReasonDisconnect && arp_gateway_;
148
149 if (!should_keep_lease && proxy_.get()) {
Darin Petkova7b89492011-07-27 12:48:17 -0700150 proxy_->Release(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700151 }
mukesh agrawal1835e772013-01-15 18:35:03 -0800152 Stop(__func__);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700153 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700154}
155
Darin Petkova7b89492011-07-27 12:48:17 -0700156void DHCPConfig::InitProxy(const string &service) {
Darin Petkova7b89492011-07-27 12:48:17 -0700157 if (!proxy_.get()) {
Christopher Wiley0236e762012-11-16 14:11:05 -0800158 LOG(INFO) << "Init DHCP Proxy: " << device_name() << " at " << service;
Darin Petkovab565bb2011-10-06 02:55:51 -0700159 proxy_.reset(proxy_factory_->CreateDHCPProxy(service));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700160 }
161}
162
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700163void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700164 const Configuration &configuration) {
165 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700166 if (reason == kReasonFail) {
167 LOG(ERROR) << "Received failure event from DHCP client.";
Paul Stewartc5099532013-12-12 07:53:15 -0800168 NotifyFailure();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700169 return;
Paul Stewart94571f12013-09-10 17:26:07 -0700170 } else if (reason == kReasonNak) {
171 // If we got a NAK, this means the DHCP server is active, and any
172 // Gateway ARP state we have is no longer sufficient.
173 LOG_IF(ERROR, is_gateway_arp_active_)
174 << "Received NAK event for our gateway-ARP lease.";
175 is_gateway_arp_active_ = false;
176 return;
177 } else if (reason != kReasonBound &&
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700178 reason != kReasonRebind &&
179 reason != kReasonReboot &&
Paul Stewarta02ee492012-05-16 10:04:53 -0700180 reason != kReasonRenew &&
181 reason != kReasonGatewayArp) {
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700182 LOG(WARNING) << "Event ignored.";
183 return;
184 }
185 IPConfig::Properties properties;
186 CHECK(ParseConfiguration(configuration, &properties));
Paul Stewart217c61d2013-06-13 15:12:02 -0700187
188 // This needs to be set before calling UpdateProperties() below since
189 // those functions may indirectly call other methods like ReleaseIP that
190 // depend on or change this value.
191 is_lease_active_ = true;
192
Paul Stewarta02ee492012-05-16 10:04:53 -0700193 if (reason == kReasonGatewayArp) {
194 // This is a non-authoritative confirmation that we or on the same
195 // network as the one we received a lease on previously. The DHCP
196 // client is still running, so we should not cancel the timeout
197 // until that completes. In the meantime, however, we can tentatively
198 // configure our network in anticipation of successful completion.
Paul Stewartc5099532013-12-12 07:53:15 -0800199 IPConfig::UpdateProperties(properties);
Paul Stewart94571f12013-09-10 17:26:07 -0700200 is_gateway_arp_active_ = true;
Paul Stewarta02ee492012-05-16 10:04:53 -0700201 } else {
Paul Stewartc5099532013-12-12 07:53:15 -0800202 UpdateProperties(properties);
Paul Stewart94571f12013-09-10 17:26:07 -0700203 is_gateway_arp_active_ = false;
Paul Stewarta02ee492012-05-16 10:04:53 -0700204 }
Darin Petkove7cb7f82011-06-03 13:21:51 -0700205}
206
Paul Stewartc5099532013-12-12 07:53:15 -0800207void DHCPConfig::UpdateProperties(const Properties &properties) {
Paul Stewart1f916e42013-12-23 09:52:54 -0800208 StopAcquisitionTimeout();
Paul Stewartc5099532013-12-12 07:53:15 -0800209 IPConfig::UpdateProperties(properties);
Paul Stewart1f916e42013-12-23 09:52:54 -0800210 if (properties.lease_duration_seconds) {
211 StartExpirationTimeout(properties.lease_duration_seconds);
212 } else {
213 LOG(WARNING) << "Lease duration is zero; not starting an expiration timer.";
214 StopExpirationTimeout();
215 }
Paul Stewartc5099532013-12-12 07:53:15 -0800216}
217
218void DHCPConfig::NotifyFailure() {
Paul Stewart1f916e42013-12-23 09:52:54 -0800219 StopAcquisitionTimeout();
220 StopExpirationTimeout();
Paul Stewartc5099532013-12-12 07:53:15 -0800221 IPConfig::NotifyFailure();
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700222}
223
Darin Petkovd1b715b2011-06-02 21:21:22 -0700224bool DHCPConfig::Start() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700225 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700226
mukesh agrawalae30e9e2013-05-28 14:09:16 -0700227 // TODO(quiche): This should be migrated to use ExternalTask.
228 // (crbug.com/246263).
Paul Stewartd32f4842012-01-11 16:08:13 -0800229 vector<char *> args;
230 args.push_back(const_cast<char *>(kDHCPCDPath));
Paul Stewartd408fdf2012-05-07 17:15:57 -0700231 args.push_back(const_cast<char *>("-B")); // Run in foreground.
mukesh agrawal7eb02892012-05-29 11:22:37 -0700232 args.push_back(const_cast<char *>("-q")); // Only warnings+errors to stderr.
Paul Stewartd32f4842012-01-11 16:08:13 -0800233 if (!request_hostname_.empty()) {
Paul Stewartd408fdf2012-05-07 17:15:57 -0700234 args.push_back(const_cast<char *>("-h")); // Request hostname from server.
Paul Stewartd32f4842012-01-11 16:08:13 -0800235 args.push_back(const_cast<char *>(request_hostname_.c_str()));
236 }
Paul Stewartd408fdf2012-05-07 17:15:57 -0700237 if (arp_gateway_) {
238 args.push_back(const_cast<char *>("-R")); // ARP for default gateway.
Paul Stewarta02ee492012-05-16 10:04:53 -0700239 args.push_back(const_cast<char *>("-U")); // Enable unicast ARP on renew.
Paul Stewartd408fdf2012-05-07 17:15:57 -0700240 }
Paul Stewart75a68b92013-10-24 10:50:27 -0700241 if (is_minimal_config_) {
242 args.push_back(const_cast<char *>("-f")); // Supply a configuration file.
243 args.push_back(const_cast<char *>(kDHCPCDMinimalConfig));
244 }
Paul Stewartd408fdf2012-05-07 17:15:57 -0700245 string interface_arg(device_name());
246 if (lease_file_suffix_ != device_name()) {
247 interface_arg = base::StringPrintf("%s=%s", device_name().c_str(),
248 lease_file_suffix_.c_str());
249 }
250 args.push_back(const_cast<char *>(interface_arg.c_str()));
Paul Stewartd32f4842012-01-11 16:08:13 -0800251 args.push_back(NULL);
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -0700252
253 struct minijail *jail = minijail_->New();
254 minijail_->DropRoot(jail, kDHCPCDUser);
255 minijail_->UseCapabilities(jail,
256 CAP_TO_MASK(CAP_NET_BIND_SERVICE) |
257 CAP_TO_MASK(CAP_NET_BROADCAST) |
258 CAP_TO_MASK(CAP_NET_ADMIN) |
259 CAP_TO_MASK(CAP_NET_RAW));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700260
Darin Petkov98dd6a02011-06-10 15:12:57 -0700261 CHECK(!pid_);
Jorge Lucangeli Obesad43cc62012-04-11 16:25:43 -0700262 if (!minijail_->RunAndDestroy(jail, args, &pid_)) {
263 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath << " in a jail.";
Darin Petkovd1b715b2011-06-02 21:21:22 -0700264 return false;
265 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700266 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700267 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700268 CHECK(!child_watch_tag_);
269 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Paul Stewart1f916e42013-12-23 09:52:54 -0800270 StartAcquisitionTimeout();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700271 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700272}
273
mukesh agrawal1835e772013-01-15 18:35:03 -0800274void DHCPConfig::Stop(const char *reason) {
Darin Petkov3fe17662013-02-04 14:19:08 +0100275 LOG_IF(INFO, pid_) << "Stopping " << pid_ << " (" << reason << ")";
276 KillClient();
277 // KillClient waits for the client to terminate so it's safe to cleanup the
278 // state.
279 CleanupClientState();
280}
281
282void DHCPConfig::KillClient() {
283 if (!pid_) {
284 return;
Darin Petkov92c43902011-06-09 20:46:06 -0700285 }
Darin Petkov3fe17662013-02-04 14:19:08 +0100286 if (kill(pid_, SIGTERM) < 0) {
287 PLOG(ERROR);
288 return;
289 }
290 pid_t ret;
291 int num_iterations =
292 kDHCPCDExitWaitMilliseconds / kDHCPCDExitPollMilliseconds;
293 for (int count = 0; count < num_iterations; ++count) {
294 ret = waitpid(pid_, NULL, WNOHANG);
295 if (ret == pid_ || ret == -1)
296 break;
297 usleep(kDHCPCDExitPollMilliseconds * 1000);
298 if (count == num_iterations / 2) {
299 // Make one last attempt to kill dhcpcd.
300 LOG(WARNING) << "Terminating " << pid_ << " with SIGKILL.";
301 kill(pid_, SIGKILL);
302 }
303 }
304 if (ret != pid_)
305 PLOG(ERROR);
Darin Petkov92c43902011-06-09 20:46:06 -0700306}
307
Darin Petkov98dd6a02011-06-10 15:12:57 -0700308bool DHCPConfig::Restart() {
Darin Petkov3fe17662013-02-04 14:19:08 +0100309 // Take a reference of this instance to make sure we don't get destroyed in
310 // the middle of this call.
311 DHCPConfigRefPtr me = this;
312 me->Stop(__func__);
313 return me->Start();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700314}
315
mukesh agrawal28fd88e2013-05-02 14:06:10 -0700316// static
Darin Petkove7cb7f82011-06-03 13:21:51 -0700317string DHCPConfig::GetIPv4AddressString(unsigned int address) {
318 char str[INET_ADDRSTRLEN];
319 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
320 return str;
321 }
322 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
323 return "";
324}
325
Paul Stewart65bcd082012-11-16 09:37:14 -0800326// static
327bool DHCPConfig::ParseClasslessStaticRoutes(const string &classless_routes,
328 IPConfig::Properties *properties) {
329 if (classless_routes.empty()) {
330 // It is not an error for this string to be empty.
331 return true;
332 }
333
334 vector<string> route_strings;
335 base::SplitString(classless_routes, ' ', &route_strings);
336 if (route_strings.size() % 2) {
337 LOG(ERROR) << "In " << __func__ << ": Size of route_strings array "
338 << "is a non-even number: " << route_strings.size();
339 return false;
340 }
341
342 vector<IPConfig::Route> routes;
343 vector<string>::iterator route_iterator = route_strings.begin();
344 // Classless routes are a space-delimited array of
345 // "destination/prefix gateway" values. As such, we iterate twice
346 // for each pass of the loop below.
347 while (route_iterator != route_strings.end()) {
348 const string &destination_as_string(*route_iterator);
349 route_iterator++;
350 IPAddress destination(IPAddress::kFamilyIPv4);
351 if (!destination.SetAddressAndPrefixFromString(
352 destination_as_string)) {
353 LOG(ERROR) << "In " << __func__ << ": Expected an IP address/prefix "
354 << "but got an unparsable: " << destination_as_string;
355 return false;
356 }
357
358 CHECK(route_iterator != route_strings.end());
359 const string &gateway_as_string(*route_iterator);
360 route_iterator++;
361 IPAddress gateway(IPAddress::kFamilyIPv4);
362 if (!gateway.SetAddressFromString(gateway_as_string)) {
363 LOG(ERROR) << "In " << __func__ << ": Expected a router IP address "
364 << "but got an unparsable: " << gateway_as_string;
365 return false;
366 }
367
368 if (destination.prefix() == 0 && properties->gateway.empty()) {
369 // If a default route is provided in the classless parameters and
370 // we don't already have one, apply this as the default route.
371 SLOG(DHCP, 2) << "In " << __func__ << ": Setting default gateway to "
372 << gateway_as_string;
373 CHECK(gateway.IntoString(&properties->gateway));
374 } else {
375 IPConfig::Route route;
376 CHECK(destination.IntoString(&route.host));
377 IPAddress netmask(IPAddress::GetAddressMaskFromPrefix(
378 destination.family(), destination.prefix()));
379 CHECK(netmask.IntoString(&route.netmask));
380 CHECK(gateway.IntoString(&route.gateway));
381 routes.push_back(route);
382 SLOG(DHCP, 2) << "In " << __func__ << ": Adding route to to "
383 << destination_as_string << " via " << gateway_as_string;
384 }
385 }
386
387 if (!routes.empty()) {
388 properties->routes.swap(routes);
389 }
390
391 return true;
392}
393
mukesh agrawal28fd88e2013-05-02 14:06:10 -0700394// static
Paul Stewart65bcd082012-11-16 09:37:14 -0800395bool DHCPConfig::ParseConfiguration(const Configuration &configuration,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700396 IPConfig::Properties *properties) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700397 SLOG(DHCP, 2) << __func__;
Ben Chan0295e0f2013-09-20 13:47:29 -0700398 properties->method = kTypeDHCP;
Paul Stewart7355ce12011-09-02 10:47:01 -0700399 properties->address_family = IPAddress::kFamilyIPv4;
Paul Stewart65bcd082012-11-16 09:37:14 -0800400 string classless_static_routes;
401 bool default_gateway_parse_error = false;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700402 for (Configuration::const_iterator it = configuration.begin();
403 it != configuration.end(); ++it) {
404 const string &key = it->first;
405 const DBus::Variant &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700406 SLOG(DHCP, 2) << "Processing key: " << key;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700407 if (key == kConfigurationKeyIPAddress) {
408 properties->address = GetIPv4AddressString(value.reader().get_uint32());
409 if (properties->address.empty()) {
410 return false;
411 }
412 } else if (key == kConfigurationKeySubnetCIDR) {
Paul Stewart48100b02012-03-19 07:53:52 -0700413 properties->subnet_prefix = value.reader().get_byte();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700414 } else if (key == kConfigurationKeyBroadcastAddress) {
415 properties->broadcast_address =
416 GetIPv4AddressString(value.reader().get_uint32());
417 if (properties->broadcast_address.empty()) {
418 return false;
419 }
420 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700421 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700422 if (routers.empty()) {
423 LOG(ERROR) << "No routers provided.";
Paul Stewart65bcd082012-11-16 09:37:14 -0800424 default_gateway_parse_error = true;
425 } else {
426 properties->gateway = GetIPv4AddressString(routers[0]);
427 if (properties->gateway.empty()) {
428 LOG(ERROR) << "Failed to parse router parameter provided.";
429 default_gateway_parse_error = true;
430 }
Darin Petkove7cb7f82011-06-03 13:21:51 -0700431 }
432 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700433 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700434 for (vector<unsigned int>::const_iterator it = servers.begin();
435 it != servers.end(); ++it) {
436 string server = GetIPv4AddressString(*it);
437 if (server.empty()) {
438 return false;
439 }
440 properties->dns_servers.push_back(server);
441 }
442 } else if (key == kConfigurationKeyDomainName) {
443 properties->domain_name = value.reader().get_string();
444 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700445 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700446 } else if (key == kConfigurationKeyMTU) {
447 int mtu = value.reader().get_uint16();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100448 if (mtu >= kMinMTU) {
Darin Petkove7cb7f82011-06-03 13:21:51 -0700449 properties->mtu = mtu;
450 }
Paul Stewart65bcd082012-11-16 09:37:14 -0800451 } else if (key == kConfigurationKeyClasslessStaticRoutes) {
452 classless_static_routes = value.reader().get_string();
Paul Stewartc3fdba92013-12-02 11:12:38 -0800453 } else if (key == kConfigurationKeyVendorEncapsulatedOptions) {
454 properties->vendor_encapsulated_options = value.reader().get_string();
Paul Stewarta63f5212013-06-25 15:29:40 -0700455 } else if (key == kConfigurationKeyWebProxyAutoDiscoveryUrl) {
456 properties->web_proxy_auto_discovery = value.reader().get_string();
Paul Stewart1f916e42013-12-23 09:52:54 -0800457 } else if (key == kConfigurationKeyLeaseTime) {
458 properties->lease_duration_seconds = value.reader().get_uint32();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700459 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700460 SLOG(DHCP, 2) << "Key ignored.";
Darin Petkove7cb7f82011-06-03 13:21:51 -0700461 }
462 }
Paul Stewart65bcd082012-11-16 09:37:14 -0800463 ParseClasslessStaticRoutes(classless_static_routes, properties);
464 if (default_gateway_parse_error && properties->gateway.empty()) {
465 return false;
466 }
Darin Petkove7cb7f82011-06-03 13:21:51 -0700467 return true;
468}
469
Darin Petkov92c43902011-06-09 20:46:06 -0700470void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
mukesh agrawal6c6655d2012-12-06 14:49:50 -0800471 if (status == EXIT_SUCCESS) {
472 SLOG(DHCP, 2) << "pid " << pid << " exit status " << status;
473 } else {
474 LOG(WARNING) << "pid " << pid << " exit status " << status;
475 }
Darin Petkov92c43902011-06-09 20:46:06 -0700476 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
477 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700478 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700479 // |config| instance may be destroyed after this call.
Darin Petkov3fe17662013-02-04 14:19:08 +0100480 config->CleanupClientState();
Darin Petkov92c43902011-06-09 20:46:06 -0700481}
482
483void DHCPConfig::CleanupClientState() {
Darin Petkov3fe17662013-02-04 14:19:08 +0100484 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Paul Stewart1f916e42013-12-23 09:52:54 -0800485 StopAcquisitionTimeout();
486 StopExpirationTimeout();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700487 if (child_watch_tag_) {
488 glib_->SourceRemove(child_watch_tag_);
489 child_watch_tag_ = 0;
490 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700491 proxy_.reset();
Paul Stewartd408fdf2012-05-07 17:15:57 -0700492 if (lease_file_suffix_ == device_name()) {
493 // If the lease file suffix was left as default, clean it up at exit.
Ben Chana0ddf462014-02-06 11:32:42 -0800494 base::DeleteFile(root_.Append(
Albert Chaulk0e1cdea2013-02-27 15:32:55 -0800495 base::StringPrintf(DHCPProvider::kDHCPCDPathFormatLease,
Paul Stewartd408fdf2012-05-07 17:15:57 -0700496 device_name().c_str())), false);
497 }
Ben Chana0ddf462014-02-06 11:32:42 -0800498 base::DeleteFile(root_.Append(
Paul Stewartd408fdf2012-05-07 17:15:57 -0700499 base::StringPrintf(kDHCPCDPathFormatPID, device_name().c_str())), false);
Darin Petkov3fe17662013-02-04 14:19:08 +0100500 if (pid_) {
501 int pid = pid_;
502 pid_ = 0;
503 // |this| instance may be destroyed after this call.
504 provider_->UnbindPID(pid);
505 }
Paul Stewart217c61d2013-06-13 15:12:02 -0700506 is_lease_active_ = false;
Darin Petkov92c43902011-06-09 20:46:06 -0700507}
508
Paul Stewart1f916e42013-12-23 09:52:54 -0800509void DHCPConfig::StartAcquisitionTimeout() {
510 CHECK(lease_expiration_callback_.IsCancelled());
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700511 lease_acquisition_timeout_callback_.Reset(
Paul Stewart1f916e42013-12-23 09:52:54 -0800512 Bind(&DHCPConfig::ProcessAcquisitionTimeout,
513 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700514 dispatcher_->PostDelayedTask(
515 lease_acquisition_timeout_callback_.callback(),
516 lease_acquisition_timeout_seconds_ * 1000);
517}
518
Paul Stewart1f916e42013-12-23 09:52:54 -0800519void DHCPConfig::StopAcquisitionTimeout() {
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700520 lease_acquisition_timeout_callback_.Cancel();
521}
522
Paul Stewart1f916e42013-12-23 09:52:54 -0800523void DHCPConfig::ProcessAcquisitionTimeout() {
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700524 LOG(ERROR) << "Timed out waiting for DHCP lease on " << device_name() << " "
525 << "(after " << lease_acquisition_timeout_seconds_ << " seconds).";
Paul Stewart94571f12013-09-10 17:26:07 -0700526 if (is_gateway_arp_active_) {
527 LOG(INFO) << "Continuing to use our previous lease, due to gateway-ARP.";
528 } else {
Paul Stewartc5099532013-12-12 07:53:15 -0800529 NotifyFailure();
Paul Stewart94571f12013-09-10 17:26:07 -0700530 }
mukesh agrawalcc0fded2012-05-09 13:40:58 -0700531}
532
Paul Stewart1f916e42013-12-23 09:52:54 -0800533void DHCPConfig::StartExpirationTimeout(uint32 lease_duration_seconds) {
534 CHECK(lease_acquisition_timeout_callback_.IsCancelled());
535 SLOG(DHCP, 2) << __func__ << ": " << device_name()
536 << ": " << "Lease timeout is " << lease_duration_seconds
537 << " seconds.";
538 lease_expiration_callback_.Reset(
539 Bind(&DHCPConfig::ProcessExpirationTimeout,
540 weak_ptr_factory_.GetWeakPtr()));
541 dispatcher_->PostDelayedTask(
542 lease_expiration_callback_.callback(),
543 lease_duration_seconds * 1000);
544}
545
546void DHCPConfig::StopExpirationTimeout() {
547 lease_expiration_callback_.Cancel();
548}
549
550void DHCPConfig::ProcessExpirationTimeout() {
551 LOG(ERROR) << "DHCP lease expired on " << device_name()
552 << "; restarting DHCP client instance.";
553 NotifyExpiry();
554 if (!Restart()) {
555 NotifyFailure();
556 }
557}
558
Darin Petkov50308cd2011-06-01 18:25:07 -0700559} // namespace shill