blob: 2511e2fcdc6a8bef152262702ce93cd37b5ba520 [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
Eric Shienbrood3e20a232012-02-16 11:35:56 -050010#include <base/bind.h>
Darin Petkov92c43902011-06-09 20:46:06 -070011#include <base/file_util.h>
Darin Petkov50308cd2011-06-01 18:25:07 -070012#include <base/logging.h>
Darin Petkov92c43902011-06-09 20:46:06 -070013#include <base/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"
Darin Petkovaceede32011-07-18 15:32:38 -070021#include "shill/proxy_factory.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070022
Eric Shienbrood3e20a232012-02-16 11:35:56 -050023using base::Bind;
Darin Petkove7cb7f82011-06-03 13:21:51 -070024using std::string;
25using std::vector;
26
Darin Petkov50308cd2011-06-01 18:25:07 -070027namespace shill {
28
Chris Masone0756f232011-07-21 17:24:00 -070029// static
Darin Petkove7cb7f82011-06-03 13:21:51 -070030const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
31const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
32const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
33const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
34const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
35const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
36const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
37const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Thieu Le94eed562012-02-21 15:57:29 -080038const int DHCPConfig::kDHCPCDExitPollMilliseconds = 50;
39const int DHCPConfig::kDHCPCDExitWaitMilliseconds = 3000;
Darin Petkovd1b715b2011-06-02 21:21:22 -070040const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Darin Petkov92c43902011-06-09 20:46:06 -070041const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
42const 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,
Darin Petkov3258a812011-06-23 11:28:45 -070058 GLib *glib)
Chris Masone0756f232011-07-21 17:24:00 -070059 : IPConfig(control_interface, device_name, kType),
Darin Petkovab565bb2011-10-06 02:55:51 -070060 proxy_factory_(ProxyFactory::GetInstance()),
Darin Petkovd1b715b2011-06-02 21:21:22 -070061 provider_(provider),
Paul Stewartd32f4842012-01-11 16:08:13 -080062 request_hostname_(request_hostname),
Darin Petkovf7897bc2011-06-08 17:13:36 -070063 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070064 child_watch_tag_(0),
65 root_("/"),
Darin Petkova7b89492011-07-27 12:48:17 -070066 dispatcher_(dispatcher),
Darin Petkovf7897bc2011-06-08 17:13:36 -070067 glib_(glib) {
mukesh agrawalde29fa82011-09-16 16:16:36 -070068 mutable_store()->RegisterConstString(flimflam::kAddressProperty,
69 &(properties().address));
Darin Petkovf65e9282011-06-21 14:29:56 -070070 VLOG(2) << __func__ << ": " << device_name;
Darin Petkov50308cd2011-06-01 18:25:07 -070071}
72
73DHCPConfig::~DHCPConfig() {
Darin Petkovf65e9282011-06-21 14:29:56 -070074 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070075
76 // Don't leave behind dhcpcd running.
77 Stop();
78
Darin Petkov98dd6a02011-06-10 15:12:57 -070079 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070080 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070081}
82
Darin Petkov92c43902011-06-09 20:46:06 -070083bool DHCPConfig::RequestIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070084 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070085 if (!pid_) {
86 return Start();
87 }
88 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070089 LOG(ERROR) << "Unable to request IP before acquiring destination.";
90 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070091 }
Darin Petkov92c43902011-06-09 20:46:06 -070092 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070093}
94
Darin Petkov92c43902011-06-09 20:46:06 -070095bool DHCPConfig::RenewIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070096 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070097 if (!pid_) {
98 return false;
99 }
Darin Petkovaceede32011-07-18 15:32:38 -0700100 proxy_->Rebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -0700101 return true;
102}
103
Darin Petkov92c43902011-06-09 20:46:06 -0700104bool DHCPConfig::ReleaseIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -0700105 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -0700106 if (!pid_) {
107 return true;
108 }
Darin Petkova7b89492011-07-27 12:48:17 -0700109 if (proxy_.get()) {
110 proxy_->Release(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700111 }
Darin Petkov98dd6a02011-06-10 15:12:57 -0700112 Stop();
113 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700114}
115
Darin Petkova7b89492011-07-27 12:48:17 -0700116void DHCPConfig::InitProxy(const string &service) {
117 // Defer proxy creation because dbus-c++ doesn't allow registration of new
118 // D-Bus objects in the context of a D-Bus signal handler.
Darin Petkovd1b715b2011-06-02 21:21:22 -0700119 if (!proxy_.get()) {
Darin Petkova7b89492011-07-27 12:48:17 -0700120 dispatcher_->PostTask(
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500121 Bind(&DHCPConfig::InitProxyTask, AsWeakPtr(), service));
Darin Petkova7b89492011-07-27 12:48:17 -0700122 }
123}
124
125void DHCPConfig::InitProxyTask(const string &service) {
126 if (!proxy_.get()) {
127 VLOG(2) << "Init DHCP Proxy: " << device_name() << " at " << service;
Darin Petkovab565bb2011-10-06 02:55:51 -0700128 proxy_.reset(proxy_factory_->CreateDHCPProxy(service));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700129 }
130}
131
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700132void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700133 const Configuration &configuration) {
134 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700135 if (reason == kReasonFail) {
136 LOG(ERROR) << "Received failure event from DHCP client.";
137 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700138 return;
139 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700140 if (reason != kReasonBound &&
141 reason != kReasonRebind &&
142 reason != kReasonReboot &&
143 reason != kReasonRenew) {
144 LOG(WARNING) << "Event ignored.";
145 return;
146 }
147 IPConfig::Properties properties;
148 CHECK(ParseConfiguration(configuration, &properties));
149 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700150}
151
Darin Petkovd1b715b2011-06-02 21:21:22 -0700152bool DHCPConfig::Start() {
Darin Petkovf65e9282011-06-21 14:29:56 -0700153 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700154
Paul Stewartd32f4842012-01-11 16:08:13 -0800155 vector<char *> args;
156 args.push_back(const_cast<char *>(kDHCPCDPath));
157 args.push_back(const_cast<char *>("-B")); // foreground
158 args.push_back(const_cast<char *>(device_name().c_str()));
159 if (!request_hostname_.empty()) {
160 args.push_back(const_cast<char *>("-h")); // request hostname
161 args.push_back(const_cast<char *>(request_hostname_.c_str()));
162 }
163 args.push_back(NULL);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700164 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700165
Darin Petkov98dd6a02011-06-10 15:12:57 -0700166 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700167 if (!glib_->SpawnAsync(NULL,
Paul Stewartd32f4842012-01-11 16:08:13 -0800168 args.data(),
Darin Petkovf7897bc2011-06-08 17:13:36 -0700169 envp,
170 G_SPAWN_DO_NOT_REAP_CHILD,
171 NULL,
172 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700173 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700174 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700175 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
176 return false;
177 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700178 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700179 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700180 CHECK(!child_watch_tag_);
181 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700182 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700183}
184
Darin Petkov92c43902011-06-09 20:46:06 -0700185void DHCPConfig::Stop() {
186 if (pid_) {
187 VLOG(2) << "Terminating " << pid_;
Thieu Le94eed562012-02-21 15:57:29 -0800188 if (kill(pid_, SIGTERM) < 0) {
189 PLOG(ERROR);
190 return;
191 }
192 pid_t ret;
193 int num_iterations =
194 kDHCPCDExitWaitMilliseconds / kDHCPCDExitPollMilliseconds;
195 for (int count = 0; count < num_iterations; ++count) {
196 ret = waitpid(pid_, NULL, WNOHANG);
197 if (ret == pid_ || ret == -1)
198 break;
199 usleep(kDHCPCDExitPollMilliseconds * 1000);
200 if (count == num_iterations / 2) // Make one last attempt to kill dhcpcd.
201 kill(pid_, SIGKILL);
202 }
203 if (ret != pid_)
204 PLOG(ERROR);
Darin Petkov92c43902011-06-09 20:46:06 -0700205 }
206}
207
Darin Petkov98dd6a02011-06-10 15:12:57 -0700208bool DHCPConfig::Restart() {
209 // Check to ensure that this instance doesn't get destroyed in the middle of
210 // this call. If stopping a running client while there's only one reference to
211 // this instance, we will end up destroying it when the PID is unbound from
212 // the Provider. Since the Provider doesn't invoke Restart, this would mean
213 // that Restart was erroneously executed through a bare reference.
214 CHECK(!pid_ || !HasOneRef());
215 Stop();
216 if (pid_) {
217 provider_->UnbindPID(pid_);
218 }
219 CleanupClientState();
220 return Start();
221}
222
Darin Petkove7cb7f82011-06-03 13:21:51 -0700223string DHCPConfig::GetIPv4AddressString(unsigned int address) {
224 char str[INET_ADDRSTRLEN];
225 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
226 return str;
227 }
228 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
229 return "";
230}
231
232bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
233 IPConfig::Properties *properties) {
234 VLOG(2) << __func__;
Chris Masone43b48a12011-07-01 13:37:07 -0700235 properties->method = flimflam::kTypeDHCP;
Paul Stewart7355ce12011-09-02 10:47:01 -0700236 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700237 for (Configuration::const_iterator it = configuration.begin();
238 it != configuration.end(); ++it) {
239 const string &key = it->first;
240 const DBus::Variant &value = it->second;
241 VLOG(2) << "Processing key: " << key;
242 if (key == kConfigurationKeyIPAddress) {
243 properties->address = GetIPv4AddressString(value.reader().get_uint32());
244 if (properties->address.empty()) {
245 return false;
246 }
247 } else if (key == kConfigurationKeySubnetCIDR) {
Paul Stewart48100b02012-03-19 07:53:52 -0700248 properties->subnet_prefix = value.reader().get_byte();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700249 } else if (key == kConfigurationKeyBroadcastAddress) {
250 properties->broadcast_address =
251 GetIPv4AddressString(value.reader().get_uint32());
252 if (properties->broadcast_address.empty()) {
253 return false;
254 }
255 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700256 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700257 if (routers.empty()) {
258 LOG(ERROR) << "No routers provided.";
259 return false;
260 }
261 properties->gateway = GetIPv4AddressString(routers[0]);
262 if (properties->gateway.empty()) {
263 return false;
264 }
265 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700266 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700267 for (vector<unsigned int>::const_iterator it = servers.begin();
268 it != servers.end(); ++it) {
269 string server = GetIPv4AddressString(*it);
270 if (server.empty()) {
271 return false;
272 }
273 properties->dns_servers.push_back(server);
274 }
275 } else if (key == kConfigurationKeyDomainName) {
276 properties->domain_name = value.reader().get_string();
277 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700278 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700279 } else if (key == kConfigurationKeyMTU) {
280 int mtu = value.reader().get_uint16();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100281 if (mtu >= kMinMTU) {
Darin Petkove7cb7f82011-06-03 13:21:51 -0700282 properties->mtu = mtu;
283 }
284 } else {
285 VLOG(2) << "Key ignored.";
286 }
287 }
288 return true;
289}
290
Darin Petkov92c43902011-06-09 20:46:06 -0700291void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
292 VLOG(2) << "pid " << pid << " exit status " << status;
293 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
294 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700295 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700296 config->CleanupClientState();
297
298 // |config| instance may be destroyed after this call.
299 config->provider_->UnbindPID(pid);
300}
301
302void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700303 if (child_watch_tag_) {
304 glib_->SourceRemove(child_watch_tag_);
305 child_watch_tag_ = 0;
306 }
307 if (pid_) {
308 glib_->SpawnClosePID(pid_);
309 pid_ = 0;
310 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700311 proxy_.reset();
Darin Petkov92c43902011-06-09 20:46:06 -0700312 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
Darin Petkovf65e9282011-06-21 14:29:56 -0700313 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700314 false);
315 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
Darin Petkovf65e9282011-06-21 14:29:56 -0700316 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700317 false);
318}
319
Darin Petkov50308cd2011-06-01 18:25:07 -0700320} // namespace shill