blob: 9d87da355105c1bc61b8ce399c12c80fb839bf32 [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>
Thieu Le94eed562012-02-21 15:57:29 -08008#include <sys/wait.h>
Darin Petkove7cb7f82011-06-03 13:21:51 -07009
Darin Petkov92c43902011-06-09 20:46:06 -070010#include <base/file_util.h>
Darin Petkov50308cd2011-06-01 18:25:07 -070011#include <base/logging.h>
Darin Petkov92c43902011-06-09 20:46:06 -070012#include <base/stringprintf.h>
Chris Masone43b48a12011-07-01 13:37:07 -070013#include <chromeos/dbus/service_constants.h>
Darin Petkovd1b715b2011-06-02 21:21:22 -070014
15#include "shill/dhcpcd_proxy.h"
16#include "shill/dhcp_provider.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070017#include "shill/event_dispatcher.h"
Darin Petkov3258a812011-06-23 11:28:45 -070018#include "shill/glib.h"
Paul Stewart1d18e8c2011-07-15 11:00:31 -070019#include "shill/ip_address.h"
Darin Petkovaceede32011-07-18 15:32:38 -070020#include "shill/proxy_factory.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070021#include "shill/scope_logger.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070022
Darin Petkove7cb7f82011-06-03 13:21:51 -070023using std::string;
24using std::vector;
25
Darin Petkov50308cd2011-06-01 18:25:07 -070026namespace shill {
27
Chris Masone0756f232011-07-21 17:24:00 -070028// static
Darin Petkove7cb7f82011-06-03 13:21:51 -070029const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
30const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
31const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
32const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
33const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
34const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
35const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
36const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Thieu Le94eed562012-02-21 15:57:29 -080037const int DHCPConfig::kDHCPCDExitPollMilliseconds = 50;
38const int DHCPConfig::kDHCPCDExitWaitMilliseconds = 3000;
Darin Petkovd1b715b2011-06-02 21:21:22 -070039const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Paul Stewartd408fdf2012-05-07 17:15:57 -070040const char DHCPConfig::kDHCPCDPathFormatLease[] =
41 "var/lib/dhcpcd/dhcpcd-%s.lease";
Jorge Lucangeli Obes2f3169d2012-04-25 11:38:25 -070042const char DHCPConfig::kDHCPCDPathFormatPID[] =
43 "var/run/dhcpcd/dhcpcd-%s.pid";
Darin Petkov14c29ec2012-03-02 11:34:19 +010044const int DHCPConfig::kMinMTU = 576;
Darin Petkovf9b0ca82011-06-20 12:10:23 -070045const char DHCPConfig::kReasonBound[] = "BOUND";
46const char DHCPConfig::kReasonFail[] = "FAIL";
47const char DHCPConfig::kReasonRebind[] = "REBIND";
48const char DHCPConfig::kReasonReboot[] = "REBOOT";
49const char DHCPConfig::kReasonRenew[] = "RENEW";
Chris Masone0756f232011-07-21 17:24:00 -070050// static
51const char DHCPConfig::kType[] = "dhcp";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070052
Darin Petkove7cb7f82011-06-03 13:21:51 -070053
Chris Masone19e30402011-07-19 15:48:47 -070054DHCPConfig::DHCPConfig(ControlInterface *control_interface,
Darin Petkova7b89492011-07-27 12:48:17 -070055 EventDispatcher *dispatcher,
Chris Masone19e30402011-07-19 15:48:47 -070056 DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070057 const string &device_name,
Paul Stewartd32f4842012-01-11 16:08:13 -080058 const string &request_hostname,
Paul Stewartd408fdf2012-05-07 17:15:57 -070059 const string &lease_file_suffix,
60 bool arp_gateway,
Darin Petkov3258a812011-06-23 11:28:45 -070061 GLib *glib)
Chris Masone0756f232011-07-21 17:24:00 -070062 : IPConfig(control_interface, device_name, kType),
Darin Petkovab565bb2011-10-06 02:55:51 -070063 proxy_factory_(ProxyFactory::GetInstance()),
Darin Petkovd1b715b2011-06-02 21:21:22 -070064 provider_(provider),
Paul Stewartd32f4842012-01-11 16:08:13 -080065 request_hostname_(request_hostname),
Paul Stewartd408fdf2012-05-07 17:15:57 -070066 lease_file_suffix_(lease_file_suffix),
67 arp_gateway_(arp_gateway),
Darin Petkovf7897bc2011-06-08 17:13:36 -070068 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070069 child_watch_tag_(0),
70 root_("/"),
Darin Petkova7b89492011-07-27 12:48:17 -070071 dispatcher_(dispatcher),
Darin Petkovf7897bc2011-06-08 17:13:36 -070072 glib_(glib) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070073 SLOG(DHCP, 2) << __func__ << ": " << device_name;
Paul Stewartd408fdf2012-05-07 17:15:57 -070074 if (lease_file_suffix_.empty()) {
75 lease_file_suffix_ = device_name;
76 }
Darin Petkov50308cd2011-06-01 18:25:07 -070077}
78
79DHCPConfig::~DHCPConfig() {
Ben Chanfad4a0b2012-04-18 15:49:59 -070080 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070081
82 // Don't leave behind dhcpcd running.
83 Stop();
84
Darin Petkov98dd6a02011-06-10 15:12:57 -070085 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070086 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070087}
88
Darin Petkov92c43902011-06-09 20:46:06 -070089bool DHCPConfig::RequestIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -070090 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070091 if (!pid_) {
92 return Start();
93 }
94 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070095 LOG(ERROR) << "Unable to request IP before acquiring destination.";
96 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070097 }
Darin Petkov92c43902011-06-09 20:46:06 -070098 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070099}
100
Darin Petkov92c43902011-06-09 20:46:06 -0700101bool DHCPConfig::RenewIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700102 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700103 if (!pid_) {
104 return false;
105 }
Darin Petkovaceede32011-07-18 15:32:38 -0700106 proxy_->Rebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -0700107 return true;
108}
109
Darin Petkov92c43902011-06-09 20:46:06 -0700110bool DHCPConfig::ReleaseIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700111 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700112 if (!pid_) {
113 return true;
114 }
Darin Petkova7b89492011-07-27 12:48:17 -0700115 if (proxy_.get()) {
116 proxy_->Release(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700117 }
Darin Petkov98dd6a02011-06-10 15:12:57 -0700118 Stop();
119 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700120}
121
Darin Petkova7b89492011-07-27 12:48:17 -0700122void DHCPConfig::InitProxy(const string &service) {
Darin Petkova7b89492011-07-27 12:48:17 -0700123 if (!proxy_.get()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700124 SLOG(DHCP, 2) << "Init DHCP Proxy: " << device_name() << " at " << service;
Darin Petkovab565bb2011-10-06 02:55:51 -0700125 proxy_.reset(proxy_factory_->CreateDHCPProxy(service));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700126 }
127}
128
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700129void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700130 const Configuration &configuration) {
131 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700132 if (reason == kReasonFail) {
133 LOG(ERROR) << "Received failure event from DHCP client.";
134 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700135 return;
136 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700137 if (reason != kReasonBound &&
138 reason != kReasonRebind &&
139 reason != kReasonReboot &&
140 reason != kReasonRenew) {
141 LOG(WARNING) << "Event ignored.";
142 return;
143 }
144 IPConfig::Properties properties;
145 CHECK(ParseConfiguration(configuration, &properties));
146 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700147}
148
Darin Petkovd1b715b2011-06-02 21:21:22 -0700149bool DHCPConfig::Start() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700150 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700151
Paul Stewartd32f4842012-01-11 16:08:13 -0800152 vector<char *> args;
153 args.push_back(const_cast<char *>(kDHCPCDPath));
Paul Stewartd408fdf2012-05-07 17:15:57 -0700154 args.push_back(const_cast<char *>("-B")); // Run in foreground.
Paul Stewartd32f4842012-01-11 16:08:13 -0800155 if (!request_hostname_.empty()) {
Paul Stewartd408fdf2012-05-07 17:15:57 -0700156 args.push_back(const_cast<char *>("-h")); // Request hostname from server.
Paul Stewartd32f4842012-01-11 16:08:13 -0800157 args.push_back(const_cast<char *>(request_hostname_.c_str()));
158 }
Paul Stewartd408fdf2012-05-07 17:15:57 -0700159 if (arp_gateway_) {
160 args.push_back(const_cast<char *>("-R")); // ARP for default gateway.
161 }
162 string interface_arg(device_name());
163 if (lease_file_suffix_ != device_name()) {
164 interface_arg = base::StringPrintf("%s=%s", device_name().c_str(),
165 lease_file_suffix_.c_str());
166 }
167 args.push_back(const_cast<char *>(interface_arg.c_str()));
Paul Stewartd32f4842012-01-11 16:08:13 -0800168 args.push_back(NULL);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700169 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700170
Darin Petkov98dd6a02011-06-10 15:12:57 -0700171 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700172 if (!glib_->SpawnAsync(NULL,
Paul Stewartd32f4842012-01-11 16:08:13 -0800173 args.data(),
Darin Petkovf7897bc2011-06-08 17:13:36 -0700174 envp,
175 G_SPAWN_DO_NOT_REAP_CHILD,
176 NULL,
177 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700178 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700179 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700180 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
181 return false;
182 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700183 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700184 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700185 CHECK(!child_watch_tag_);
186 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700187 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700188}
189
Darin Petkov92c43902011-06-09 20:46:06 -0700190void DHCPConfig::Stop() {
191 if (pid_) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700192 SLOG(DHCP, 2) << "Terminating " << pid_;
Thieu Le94eed562012-02-21 15:57:29 -0800193 if (kill(pid_, SIGTERM) < 0) {
194 PLOG(ERROR);
195 return;
196 }
197 pid_t ret;
198 int num_iterations =
199 kDHCPCDExitWaitMilliseconds / kDHCPCDExitPollMilliseconds;
200 for (int count = 0; count < num_iterations; ++count) {
201 ret = waitpid(pid_, NULL, WNOHANG);
202 if (ret == pid_ || ret == -1)
203 break;
204 usleep(kDHCPCDExitPollMilliseconds * 1000);
205 if (count == num_iterations / 2) // Make one last attempt to kill dhcpcd.
206 kill(pid_, SIGKILL);
207 }
208 if (ret != pid_)
209 PLOG(ERROR);
Darin Petkov92c43902011-06-09 20:46:06 -0700210 }
211}
212
Darin Petkov98dd6a02011-06-10 15:12:57 -0700213bool DHCPConfig::Restart() {
214 // Check to ensure that this instance doesn't get destroyed in the middle of
215 // this call. If stopping a running client while there's only one reference to
216 // this instance, we will end up destroying it when the PID is unbound from
217 // the Provider. Since the Provider doesn't invoke Restart, this would mean
218 // that Restart was erroneously executed through a bare reference.
219 CHECK(!pid_ || !HasOneRef());
220 Stop();
221 if (pid_) {
222 provider_->UnbindPID(pid_);
223 }
224 CleanupClientState();
225 return Start();
226}
227
Darin Petkove7cb7f82011-06-03 13:21:51 -0700228string DHCPConfig::GetIPv4AddressString(unsigned int address) {
229 char str[INET_ADDRSTRLEN];
230 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
231 return str;
232 }
233 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
234 return "";
235}
236
237bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
238 IPConfig::Properties *properties) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700239 SLOG(DHCP, 2) << __func__;
Chris Masone43b48a12011-07-01 13:37:07 -0700240 properties->method = flimflam::kTypeDHCP;
Paul Stewart7355ce12011-09-02 10:47:01 -0700241 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700242 for (Configuration::const_iterator it = configuration.begin();
243 it != configuration.end(); ++it) {
244 const string &key = it->first;
245 const DBus::Variant &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700246 SLOG(DHCP, 2) << "Processing key: " << key;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700247 if (key == kConfigurationKeyIPAddress) {
248 properties->address = GetIPv4AddressString(value.reader().get_uint32());
249 if (properties->address.empty()) {
250 return false;
251 }
252 } else if (key == kConfigurationKeySubnetCIDR) {
Paul Stewart48100b02012-03-19 07:53:52 -0700253 properties->subnet_prefix = value.reader().get_byte();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700254 } else if (key == kConfigurationKeyBroadcastAddress) {
255 properties->broadcast_address =
256 GetIPv4AddressString(value.reader().get_uint32());
257 if (properties->broadcast_address.empty()) {
258 return false;
259 }
260 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700261 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700262 if (routers.empty()) {
263 LOG(ERROR) << "No routers provided.";
264 return false;
265 }
266 properties->gateway = GetIPv4AddressString(routers[0]);
267 if (properties->gateway.empty()) {
268 return false;
269 }
270 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700271 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700272 for (vector<unsigned int>::const_iterator it = servers.begin();
273 it != servers.end(); ++it) {
274 string server = GetIPv4AddressString(*it);
275 if (server.empty()) {
276 return false;
277 }
278 properties->dns_servers.push_back(server);
279 }
280 } else if (key == kConfigurationKeyDomainName) {
281 properties->domain_name = value.reader().get_string();
282 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700283 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700284 } else if (key == kConfigurationKeyMTU) {
285 int mtu = value.reader().get_uint16();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100286 if (mtu >= kMinMTU) {
Darin Petkove7cb7f82011-06-03 13:21:51 -0700287 properties->mtu = mtu;
288 }
289 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700290 SLOG(DHCP, 2) << "Key ignored.";
Darin Petkove7cb7f82011-06-03 13:21:51 -0700291 }
292 }
293 return true;
294}
295
Darin Petkov92c43902011-06-09 20:46:06 -0700296void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700297 SLOG(DHCP, 2) << "pid " << pid << " exit status " << status;
Darin Petkov92c43902011-06-09 20:46:06 -0700298 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
299 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700300 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700301 config->CleanupClientState();
302
303 // |config| instance may be destroyed after this call.
304 config->provider_->UnbindPID(pid);
305}
306
307void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700308 if (child_watch_tag_) {
309 glib_->SourceRemove(child_watch_tag_);
310 child_watch_tag_ = 0;
311 }
312 if (pid_) {
313 glib_->SpawnClosePID(pid_);
314 pid_ = 0;
315 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700316 proxy_.reset();
Paul Stewartd408fdf2012-05-07 17:15:57 -0700317 if (lease_file_suffix_ == device_name()) {
318 // If the lease file suffix was left as default, clean it up at exit.
319 file_util::Delete(root_.Append(
320 base::StringPrintf(kDHCPCDPathFormatLease,
321 device_name().c_str())), false);
322 }
323 file_util::Delete(root_.Append(
324 base::StringPrintf(kDHCPCDPathFormatPID, device_name().c_str())), false);
Darin Petkov92c43902011-06-09 20:46:06 -0700325}
326
Darin Petkov50308cd2011-06-01 18:25:07 -0700327} // namespace shill