blob: b3605bef380d7fc5bef58ea576760d7a85627579 [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"
Darin Petkov3258a812011-06-23 11:28:45 -070016#include "shill/glib.h"
Paul Stewart1d18e8c2011-07-15 11:00:31 -070017#include "shill/ip_address.h"
Darin Petkovaceede32011-07-18 15:32:38 -070018#include "shill/proxy_factory.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070019
Darin Petkove7cb7f82011-06-03 13:21:51 -070020using std::string;
21using std::vector;
22
Darin Petkov50308cd2011-06-01 18:25:07 -070023namespace shill {
24
Darin Petkove7cb7f82011-06-03 13:21:51 -070025const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
26const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
27const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
28const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
29const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
30const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
31const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
32const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Darin Petkovd1b715b2011-06-02 21:21:22 -070033const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Darin Petkov92c43902011-06-09 20:46:06 -070034const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
35const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
Darin Petkovf9b0ca82011-06-20 12:10:23 -070036const char DHCPConfig::kReasonBound[] = "BOUND";
37const char DHCPConfig::kReasonFail[] = "FAIL";
38const char DHCPConfig::kReasonRebind[] = "REBIND";
39const char DHCPConfig::kReasonReboot[] = "REBOOT";
40const char DHCPConfig::kReasonRenew[] = "RENEW";
41
Darin Petkove7cb7f82011-06-03 13:21:51 -070042
Chris Masone19e30402011-07-19 15:48:47 -070043DHCPConfig::DHCPConfig(ControlInterface *control_interface,
44 DHCPProvider *provider,
Darin Petkovf65e9282011-06-21 14:29:56 -070045 const string &device_name,
Darin Petkov3258a812011-06-23 11:28:45 -070046 GLib *glib)
Chris Masone19e30402011-07-19 15:48:47 -070047 : IPConfig(control_interface, device_name),
Darin Petkovd1b715b2011-06-02 21:21:22 -070048 provider_(provider),
Darin Petkovf7897bc2011-06-08 17:13:36 -070049 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070050 child_watch_tag_(0),
51 root_("/"),
Darin Petkovf7897bc2011-06-08 17:13:36 -070052 glib_(glib) {
Chris Masone27c4aa52011-07-02 13:10:14 -070053 store_.RegisterConstString(flimflam::kAddressProperty,
54 &(properties().address));
Darin Petkovf65e9282011-06-21 14:29:56 -070055 VLOG(2) << __func__ << ": " << device_name;
Darin Petkov50308cd2011-06-01 18:25:07 -070056}
57
58DHCPConfig::~DHCPConfig() {
Darin Petkovf65e9282011-06-21 14:29:56 -070059 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov92c43902011-06-09 20:46:06 -070060
61 // Don't leave behind dhcpcd running.
62 Stop();
63
Darin Petkov98dd6a02011-06-10 15:12:57 -070064 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070065 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070066}
67
Darin Petkov92c43902011-06-09 20:46:06 -070068bool DHCPConfig::RequestIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070069 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -070070 if (!pid_) {
71 return Start();
72 }
73 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070074 LOG(ERROR) << "Unable to request IP before acquiring destination.";
75 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070076 }
Darin Petkov92c43902011-06-09 20:46:06 -070077 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070078}
79
Darin Petkov92c43902011-06-09 20:46:06 -070080bool DHCPConfig::RenewIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070081 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070082 if (!pid_) {
83 return false;
84 }
85 if (!proxy_.get()) {
86 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
Darin Petkovd1b715b2011-06-02 21:21:22 -070087 return false;
88 }
Darin Petkovaceede32011-07-18 15:32:38 -070089 proxy_->Rebind(device_name());
Darin Petkovd1b715b2011-06-02 21:21:22 -070090 return true;
91}
92
Darin Petkov92c43902011-06-09 20:46:06 -070093bool DHCPConfig::ReleaseIP() {
Darin Petkovf65e9282011-06-21 14:29:56 -070094 VLOG(2) << __func__ << ": " << device_name();
Darin Petkov98dd6a02011-06-10 15:12:57 -070095 if (!pid_) {
96 return true;
97 }
98 if (!proxy_.get()) {
99 LOG(ERROR) << "Unable to release IP before acquiring destination.";
100 return false;
101 }
Darin Petkovaceede32011-07-18 15:32:38 -0700102 proxy_->Release(device_name());
Darin Petkov98dd6a02011-06-10 15:12:57 -0700103 Stop();
104 return true;
Darin Petkov92c43902011-06-09 20:46:06 -0700105}
106
Darin Petkovaceede32011-07-18 15:32:38 -0700107void DHCPConfig::InitProxy(const char *service) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700108 if (!proxy_.get()) {
Darin Petkovaceede32011-07-18 15:32:38 -0700109 proxy_.reset(ProxyFactory::factory()->CreateDHCPProxy(service));
Darin Petkovd1b715b2011-06-02 21:21:22 -0700110 }
111}
112
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700113void DHCPConfig::ProcessEventSignal(const string &reason,
Darin Petkove7cb7f82011-06-03 13:21:51 -0700114 const Configuration &configuration) {
115 LOG(INFO) << "Event reason: " << reason;
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700116 if (reason == kReasonFail) {
117 LOG(ERROR) << "Received failure event from DHCP client.";
118 UpdateProperties(IPConfig::Properties(), false);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700119 return;
120 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700121 if (reason != kReasonBound &&
122 reason != kReasonRebind &&
123 reason != kReasonReboot &&
124 reason != kReasonRenew) {
125 LOG(WARNING) << "Event ignored.";
126 return;
127 }
128 IPConfig::Properties properties;
129 CHECK(ParseConfiguration(configuration, &properties));
130 UpdateProperties(properties, true);
Darin Petkove7cb7f82011-06-03 13:21:51 -0700131}
132
Darin Petkovd1b715b2011-06-02 21:21:22 -0700133bool DHCPConfig::Start() {
Darin Petkovf65e9282011-06-21 14:29:56 -0700134 VLOG(2) << __func__ << ": " << device_name();
Darin Petkovd1b715b2011-06-02 21:21:22 -0700135
Darin Petkov98dd6a02011-06-10 15:12:57 -0700136 char *argv[4] = {
137 const_cast<char *>(kDHCPCDPath),
138 const_cast<char *>("-B"), // foreground
Darin Petkovf65e9282011-06-21 14:29:56 -0700139 const_cast<char *>(device_name().c_str()),
Darin Petkov98dd6a02011-06-10 15:12:57 -0700140 NULL
141 };
142 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700143
Darin Petkov98dd6a02011-06-10 15:12:57 -0700144 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700145 if (!glib_->SpawnAsync(NULL,
146 argv,
147 envp,
148 G_SPAWN_DO_NOT_REAP_CHILD,
149 NULL,
150 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700151 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700152 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700153 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
154 return false;
155 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700156 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700157 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700158 CHECK(!child_watch_tag_);
159 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700160 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700161}
162
Darin Petkov92c43902011-06-09 20:46:06 -0700163void DHCPConfig::Stop() {
164 if (pid_) {
165 VLOG(2) << "Terminating " << pid_;
166 PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
167 }
168}
169
Darin Petkov98dd6a02011-06-10 15:12:57 -0700170bool DHCPConfig::Restart() {
171 // Check to ensure that this instance doesn't get destroyed in the middle of
172 // this call. If stopping a running client while there's only one reference to
173 // this instance, we will end up destroying it when the PID is unbound from
174 // the Provider. Since the Provider doesn't invoke Restart, this would mean
175 // that Restart was erroneously executed through a bare reference.
176 CHECK(!pid_ || !HasOneRef());
177 Stop();
178 if (pid_) {
179 provider_->UnbindPID(pid_);
180 }
181 CleanupClientState();
182 return Start();
183}
184
Darin Petkove7cb7f82011-06-03 13:21:51 -0700185string DHCPConfig::GetIPv4AddressString(unsigned int address) {
186 char str[INET_ADDRSTRLEN];
187 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
188 return str;
189 }
190 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
191 return "";
192}
193
194bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
195 IPConfig::Properties *properties) {
196 VLOG(2) << __func__;
Chris Masone43b48a12011-07-01 13:37:07 -0700197 properties->method = flimflam::kTypeDHCP;
Paul Stewart1d18e8c2011-07-15 11:00:31 -0700198 properties->address_family = IPAddress::kAddressFamilyIPv4;
Darin Petkove7cb7f82011-06-03 13:21:51 -0700199 for (Configuration::const_iterator it = configuration.begin();
200 it != configuration.end(); ++it) {
201 const string &key = it->first;
202 const DBus::Variant &value = it->second;
203 VLOG(2) << "Processing key: " << key;
204 if (key == kConfigurationKeyIPAddress) {
205 properties->address = GetIPv4AddressString(value.reader().get_uint32());
206 if (properties->address.empty()) {
207 return false;
208 }
209 } else if (key == kConfigurationKeySubnetCIDR) {
210 properties->subnet_cidr = value.reader().get_byte();
211 } else if (key == kConfigurationKeyBroadcastAddress) {
212 properties->broadcast_address =
213 GetIPv4AddressString(value.reader().get_uint32());
214 if (properties->broadcast_address.empty()) {
215 return false;
216 }
217 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700218 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700219 if (routers.empty()) {
220 LOG(ERROR) << "No routers provided.";
221 return false;
222 }
223 properties->gateway = GetIPv4AddressString(routers[0]);
224 if (properties->gateway.empty()) {
225 return false;
226 }
227 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700228 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700229 for (vector<unsigned int>::const_iterator it = servers.begin();
230 it != servers.end(); ++it) {
231 string server = GetIPv4AddressString(*it);
232 if (server.empty()) {
233 return false;
234 }
235 properties->dns_servers.push_back(server);
236 }
237 } else if (key == kConfigurationKeyDomainName) {
238 properties->domain_name = value.reader().get_string();
239 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700240 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700241 } else if (key == kConfigurationKeyMTU) {
242 int mtu = value.reader().get_uint16();
243 if (mtu >= 576) {
244 properties->mtu = mtu;
245 }
246 } else {
247 VLOG(2) << "Key ignored.";
248 }
249 }
250 return true;
251}
252
Darin Petkov92c43902011-06-09 20:46:06 -0700253void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
254 VLOG(2) << "pid " << pid << " exit status " << status;
255 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
256 config->child_watch_tag_ = 0;
Darin Petkov92c43902011-06-09 20:46:06 -0700257 CHECK_EQ(pid, config->pid_);
Darin Petkov92c43902011-06-09 20:46:06 -0700258 config->CleanupClientState();
259
260 // |config| instance may be destroyed after this call.
261 config->provider_->UnbindPID(pid);
262}
263
264void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700265 if (child_watch_tag_) {
266 glib_->SourceRemove(child_watch_tag_);
267 child_watch_tag_ = 0;
268 }
269 if (pid_) {
270 glib_->SpawnClosePID(pid_);
271 pid_ = 0;
272 }
Darin Petkovf9b0ca82011-06-20 12:10:23 -0700273 proxy_.reset();
Darin Petkov92c43902011-06-09 20:46:06 -0700274 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
Darin Petkovf65e9282011-06-21 14:29:56 -0700275 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700276 false);
277 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
Darin Petkovf65e9282011-06-21 14:29:56 -0700278 device_name().c_str())),
Darin Petkov92c43902011-06-09 20:46:06 -0700279 false);
280}
281
Darin Petkov50308cd2011-06-01 18:25:07 -0700282} // namespace shill