blob: 8b4732b28f5ef96b8b255508373972c770b3373a [file] [log] [blame]
Darin Petkov50308cd2011-06-01 18:25:07 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2// 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>
8
Darin Petkov92c43902011-06-09 20:46:06 -07009#include <base/file_util.h>
Darin Petkov50308cd2011-06-01 18:25:07 -070010#include <base/logging.h>
Darin Petkov92c43902011-06-09 20:46:06 -070011#include <base/stringprintf.h>
Chris Masone43b48a12011-07-01 13:37:07 -070012#include <chromeos/dbus/service_constants.h>
Darin Petkovd1b715b2011-06-02 21:21:22 -070013
14#include "shill/dhcpcd_proxy.h"
15#include "shill/dhcp_provider.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070016#include "shill/event_dispatcher.h"
Darin Petkov3258a812011-06-23 11:28:45 -070017#include "shill/glib.h"
Paul Stewart1d18e8c2011-07-15 11:00:31 -070018#include "shill/ip_address.h"
Darin Petkovaceede32011-07-18 15:32:38 -070019#include "shill/proxy_factory.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070020
Darin Petkove7cb7f82011-06-03 13:21:51 -070021using std::string;
22using std::vector;
23
Darin Petkov50308cd2011-06-01 18:25:07 -070024namespace shill {
25
Chris Masone0756f232011-07-21 17:24:00 -070026// static
Darin Petkove7cb7f82011-06-03 13:21:51 -070027const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
28const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
29const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
30const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
31const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
32const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
33const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
34const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Darin Petkovd1b715b2011-06-02 21:21:22 -070035const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Darin Petkov92c43902011-06-09 20:46:06 -070036const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
37const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070038const char DHCPConfig::kReasonBound[] = "BOUND";
39const char DHCPConfig::kReasonFail[] = "FAIL";
40const char DHCPConfig::kReasonRebind[] = "REBIND";
41const char DHCPConfig::kReasonReboot[] = "REBOOT";
42const char DHCPConfig::kReasonRenew[] = "RENEW";
Chris Masone0756f232011-07-21 17:24:00 -070043// static
44const char DHCPConfig::kType[] = "dhcp";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070045
Darin Petkove7cb7f82011-06-03 13:21:51 -070046
Chris Masone19e30402011-07-19 15:48:47 -070047DHCPConfig::DHCPConfig(ControlInterface *control_interface,
Darin Petkova7b89492011-07-27 12:48:17 -070048 EventDispatcher *dispatcher,
Chris Masone19e30402011-07-19 15:48:47 -070049 DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070050 const string &device_name,
Darin Petkov3258a812011-06-23 11:28:45 -070051 GLib *glib)
Chris Masone0756f232011-07-21 17:24:00 -070052 : IPConfig(control_interface, device_name, kType),
Darin Petkovab565bb2011-10-06 02:55:51 -070053 proxy_factory_(ProxyFactory::GetInstance()),
Darin Petkovd1b715b2011-06-02 21:21:22 -070054 provider_(provider),
Darin Petkovf7897bc2011-06-08 17:13:36 -070055 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070056 child_watch_tag_(0),
57 root_("/"),
Darin Petkova7b89492011-07-27 12:48:17 -070058 task_factory_(this),
59 dispatcher_(dispatcher),
Darin Petkovf7897bc2011-06-08 17:13:36 -070060 glib_(glib) {
mukesh agrawalde29fa82011-09-16 16:16:36 -070061 mutable_store()->RegisterConstString(flimflam::kAddressProperty,
62 &(properties().address));
Darin Petkovf65e9282011-06-21 14:29:56 -070063 VLOG(2) << __func__ << ": " << device_name;
Darin Petkov50308cd2011-06-01 18:25:07 -070064}
65
66DHCPConfig::~DHCPConfig() {
Darin Petkovf65e9282011-06-21 14:29:56 -070067 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070068
69 // Don't leave behind dhcpcd running.
70 Stop();
71
Darin Petkov98dd6a02011-06-10 15:12:57 -070072 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070073 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070074}
75
Darin Petkov92c43902011-06-09 20:46:06 -070076bool DHCPConfig::RequestIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070077 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070078 if (!pid_) {
79 return Start();
80 }
81 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070082 LOG(ERROR) << "Unable to request IP before acquiring destination.";
83 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070084 }
Darin Petkov92c43902011-06-09 20:46:06 -070085 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070086}
87
Darin Petkov92c43902011-06-09 20:46:06 -070088bool DHCPConfig::RenewIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070089 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070090 if (!pid_) {
91 return false;
92 }
Darin Petkovaceede32011-07-18 15:32:38 -070093 proxy_->Rebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -070094 return true;
95}
96
Darin Petkov92c43902011-06-09 20:46:06 -070097bool DHCPConfig::ReleaseIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070098 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070099 if (!pid_) {
100 return true;
101 }
Darin Petkova7b89492011-07-27 12:48:17 -0700102 if (proxy_.get()) {
103 proxy_->Release(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700104 }
Darin Petkov98dd6a02011-06-10 15:12:57 -0700105 Stop();
106 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700107}
108
Darin Petkova7b89492011-07-27 12:48:17 -0700109void DHCPConfig::InitProxy(const string &service) {
110 // Defer proxy creation because dbus-c++ doesn't allow registration of new
111 // D-Bus objects in the context of a D-Bus signal handler.
Darin Petkovd1b715b2011-06-02 21:21:22 -0700112 if (!proxy_.get()) {
Darin Petkova7b89492011-07-27 12:48:17 -0700113 dispatcher_->PostTask(
114 task_factory_.NewRunnableMethod(&DHCPConfig::InitProxyTask, service));
115 }
116}
117
118void DHCPConfig::InitProxyTask(const string &service) {
119 if (!proxy_.get()) {
120 VLOG(2) << "Init DHCP Proxy: " << device_name() << " at " << service;
Darin Petkovab565bb2011-10-06 02:55:51 -0700121 proxy_.reset(proxy_factory_->CreateDHCPProxy(service));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700122 }
123}
124
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700125void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700126 const Configuration &configuration) {
127 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700128 if (reason == kReasonFail) {
129 LOG(ERROR) << "Received failure event from DHCP client.";
130 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700131 return;
132 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700133 if (reason != kReasonBound &&
134 reason != kReasonRebind &&
135 reason != kReasonReboot &&
136 reason != kReasonRenew) {
137 LOG(WARNING) << "Event ignored.";
138 return;
139 }
140 IPConfig::Properties properties;
141 CHECK(ParseConfiguration(configuration, &properties));
142 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700143}
144
Darin Petkovd1b715b2011-06-02 21:21:22 -0700145bool DHCPConfig::Start() {
Darin Petkovf65e9282011-06-21 14:29:56 -0700146 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700147
Darin Petkov98dd6a02011-06-10 15:12:57 -0700148 char *argv[4] = {
149 const_cast<char *>(kDHCPCDPath),
150 const_cast<char *>("-B"), // foreground
Darin Petkovf65e9282011-06-21 14:29:56 -0700151 const_cast<char *>(device_name().c_str()),
Darin Petkov98dd6a02011-06-10 15:12:57 -0700152 NULL
153 };
154 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700155
Darin Petkov98dd6a02011-06-10 15:12:57 -0700156 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700157 if (!glib_->SpawnAsync(NULL,
158 argv,
159 envp,
160 G_SPAWN_DO_NOT_REAP_CHILD,
161 NULL,
162 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700163 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700164 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700165 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
166 return false;
167 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700168 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700169 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700170 CHECK(!child_watch_tag_);
171 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700172 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700173}
174
Darin Petkov92c43902011-06-09 20:46:06 -0700175void DHCPConfig::Stop() {
176 if (pid_) {
177 VLOG(2) << "Terminating " << pid_;
178 PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
179 }
180}
181
Darin Petkov98dd6a02011-06-10 15:12:57 -0700182bool DHCPConfig::Restart() {
183 // Check to ensure that this instance doesn't get destroyed in the middle of
184 // this call. If stopping a running client while there's only one reference to
185 // this instance, we will end up destroying it when the PID is unbound from
186 // the Provider. Since the Provider doesn't invoke Restart, this would mean
187 // that Restart was erroneously executed through a bare reference.
188 CHECK(!pid_ || !HasOneRef());
189 Stop();
190 if (pid_) {
191 provider_->UnbindPID(pid_);
192 }
193 CleanupClientState();
194 return Start();
195}
196
Darin Petkove7cb7f82011-06-03 13:21:51 -0700197string DHCPConfig::GetIPv4AddressString(unsigned int address) {
198 char str[INET_ADDRSTRLEN];
199 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
200 return str;
201 }
202 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
203 return "";
204}
205
206bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
207 IPConfig::Properties *properties) {
208 VLOG(2) << __func__;
Chris Masone43b48a12011-07-01 13:37:07 -0700209 properties->method = flimflam::kTypeDHCP;
Paul Stewart7355ce12011-09-02 10:47:01 -0700210 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700211 for (Configuration::const_iterator it = configuration.begin();
212 it != configuration.end(); ++it) {
213 const string &key = it->first;
214 const DBus::Variant &value = it->second;
215 VLOG(2) << "Processing key: " << key;
216 if (key == kConfigurationKeyIPAddress) {
217 properties->address = GetIPv4AddressString(value.reader().get_uint32());
218 if (properties->address.empty()) {
219 return false;
220 }
221 } else if (key == kConfigurationKeySubnetCIDR) {
222 properties->subnet_cidr = value.reader().get_byte();
223 } else if (key == kConfigurationKeyBroadcastAddress) {
224 properties->broadcast_address =
225 GetIPv4AddressString(value.reader().get_uint32());
226 if (properties->broadcast_address.empty()) {
227 return false;
228 }
229 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700230 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700231 if (routers.empty()) {
232 LOG(ERROR) << "No routers provided.";
233 return false;
234 }
235 properties->gateway = GetIPv4AddressString(routers[0]);
236 if (properties->gateway.empty()) {
237 return false;
238 }
239 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700240 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700241 for (vector<unsigned int>::const_iterator it = servers.begin();
242 it != servers.end(); ++it) {
243 string server = GetIPv4AddressString(*it);
244 if (server.empty()) {
245 return false;
246 }
247 properties->dns_servers.push_back(server);
248 }
249 } else if (key == kConfigurationKeyDomainName) {
250 properties->domain_name = value.reader().get_string();
251 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700252 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700253 } else if (key == kConfigurationKeyMTU) {
254 int mtu = value.reader().get_uint16();
255 if (mtu >= 576) {
256 properties->mtu = mtu;
257 }
258 } else {
259 VLOG(2) << "Key ignored.";
260 }
261 }
262 return true;
263}
264
Darin Petkov92c43902011-06-09 20:46:06 -0700265void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
266 VLOG(2) << "pid " << pid << " exit status " << status;
267 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
268 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700269 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700270 config->CleanupClientState();
271
272 // |config| instance may be destroyed after this call.
273 config->provider_->UnbindPID(pid);
274}
275
276void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700277 if (child_watch_tag_) {
278 glib_->SourceRemove(child_watch_tag_);
279 child_watch_tag_ = 0;
280 }
281 if (pid_) {
282 glib_->SpawnClosePID(pid_);
283 pid_ = 0;
284 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700285 proxy_.reset();
Darin Petkov92c43902011-06-09 20:46:06 -0700286 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
Darin Petkovf65e9282011-06-21 14:29:56 -0700287 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700288 false);
289 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
Darin Petkovf65e9282011-06-21 14:29:56 -0700290 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700291 false);
292}
293
Darin Petkov50308cd2011-06-01 18:25:07 -0700294} // namespace shill