blob: 18bfcc028a48d461a96c1159ca03203723156157 [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";
Darin Petkov92c43902011-06-09 20:46:06 -070042const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
Darin Petkov14c29ec2012-03-02 11:34:19 +010043const int DHCPConfig::kMinMTU = 576;
Darin Petkovf9b0ca82011-06-20 12:10:23 -070044const char DHCPConfig::kReasonBound[] = "BOUND";
45const char DHCPConfig::kReasonFail[] = "FAIL";
46const char DHCPConfig::kReasonRebind[] = "REBIND";
47const char DHCPConfig::kReasonReboot[] = "REBOOT";
48const char DHCPConfig::kReasonRenew[] = "RENEW";
Chris Masone0756f232011-07-21 17:24:00 -070049// static
50const char DHCPConfig::kType[] = "dhcp";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070051
Darin Petkove7cb7f82011-06-03 13:21:51 -070052
Chris Masone19e30402011-07-19 15:48:47 -070053DHCPConfig::DHCPConfig(ControlInterface *control_interface,
Darin Petkova7b89492011-07-27 12:48:17 -070054 EventDispatcher *dispatcher,
Chris Masone19e30402011-07-19 15:48:47 -070055 DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070056 const string &device_name,
Paul Stewartd32f4842012-01-11 16:08:13 -080057 const string &request_hostname,
Paul Stewartd408fdf2012-05-07 17:15:57 -070058 const string &lease_file_suffix,
59 bool arp_gateway,
Darin Petkov3258a812011-06-23 11:28:45 -070060 GLib *glib)
Chris Masone0756f232011-07-21 17:24:00 -070061 : IPConfig(control_interface, device_name, kType),
Darin Petkovab565bb2011-10-06 02:55:51 -070062 proxy_factory_(ProxyFactory::GetInstance()),
Darin Petkovd1b715b2011-06-02 21:21:22 -070063 provider_(provider),
Paul Stewartd32f4842012-01-11 16:08:13 -080064 request_hostname_(request_hostname),
Paul Stewartd408fdf2012-05-07 17:15:57 -070065 lease_file_suffix_(lease_file_suffix),
66 arp_gateway_(arp_gateway),
Darin Petkovf7897bc2011-06-08 17:13:36 -070067 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070068 child_watch_tag_(0),
69 root_("/"),
Darin Petkova7b89492011-07-27 12:48:17 -070070 dispatcher_(dispatcher),
Darin Petkovf7897bc2011-06-08 17:13:36 -070071 glib_(glib) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070072 SLOG(DHCP, 2) << __func__ << ": " << device_name;
Paul Stewartd408fdf2012-05-07 17:15:57 -070073 if (lease_file_suffix_.empty()) {
74 lease_file_suffix_ = device_name;
75 }
Darin Petkov50308cd2011-06-01 18:25:07 -070076}
77
78DHCPConfig::~DHCPConfig() {
Ben Chanfad4a0b2012-04-18 15:49:59 -070079 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070080
81 // Don't leave behind dhcpcd running.
82 Stop();
83
Darin Petkov98dd6a02011-06-10 15:12:57 -070084 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070085 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070086}
87
Darin Petkov92c43902011-06-09 20:46:06 -070088bool DHCPConfig::RequestIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -070089 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070090 if (!pid_) {
91 return Start();
92 }
93 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070094 LOG(ERROR) << "Unable to request IP before acquiring destination.";
95 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070096 }
Darin Petkov92c43902011-06-09 20:46:06 -070097 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070098}
99
Darin Petkov92c43902011-06-09 20:46:06 -0700100bool DHCPConfig::RenewIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700101 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700102 if (!pid_) {
103 return false;
104 }
Darin Petkovaceede32011-07-18 15:32:38 -0700105 proxy_->Rebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -0700106 return true;
107}
108
Darin Petkov92c43902011-06-09 20:46:06 -0700109bool DHCPConfig::ReleaseIP() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700110 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700111 if (!pid_) {
112 return true;
113 }
Darin Petkova7b89492011-07-27 12:48:17 -0700114 if (proxy_.get()) {
115 proxy_->Release(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700116 }
Darin Petkov98dd6a02011-06-10 15:12:57 -0700117 Stop();
118 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700119}
120
Darin Petkova7b89492011-07-27 12:48:17 -0700121void DHCPConfig::InitProxy(const string &service) {
Darin Petkova7b89492011-07-27 12:48:17 -0700122 if (!proxy_.get()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700123 SLOG(DHCP, 2) << "Init DHCP Proxy: " << device_name() << " at " << service;
Darin Petkovab565bb2011-10-06 02:55:51 -0700124 proxy_.reset(proxy_factory_->CreateDHCPProxy(service));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700125 }
126}
127
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700128void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700129 const Configuration &configuration) {
130 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700131 if (reason == kReasonFail) {
132 LOG(ERROR) << "Received failure event from DHCP client.";
133 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700134 return;
135 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700136 if (reason != kReasonBound &&
137 reason != kReasonRebind &&
138 reason != kReasonReboot &&
139 reason != kReasonRenew) {
140 LOG(WARNING) << "Event ignored.";
141 return;
142 }
143 IPConfig::Properties properties;
144 CHECK(ParseConfiguration(configuration, &properties));
145 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700146}
147
Darin Petkovd1b715b2011-06-02 21:21:22 -0700148bool DHCPConfig::Start() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700149 SLOG(DHCP, 2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700150
Paul Stewartd32f4842012-01-11 16:08:13 -0800151 vector<char *> args;
152 args.push_back(const_cast<char *>(kDHCPCDPath));
Paul Stewartd408fdf2012-05-07 17:15:57 -0700153 args.push_back(const_cast<char *>("-B")); // Run in foreground.
Paul Stewartd32f4842012-01-11 16:08:13 -0800154 if (!request_hostname_.empty()) {
Paul Stewartd408fdf2012-05-07 17:15:57 -0700155 args.push_back(const_cast<char *>("-h")); // Request hostname from server.
Paul Stewartd32f4842012-01-11 16:08:13 -0800156 args.push_back(const_cast<char *>(request_hostname_.c_str()));
157 }
Paul Stewartd408fdf2012-05-07 17:15:57 -0700158 if (arp_gateway_) {
159 args.push_back(const_cast<char *>("-R")); // ARP for default gateway.
160 }
161 string interface_arg(device_name());
162 if (lease_file_suffix_ != device_name()) {
163 interface_arg = base::StringPrintf("%s=%s", device_name().c_str(),
164 lease_file_suffix_.c_str());
165 }
166 args.push_back(const_cast<char *>(interface_arg.c_str()));
Paul Stewartd32f4842012-01-11 16:08:13 -0800167 args.push_back(NULL);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700168 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700169
Darin Petkov98dd6a02011-06-10 15:12:57 -0700170 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700171 if (!glib_->SpawnAsync(NULL,
Paul Stewartd32f4842012-01-11 16:08:13 -0800172 args.data(),
Darin Petkovf7897bc2011-06-08 17:13:36 -0700173 envp,
174 G_SPAWN_DO_NOT_REAP_CHILD,
175 NULL,
176 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700177 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700178 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700179 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
180 return false;
181 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700182 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700183 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700184 CHECK(!child_watch_tag_);
185 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700186 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700187}
188
Darin Petkov92c43902011-06-09 20:46:06 -0700189void DHCPConfig::Stop() {
190 if (pid_) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700191 SLOG(DHCP, 2) << "Terminating " << pid_;
Thieu Le94eed562012-02-21 15:57:29 -0800192 if (kill(pid_, SIGTERM) < 0) {
193 PLOG(ERROR);
194 return;
195 }
196 pid_t ret;
197 int num_iterations =
198 kDHCPCDExitWaitMilliseconds / kDHCPCDExitPollMilliseconds;
199 for (int count = 0; count < num_iterations; ++count) {
200 ret = waitpid(pid_, NULL, WNOHANG);
201 if (ret == pid_ || ret == -1)
202 break;
203 usleep(kDHCPCDExitPollMilliseconds * 1000);
204 if (count == num_iterations / 2) // Make one last attempt to kill dhcpcd.
205 kill(pid_, SIGKILL);
206 }
207 if (ret != pid_)
208 PLOG(ERROR);
Darin Petkov92c43902011-06-09 20:46:06 -0700209 }
210}
211
Darin Petkov98dd6a02011-06-10 15:12:57 -0700212bool DHCPConfig::Restart() {
213 // Check to ensure that this instance doesn't get destroyed in the middle of
214 // this call. If stopping a running client while there's only one reference to
215 // this instance, we will end up destroying it when the PID is unbound from
216 // the Provider. Since the Provider doesn't invoke Restart, this would mean
217 // that Restart was erroneously executed through a bare reference.
218 CHECK(!pid_ || !HasOneRef());
219 Stop();
220 if (pid_) {
221 provider_->UnbindPID(pid_);
222 }
223 CleanupClientState();
224 return Start();
225}
226
Darin Petkove7cb7f82011-06-03 13:21:51 -0700227string DHCPConfig::GetIPv4AddressString(unsigned int address) {
228 char str[INET_ADDRSTRLEN];
229 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
230 return str;
231 }
232 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
233 return "";
234}
235
236bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
237 IPConfig::Properties *properties) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700238 SLOG(DHCP, 2) << __func__;
Chris Masone43b48a12011-07-01 13:37:07 -0700239 properties->method = flimflam::kTypeDHCP;
Paul Stewart7355ce12011-09-02 10:47:01 -0700240 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700241 for (Configuration::const_iterator it = configuration.begin();
242 it != configuration.end(); ++it) {
243 const string &key = it->first;
244 const DBus::Variant &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700245 SLOG(DHCP, 2) << "Processing key: " << key;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700246 if (key == kConfigurationKeyIPAddress) {
247 properties->address = GetIPv4AddressString(value.reader().get_uint32());
248 if (properties->address.empty()) {
249 return false;
250 }
251 } else if (key == kConfigurationKeySubnetCIDR) {
Paul Stewart48100b02012-03-19 07:53:52 -0700252 properties->subnet_prefix = value.reader().get_byte();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700253 } else if (key == kConfigurationKeyBroadcastAddress) {
254 properties->broadcast_address =
255 GetIPv4AddressString(value.reader().get_uint32());
256 if (properties->broadcast_address.empty()) {
257 return false;
258 }
259 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700260 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700261 if (routers.empty()) {
262 LOG(ERROR) << "No routers provided.";
263 return false;
264 }
265 properties->gateway = GetIPv4AddressString(routers[0]);
266 if (properties->gateway.empty()) {
267 return false;
268 }
269 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700270 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700271 for (vector<unsigned int>::const_iterator it = servers.begin();
272 it != servers.end(); ++it) {
273 string server = GetIPv4AddressString(*it);
274 if (server.empty()) {
275 return false;
276 }
277 properties->dns_servers.push_back(server);
278 }
279 } else if (key == kConfigurationKeyDomainName) {
280 properties->domain_name = value.reader().get_string();
281 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700282 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700283 } else if (key == kConfigurationKeyMTU) {
284 int mtu = value.reader().get_uint16();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100285 if (mtu >= kMinMTU) {
Darin Petkove7cb7f82011-06-03 13:21:51 -0700286 properties->mtu = mtu;
287 }
288 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700289 SLOG(DHCP, 2) << "Key ignored.";
Darin Petkove7cb7f82011-06-03 13:21:51 -0700290 }
291 }
292 return true;
293}
294
Darin Petkov92c43902011-06-09 20:46:06 -0700295void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700296 SLOG(DHCP, 2) << "pid " << pid << " exit status " << status;
Darin Petkov92c43902011-06-09 20:46:06 -0700297 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
298 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700299 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700300 config->CleanupClientState();
301
302 // |config| instance may be destroyed after this call.
303 config->provider_->UnbindPID(pid);
304}
305
306void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700307 if (child_watch_tag_) {
308 glib_->SourceRemove(child_watch_tag_);
309 child_watch_tag_ = 0;
310 }
311 if (pid_) {
312 glib_->SpawnClosePID(pid_);
313 pid_ = 0;
314 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700315 proxy_.reset();
Paul Stewartd408fdf2012-05-07 17:15:57 -0700316 if (lease_file_suffix_ == device_name()) {
317 // If the lease file suffix was left as default, clean it up at exit.
318 file_util::Delete(root_.Append(
319 base::StringPrintf(kDHCPCDPathFormatLease,
320 device_name().c_str())), false);
321 }
322 file_util::Delete(root_.Append(
323 base::StringPrintf(kDHCPCDPathFormatPID, device_name().c_str())), false);
Darin Petkov92c43902011-06-09 20:46:06 -0700324}
325
Darin Petkov50308cd2011-06-01 18:25:07 -0700326} // namespace shill